diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2016-05-25 11:37:40 -0400 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2016-05-25 11:37:40 -0400 |
commit | f582a36e4df16d5709943f7df17a900c8bcc12ab (patch) | |
tree | 628d927c4f3e19e54618f7f47c7a44af66bf0c2f /scripts | |
parent | 37a91e9c10648208de002c75df304e23ca89e256 (diff) | |
download | kutter-f582a36e4df16d5709943f7df17a900c8bcc12ab.tar.gz kutter-f582a36e4df16d5709943f7df17a900c8bcc12ab.tar.xz kutter-f582a36e4df16d5709943f7df17a900c8bcc12ab.zip |
Initial commit of source code.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/avrsim.py | 212 | ||||
-rw-r--r-- | scripts/buildcommands.py | 313 | ||||
-rwxr-xr-x | scripts/checkstack.py | 238 |
3 files changed, 763 insertions, 0 deletions
diff --git a/scripts/avrsim.py b/scripts/avrsim.py new file mode 100755 index 00000000..bbcc20b7 --- /dev/null +++ b/scripts/avrsim.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python +# Script to interact with simulavr by simulating a serial port. +# +# Copyright (C) 2015 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import sys, optparse, os, pty, select, fcntl, termios, traceback, errno +import pysimulavr + +SERIALBITS = 10 # 8N1 = 1 start, 8 data, 1 stop + +# Class to read serial data from AVR serial transmit pin. +class SerialRxPin(pysimulavr.PySimulationMember, pysimulavr.Pin): + def __init__(self, baud): + pysimulavr.Pin.__init__(self) + pysimulavr.PySimulationMember.__init__(self) + self.sc = pysimulavr.SystemClock.Instance() + self.delay = 10**9 / baud + self.current = 0 + self.pos = -1 + self.queue = "" + def SetInState(self, pin): + pysimulavr.Pin.SetInState(self, pin) + self.state = pin.outState + if self.pos < 0 and pin.outState == pin.LOW: + self.pos = 0 + self.sc.Add(self) + def DoStep(self, trueHwStep): + ishigh = self.state == self.HIGH + self.current |= ishigh << self.pos + self.pos += 1 + if self.pos == 1: + return int(self.delay * 1.5) + if self.pos >= SERIALBITS: + self.queue += chr((self.current >> 1) & 0xff) + self.pos = -1 + self.current = 0 + return -1 + return self.delay + def popChars(self): + d = self.queue + self.queue = "" + return d + +# Class to send serial data to AVR serial receive pin. +class SerialTxPin(pysimulavr.PySimulationMember, pysimulavr.Pin): + MAX_QUEUE = 64 + def __init__(self, baud): + pysimulavr.Pin.__init__(self) + pysimulavr.PySimulationMember.__init__(self) + self.SetPin('H') + self.sc = pysimulavr.SystemClock.Instance() + self.delay = 10**9 / baud + self.current = 0 + self.pos = 0 + self.queue = "" + def DoStep(self, trueHwStep): + if not self.pos: + if not self.queue: + return -1 + self.current = (ord(self.queue[0]) << 1) | 0x200 + self.queue = self.queue[1:] + newstate = 'L' + if self.current & (1 << self.pos): + newstate = 'H' + self.SetPin(newstate) + self.pos += 1 + if self.pos >= SERIALBITS: + self.pos = 0 + return self.delay + def needChars(self): + if len(self.queue) > self.MAX_QUEUE / 2: + return 0 + return self.MAX_QUEUE - len(self.queue) + def pushChars(self, c): + queueEmpty = not self.queue + self.queue += c + if queueEmpty: + self.sc.Add(self) + +# Support for creating VCD trace files +class Tracing: + def __init__(self, filename, signals): + self.filename = filename + self.signals = signals + if not signals: + self.dman = None + return + self.dman = pysimulavr.DumpManager.Instance() + self.dman.SetSingleDeviceApp() + def show_help(self): + ostr = pysimulavr.ostringstream() + self.dman.save(ostr) + sys.stdout.write(ostr.str()) + sys.exit(1) + def load_options(self): + if self.dman is None: + return + if self.signals.strip() == '?': + self.show_help() + sigs = "\n".join(["+ " + s for s in self.signals.split(',')]) + self.dman.addDumpVCD(self.filename, sigs, "ns", False, False) + def start(self): + if self.dman is not None: + self.dman.start() + def finish(self): + if self.dman is not None: + self.dman.stopApplication() + +# Support for creating a pseudo-tty for emulating a serial port +def create_pty(ptyname): + mfd, sfd = pty.openpty() + try: + os.unlink(ptyname) + except os.error: + pass + os.symlink(os.ttyname(sfd), ptyname) + fcntl.fcntl(mfd, fcntl.F_SETFL + , fcntl.fcntl(mfd, fcntl.F_GETFL) | os.O_NONBLOCK) + old = termios.tcgetattr(mfd) + old[3] = old[3] & ~termios.ECHO + termios.tcsetattr(mfd, termios.TCSADRAIN, old) + return mfd + +def main(): + usage = "%prog [options] <program.elf>" + opts = optparse.OptionParser(usage) + opts.add_option("-m", "--machine", type="string", dest="machine", + default="atmega644", help="type of AVR machine to simulate") + opts.add_option("-s", "--speed", type="int", dest="speed", default=8000000, + help="machine speed") + opts.add_option("-b", "--baud", type="int", dest="baud", default=38400, + help="baud rate of the emulated serial port") + opts.add_option("-t", "--trace", type="string", dest="trace", + help="signals to trace (? for help)") + opts.add_option("-p", "--port", type="string", dest="port", + default="/tmp/pseudoserial", + help="pseudo-tty device to create for serial port") + deffile = os.path.splitext(os.path.basename(sys.argv[0]))[0] + ".vcd" + opts.add_option("-f", "--tracefile", type="string", dest="tracefile", + default=deffile, help="filename to write signal trace to") + options, args = opts.parse_args() + if len(args) != 1: + opts.error("Incorrect number of arguments") + elffile = args[0] + proc = options.machine + ptyname = options.port + speed = options.speed + baud = options.baud + + # launch simulator + sc = pysimulavr.SystemClock.Instance() + trace = Tracing(options.tracefile, options.trace) + dev = pysimulavr.AvrFactory.instance().makeDevice(proc) + dev.Load(elffile) + dev.SetClockFreq(10**9 / speed) + sc.Add(dev) + trace.load_options() + + # Setup rx pin + rxpin = SerialRxPin(baud) + net = pysimulavr.Net() + net.Add(rxpin) + net.Add(dev.GetPin("D1")) + + # Setup tx pin + txpin = SerialTxPin(baud) + net2 = pysimulavr.Net() + net2.Add(dev.GetPin("D0")) + net2.Add(txpin) + + # Display start banner + msg = "Starting AVR simulation: machine=%s speed=%d\n" % (proc, speed) + msg += "Serial: port=%s baud=%d\n" % (ptyname, baud) + if options.trace: + msg += "Trace file: %s\n" % (options.tracefile,) + sys.stdout.write(msg) + sys.stdout.flush() + + # Create terminal device + fd = create_pty(ptyname) + + # Run loop + try: + trace.start() + while 1: + starttime = sc.GetCurrentTime() + r = sc.RunTimeRange(speed/1000) + endtime = sc.GetCurrentTime() + if starttime == endtime: + break + d = rxpin.popChars() + if d: + os.write(fd, d) + txsize = txpin.needChars() + if txsize: + res = select.select([fd], [], [], 0) + if res[0]: + try: + d = os.read(fd, txsize) + except os.error, e: + if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK): + continue + break + txpin.pushChars(d) + trace.finish() + finally: + os.unlink(ptyname) + +if __name__ == '__main__': + main() diff --git a/scripts/buildcommands.py b/scripts/buildcommands.py new file mode 100644 index 00000000..8b9cc9d8 --- /dev/null +++ b/scripts/buildcommands.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python +# Script to handle build time requests embedded in C code. +# +# Copyright (C) 2016 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import sys, os, subprocess, optparse, logging, shlex, socket, time +import json, zlib +sys.path.append('./klippy') +import msgproto + +FILEHEADER = """ +/* DO NOT EDIT! This is an autogenerated file. See scripts/buildcommands.py. */ + +#include "board/pgm.h" +#include "command.h" +""" + +def error(msg): + sys.stderr.write(msg + "\n") + sys.exit(-1) + +# Parser for constants in simple C header files. +def scan_config(file): + f = open(file, 'r') + opts = {} + for l in f.readlines(): + parts = l.split() + if len(parts) != 3: + continue + if parts[0] != '#define': + continue + value = parts[2] + if value.isdigit() or (value.startswith('0x') and value[2:].isdigit()): + value = int(value, 0) + elif value.startswith('"'): + value = value[1:-1] + opts[parts[1]] = value + f.close() + return opts + + +###################################################################### +# Command and output parser generation +###################################################################### + +def build_parser(parser, iscmd, all_param_types): + if parser.name == "#empty": + return "\n // Empty message\n .max_size=0," + if parser.name == "#output": + comment = "Output: " + parser.msgformat + else: + comment = parser.msgformat + params = '0' + types = tuple([t.__class__.__name__ for t in parser.param_types]) + if types: + paramid = all_param_types.get(types) + if paramid is None: + paramid = len(all_param_types) + all_param_types[types] = paramid + params = 'command_parameters%d' % (paramid,) + out = """ + // %s + .msg_id=%d, + .num_params=%d, + .param_types = %s, +""" % (comment, parser.msgid, len(types), params) + if iscmd: + num_args = (len(types) + types.count('PT_progmem_buffer') + + types.count('PT_buffer')) + out += " .num_args=%d," % (num_args,) + else: + max_size = min(msgproto.MESSAGE_MAX + , 1 + sum([t.max_length for t in parser.param_types])) + out += " .max_size=%d," % (max_size,) + return out + +def build_parsers(parsers, msg_to_id, all_param_types): + pcode = [] + for msgname, msg in parsers: + msgid = msg_to_id[msg] + if msgname is None: + parser = msgproto.OutputFormat(msgid, msg) + else: + parser = msgproto.MessageFormat(msgid, msg) + parsercode = build_parser(parser, 0, all_param_types) + pcode.append("{%s\n}, " % (parsercode,)) + fmt = """ +const struct command_encoder command_encoders[] PROGMEM = { +%s +}; +""" + return fmt % ("".join(pcode).strip(),) + +def build_param_types(all_param_types): + sorted_param_types = sorted([(i, a) for a, i in all_param_types.items()]) + params = [''] + for paramid, argtypes in sorted_param_types: + params.append( + 'static const uint8_t command_parameters%d[] PROGMEM = {\n' + ' %s };' % ( + paramid, ', '.join(argtypes),)) + params.append('') + return "\n".join(params) + +def build_commands(cmd_by_id, messages_by_name, all_param_types): + max_cmd_msgid = max(cmd_by_id.keys()) + index = [] + parsers = [] + externs = {} + for msgid in range(max_cmd_msgid+1): + if msgid not in cmd_by_id: + index.append(" 0,") + continue + funcname, flags, msgname = cmd_by_id[msgid] + msg = messages_by_name[msgname] + externs[funcname] = 1 + parsername = 'parser_%s' % (funcname,) + index.append(" &%s," % (parsername,)) + parser = msgproto.MessageFormat(msgid, msg) + parsercode = build_parser(parser, 1, all_param_types) + parsers.append("const struct command_parser %s PROGMEM = {" + " %s\n .flags=%s,\n .func=%s\n};" % ( + parsername, parsercode, flags, funcname)) + index = "\n".join(index) + externs = "\n".join(["extern void "+funcname+"(uint32_t*);" + for funcname in sorted(externs)]) + fmt = """ +%s + +%s + +const struct command_parser * const command_index[] PROGMEM = { +%s +}; + +const uint8_t command_index_size PROGMEM = ARRAY_SIZE(command_index); +""" + return fmt % (externs, '\n'.join(parsers), index) + + +###################################################################### +# Identify data dictionary generation +###################################################################### + +def build_identify(cmd_by_id, msg_to_id, responses, static_strings + , config, version): + #commands, messages, static_strings + messages = dict((msgid, msg) for msg, msgid in msg_to_id.items()) + data = {} + data['messages'] = messages + data['commands'] = sorted(cmd_by_id.keys()) + data['responses'] = sorted(responses) + data['static_strings'] = static_strings + configlist = ['MCU', 'CLOCK_FREQ'] + data['config'] = dict((i, config['CONFIG_'+i]) for i in configlist + if 'CONFIG_'+i in config) + data['version'] = version + + # Format compressed info into C code + data = json.dumps(data) + zdata = zlib.compress(data, 9) + out = [] + for i in range(len(zdata)): + if i % 8 == 0: + out.append('\n ') + out.append(" 0x%02x," % (ord(zdata[i]),)) + fmt = """ +const uint8_t command_identify_data[] PROGMEM = {%s +}; + +// Identify size = %d (%d uncompressed) +const uint32_t command_identify_size PROGMEM = ARRAY_SIZE(command_identify_data); +""" + return fmt % (''.join(out), len(zdata), len(data)) + + +###################################################################### +# Version generation +###################################################################### + +# Run program and return the specified output +def check_output(prog): + logging.debug("Running %s" % (repr(prog),)) + try: + process = subprocess.Popen(shlex.split(prog), stdout=subprocess.PIPE) + output = process.communicate()[0] + retcode = process.poll() + except OSError: + logging.debug("Exception on run: %s" % (traceback.format_exc(),)) + return "" + logging.debug("Got (code=%s): %s" % (retcode, repr(output))) + if retcode: + return "" + try: + return output.decode() + except UnicodeError: + logging.debug("Exception on decode: %s" % (traceback.format_exc(),)) + return "" + +# Obtain version info from "git" program +def git_version(): + if not os.path.exists('.git'): + logging.debug("No '.git' file/directory found") + return "" + ver = check_output("git describe --tags --long --dirty").strip() + logging.debug("Got git version: %s" % (repr(ver),)) + return ver + +def build_version(extra): + version = git_version() + if not version: + version = "?" + btime = time.strftime("%Y%m%d_%H%M%S") + hostname = socket.gethostname() + version = "%s-%s-%s%s" % (version, btime, hostname, extra) + return version + + +###################################################################### +# Main code +###################################################################### + +def main(): + usage = "%prog [options] <cmd section file> <autoconf.h> <output.c>" + opts = optparse.OptionParser(usage) + opts.add_option("-e", "--extra", dest="extra", default="", + help="extra version string to append to version") + opts.add_option("-v", action="store_true", dest="verbose", + help="enable debug messages") + + options, args = opts.parse_args() + if len(args) != 3: + opts.error("Incorrect arguments") + incmdfile, inheader, outcfile = args + if options.verbose: + logging.basicConfig(level=logging.DEBUG) + + # Setup + commands = {} + messages_by_name = dict((m.split()[0], m) + for m in msgproto.DefaultMessages.values()) + parsers = [] + static_strings = [] + # Parse request file + f = open(incmdfile, 'rb') + data = f.read() + f.close() + for req in data.split('\0'): + req = req.lstrip() + parts = req.split() + if not parts: + continue + cmd = parts[0] + msg = req[len(cmd)+1:] + if cmd == '_DECL_COMMAND': + funcname, flags, msgname = parts[1:4] + if msgname in commands: + error("Multiple definitions for command '%s'" % msgname) + commands[msgname] = (funcname, flags, msgname) + msg = req.split(None, 3)[3] + m = messages_by_name.get(msgname) + if m is not None and m != msg: + error("Conflicting definition for command '%s'" % msgname) + messages_by_name[msgname] = msg + elif cmd == '_DECL_PARSER': + if len(parts) == 1: + msgname = msg = "#empty" + else: + msgname = parts[1] + m = messages_by_name.get(msgname) + if m is not None and m != msg: + error("Conflicting definition for message '%s'" % msgname) + messages_by_name[msgname] = msg + parsers.append((msgname, msg)) + elif cmd == '_DECL_OUTPUT': + parsers.append((None, msg)) + elif cmd == '_DECL_STATIC_STR': + static_strings.append(req[17:]) + else: + error("Unknown build time command '%s'" % cmd) + # Create unique ids for each message type + msgid = max(msgproto.DefaultMessages.keys()) + msg_to_id = dict((m, i) for i, m in msgproto.DefaultMessages.items()) + for msgname in commands.keys() + [m for n, m in parsers]: + msg = messages_by_name.get(msgname, msgname) + if msg not in msg_to_id: + msgid += 1 + msg_to_id[msg] = msgid + # Create message definitions + all_param_types = {} + parsercode = build_parsers(parsers, msg_to_id, all_param_types) + # Create command definitions + cmd_by_id = dict((msg_to_id[messages_by_name.get(msgname, msgname)], cmd) + for msgname, cmd in commands.items()) + cmdcode = build_commands(cmd_by_id, messages_by_name, all_param_types) + paramcode = build_param_types(all_param_types) + # Create identify information + config = scan_config(inheader) + version = build_version(options.extra) + sys.stdout.write("Version: %s\n" % (version,)) + responses = [msg_to_id[msg] for msgname, msg in messages_by_name.items() + if msgname not in commands] + icode = build_identify(cmd_by_id, msg_to_id, responses, static_strings + , config, version) + # Write output + f = open(outcfile, 'wb') + f.write(FILEHEADER + paramcode + parsercode + cmdcode + icode) + f.close() + +if __name__ == '__main__': + main() diff --git a/scripts/checkstack.py b/scripts/checkstack.py new file mode 100755 index 00000000..d4f58cf3 --- /dev/null +++ b/scripts/checkstack.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python +# Script that tries to find how much stack space each function in an +# object is using. +# +# Copyright (C) 2015 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +# Usage: +# avr-objdump -d out/klipper.elf | scripts/checkstack.py + +import sys +import re + +# Functions that change stacks +STACKHOP = [] +# List of functions we can assume are never called. +IGNORE = [] + +OUTPUTDESC = """ +#funcname1[preamble_stack_usage,max_usage_with_callers]: +# insn_addr:called_function [usage_at_call_point+caller_preamble,total_usage] +# +#funcname2[p,m,max_usage_to_yield_point]: +# insn_addr:called_function [u+c,t,usage_to_yield_point] +""" + +class function: + def __init__(self, funcaddr, funcname): + self.funcaddr = funcaddr + self.funcname = funcname + self.basic_stack_usage = 0 + self.max_stack_usage = None + self.yield_usage = -1 + self.max_yield_usage = None + self.total_calls = 0 + # called_funcs = [(insnaddr, calladdr, stackusage), ...] + self.called_funcs = [] + self.subfuncs = {} + # Update function info with a found "yield" point. + def noteYield(self, stackusage): + if self.yield_usage < stackusage: + self.yield_usage = stackusage + # Update function info with a found "call" point. + def noteCall(self, insnaddr, calladdr, stackusage): + if (calladdr, stackusage) in self.subfuncs: + # Already noted a nearly identical call - ignore this one. + return + self.called_funcs.append((insnaddr, calladdr, stackusage)) + self.subfuncs[(calladdr, stackusage)] = 1 + +# Find out maximum stack usage for a function +def calcmaxstack(info, funcs): + if info.max_stack_usage is not None: + return + info.max_stack_usage = max_stack_usage = info.basic_stack_usage + info.max_yield_usage = max_yield_usage = info.yield_usage + total_calls = 0 + seenbefore = {} + # Find max of all nested calls. + for insnaddr, calladdr, usage in info.called_funcs: + callinfo = funcs.get(calladdr) + if callinfo is None: + continue + calcmaxstack(callinfo, funcs) + if callinfo.funcname not in seenbefore: + seenbefore[callinfo.funcname] = 1 + total_calls += callinfo.total_calls + 1 + funcnameroot = callinfo.funcname.split('.')[0] + if funcnameroot in IGNORE: + # This called function is ignored - don't contribute it to + # the max stack. + continue + totusage = usage + callinfo.max_stack_usage + totyieldusage = usage + callinfo.max_yield_usage + if funcnameroot in STACKHOP: + # Don't count children of this function + totusage = totyieldusage = usage + if totusage > max_stack_usage: + max_stack_usage = totusage + if callinfo.max_yield_usage >= 0 and totyieldusage > max_yield_usage: + max_yield_usage = totyieldusage + info.max_stack_usage = max_stack_usage + info.max_yield_usage = max_yield_usage + info.total_calls = total_calls + +# Try to arrange output so that functions that call each other are +# near each other. +def orderfuncs(funcaddrs, availfuncs): + l = [(availfuncs[funcaddr].total_calls + , availfuncs[funcaddr].funcname, funcaddr) + for funcaddr in funcaddrs if funcaddr in availfuncs] + l.sort() + l.reverse() + out = [] + while l: + count, name, funcaddr = l.pop(0) + info = availfuncs.get(funcaddr) + if info is None: + continue + calladdrs = [calls[1] for calls in info.called_funcs] + del availfuncs[funcaddr] + out = out + orderfuncs(calladdrs, availfuncs) + [info] + return out + +hex_s = r'[0-9a-f]+' +re_func = re.compile(r'^(?P<funcaddr>' + hex_s + r') <(?P<func>.*)>:$') +re_asm = re.compile( + r'^[ ]*(?P<insnaddr>' + hex_s + + r'):\t[^\t]*\t(?P<insn>[^\t]+?)(?P<params>\t[^;]*)?' + + r'[ ]*(; (?P<calladdr>0x' + hex_s + + r') <(?P<ref>.*)>)?$') + +def main(): + unknownfunc = function(None, "<unknown>") + indirectfunc = function(-1, '<indirect>') + unknownfunc.max_stack_usage = indirectfunc.max_stack_usage = 0 + unknownfunc.max_yield_usage = indirectfunc.max_yield_usage = -1 + funcs = {-1: indirectfunc} + funcaddr = None + datalines = {} + cur = None + atstart = 0 + stackusage = 0 + + # Parse input lines + for line in sys.stdin.readlines(): + m = re_func.match(line) + if m is not None: + # Found function + funcaddr = int(m.group('funcaddr'), 16) + funcs[funcaddr] = cur = function(funcaddr, m.group('func')) + stackusage = 0 + atstart = 1 + continue + m = re_asm.match(line) + if m is None: + if funcaddr not in datalines: + datalines[funcaddr] = line.split() + #print("other", repr(line)) + continue + insn = m.group('insn') + + if insn == 'push': + stackusage += 1 + continue + if insn == 'rcall' and m.group('params').strip() == '.+0': + stackusage += 2 + continue + + if atstart: + if insn in ['in', 'eor']: + continue + cur.basic_stack_usage = stackusage + atstart = 0 + + insnaddr = m.group('insnaddr') + calladdr = m.group('calladdr') + if calladdr is None: + if insn == 'ijmp': + # Indirect tail call + cur.noteCall(insnaddr, -1, 0) + elif insn == 'icall': + cur.noteCall(insnaddr, -1, stackusage + 2) + else: + # misc instruction + continue + else: + # Jump or call insn + calladdr = int(calladdr, 16) + ref = m.group('ref') + if '+' in ref: + # Inter-function jump. + pass + elif insn in ('rjmp', 'jmp', 'brne', 'brcs'): + # Tail call + cur.noteCall(insnaddr, calladdr, 0) + elif insn in ('rcall', 'call'): + cur.noteCall(insnaddr, calladdr, stackusage + 2) + else: + print("unknown call", ref) + cur.noteCall(insnaddr, calladdr, stackusage) + # Reset stack usage to preamble usage + stackusage = cur.basic_stack_usage + + # Update for known indirect functions + funcsbyname = {} + for info in funcs.values(): + funcnameroot = info.funcname.split('.')[0] + funcsbyname[funcnameroot] = info + mainfunc = funcsbyname.get('sched_main') + cmdfunc = funcsbyname.get('command_task') + eventfunc = funcsbyname.get('__vector_13') + for funcnameroot, info in funcsbyname.items(): + if (funcnameroot.startswith('_DECL_taskfuncs_') + or funcnameroot.startswith('_DECL_initfuncs_') + or funcnameroot.startswith('_DECL_shutdownfuncs_')): + funcname = funcnameroot[funcnameroot.index('_', 7)+1:] + f = funcsbyname[funcname] + mainfunc.noteCall(0, f.funcaddr, mainfunc.basic_stack_usage + 2) + if funcnameroot.startswith('parser_'): + f = funcsbyname.get(funcnameroot[7:]) + if f is not None: + numparams = int(datalines[info.funcaddr][2], 16) + stackusage = cmdfunc.basic_stack_usage + 2 + numparams * 4 + cmdfunc.noteCall(0, f.funcaddr, stackusage) + if funcnameroot.endswith('_event'): + eventfunc.noteCall(0, info.funcaddr, eventfunc.basic_stack_usage + 2) + + # Calculate maxstackusage + for info in funcs.values(): + calcmaxstack(info, funcs) + + # Sort functions for output + funcinfos = orderfuncs(funcs.keys(), funcs.copy()) + + # Show all functions + print(OUTPUTDESC) + for info in funcinfos: + if info.max_stack_usage == 0 and info.max_yield_usage < 0: + continue + yieldstr = "" + if info.max_yield_usage >= 0: + yieldstr = ",%d" % info.max_yield_usage + print("\n%s[%d,%d%s]:" % (info.funcname, info.basic_stack_usage + , info.max_stack_usage, yieldstr)) + for insnaddr, calladdr, stackusage in info.called_funcs: + callinfo = funcs.get(calladdr, unknownfunc) + yieldstr = "" + if callinfo.max_yield_usage >= 0: + yieldstr = ",%d" % (stackusage + callinfo.max_yield_usage) + print(" %04s:%-40s [%d+%d,%d%s]" % ( + insnaddr, callinfo.funcname, stackusage + , callinfo.basic_stack_usage + , stackusage+callinfo.max_stack_usage, yieldstr)) + +if __name__ == '__main__': + main() |