From fef26ead3e268768bce3aa658b2a05dbc3b3ee4e Mon Sep 17 00:00:00 2001 From: "liang.he" Date: Fri, 12 Apr 2024 11:43:40 +0800 Subject: [PATCH] addr2line.py: Support sourceMappingURL section produced by emcc (#3302) And update the debug-tools sample. --- .../compilation_on_android_ubuntu.yml | 4 +- .github/workflows/compilation_on_macos.yml | 8 +- .github/workflows/nightly_run.yml | 12 +- samples/debug-tools/CMakeLists.txt | 33 ++++- samples/debug-tools/README.md | 35 ++++- .../debug-tools/cmake/FindEMSCRIPTEN.cmake | 45 +++++++ samples/debug-tools/cmake/FindWAMRC.cmake | 27 ++++ samples/debug-tools/cmake/FindWASISDK.cmake | 24 ++++ samples/debug-tools/wasm-apps/CMakeLists.txt | 121 ++++++----------- test-tools/addr2line/addr2line.py | 125 +++++++++++++++--- 10 files changed, 324 insertions(+), 110 deletions(-) create mode 100644 samples/debug-tools/cmake/FindEMSCRIPTEN.cmake create mode 100644 samples/debug-tools/cmake/FindWAMRC.cmake create mode 100644 samples/debug-tools/cmake/FindWASISDK.cmake diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index f1e43777..6b2a1a11 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.github/workflows/compilation_on_android_ubuntu.yml @@ -389,14 +389,14 @@ jobs: cd /opt sudo wget ${{ matrix.wasi_sdk_release }} sudo tar -xzf wasi-sdk-*.tar.gz - sudo mv wasi-sdk-20.0 wasi-sdk + sudo ln -sf wasi-sdk-20.0 wasi-sdk - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} sudo tar -xzf wabt-1.0.31-*.tar.gz - sudo mv wabt-1.0.31 wabt + sudo ln -sf wabt-1.0.31 wabt - name: Get LLVM libraries id: retrieve_llvm_libs uses: actions/cache@v4 diff --git a/.github/workflows/compilation_on_macos.yml b/.github/workflows/compilation_on_macos.yml index 4f59f238..ec094323 100644 --- a/.github/workflows/compilation_on_macos.yml +++ b/.github/workflows/compilation_on_macos.yml @@ -273,14 +273,14 @@ jobs: cd /opt sudo wget ${{ matrix.wasi_sdk_release }} sudo tar -xzf wasi-sdk-*.tar.gz - sudo mv wasi-sdk-20.0 wasi-sdk + sudo ln -sf wasi-sdk-20.0 wasi-sdk - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} sudo tar -xzf wabt-1.0.31-*.tar.gz - sudo mv wabt-1.0.31 wabt + sudo ln -sf wabt-1.0.31 wabt - name: Build Sample [basic] run: | @@ -346,7 +346,7 @@ jobs: cmake .. cmake --build . --config Release --parallel 4 working-directory: wamr-compiler - + - name: Build Sample [wasi-threads] run: | cd samples/wasi-threads @@ -378,4 +378,4 @@ jobs: cmake --build . --config Debug --parallel 4 ./iwasm wasm-apps/trap.wasm | grep "#" > call_stack.txt ./iwasm wasm-apps/trap.aot | grep "#" > call_stack_aot.txt - bash -x ../symbolicate.sh + bash -x ../symbolicate.sh diff --git a/.github/workflows/nightly_run.yml b/.github/workflows/nightly_run.yml index 341194df..4b62d110 100644 --- a/.github/workflows/nightly_run.yml +++ b/.github/workflows/nightly_run.yml @@ -8,7 +8,7 @@ on: types: - opened - synchronize - # running nightly pipeline if you're changing it + # running nightly pipeline if you're changing it # stress tests are run only in nightly at the moment, so running them in they are changed paths: - ".github/workflows/nightly_run.yml" @@ -54,7 +54,7 @@ jobs: with: os: "ubuntu-22.04" arch: "X86" - + build_wamrc: needs: [ @@ -65,7 +65,7 @@ jobs: matrix: include: - os: ubuntu-20.04 - llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2004.outputs.cache_key }} + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2004.outputs.cache_key }} steps: - name: checkout uses: actions/checkout@v4 @@ -459,13 +459,13 @@ jobs: cd /opt sudo wget ${{ matrix.wasi_sdk_release }} sudo tar -xzf wasi-sdk-*.tar.gz - sudo mv wasi-sdk-20.0 wasi-sdk + sudo ln -sf wasi-sdk-20.0 wasi-sdk - name: download and install wabt run: | cd /opt sudo wget ${{ matrix.wabt_release }} sudo tar -xzf wabt-1.0.31-*.tar.gz - sudo mv wabt-1.0.31 wabt + sudo ln -sf wabt-1.0.31 wabt - name: Get LLVM libraries id: retrieve_llvm_libs @@ -643,7 +643,7 @@ jobs: sudo tar -xzf wasi-sdk-*.tar.gz sudo mv wasi-sdk-20.0 wasi-sdk - # It is a temporary solution until new wasi-sdk that includes bug fixes is released + # It is a temporary solution until new wasi-sdk that includes bug fixes is released - name: build wasi-libc from source if: matrix.test_option == '$WASI_TEST_OPTIONS' run: | diff --git a/samples/debug-tools/CMakeLists.txt b/samples/debug-tools/CMakeLists.txt index 5143462a..ce06029a 100644 --- a/samples/debug-tools/CMakeLists.txt +++ b/samples/debug-tools/CMakeLists.txt @@ -7,6 +7,14 @@ include(CheckPIESupported) project(debug_tools_sample) +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +find_package(WASISDK REQUIRED) + +option(SOURCE_MAP_DEMO "Enable source map demo" OFF) +if (SOURCE_MAP_DEMO) + find_package(EMSCRIPTEN 3.1.50 REQUIRED) +endif () + ################ runtime settings ################ string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) if (APPLE) @@ -61,7 +69,30 @@ include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) ################ wasm application ################ -add_subdirectory(wasm-apps) +include(ExternalProject) + +# wasm32-wasi +ExternalProject_Add(wasm33-wasi + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps -B build + -DWASI_SDK_PREFIX=${WASISDK_HOME} + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} +) + +if (EMSCRIPTEN_FOUND) + # wasm32-emscripten + ExternalProject_Add(wasm32-emscripten + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps -B build + -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_TOOLCHAIN} + -DCMAKE_VERBOSE_MAKEFILE=On + -DSOURCE_MAP_DEMO=On + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR}/emscripten + ) +endif () ################ wamr runtime ################ include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) diff --git a/samples/debug-tools/README.md b/samples/debug-tools/README.md index 634d3119..b0358b9e 100644 --- a/samples/debug-tools/README.md +++ b/samples/debug-tools/README.md @@ -80,6 +80,39 @@ $ python3 ../../../test-tools/addr2line/addr2line.py \ call_stack.txt --no-addr ``` +#### sourcemap + +This script also supports _sourcemap_ which is produced by [_emscripten_](https://emscripten.org/docs/tools_reference/emcc.html). The _sourcemap_ is used to map the wasm function to the original source file. To use it, add `-gsource-map` option to _emcc_ command line. The output should be a section named "sourceMappingURL" and a separated file named "_.map_. + +If the wasm file is with _sourcemap_, the script will use it to get the source file and line info. It needs an extra command line option `--emsdk` to specify the path of _emsdk_. The script will use _emsymbolizer_ to query the source file and line info. + +````bash +$ python3 ../../../test-tools/addr2line/addr2line.py \ + --wasi-sdk /opt/wasi-sdk \ + --wabt /opt/wabt \ + --wasm-file emscripten/wasm-apps/trap.wasm \ + --emsdk /opt/emsdk \ + call_stack.from_wasm_w_sourcemap.txt + +The output should be something like: + +```text +1: c + at ../../../../../wasm-apps/trap.c:5:1 +2: b + at ../../../../../wasm-apps/trap.c:11:12 +3: a + at ../../../../../wasm-apps/trap.c:17:12 +4: main + at ../../../../../wasm-apps/trap.c:24:5 +5: __main_void + at ../../../../../../../../../emsdk/emscripten/system/lib/standalone/__main_void.c:53:10 +6: _start + at ../../../../../../../../../emsdk/emscripten/system/lib/libc/crt1.c:27:3 +```` + +> The script assume the separated map file _.map_ is in the same directory as the wasm file. + ### Another approach If the wasm file is with "name" section, it is able to output function name in the stack trace. To achieve that, need to enable `WAMR_BUILD_LOAD_CUSTOM_SECTION` and `WAMR_BUILD_CUSTOM_NAME_SECTION`. If using .aot file, need to add `--emit-custom-sections=name` into wamrc command line options. @@ -97,4 +130,4 @@ Then the output should be something like Exception: unreachable ``` -Also, it is able to use *addr2line.py* to add file and line info to the stack trace. +Also, it is able to use _addr2line.py_ to add file and line info to the stack trace. diff --git a/samples/debug-tools/cmake/FindEMSCRIPTEN.cmake b/samples/debug-tools/cmake/FindEMSCRIPTEN.cmake new file mode 100644 index 00000000..8f63ec54 --- /dev/null +++ b/samples/debug-tools/cmake/FindEMSCRIPTEN.cmake @@ -0,0 +1,45 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +find_path(EMSCRIPTEN_HOME + NAMES upstream/emscripten + PATHS /opt/emsdk + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +find_file(EMSCRIPTEN_VERSION_FILE + NAMES emscripten-version.txt + PATHS ${EMSCRIPTEN_HOME}/upstream/emscripten + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +file(READ ${EMSCRIPTEN_VERSION_FILE} EMSCRIPTEN_VERSION_FILE_CONTENT) + +string(REGEX + MATCH + "[0-9]+\.[0-9]+(\.[0-9]+)*" + EMSCRIPTEN_VERSION + ${EMSCRIPTEN_VERSION_FILE_CONTENT} +) + +find_package_handle_standard_args(EMSCRIPTEN + REQUIRED_VARS EMSCRIPTEN_HOME + VERSION_VAR EMSCRIPTEN_VERSION + HANDLE_VERSION_RANGE +) + +if(EMSCRIPTEN_FOUND) + set(EMSCRIPTEN_TOOLCHAIN ${EMSCRIPTEN_HOME}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake) + set(EMCC ${EMSCRIPTEN_HOME}/upstream/emscripten/emcc) +endif() +mark_as_advanced(EMSCRIPTEN_TOOLCHAIN EMCC) diff --git a/samples/debug-tools/cmake/FindWAMRC.cmake b/samples/debug-tools/cmake/FindWAMRC.cmake new file mode 100644 index 00000000..20f9416f --- /dev/null +++ b/samples/debug-tools/cmake/FindWAMRC.cmake @@ -0,0 +1,27 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +find_path(WAMRC_HOME + wamr-compiler + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/../../.. + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +find_file(WAMRC_BIN + wamrc + HINTS ${WAMRC_HOME}/wamr-compiler/build + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +find_package_handle_standard_args(WAMRC REQUIRED_VARS WAMRC_BIN) +mark_as_advanced(WAMRC_BIN) diff --git a/samples/debug-tools/cmake/FindWASISDK.cmake b/samples/debug-tools/cmake/FindWASISDK.cmake new file mode 100644 index 00000000..0caf374d --- /dev/null +++ b/samples/debug-tools/cmake/FindWASISDK.cmake @@ -0,0 +1,24 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +file(GLOB WASISDK_SEARCH_PATH "/opt/wasi-sdk-*") +find_path(WASISDK_HOME + NAMES share/wasi-sysroot + PATHS ${WASISDK_SEARCH_PATH} + NO_DEFAULT_PATH + REQUIRED +) + +string(REGEX MATCH [0-9]+\.[0-9]+\.*[0-9]* WASISDK_VERSION ${WASISDK_HOME}) + +find_package_handle_standard_args(WASISDK REQUIRED_VARS WASISDK_HOME VERSION_VAR WASISDK_VERSION) + +if(WASISDK_FOUND) + set(WASISDK_CC_COMMAND ${WASISDK_HOME}/bin/clang) + set(WASISDK_CXX_COMMAND ${WASISDK_HOME}/bin/clang++) + set(WASISDK_TOOLCHAIN ${WASISDK_HOME}/share/cmake/wasi-sdk.cmake) + set(WASISDK_SYSROOT ${WASISDK_HOME}/share/wasi-sysroot) +endif() +mark_as_advanced(WASISDK_CC_COMMAND WASISDK_CXX_COMMAND WASISDK_TOOLCHAIN WASISDK_SYSROOT WASISDK_HOME) diff --git a/samples/debug-tools/wasm-apps/CMakeLists.txt b/samples/debug-tools/wasm-apps/CMakeLists.txt index 3ca8aff2..527b5f37 100644 --- a/samples/debug-tools/wasm-apps/CMakeLists.txt +++ b/samples/debug-tools/wasm-apps/CMakeLists.txt @@ -1,91 +1,58 @@ # Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -if (APPLE) - set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) - set (CMAKE_C_LINK_FLAGS "") - set (CMAKE_CXX_LINK_FLAGS "") +cmake_minimum_required (VERSION 3.14) + +project (debut_tools_wasm) + +set (CMAKE_BUILD_TYPE Debug) # Otherwise no debug symbols (addr2line) + +list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../cmake) +find_package (WAMRC REQUIRED) + +option(SOURCE_MAP_DEMO "Enable source map demo" OFF) +if (SOURCE_MAP_DEMO) + find_package(EMSCRIPTEN 3.1.50 REQUIRED) endif () -if (NOT DEFINED WASI_SDK_DIR) - set (WASI_SDK_DIR "/opt/wasi-sdk") -endif () - -if (DEFINED WASI_SYSROOT) - set (CMAKE_SYSROOT "${WASI_SYSROOT}") -endif () - -set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") -set (CMAKE_ASM_COMPILER "${WASI_SDK_DIR}/bin/clang") -set (CMAKE_EXE_LINKER_FLAGS "-target wasm32-wasi") - -################ wabt and wamrc dependencies ################ -message(CHECK_START "Detecting WABT") -if(NOT (DEFINED WABT_DIR OR DEFINED CACHE{WABT_DIR})) - find_path(WABT_DIR - wabt - PATHS /opt - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH - ) - if(DEFINED WABT_DIR) - set(WABT_DIR ${WABT_DIR}/wabt) - endif() -endif() -if(WABT_DIR) - message(CHECK_PASS "found") -else() - message(CHECK_FAIL "not found") -endif() - -message(CHECK_START "Detecting WASM_OBJDUMP at ${WABT_DIR}") -find_program(WASM_OBJDUMP - wasm-objdump - PATHS "${WABT_DIR}/bin" - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH -) -if(WASM_OBJDUMP) - message(CHECK_PASS "found") -else() - message(CHECK_FAIL "not found") -endif() -if((NOT EXISTS ${WASM_OBJDUMP}) ) - message(FATAL_ERROR "Please make sure to have wasm-objdump under the path=${WABT_DIR}/bin ") -endif() - -set(WAMR_COMPILER_DIR ${CMAKE_CURRENT_LIST_DIR}/../../wamr-compiler/build) -message(CHECK_START "Detecting WAMR_COMPILER at ${WAMR_COMPILER_DIR}") -find_file(WAMR_COMPILER - wamrc - PATHS "${CMAKE_CURRENT_LIST_DIR}/../../../wamr-compiler/build" - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH -) -if(WAMR_COMPILER) - message(CHECK_PASS "found") -else() - message(CHECK_FAIL "not found") -endif() -if((NOT EXISTS ${WAMR_COMPILER}) ) - message(FATAL_ERROR "Please build wamrc under the path=${WAMR_ROOT_DIR}/wamr-compiler/") -endif() - ################ wasm and aot compilation ################ function (compile_sample SOURCE_FILE) get_filename_component (FILE_NAME ${SOURCE_FILE} NAME_WLE) - set (WASM_MODULE ${FILE_NAME}.wasm) - add_executable (${WASM_MODULE} ${SOURCE_FILE}) - add_custom_target( - wasm_to_aot + ## wasm + set (WASM_FILE ${FILE_NAME}.wasm) + add_executable (${FILE_NAME} ${SOURCE_FILE}) + set_target_properties (${FILE_NAME} PROPERTIES SUFFIX .wasm) + + ## aot + set (AOT_FILE ${FILE_NAME}.aot) + add_custom_target ( + ${FILE_NAME}_aot ALL - DEPENDS ${WAMR_COMPILER} ${WASM_MODULE} + DEPENDS ${WAMRC_BIN} ${WASM_FILE} # Use --enable-dump-call-stack to generate stack trace (addr2line) - COMMAND ${WAMR_COMPILER} --size-level=0 --enable-dump-call-stack -o wasm-apps/trap.aot wasm-apps/trap.wasm - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${WAMRC_BIN} --size-level=0 --enable-dump-call-stack -o ${AOT_FILE} ${WASM_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) + + ## wasm + sourcemap + if (DEFINED EMSCRIPTEN) + add_custom_target( + ${FILE_NAME}_w_sourcemap + ALL + DEPENDS ${SOURCE_FILE} + COMMAND ${EMCC} -O0 -gsource-map -o ${FILE_NAME}.sourcemap.wasm ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + endif () + + ## install both + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${WASM_FILE} DESTINATION wasm-apps) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${AOT_FILE} DESTINATION wasm-apps) + if (DEFINED EMSCRIPTEN) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.sourcemap.wasm DESTINATION wasm-apps) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.sourcemap.wasm.map DESTINATION wasm-apps) + endif () endfunction () -set(CMAKE_BUILD_TYPE Debug) # Otherwise no debug symbols (addr2line) -compile_sample(trap.c) \ No newline at end of file +compile_sample(trap.c) diff --git a/test-tools/addr2line/addr2line.py b/test-tools/addr2line/addr2line.py index 594f8e19..421b0bdb 100644 --- a/test-tools/addr2line/addr2line.py +++ b/test-tools/addr2line/addr2line.py @@ -43,6 +43,28 @@ For example, there is a call-stack dump: """ +def locate_sourceMappingURL_section(wasm_objdump: Path, wasm_file: Path) -> bool: + """ + Figure out if the wasm file has a sourceMappingURL section. + """ + 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) + + for line in outputs: + line = line.strip() + if "sourceMappingURL" in line: + return True + + return False + + def get_code_section_start(wasm_objdump: Path, wasm_file: Path) -> int: """ Find the start offset of Code section in a wasm file. @@ -62,15 +84,6 @@ def get_code_section_start(wasm_objdump: Path, wasm_file: Path) -> int: ) 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: @@ -79,7 +92,7 @@ def get_code_section_start(wasm_objdump: Path, wasm_file: Path) -> int: return -1 -def get_line_info_from_function_addr( +def get_line_info_from_function_addr_dwarf( dwarf_dump: Path, wasm_file: Path, offset: int ) -> tuple[str, str, str, str]: """ @@ -126,7 +139,7 @@ def get_dwarf_tag_value(tag: str, line: str) -> str: return m.groups()[0] -def get_line_info_from_function_name( +def get_line_info_from_function_name_dwarf( dwarf_dump: Path, wasm_file: Path, function_name: str ) -> tuple[str, str, str]: """ @@ -160,6 +173,51 @@ def get_line_info_from_function_name( return (function_name, function_file, function_line) +def get_line_info_from_function_addr_sourcemapping( + emsymbolizer: Path, wasm_file: Path, offset: int +) -> tuple[str, str, str, str]: + """ + Find the location info of a given offset in a wasm file which is compiled with emcc. + + {emsymbolizer} {wasm_file} {offset of file} + + there usually are two lines: + ?? + relative path to source file:line:column + """ + debug_info_source = wasm_file.with_name(f"{wasm_file.name}.map") + cmd = f"{emsymbolizer} -t code -f {debug_info_source} {wasm_file} {offset}" + p = subprocess.run( + shlex.split(cmd), + check=False, + capture_output=True, + text=True, + universal_newlines=True, + cwd=Path.cwd(), + ) + outputs = p.stdout.split(os.linesep) + + function_name, function_file = "", "unknown" + function_line, function_column = "?", "?" + + for line in outputs: + line = line.strip() + + if not line: + continue + + m = re.match("(.*):(\d+):(\d+)", line) + if m: + function_file, function_line, function_column = m.groups() + continue + else: + # it's always ??, not sure about that + if "??" != line: + function_name = line + + return (function_name, function_file, function_line, function_column) + + def parse_line_info(line_info: str) -> tuple[str, str, str]: """ line_info -> [file, line, column] @@ -250,6 +308,7 @@ def main(): action="store_true", help="use call stack without addresses or from fast interpreter mode", ) + parser.add_argument("--emsdk", type=Path, help="path to emsdk") args = parser.parse_args() wasm_objdump = args.wabt.joinpath("bin/wasm-objdump") @@ -261,6 +320,15 @@ def main(): llvm_cxxfilt = args.wasi_sdk.joinpath("bin/llvm-cxxfilt") assert llvm_cxxfilt.exists() + emcc_production = locate_sourceMappingURL_section(wasm_objdump, args.wasm_file) + if emcc_production: + if args.emsdk is None: + print("Please provide the path to emsdk via --emsdk") + return -1 + + emsymbolizer = args.emsdk.joinpath("upstream/emscripten/emsymbolizer") + assert emsymbolizer.exists() + code_section_start = get_code_section_start(wasm_objdump, args.wasm_file) if code_section_start == -1: return -1 @@ -281,6 +349,7 @@ def main(): _, offset, index = splitted if args.no_addr: + # FIXME: w/ emcc production if not index.startswith("$f"): # E.g. _start or Text format print(f"{i}: {index}") continue @@ -290,22 +359,40 @@ def main(): print(f"{i}: {line}") continue - line_info = get_line_info_from_function_name( - llvm_dwarf_dump, args.wasm_file, function_index_to_name[index] - ) + if not emcc_production: + _, function_file, function_line = ( + get_line_info_from_function_name_dwarf( + llvm_dwarf_dump, + args.wasm_file, + function_index_to_name[index], + ) + ) + else: + _, function_file, function_line = _, "unknown", "?" - _, function_file, function_line = line_info function_name = demangle(llvm_cxxfilt, function_index_to_name[index]) print(f"{i}: {function_name}") print(f"\tat {function_file}:{function_line}") else: offset = int(offset, 16) + # match the algorithm in wasm_interp_create_call_stack() + # either a *offset* to *code* section start + # or a *offset* in a file + assert offset > code_section_start offset = offset - code_section_start - function_name, function_file, function_line, function_column = ( - get_line_info_from_function_addr( - llvm_dwarf_dump, args.wasm_file, offset + + if emcc_production: + function_name, function_file, function_line, function_column = ( + get_line_info_from_function_addr_sourcemapping( + emsymbolizer, args.wasm_file, offset + ) + ) + else: + function_name, function_file, function_line, function_column = ( + get_line_info_from_function_addr_dwarf( + llvm_dwarf_dump, args.wasm_file, offset + ) ) - ) # if can't parse function_name, use name section or if function_name == "":