| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 | import sysimport reimport enumimport structfrom . import avrFILL_BYTE   = b'\0'      # used to fill memory gaps in the dumpDUMP_MAGIC  = 0x55525547 # XFLASH dump magicDUMP_OFFSET = 0x3d000    # XFLASH dump offsetDUMP_SIZE   = 0x2300     # XFLASH dump sizeclass CrashReason(enum.IntEnum):    MANUAL = 0    STACK_ERROR = 1    WATCHDOG = 2    BAD_ISR = 3class 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+sizedef 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, datadef 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 retdef 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('error: incomplete D23 dump', file=sys.stderr)            return None        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 != DUMP_MAGIC:            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)
 |