123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- import sys
- import re
- import enum
- import struct
- from . import avr
- FILL_BYTE = b'\0' # used to fill memory gaps in the dump
- class CrashReason(enum.IntEnum):
- MANUAL = 0
- STACK_ERROR = 1
- WATCHDOG = 2
- BAD_ISR = 3
- class Dump():
- def __init__(self, typ, reason, regs, pc, sp, data, ranges):
- self.typ = typ
- self.reason = reason
- self.regs = regs
- self.pc = pc
- self.sp = sp
- self.data = data
- self.ranges = ranges
- # expand the buffer identified by addr+data to fill the region start+size
- def region_expand(addr, data, start, size):
- if start < addr:
- data = FILL_BYTE * (addr - start) + data
- addr = start
- end = start + size
- data_end = addr + len(data)
- if end > data_end:
- data += FILL_BYTE * (data_end - end)
- return addr, data
- def merge_ranges(ranges):
- ranges = list(sorted(ranges, key=lambda x: x[0]))
- if len(ranges) < 2:
- return ranges
- ret = [ranges[0]]
- for cur in ranges[1:]:
- last = ret[-1]
- last_end = last[0] + last[1]
- if last_end < cur[0]:
- ret.append(cur)
- else:
- cur_end = cur[0] + cur[1]
- last = (last[0], max(last_end, cur_end) - last[0])
- ret[-1] = last
- return ret
- def decode_dump(path):
- fd = open(path, 'r')
- if fd is None:
- return None
- buf_addr = None # starting address
- buf_data = None # data
- typ = None # dump type
- reason = None # crash reason
- regs = None # registers present
- pc = None # PC address
- sp = None # SP address
- ranges = [] # dumped ranges
- in_dump = False
- for line in enumerate(fd):
- line = (line[0], line[1].rstrip())
- tokens = line[1].split(maxsplit=1)
- def line_error():
- print('malformed line {}: {}'.format(*line), file=sys.stderr)
- # handle metadata
- if not in_dump:
- if len(tokens) > 0 and tokens[0] in ['D2', 'D21', 'D23']:
- in_dump = True
- typ = tokens[0]
- continue
- else:
- if len(tokens) == 0:
- line_error()
- continue
- elif tokens[0] == 'ok':
- break
- elif tokens[0] == 'error:' and len(tokens) == 2:
- values = tokens[1].split(' ')
- if typ == 'D23' and len(values) >= 3:
- reason = CrashReason(int(values[0], 0))
- pc = int(values[1], 0)
- sp = int(values[2], 0)
- else:
- line_error()
- continue
- elif len(tokens) != 2 or not re.match(r'^[0-9a-fA-F]+$', tokens[0]):
- line_error()
- continue
- # decode hex data
- addr = int.from_bytes(bytes.fromhex(tokens[0]), 'big')
- data = bytes.fromhex(tokens[1])
- ranges.append((addr, len(data)))
- if buf_addr is None:
- buf_addr = addr
- buf_data = data
- else:
- # grow buffer as needed
- buf_addr, buf_data = region_expand(buf_addr, buf_data,
- addr, len(data))
- # replace new part
- rep_start = addr - buf_addr
- rep_end = rep_start + len(data)
- buf_data = buf_data[:rep_start] + data + buf_data[rep_end:]
- # merge continuous ranges
- ranges = merge_ranges(ranges)
- if typ == 'D2':
- # D2 doesn't guarantee registers to be present
- regs = len(ranges) > 0 and \
- ranges[0][0] == 0 and \
- ranges[0][1] >= avr.SRAM_START
- # fill to fit for easy loading
- buf_addr, buf_data = region_expand(
- buf_addr, buf_data, 0, avr.SRAM_START + avr.SRAM_SIZE)
- elif typ == 'D23':
- # check if the dump is complete
- if len(ranges) != 1 or ranges[0][0] != 0 or \
- ranges[0][1] != avr.SRAM_START + avr.SRAM_SIZE:
- print('warning: incomplete D23 dump', file=sys.stderr)
- else:
- regs = True
- if reason is None:
- print('warning: no error line in D23', file=sys.stderr)
- elif typ == 'D21':
- if len(ranges) != 1 or len(buf_data) != (avr.SRAM_START + avr.SRAM_SIZE + 256):
- print('error: incomplete D21 dump', file=sys.stderr)
- return None
- # decode the header structure
- magic, regs_present, crash_reason, pc, sp = struct.unpack('<LBBLH', buf_data[0:12])
- if magic != 0x55525547:
- print('error: invalid dump header in D21', file=sys.stderr)
- return None
- regs = bool(regs_present)
- reason = CrashReason(crash_reason)
- # extract the data section
- buf_addr = 0
- buf_data = buf_data[256:]
- ranges[0] = (0, len(buf_data))
- return Dump(typ, reason, regs, pc, sp, buf_data, ranges)
|