Query addr2line for source file and line info for a batch of addresses. Returns a dict mapping address -> (filename, line_number).
(addr2line_tool, elf_path, addresses, batch_size=500)
| 133 | |
| 134 | |
| 135 | def get_source_info_batch(addr2line_tool, elf_path, addresses, batch_size=500): |
| 136 | """ |
| 137 | Query addr2line for source file and line info for a batch of addresses. |
| 138 | |
| 139 | Returns a dict mapping address -> (filename, line_number). |
| 140 | """ |
| 141 | result_map = {} |
| 142 | |
| 143 | for i in range(0, len(addresses), batch_size): |
| 144 | batch = addresses[i : i + batch_size] |
| 145 | hex_addrs = [f"0x{addr:x}" for addr in batch] |
| 146 | |
| 147 | cmd = [addr2line_tool, "-e", str(elf_path), "-C", "-f"] + hex_addrs |
| 148 | proc = subprocess.run(cmd, capture_output=True, text=True) |
| 149 | if proc.returncode != 0: |
| 150 | print(f"Warning: addr2line error: {proc.stderr}", file=sys.stderr) |
| 151 | continue |
| 152 | |
| 153 | lines = proc.stdout.splitlines() |
| 154 | # addr2line outputs pairs: function_name\nfile:line |
| 155 | idx = 0 |
| 156 | for addr in batch: |
| 157 | if idx + 1 < len(lines): |
| 158 | # function_name = lines[idx] # We already have this from nm |
| 159 | file_line = lines[idx + 1] |
| 160 | idx += 2 |
| 161 | else: |
| 162 | file_line = "??:0" |
| 163 | idx += 2 |
| 164 | |
| 165 | # Parse "file:line" or "file:line (discriminator N)" |
| 166 | file_line = re.sub(r"\s*\(discriminator.*\)", "", file_line) |
| 167 | if ":" in file_line: |
| 168 | parts = file_line.rsplit(":", 1) |
| 169 | filename = parts[0] |
| 170 | try: |
| 171 | line_num = int(parts[1]) |
| 172 | except ValueError: |
| 173 | line_num = 0 |
| 174 | else: |
| 175 | filename = file_line |
| 176 | line_num = 0 |
| 177 | |
| 178 | result_map[addr] = (filename, line_num) |
| 179 | |
| 180 | return result_map |
| 181 | |
| 182 | |
| 183 | def shorten_path(filepath): |
no test coverage detected