dump.py 4.8 KB

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