mirror of
				https://github.com/bytecodealliance/wasm-micro-runtime.git
				synced 2025-10-30 21:02:27 +00:00 
			
		
		
		
	 16a4d71b34
			
		
	
	
		16a4d71b34
		
			
		
	
	
	
	
		
			
			Implement the GC (Garbage Collection) feature for interpreter mode, AOT mode and LLVM-JIT mode, and support most features of the latest spec proposal, and also enable the stringref feature. Use `cmake -DWAMR_BUILD_GC=1/0` to enable/disable the feature, and `wamrc --enable-gc` to generate the AOT file with GC supported. And update the AOT file version from 2 to 3 since there are many AOT ABI breaks, including the changes of AOT file format, the changes of AOT module/memory instance layouts, the AOT runtime APIs for the AOT code to invoke and so on.
		
			
				
	
	
		
			193 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python3
 | |
| #
 | |
| # Copyright (C) 2019 Intel Corporation.  All rights reserved.
 | |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| #
 | |
| import argparse
 | |
| import os
 | |
| from pathlib import Path
 | |
| import re
 | |
| import shlex
 | |
| import subprocess
 | |
| import sys
 | |
| 
 | |
| """
 | |
| it is a tool to transfer the address, which is from a call-stack dump generated by iwasm, to line info for a wasm file.
 | |
| 
 | |
| > in order to generate the call-stack dump, you can use the following command: `$ cmake -DWAMR_BUILD_DUMP_CALL_STACK=1 ...`
 | |
| 
 | |
| When a wasm file is compiled with debug info, it is possible to transfer the address to line info.
 | |
| 
 | |
| For example, there is a call-stack dump:
 | |
| 
 | |
| ```
 | |
| #00: 0x0a04 - $f18
 | |
| #01: 0x08e4 - $f11
 | |
| #02: 0x096f - $f12
 | |
| #03: 0x01aa - _start
 | |
| ```
 | |
| 
 | |
| - store the call-stack dump into a file, e.g. call_stack.txt
 | |
| - run the following command to transfer the address to line info:
 | |
|   ```
 | |
|   $ cd test-tools/addr2line
 | |
|   $ python3 addr2line.py --wasi-sdk <wasi-sdk installation> --wabt <wabt installation> --wasm-file <wasm file path> call_stack.txt
 | |
|   ```
 | |
| - the script will use *wasm-objdump* in wabt to transform address, then use *llvm-dwarfdump* to lookup the line info for each address
 | |
|   in the call-stack dump.
 | |
| - the output will be:
 | |
|   ```
 | |
|   #00: 0x0a04 - $f18
 | |
|   #01: 0x08e4 - $f11 (FILE:quicksort.c LINE:  176 COLUMN: 11 FUNC:Quick)
 | |
|   #02: 0x096f - $f12 (FILE:quicksort.c LINE:  182 COLUMN:  3 FUNC:main)
 | |
|   #03: 0x01aa - _start
 | |
|   ```
 | |
| 
 | |
| """
 | |
| 
 | |
| 
 | |
| def get_code_section_start(wasm_objdump: Path, wasm_file: Path) -> int:
 | |
|     """
 | |
|     Find the start offset of Code section in a wasm file.
 | |
| 
 | |
|     if the code section header likes:
 | |
|       Code start=0x0000017c end=0x00004382 (size=0x00004206) count: 47
 | |
| 
 | |
|     the start offset is 0x0000017c
 | |
|     """
 | |
|     cmd = f"{wasm_objdump} -h {wasm_file}"
 | |
|     p = subprocess.run(
 | |
|         shlex.split(cmd),
 | |
|         check=True,
 | |
|         capture_output=True,
 | |
|         text=True,
 | |
|         universal_newlines=True,
 | |
|     )
 | |
|     outputs = p.stdout.split(os.linesep)
 | |
| 
 | |
|     # if there is no .debug section, return -1
 | |
|     for line in outputs:
 | |
|         line = line.strip()
 | |
|         if ".debug_info" in line:
 | |
|             break
 | |
|     else:
 | |
|         print(f"No .debug_info section found {wasm_file}")
 | |
|         return -1
 | |
| 
 | |
|     for line in outputs:
 | |
|         line = line.strip()
 | |
|         if "Code" in line:
 | |
|             return int(line.split()[1].split("=")[1], 16)
 | |
| 
 | |
|     return -1
 | |
| 
 | |
| 
 | |
| def get_line_info(dwarf_dump: Path, wasm_file: Path, offset: int) -> str:
 | |
