From b166390c905cdb534490a6e58c002185eb373206 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 22 Jan 2026 07:41:13 +0000 Subject: [PATCH 1/2] Remove source map 'names' field support We used 'names' field to encode the enclosing function name of a given offset, so that we can provide the correct source location + function info that can be used in displaying stack traces. I wasn't aware JS uses it for a very different purpose. JS uses it to display original variable/function names for minified names. In JS an 'offset' is a location in the minified JS code, and if that location happens to point to a minified variable name `a` whose original name was `apple`, its mapping contains `apple`. So while we can use this field to support emsymbolizer, the browser devtools is unlikely to ever implement the support for our 'names' field for stack traces, which is basically unrelated from what JS does. So this removes the support for the 'names' field. But it doesn't actually remove the implementation; it just puts it behind a fake flag that is set to `False` for now. The reason is, we want to provide this function info anyway for better stack traces support, and "Scopes" proposal for source maps, which was recently added, seems designed for this purpose. And I think we can reuse much of our current implementation (`extract_func_ranges`) to generate that format. --- ChangeLog.md | 3 +++ test/test_other.py | 26 +++++++++++--------------- tools/wasm-sourcemap.py | 24 +++++++++++++++++------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 3969c0cd33209..8f572dd26afb1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works. 4.0.24 (in development) ----------------------- +- Source map's 'names' field support is removed, because the way we used it was + inconsistent with JS and was not supported in browser devtools. We plan to + provide this information using Scopes encoding later. (#26149) - compiler-rt, libcxx, libcxxabi, and libunwind were updated to LLVM 21.1.8. - (#26036, #26045, and #26058) - Calling pthread_create in a single-threaded build will now return ENOTSUP diff --git a/test/test_other.py b/test/test_other.py index 80518c7af7264..95eb63179f584 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -9651,36 +9651,32 @@ def check_source_map_loc_info(address, func, loc): out = self.run_process( [emsymbolizer, '-s', 'sourcemap', 'test_dwarf.wasm', address], stdout=PIPE).stdout - self.assertIn(func, out) + if func: + self.assertIn(func, out) self.assertIn(loc, out) def do_tests(src): # 1. Test DWARF + source map together - # For DWARF, we check for the full inlined info for both function names and - # source locations. Source maps does not provide inlined info. So we only - # check for the info of the outermost function. + # For DWARF, we check for the full inlined info for both function names + # and source locations. Source maps does not provide inlining information + # and function name, so we only check for the info of the outermost + # location. self.run_process([EMCC, test_file(src), '-g', '-gsource-map', '-O1', '-o', 'test_dwarf.js']) check_dwarf_loc_info(out_to_js_call_addr, out_to_js_call_func, out_to_js_call_loc) - check_source_map_loc_info(out_to_js_call_addr, out_to_js_call_func[0], + check_source_map_loc_info(out_to_js_call_addr, None, out_to_js_call_loc[0]) check_dwarf_loc_info(unreachable_addr, unreachable_func, unreachable_loc) - # Source map shows the original (inlined) source location with the original - # function name - check_source_map_loc_info(unreachable_addr, unreachable_func[0], - unreachable_loc[0]) + # Source map shows the original (inlined) source location + check_source_map_loc_info(unreachable_addr, None, unreachable_loc[0]) # 2. Test source map only - # The addresses, function names, and source locations are the same across - # the builds because they are relative offsets from the code section, so we - # don't need to recompute them self.run_process([EMCC, test_file(src), '-gsource-map', '-O1', '-o', 'test_dwarf.js']) - check_source_map_loc_info(out_to_js_call_addr, out_to_js_call_func[0], + check_source_map_loc_info(out_to_js_call_addr, None, out_to_js_call_loc[0]) - check_source_map_loc_info(unreachable_addr, unreachable_func[0], - unreachable_loc[0]) + check_source_map_loc_info(unreachable_addr, None, unreachable_loc[0]) # 3. Test DWARF only self.run_process([EMCC, test_file(src), '-g', '-O1', '-o', diff --git a/tools/wasm-sourcemap.py b/tools/wasm-sourcemap.py index c3768cbed4739..a29a4cefae848 100755 --- a/tools/wasm-sourcemap.py +++ b/tools/wasm-sourcemap.py @@ -31,6 +31,8 @@ logger = logging.getLogger('wasm-sourcemap') +# FIXME: Generate Scopes info +generate_scopes = False def parse_args(args): parser = argparse.ArgumentParser(prog='wasm-sourcemap.py', description=__doc__) @@ -399,12 +401,17 @@ def read_dwarf_info(wasm, options): logger.debug('Reading DWARF information from %s' % wasm) if not os.path.exists(options.dwarfdump): utils.exit_with_error('llvm-dwarfdump not found: ' + options.dwarfdump) - # We need only three tags in the debug info: DW_TAG_compile_unit for - # source location, and DW_TAG_subprogram and DW_TAG_inlined_subroutine - # for the function ranges. - dwarfdump_cmd = [options.dwarfdump, '-debug-info', '-debug-line', wasm, - '-t', 'DW_TAG_compile_unit', '-t', 'DW_TAG_subprogram', - '-t', 'DW_TAG_inlined_subroutine'] + dwarfdump_cmd = [options.dwarfdump, '-debug-info', '-debug-line', wasm] + if generate_scopes: + # We need only three tags in the debug info: DW_TAG_compile_unit for + # source location, and DW_TAG_subprogram and DW_TAG_inlined_subroutine + # for the function ranges. + dwarfdump_cmd += ['-t', 'DW_TAG_compile_unit', '-t', 'DW_TAG_subprogram', + '-t', 'DW_TAG_inlined_subroutine'] + else: + # We only need the top-level DW_TAG_compile_unit tags when not generating + # the names field + dwarfdump_cmd += ['--recurse-depth=0'] proc = shared.check_call(dwarfdump_cmd, stdout=shared.PIPE) output = proc.stdout else: @@ -481,7 +488,10 @@ def read_dwarf_info(wasm, options): # return entries sorted by the address field entries = sorted(entries, key=lambda entry: entry['address']) - func_ranges = extract_func_ranges(debug_info) + if generate_scopes: + func_ranges = extract_func_ranges(debug_info) + else: + func_ranges = [] return entries, func_ranges From 1879e67b19bf40957d23de6e9e906b01204e6619 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 22 Jan 2026 07:54:36 +0000 Subject: [PATCH 2/2] ruff fix --- tools/wasm-sourcemap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/wasm-sourcemap.py b/tools/wasm-sourcemap.py index a29a4cefae848..14bd45bc0187e 100755 --- a/tools/wasm-sourcemap.py +++ b/tools/wasm-sourcemap.py @@ -34,6 +34,7 @@ # FIXME: Generate Scopes info generate_scopes = False + def parse_args(args): parser = argparse.ArgumentParser(prog='wasm-sourcemap.py', description=__doc__) parser.add_argument('wasm', help='wasm file')