|
| 1 | +import struct |
| 2 | +import subprocess |
| 3 | +import tempfile |
| 4 | +import time |
| 5 | + |
| 6 | + |
| 7 | +def format_core_dump(data, elf_path=None): |
| 8 | + core_dumped = struct.unpack('<I', data[0:4])[0] |
| 9 | + pc = struct.unpack('<I', data[4:8])[0] |
| 10 | + ps = struct.unpack('<I', data[8:12])[0] |
| 11 | + a_regs = struct.unpack('<16I', data[12:76]) |
| 12 | + sar = struct.unpack('<I', data[76:80])[0] |
| 13 | + exccause = struct.unpack('<I', data[80:84])[0] |
| 14 | + excvaddr = struct.unpack('<I', data[84:88])[0] |
| 15 | + lbeg = struct.unpack('<I', data[88:92])[0] |
| 16 | + |
| 17 | + if core_dumped == 0: |
| 18 | + return "No core dump available" |
| 19 | + output = ( |
| 20 | + f"PC : 0x{pc:08x} PS : 0x{ps:08x} " |
| 21 | + + f"A0 : 0x{a_regs[0]:08x} A1 : 0x{a_regs[1]:08x}\n" |
| 22 | + + f"A2 : 0x{a_regs[2]:08x} A3 : 0x{a_regs[3]:08x} " |
| 23 | + + f"A4 : 0x{a_regs[4]:08x} A5 : 0x{a_regs[5]:08x}\n" |
| 24 | + + f"A6 : 0x{a_regs[6]:08x} A7 : 0x{a_regs[7]:08x} " |
| 25 | + + f"A8 : 0x{a_regs[8]:08x} A9 : 0x{a_regs[9]:08x}\n" |
| 26 | + + f"A10 : 0x{a_regs[10]:08x} A11 : 0x{a_regs[11]:08x} " |
| 27 | + + f"A12 : 0x{a_regs[12]:08x} A13 : 0x{a_regs[13]:08x}\n" |
| 28 | + + f"A14 : 0x{a_regs[14]:08x} A15 : 0x{a_regs[15]:08x} " |
| 29 | + + f"SAR : 0x{sar:08x} EXCCAUSE: 0x{exccause:08x}\n" |
| 30 | + + f"EXCVADDR: 0x{excvaddr:08x} LBEG : 0x{lbeg:08x} " |
| 31 | + + '\n' |
| 32 | + ) |
| 33 | + |
| 34 | + if elf_path is not None: |
| 35 | + from riberry.platformio.toolchain import ensure_toolchain |
| 36 | + from riberry.platformio.toolchain import get_addr2line_path |
| 37 | + ensure_toolchain() |
| 38 | + addr2line_path = get_addr2line_path() |
| 39 | + addr2line_cmd = [str(addr2line_path), "-e", str(elf_path), '-a', '-pfiaC', str(hex(pc))] |
| 40 | + output += '\n' |
| 41 | + output += 'Executing addr2line command:\n' |
| 42 | + output += ' '.join(addr2line_cmd) + '\n' |
| 43 | + output += subprocess.check_output( |
| 44 | + addr2line_cmd, stderr=subprocess.DEVNULL |
| 45 | + ).decode() |
| 46 | + return output |
| 47 | + |
| 48 | + |
| 49 | +def read_core_dump(com, elf_path=None, retry_count=5, |
| 50 | + repo_owner="iory", repo_name="riberry"): |
| 51 | + formatted_output = "" |
| 52 | + for _ in range(retry_count): |
| 53 | + with com.lock_context(): |
| 54 | + com.write([0xFD]) |
| 55 | + time.sleep(0.01) |
| 56 | + version = com.read().decode().split('_') |
| 57 | + if len(version) >= 3: |
| 58 | + import riberry |
| 59 | + from riberry.firmware_update import download_firmware_from_github |
| 60 | + riberry_git_version, lcd_rotation, use_grove = version[:3] |
| 61 | + formatted_output += f"Core dumped Firmware version: {riberry_git_version}\n" |
| 62 | + formatted_output += f"LCD rotation: {lcd_rotation}\n" |
| 63 | + formatted_output += f"Use Grove: {use_grove}\n" |
| 64 | + |
| 65 | + if elf_path is None: |
| 66 | + model = com.device_type |
| 67 | + if model == 'm5stack-LLM': |
| 68 | + device_name = 'm5stack-basic' |
| 69 | + elif "Radxa" in model or "ROCK Pi" in model \ |
| 70 | + or model == "Khadas VIM4" \ |
| 71 | + or model == "NVIDIA Jetson Xavier NX Developer Kit": |
| 72 | + device_name = 'm5stack-atoms3' |
| 73 | + else: |
| 74 | + raise NotImplementedError(f"Not supported device {model}. Please feel free to add the device name to the list or ask the developer to add it.") |
| 75 | + |
| 76 | + url = f'https://github.com/iory/riberry/releases/download/v{riberry.__version__}-{riberry_git_version}/{device_name}-lcd{lcd_rotation}-grove{use_grove}.elf' |
| 77 | + temp_file = tempfile.NamedTemporaryFile(suffix=".elf", delete=True) |
| 78 | + print(f"Downloading firmware elf from {url} to temporary file {temp_file.name}...") |
| 79 | + try: |
| 80 | + elf_path = download_firmware_from_github(url, temp_file) |
| 81 | + except Exception as e: |
| 82 | + print(f"Failed to download firmware: {e}") |
| 83 | + break |
| 84 | + time.sleep(0.1) |
| 85 | + if formatted_output == "": |
| 86 | + formatted_output += "Failed to read core dump version\n" |
| 87 | + formatted_output += f"Device: {com.device_type}\n" |
| 88 | + formatted_output += f"Communication: {com.__class__.__name__}\n\n" |
| 89 | + |
| 90 | + for _ in range(retry_count): |
| 91 | + success = False |
| 92 | + with com.lock_context(): |
| 93 | + com.write([0xFC]) |
| 94 | + time.sleep(0.01) |
| 95 | + response = com.read() |
| 96 | + if len(response) > 0: |
| 97 | + try: |
| 98 | + formatted_output += format_core_dump(response, |
| 99 | + elf_path=elf_path) |
| 100 | + success = True |
| 101 | + except Exception as e: |
| 102 | + print(f"Failed to format core dump: {e}") |
| 103 | + if success: |
| 104 | + break |
| 105 | + time.sleep(0.1) |
| 106 | + if success: |
| 107 | + from riberry.git_utils import generate_github_issue_url |
| 108 | + print(formatted_output) |
| 109 | + |
| 110 | + # Generate GitHub issue URL |
| 111 | + issue_title = f"Core Dump Report - Firmware {version}" |
| 112 | + message = "I have encountered a core dump while running the firmware." |
| 113 | + issue_body = f"{message}\n```\n{formatted_output}\n```" |
| 114 | + issue_url = generate_github_issue_url(repo_owner, repo_name, issue_title, issue_body) |
| 115 | + |
| 116 | + clickable_url = f"\033]8;;{issue_url}\033\\Click here to create a GitHub issue\033]8;;\033\\" |
| 117 | + print(clickable_url) |
| 118 | + print(f"If not clickable, use this URL:\n{issue_url}") |
0 commit comments