| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- #!/usr/bin/env python3
- #
- # Copyright (C) 2024 Amazon Inc. All rights reserved.
- # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #
- """
- This tool corrects function names in call stacks based on the
- instruction pointers.
- When the AOT file is generated with excluded func-idx in the
- `--call-stack-features` parameter, the function indexes are
- incorrect (likely they're zero). This script uses instruction
- pointers and the original WASM file to generate a call stack
- file with the correct function indexes (or function names,
- when available).
- Example input (call_stack.txt) - note that `__imported_wasi_snapshot_preview1_fd_close`
- had index 0, therefore it appears as a name in every line:
- ```
- #00: 0x0505 - __imported_wasi_snapshot_preview1_fd_close
- #01: 0x0309 - __imported_wasi_snapshot_preview1_fd_close
- #02: 0x037c - __imported_wasi_snapshot_preview1_fd_close
- #03: 0x03b2 - __imported_wasi_snapshot_preview1_fd_close
- #04: 0x03e4 - __imported_wasi_snapshot_preview1_fd_close
- #05: 0x02e6 - __imported_wasi_snapshot_preview1_fd_close
- ```
- Conversion command:
- ```
- python3 test-tools/ip2function/ip2function.py \
- --wasm-file opt-samp/tiny.wasm \
- call_stack.txt
- ```
- Output:
- ```
- #0: 0x0505 - abort
- #1: 0x0309 - baz
- #2: 0x037c - bar
- #3: 0x03b2 - foo
- #4: 0x03e4 - __original_main
- #5: 0x02e6 - _start
- ```
- """
- import argparse
- import bisect
- import os
- import re
- import subprocess
- import sys
- from typing import NamedTuple, Optional
- from typing import TextIO
- from pathlib import Path
- import shutil
- class FunctionInfo(NamedTuple):
- start_address: int
- idx: int
- name: Optional[str]
- def __str__(self) -> str:
- return self.name if self.name else f"$f{self.idx}"
- def load_functions(wasm_objdump: Path, wasm_file: Path) -> list[FunctionInfo]:
- objdump_function_pattern = re.compile(
- r"^([0-9a-f]+)\sfunc\[(\d+)\](?:\s\<(.+)\>)?\:$"
- )
- def parse_objdump_function_line(
- line: str,
- ) -> Optional[FunctionInfo]:
- match = objdump_function_pattern.match(line.strip())
- return (
- FunctionInfo(int(match[1], 16), int(match[2]), match[3]) if match else None
- )
- p = subprocess.run(
- [wasm_objdump, "--disassemble", wasm_file],
- check=True,
- capture_output=True,
- text=True,
- universal_newlines=True,
- )
- return list(
- filter(
- None,
- (
- parse_objdump_function_line(line.strip())
- for line in p.stdout.split(os.linesep)
- ),
- )
- )
- def parse_call_stack_file(
- functions: list[FunctionInfo], call_stack_file: TextIO, output_file: TextIO
- ) -> None:
- call_stack_line_pattern = re.compile(r"^(#\d+): (0x[0-9a-f]+) \- (\S+)$")
- for line in call_stack_file:
- match = call_stack_line_pattern.match(line.strip())
- if not match:
- output_file.write(line)
- continue
- index = match[1]
- address = match[2]
- func_pos = bisect.bisect_right(
- functions, int(address, 16), key=lambda x: x.start_address
- )
- if func_pos <= 0:
- raise ValueError(f"Cannot find function for address {address}")
- output_file.write(f"{index}: {address} - {functions[func_pos -1]}\n")
- def main() -> int:
- parser = argparse.ArgumentParser(description="addr2line for wasm")
- parser.add_argument(
- "--wasm-objdump", type=Path, default="wasm-objdump", help="path to wasm objdump"
- )
- parser.add_argument(
- "--wasm-file", required=True, type=Path, help="path to wasm file"
- )
- parser.add_argument(
- "call_stack_file", type=argparse.FileType("r"), help="path to a call stack file"
- )
- parser.add_argument(
- "-o",
- "--output",
- type=argparse.FileType("w"),
- default=sys.stdout,
- help="Output file path (default is stdout)",
- )
- args = parser.parse_args()
- wasm_objdump: Path = shutil.which(args.wasm_objdump)
- assert wasm_objdump is not None
- wasm_file: Path = args.wasm_file
- assert wasm_file.exists()
- parse_call_stack_file(
- load_functions(wasm_objdump, wasm_file), args.call_stack_file, args.output
- )
- return 0
- if __name__ == "__main__":
- sys.exit(main())
|