|     """
 | |
|     Find the location info of a given offset in a wasm file.
 | |
|     """
 | |
|     cmd = f"{dwarf_dump} --lookup={offset} {wasm_file}"
 | |
|     p = subprocess.run(
 | |
|         shlex.split(cmd),
 | |
|         check=False,
 | |
|         capture_output=True,
 | |
|         text=True,
 | |
|         universal_newlines=True,
 | |
|     )
 | |
|     outputs = p.stdout.split(os.linesep)
 | |
| 
 | |
|     capture_name = False
 | |
|     for line in outputs:
 | |
|         line = line.strip()
 | |
| 
 | |
|         if "DW_TAG_subprogram" in line:
 | |
|             capture_name = True
 | |
|             continue
 | |
| 
 | |
|         if "DW_AT_name" in line and capture_name:
 | |
|             PATTERN = r"DW_AT_name\s+\(\"(\S+)\"\)"
 | |
|             m = re.match(PATTERN, line)
 | |
|             assert m is not None
 | |
| 
 | |
|             function_name = m.groups()[0]
 | |
| 
 | |
|         if line.startswith("Line info"):
 | |
|             location = line
 | |
|             return (function_name, location)
 | |
| 
 | |
|     return ()
 | |
| 
 | |
| 
 | |
| def parse_line_info(line_info: str) -> ():
 | |
|     """
 | |
|     line_info -> [file, line, column]
 | |
|     """
 | |
|     PATTERN = r"Line info: file \'(.+)\', line ([0-9]+), column ([0-9]+)"
 | |
|     m = re.search(PATTERN, line_info)
 | |
|     assert m is not None
 | |
| 
 | |
|     file, line, column = m.groups()
 | |
|     return (file, int(line), int(column))
 | |
| 
 | |
| 
 | |
| def parse_call_stack_line(line: str) -> ():
 | |
|     """
 | |
|     #00: 0x0a04 - $f18   => (00, 0x0a04, $f18)
 | |
|     """
 | |
|     PATTERN = r"#([0-9]+): 0x([0-9a-f]+) - (\S+)"
 | |
|     m = re.match(PATTERN, line)
 | |
|     return m.groups() if m else None
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     parser = argparse.ArgumentParser(description="addr2line for wasm")
 | |
|     parser.add_argument("--wasi-sdk", type=Path, help="path to wasi-sdk")
 | |
|     parser.add_argument("--wabt", type=Path, help="path to wabt")
 | |
|     parser.add_argument("--wasm-file", type=Path, help="path to wasm file")
 | |
|     parser.add_argument("call_stack_file", type=Path, help="path to a call stack file")
 | |
|     args = parser.parse_args()
 | |
| 
 | |
|     wasm_objdump = args.wabt.joinpath("bin/wasm-objdump")
 | |
|     assert wasm_objdump.exists()
 | |
| 
 | |
|     llvm_dwarf_dump = args.wasi_sdk.joinpath("bin/llvm-dwarfdump")
 | |
|     assert llvm_dwarf_dump.exists()
 | |
| 
 | |
|     code_section_start = get_code_section_start(wasm_objdump, args.wasm_file)
 | |
|     if code_section_start == -1:
 | |
|         return -1
 | |
| 
 | |
|     assert args.call_stack_file.exists()
 | |
|     with open(args.call_stack_file, "rt", encoding="ascii") as f:
 | |
|         for line in f:
 | |
|             line = line.strip()
 | |
| 
 | |
|             if not line:
 | |
|                 continue
 | |
| 
 | |
|             splitted = parse_call_stack_line(line)
 | |
|             if splitted is None:
 | |
|                 print(line)
 | |
|                 continue
 | |
| 
 | |
|             _, offset, _ = splitted
 | |
| 
 | |
|             offset = int(offset, 16)
 | |
|             offset = offset - code_section_start
 | |
|             line_info = get_line_info(llvm_dwarf_dump, args.wasm_file, offset)
 | |
|             if not line_info:
 | |
|                 print(line)
 | |
|                 continue
 | |
| 
 | |
|             function_name, line_info = line_info
 | |
|             src_file, src_line, src_column = parse_line_info(line_info)
 | |
|             print(
 | |
|                 f"{line} (FILE:{src_file} LINE:{src_line:5} COLUMN:{src_column:3} FUNC:{function_name})"
 | |
|             )
 | |
| 
 | |
|     return 0
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     sys.exit(main())
 |