8 import pylibelf.util as elfutil
9 import pylibelf.iterators as elfiter
10 import pylibelf.constants as elfconst
13 class ESPLogTraceParserError(RuntimeError):
14 def __init__(self, message):
15 RuntimeError.__init__(self, message)
18 class ESPLogTraceRecord(object):
19 def __init__(self, fmt_addr, log_args):
20 super(ESPLogTraceRecord, self).__init__()
21 self.fmt_addr = fmt_addr
25 return "fmt_addr = 0x%x, args = %d/%s" % (self.fmt_addr, len(self.args), self.args)
28 def logtrace_parse(fname):
29 ESP32_LOGTRACE_HDR_FMT = '<BL'
30 ESP32_LOGTRACE_HDR_SZ = struct.calcsize(ESP32_LOGTRACE_HDR_FMT)
34 ftrc = open(fname, 'rb')
36 raise ESPLogTraceParserError("Failed to open trace file (%s)!" % e)
39 # read args num and format str addr
41 trc_buf = ftrc.read(ESP32_LOGTRACE_HDR_SZ)
43 raise ESPLogTraceParserError("Failed to read log record header (%s)!" % e)
44 if len(trc_buf) < ESP32_LOGTRACE_HDR_SZ:
47 print "Unprocessed %d bytes of log record header!" % len(trc_buf)
51 nargs,fmt_addr = struct.unpack(ESP32_LOGTRACE_HDR_FMT, trc_buf)
52 except struct.error as e:
53 raise ESPLogTraceParserError("Failed to unpack log record header (%s)!" % e)
55 args_sz = struct.calcsize('<%sL' % nargs)
57 trc_buf = ftrc.read(args_sz)
59 raise ESPLogTraceParserError("Failed to read log record args (%s)!" % e)
60 if len(trc_buf) < args_sz:
63 print "Unprocessed %d bytes of log record args!" % len(trc_buf)
67 log_args = struct.unpack('<%sL' % nargs, trc_buf)
68 except struct.error as e:
69 raise ESPLogTraceParserError("Failed to unpack log record args (%s)!" % e)
71 recs.append(ESPLogTraceRecord(fmt_addr, list(log_args)))
74 # sorted(recs, key=lambda rec: rec.fmt_addr)
78 def logtrace_get_str_from_elf(felf, str_addr):
80 for sect in elfiter.sections(felf):
81 hdr = elfutil.section_hdr(felf, sect)
82 if hdr.sh_addr == 0 or hdr.sh_type != elfconst.SHT_PROGBITS:
84 if str_addr < hdr.sh_addr or str_addr >= hdr.sh_addr + hdr.sh_size:
86 # print "Found SECT: %x..%x @ %x" % (hdr.sh_addr, hdr.sh_addr + hdr.sh_size, str_addr - hdr.sh_addr)
87 sec_data = elfiter.getOnlyData(sect).contents
88 buf = cast(sec_data.d_buf, POINTER(c_char))
89 for i in range(str_addr - hdr.sh_addr, hdr.sh_size):
97 def logtrace_formated_print(recs, elfname, no_err):
99 felf = elfutil.open_elf(elfname)
101 raise ESPLogTraceParserError("Failed to open ELF file (%s)!" % e)
104 fmt_str = logtrace_get_str_from_elf(felf, lrec.fmt_addr)
107 while i < len(lrec.args):
108 prcnt_idx = fmt_str.find('%', prcnt_idx, -2) # TODO: check str ending with %
111 prcnt_idx += 1 # goto next char
112 if fmt_str[prcnt_idx] == 's':
114 arg_str = logtrace_get_str_from_elf(felf, lrec.args[i])
116 lrec.args[i] = arg_str
118 # print "\nFmt = {%s}, args = %d/%s" % lrec
119 fmt_str = fmt_str.replace('%p', '%x')
120 # print "=====> " + fmt_str % lrec.args
122 print fmt_str % tuple(lrec.args),
125 except Exception as e:
127 print "Print error (%s)" % e
128 print "\nFmt = {%s}, args = %d/%s" % (fmt_str, len(lrec.args), lrec.args)
134 parser = argparse.ArgumentParser(description='ESP32 Log Trace Parsing Tool')
136 parser.add_argument('trace_file', help='Path to log trace file', type=str)
137 parser.add_argument('elf_file', help='Path to program ELF file', type=str)
138 # parser.add_argument('--print-details', '-d', help='Print detailed stats', action='store_true')
139 parser.add_argument('--no-errors', '-n', help='Do not print errors', action='store_true')
140 args = parser.parse_args()
144 print "Parse trace file '%s'..." % args.trace_file
145 lrecs = logtrace_parse(args.trace_file);
146 print "Parsing completed."
147 except ESPLogTraceParserError as e:
148 print "Failed to parse log trace (%s)!" % e
151 # get format strings and print info
152 print "===================================================================="
154 logtrace_formated_print(lrecs, args.elf_file, args.no_errors);
155 except ESPLogTraceParserError as e:
156 print "Failed to print log trace (%s)!" % e
158 print "\n====================================================================\n"
160 print "Log records count: %d" % len(lrecs)
162 if __name__ == '__main__':