Tools: add "--skip-last" argument to addr2line_backtrace for convenience

The last address in a backtrace often reference symbols before main()
which aren't found, making the command take much longer to run.
This commit is contained in:
Campbell Barton
2025-01-09 12:32:08 +11:00
parent 8ab4e8c7ad
commit 9d0aabe64a

View File

@@ -142,6 +142,21 @@ def argparse_create() -> argparse.ArgumentParser:
), ),
required=False, required=False,
) )
parser.add_argument(
"--skip-last",
dest="skip_last",
type=int,
default=0,
required=False,
help=(
"Optionally skip the last N addresses found.\n"
"You may want to set this to 1 as the first address\n"
"may point to an address that calls the main() function."
"\n"
"In this case the lookup will fail, taking significantly longer than all other lookups.\n"
"Typically values between 1-4 should be used."
),
)
parser.add_argument( parser.add_argument(
"backtraces", "backtraces",
nargs="*", nargs="*",
@@ -151,17 +166,54 @@ def argparse_create() -> argparse.ArgumentParser:
return parser return parser
def addr2line_for_filedata( def backtraces_extract_from_text(backtrace_data: str) -> set[str]:
exe: str,
base_path: str,
time_command: bool,
jobs: int,
backtrace_data: str,
) -> None:
addr_set = set() addr_set = set()
for match in RE_ADDR.finditer(backtrace_data): for match in RE_ADDR.finditer(backtrace_data):
addr = match.group(1) addr = match.group(1)
addr_set.add(addr) addr_set.add(addr)
return addr_set
def backtraces_extract_from_text_skip_last(
backtrace_data: str,
skip_last: int,
) -> tuple[set[str], set[str]]:
addr_set = set()
addr_list = []
for match in RE_ADDR.finditer(backtrace_data):
addr = match.group(1)
addr_set.add(addr)
addr_list.append(addr)
# Remove the last N items.
addr_set_skip = set()
for i, addr in enumerate(reversed(addr_list)):
if i == skip_last:
break
# Unlikely but possible, probably too many values are being skipped.
if addr in addr_set_skip:
skip_last += 1
continue
addr_set_skip.add(addr)
addr_set.remove(addr)
return addr_set, addr_set_skip
def addr2line_for_filedata(
exe: str,
base_path: str,
time_command: bool,
skip_last: int,
jobs: int,
backtrace_data: str,
) -> None:
if skip_last == 0:
addr_set = backtraces_extract_from_text(backtrace_data)
addr_set_skip: set[str] = set()
else:
addr_set, addr_set_skip = backtraces_extract_from_text_skip_last(backtrace_data, skip_last)
shared_args = exe, base_path, time_command shared_args = exe, base_path, time_command
if jobs >= len(addr_set): if jobs >= len(addr_set):
@@ -179,7 +231,13 @@ def addr2line_for_filedata(
with multiprocessing.Pool(jobs) as pool: with multiprocessing.Pool(jobs) as pool:
for i, result_list in enumerate(pool.imap_unordered(addr2line_fn, addr2line_args), 1): for i, result_list in enumerate(pool.imap_unordered(addr2line_fn, addr2line_args), 1):
for (addr, result) in result_list: for (addr, result) in result_list:
progress_output(addr_done, addr_len, "{:d} of {:d}".format(addr_done, addr_len)) if addr_set_skip:
skip_text = " (skipped {:d})".format(len(addr_set_skip))
else:
skip_text = ""
progress_output(addr_done, addr_len, "{:d} of {:d}{:s}".format(addr_done, addr_len, skip_text))
del skip_text
addr_map[addr] = result addr_map[addr] = result
addr_done += 1 addr_done += 1
@@ -188,6 +246,8 @@ def addr2line_for_filedata(
def re_replace_fn(match: re.Match[str]) -> str: def re_replace_fn(match: re.Match[str]) -> str:
addr = match.group(1) addr = match.group(1)
if addr in addr_set_skip:
return "{:s} ({:s})".format("<SKIP>", addr)
return "{:s} ({:s})".format(addr_map[addr], addr) return "{:s} ({:s})".format(addr_map[addr], addr)
backtrace_data_updated = RE_ADDR.sub(re_replace_fn, backtrace_data) backtrace_data_updated = RE_ADDR.sub(re_replace_fn, backtrace_data)
@@ -221,10 +281,10 @@ def main() -> None:
print("Filed to open {!r}, {:s}".format(backtrace_filepath, str(ex))) print("Filed to open {!r}, {:s}".format(backtrace_filepath, str(ex)))
continue continue
addr2line_for_filedata(args.exe, base_path, args.time_command, jobs, bactrace_data) addr2line_for_filedata(args.exe, base_path, args.time_command, args.skip_last, jobs, bactrace_data)
else: else:
bactrace_data = sys.stdin.read() bactrace_data = sys.stdin.read()
addr2line_for_filedata(args.exe, base_path, args.time_command, jobs, bactrace_data) addr2line_for_filedata(args.exe, base_path, args.time_command, args.skip_last, jobs, bactrace_data)
if __name__ == "__main__": if __name__ == "__main__":