logtrace_proc.py 5.7 KB

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