import os
import gdb
from array import array
'''
  Pouziti v arm-none-eabi-gdb:
    source stack.py
    stack_usage
'''
tmp_filename = 'stack.tmp'
# Vraci hodnoty symbolu definovanych v linker scriptu
def get_sym (sym):
  res = gdb.execute ('p/x &' + sym, to_string=True);
  res = res.strip ()
  res = res.split ('=')
  if len(res) < 2: return 0
  value = int (res[1], 16);
  return value
# Jen pro uplnost, neni celkem potreba
def data_usage ():
  DataBegin   = get_sym ('_sdata')
  DataEnd     = get_sym ('_edata')
  DataLen = DataEnd - DataBegin
  BssBegin    = get_sym ('_sbss')
  BssEnd      = get_sym ('_ebss')
  BssLen = BssEnd - BssBegin
  gdb.write   ('Data Total: \x1B[34;1m0x%08X \x1B[33;1m(%6d)\x1B[0m bytes\n' % (DataLen, DataLen))
  gdb.write   ('Bss  Total: \x1B[34;1m0x%08X \x1B[33;1m(%6d)\x1B[0m bytes\n' % (BssLen, BssLen))
'''
# Spocte pocet pouzitych slov ve stacku (ze souboru zjisti posledni DEADBEEF)
def parse_file (name, size):
  info = os.stat (name)
  if size != info.st_size: return 0
  file = open (name, 'r')
  data = array('I')
  size = size / 4 # size from bytes to words
  data.fromfile (file, size);
  file.close()
  count = 0
  for word in data:
    if word != 0xdeadbeef: break
    count += 1
  return size - count # in words
# Vypsat data do souboru je nekdy lepsi - muzeme je podrobne zkoumat
def method_file (adr_from, adr_to):
  command = 'dump memory %s 0x%08X 0x%08X' % (tmp_filename, adr_from, adr_to)
  print command
  gdb.execute (command)
  return parse_file (tmp_filename, adr_to - adr_from) * 4
'''
def value_at (addr):
  res = gdb.execute ('x/x 0x%08X' % (addr), to_string=True);
  res = res.strip ()
  res = res.split (':')
  if len(res) < 2: return 0
  value = int (res[1], 16);
  return value
# Hledame nejvetsi prvek souvisle mnoziny slov DEADBEEF
def bisection (a, b):
  if (b - a) <= 4 : return b
  c = int(a + b) >> 1
  c = c & ~0x3
  if value_at (c) == 0xdeadbeef:
    return bisection(c, b)
  else:
    return bisection(a, c)
# Pro vetsi RAM je lepe pouzit (rekurzivni) bisekci, DEADBEEF je vzdy ve spodni casti
def method_bisect (adr_from, adr_to):
  adr_end = bisection (adr_from, adr_to)
  return adr_to - adr_end

def stack_usage ():
  data_usage()  # mozno zakomentovat, pokud ukazuje blbosti
  StackTop    = get_sym ('_estack')
  StackBottom = get_sym ('_ebss')
  if StackTop    == 0: return
  if StackBottom == 0: return
  StackTotal  = StackTop - StackBottom
  gdb.write ('StackTotal: \x1B[34;1m0x%08X \x1B[33;1m(%6d)\x1B[0m bytes\n' % (StackTotal, StackTotal))
  StackUsed = method_bisect (StackBottom, StackTop)
  gdb.write ('Stack Used: \x1B[34;1m0x%08X \x1B[33;1m(%6d)\x1B[0m bytes\n' % (StackUsed, StackUsed))

class StackUsageCommand (gdb.Command):
  "Stack Usage, method DEADBEEF"
  def __init__ (self):
    super (StackUsageCommand, self).__init__ ("stack_usage",
                         gdb.COMMAND_SUPPORT,
                         gdb.COMPLETE_NONE, True)
  def invoke (self, arg, from_tty):
    stack_usage ()

StackUsageCommand()