logtrace_proc.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. #!/usr/bin/env python
  2. # SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. # SPDX-License-Identifier: Apache-2.0
  4. #
  5. from __future__ import print_function
  6. import argparse
  7. import struct
  8. import sys
  9. import elftools.elf.elffile as elffile
  10. import espytrace.apptrace as apptrace
  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_formated_print(recs, elfname, no_err):
  68. try:
  69. felf = elffile.ELFFile(open(elfname, 'rb'))
  70. except OSError as e:
  71. raise ESPLogTraceParserError('Failed to open ELF file (%s)!' % e)
  72. for lrec in recs:
  73. fmt_str = apptrace.get_str_from_elf(felf, lrec.fmt_addr)
  74. i = 0
  75. prcnt_idx = 0
  76. while i < len(lrec.args):
  77. prcnt_idx = fmt_str.find('%', prcnt_idx, -2) # TODO: check str ending with %
  78. if prcnt_idx == -1:
  79. break
  80. prcnt_idx += 1 # goto next char
  81. if fmt_str[prcnt_idx] == 's':
  82. # find string
  83. arg_str = apptrace.get_str_from_elf(felf, lrec.args[i])
  84. if arg_str:
  85. lrec.args[i] = arg_str
  86. i += 1
  87. # print("\nFmt = {%s}, args = %d/%s" % lrec)
  88. fmt_str = fmt_str.replace('%p', '%x')
  89. # print("=====> " + fmt_str % lrec.args)
  90. try:
  91. print(fmt_str % tuple(lrec.args), end='')
  92. # print(".", end='')
  93. pass
  94. except Exception as e:
  95. if not no_err:
  96. print('Print error (%s)' % e)
  97. print('\nFmt = {%s}, args = %d/%s' % (fmt_str, len(lrec.args), lrec.args))
  98. felf.stream.close()
  99. def main():
  100. parser = argparse.ArgumentParser(description='ESP32 Log Trace Parsing Tool')
  101. parser.add_argument('trace_file', help='Path to log trace file', type=str)
  102. parser.add_argument('elf_file', help='Path to program ELF file', type=str)
  103. # parser.add_argument('--print-details', '-d', help='Print detailed stats', action='store_true')
  104. parser.add_argument('--no-errors', '-n', help='Do not print errors', action='store_true')
  105. args = parser.parse_args()
  106. # parse trace file
  107. try:
  108. print("Parse trace file '%s'..." % args.trace_file)
  109. lrecs = logtrace_parse(args.trace_file)
  110. print('Parsing completed.')
  111. except ESPLogTraceParserError as e:
  112. print('Failed to parse log trace (%s)!' % e)
  113. sys.exit(2)
  114. # print recs
  115. # get format strings and print info
  116. print('====================================================================')
  117. try:
  118. logtrace_formated_print(lrecs, args.elf_file, args.no_errors)
  119. except ESPLogTraceParserError as e:
  120. print('Failed to print log trace (%s)!' % e)
  121. sys.exit(2)
  122. print('\n====================================================================\n')
  123. print('Log records count: %d' % len(lrecs))
  124. if __name__ == '__main__':
  125. main()