logtrace_proc.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #!/usr/bin/env python
  2. #
  3. import argparse
  4. import struct
  5. import sys
  6. import pylibelf as elf
  7. import pylibelf.util as elfutil
  8. import pylibelf.iterators as elfiter
  9. import pylibelf.constants as elfconst
  10. from ctypes import *
  11. class ESPLogTraceParserError(RuntimeError):
  12. def __init__(self, message):
  13. RuntimeError.__init__(self, message)
  14. class ESPLogTraceRecord(object):
  15. def __init__(self, fmt_addr, log_args):
  16. super(ESPLogTraceRecord, self).__init__()
  17. self.fmt_addr = fmt_addr
  18. self.args = log_args
  19. def __repr__(self):
  20. return "fmt_addr = 0x%x, args = %d/%s" % (self.fmt_addr, len(self.args), self.args)
  21. def logtrace_parse(fname):
  22. ESP32_LOGTRACE_HDR_FMT = '<BL'
  23. ESP32_LOGTRACE_HDR_SZ = struct.calcsize(ESP32_LOGTRACE_HDR_FMT)
  24. recs = []
  25. try:
  26. ftrc = open(fname, 'rb')
  27. except OSError as e:
  28. raise ESPLogTraceParserError("Failed to open trace file (%s)!" % e)
  29. # data_ok = True
  30. while True:
  31. # read args num and format str addr
  32. try:
  33. trc_buf = ftrc.read(ESP32_LOGTRACE_HDR_SZ)
  34. except IOError as e:
  35. raise ESPLogTraceParserError("Failed to read log record header (%s)!" % e)
  36. if len(trc_buf) < ESP32_LOGTRACE_HDR_SZ:
  37. # print "EOF"
  38. if len(trc_buf) > 0:
  39. print "Unprocessed %d bytes of log record header!" % len(trc_buf)
  40. # data_ok = False
  41. break
  42. try:
  43. nargs,fmt_addr = struct.unpack(ESP32_LOGTRACE_HDR_FMT, trc_buf)
  44. except struct.error as e:
  45. raise ESPLogTraceParserError("Failed to unpack log record header (%s)!" % e)
  46. # read args
  47. args_sz = struct.calcsize('<%sL' % nargs)
  48. try:
  49. trc_buf = ftrc.read(args_sz)
  50. except IOError as e:
  51. raise ESPLogTraceParserError("Failed to read log record args (%s)!" % e)
  52. if len(trc_buf) < args_sz:
  53. # print "EOF"
  54. if len(trc_buf) > 0:
  55. print "Unprocessed %d bytes of log record args!" % len(trc_buf)
  56. # data_ok = False
  57. break
  58. try:
  59. log_args = struct.unpack('<%sL' % nargs, trc_buf)
  60. except struct.error as e:
  61. raise ESPLogTraceParserError("Failed to unpack log record args (%s)!" % e)
  62. # print log_args
  63. recs.append(ESPLogTraceRecord(fmt_addr, list(log_args)))
  64. ftrc.close()
  65. # sorted(recs, key=lambda rec: rec.fmt_addr)
  66. return recs
  67. def logtrace_get_str_from_elf(felf, str_addr):
  68. tgt_str = ""
  69. for sect in elfiter.sections(felf):
  70. hdr = elfutil.section_hdr(felf, sect)
  71. if hdr.sh_addr == 0 or hdr.sh_type != elfconst.SHT_PROGBITS:
  72. continue
  73. if str_addr < hdr.sh_addr or str_addr >= hdr.sh_addr + hdr.sh_size:
  74. continue
  75. # print "Found SECT: %x..%x @ %x" % (hdr.sh_addr, hdr.sh_addr + hdr.sh_size, str_addr - hdr.sh_addr)
  76. sec_data = elfiter.getOnlyData(sect).contents
  77. buf = cast(sec_data.d_buf, POINTER(c_char))
  78. for i in range(str_addr - hdr.sh_addr, hdr.sh_size):
  79. if buf[i] == "\0":
  80. break
  81. tgt_str += buf[i]
  82. if len(tgt_str) > 0:
  83. return tgt_str
  84. return None
  85. def logtrace_formated_print(recs, elfname, no_err):
  86. try:
  87. felf = elfutil.open_elf(elfname)
  88. except OSError as e:
  89. raise ESPLogTraceParserError("Failed to open ELF file (%s)!" % e)
  90. for lrec in recs:
  91. fmt_str = logtrace_get_str_from_elf(felf, lrec.fmt_addr)
  92. i = 0
  93. prcnt_idx = 0
  94. while i < len(lrec.args):
  95. prcnt_idx = fmt_str.find('%', prcnt_idx, -2) # TODO: check str ending with %
  96. if prcnt_idx == -1:
  97. break
  98. prcnt_idx += 1 # goto next char
  99. if fmt_str[prcnt_idx] == 's':
  100. # find string
  101. arg_str = logtrace_get_str_from_elf(felf, lrec.args[i])
  102. if arg_str:
  103. lrec.args[i] = arg_str
  104. i += 1
  105. # print "\nFmt = {%s}, args = %d/%s" % lrec
  106. fmt_str = fmt_str.replace('%p', '%x')
  107. # print "=====> " + fmt_str % lrec.args
  108. try:
  109. print fmt_str % tuple(lrec.args),
  110. # print ".",
  111. pass
  112. except Exception as e:
  113. if not no_err:
  114. print "Print error (%s)" % e
  115. print "\nFmt = {%s}, args = %d/%s" % (fmt_str, len(lrec.args), lrec.args)
  116. elf.elf_end(felf)
  117. def main():
  118. parser = argparse.ArgumentParser(description='ESP32 Log Trace Parsing Tool')
  119. parser.add_argument('trace_file', help='Path to log trace file', type=str)
  120. parser.add_argument('elf_file', help='Path to program ELF file', type=str)
  121. # parser.add_argument('--print-details', '-d', help='Print detailed stats', action='store_true')
  122. parser.add_argument('--no-errors', '-n', help='Do not print errors', action='store_true')
  123. args = parser.parse_args()
  124. # parse trace file
  125. try:
  126. print "Parse trace file '%s'..." % args.trace_file
  127. lrecs = logtrace_parse(args.trace_file);
  128. print "Parsing completed."
  129. except ESPLogTraceParserError as e:
  130. print "Failed to parse log trace (%s)!" % e
  131. sys.exit(2)
  132. # print recs
  133. # get format strings and print info
  134. print "===================================================================="
  135. try:
  136. logtrace_formated_print(lrecs, args.elf_file, args.no_errors);
  137. except ESPLogTraceParserError as e:
  138. print "Failed to print log trace (%s)!" % e
  139. sys.exit(2)
  140. print "\n====================================================================\n"
  141. print "Log records count: %d" % len(lrecs)
  142. if __name__ == '__main__':
  143. main()