dump.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import sys
  2. import re
  3. import enum
  4. import struct
  5. from . import avr
  6. FILL_BYTE = b'\0' # used to fill memory gaps in the dump
  7. DUMP_MAGIC = 0x55525547 # XFLASH dump magic
  8. DUMP_OFFSET = 0x3d000 # XFLASH dump offset
  9. DUMP_SIZE = 0x2300 # XFLASH dump size
  10. class CrashReason(enum.IntEnum):
  11. MANUAL = 0
  12. STACK_ERROR = 1
  13. WATCHDOG = 2
  14. BAD_ISR = 3
  15. class Dump():
  16. def __init__(self, typ, reason, regs, pc, sp, data, ranges):
  17. self.typ = typ
  18. self.reason = reason
  19. self.regs = regs
  20. self.pc = pc
  21. self.sp = sp
  22. self.data = data
  23. self.ranges = ranges
  24. # expand the buffer identified by addr+data to fill the region start+size
  25. def region_expand(addr, data, start, size):
  26. if start < addr:
  27. data = FILL_BYTE * (addr - start) + data
  28. addr = start
  29. end = start + size
  30. data_end = addr + len(data)
  31. if end > data_end:
  32. data += FILL_BYTE * (data_end - end)
  33. return addr, data
  34. def merge_ranges(ranges):
  35. ranges = list(sorted(ranges, key=lambda x: x[0]))
  36. if len(ranges) < 2:
  37. return ranges
  38. ret = [ranges[0]]
  39. for cur in ranges[1:]:
  40. last = ret[-1]
  41. last_end = last[0] + last[1]
  42. if last_end < cur[0]:
  43. ret.append(cur)
  44. else:
  45. cur_end = cur[0] + cur[1]
  46. last = (last[0], max(last_end, cur_end) - last[0])
  47. ret[-1] = last
  48. return ret
  49. def decode_dump(path):
  50. fd = open(path, 'r')
  51. if fd is None:
  52. return None
  53. buf_addr = None # starting address
  54. buf_data = None # data
  55. typ = None # dump type
  56. reason = None # crash reason
  57. regs = None # registers present
  58. pc = None # PC address
  59. sp = None # SP address
  60. ranges = [] # dumped ranges
  61. in_dump = False
  62. for line in enumerate(fd):
  63. line = (line[0], line[1].rstrip())
  64. tokens = line[1].split(maxsplit=1)
  65. def line_error():
  66. print('malformed line {}: {}'.format(*line), file=sys.stderr)
  67. # handle metadata
  68. if not in_dump:
  69. if len(tokens) > 0 and tokens[0] in ['D2', 'D21', 'D23']:
  70. in_dump = True
  71. typ = tokens[0]
  72. continue
  73. else:
  74. if len(tokens) == 0:
  75. line_error()
  76. continue
  77. elif tokens[0] == 'ok':
  78. break
  79. elif tokens[0] == 'error:' and len(tokens) == 2:
  80. values = tokens[1].split(' ')
  81. if typ == 'D23' and len(values) >= 3:
  82. reason = CrashReason(int(values[0], 0))
  83. pc = int(values[1], 0)
  84. sp = int(values[2], 0)
  85. else:
  86. line_error()
  87. continue
  88. elif len(tokens) != 2 or not re.match(r'^[0-9a-fA-F]+$', tokens[0]):
  89. line_error()
  90. continue
  91. # decode hex data
  92. addr = int.from_bytes(bytes.fromhex(tokens[0]), 'big')
  93. data = bytes.fromhex(tokens[1])
  94. ranges.append((addr, len(data)))
  95. if buf_addr is None:
  96. buf_addr = addr
  97. buf_data = data
  98. else:
  99. # grow buffer as needed
  100. buf_addr, buf_data = region_expand(buf_addr, buf_data,
  101. addr, len(data))
  102. # replace new part
  103. rep_start = addr - buf_addr
  104. rep_end = rep_start + len(data)
  105. buf_data = buf_data[:rep_start] + data + buf_data[rep_end:]
  106. # merge continuous ranges
  107. ranges = merge_ranges(ranges)
  108. if typ == 'D2':
  109. # D2 doesn't guarantee registers to be present
  110. regs = len(ranges) > 0 and \
  111. ranges[0][0] == 0 and \
  112. ranges[0][1] >= avr.SRAM_START
  113. # fill to fit for easy loading
  114. buf_addr, buf_data = region_expand(
  115. buf_addr, buf_data, 0, avr.SRAM_START + avr.SRAM_SIZE)
  116. elif typ == 'D23':
  117. # check if the dump is complete
  118. if len(ranges) != 1 or ranges[0][0] != 0 or \
  119. ranges[0][1] != avr.SRAM_START + avr.SRAM_SIZE:
  120. print('error: incomplete D23 dump', file=sys.stderr)
  121. return None
  122. regs = True
  123. if reason is None:
  124. print('warning: no error line in D23', file=sys.stderr)
  125. elif typ == 'D21':
  126. if len(ranges) != 1 or len(buf_data) != (avr.SRAM_START + avr.SRAM_SIZE + 256):
  127. print('error: incomplete D21 dump', file=sys.stderr)
  128. return None
  129. # decode the header structure
  130. magic, regs_present, crash_reason, pc, sp = struct.unpack('<LBBLH', buf_data[0:12])
  131. if magic != DUMP_MAGIC:
  132. print('error: invalid dump header in D21', file=sys.stderr)
  133. return None
  134. regs = bool(regs_present)
  135. reason = CrashReason(crash_reason)
  136. # extract the data section
  137. buf_addr = 0
  138. buf_data = buf_data[256:]
  139. ranges[0] = (0, len(buf_data))
  140. return Dump(typ, reason, regs, pc, sp, buf_data, ranges)