dump.py 4.9 KB

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