From 13453812479566db1efccb56c14293fc3a4c1c56 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Tue, 13 Jan 2026 23:01:06 +0000 Subject: [PATCH 1/2] Make source map names field optional This makes 'names' field generation in source maps optional, turned off by default. This may be necessary if the source map size is often prohibitively large as in #26100, which I'm not sure is the common case yet. --- src/settings_internal.js | 10 +++++++--- test/test_other.py | 16 ++++++++++++---- tools/building.py | 4 +++- tools/cmdline.py | 10 ++++++++-- tools/wasm-sourcemap.py | 23 ++++++++++++++++------- 5 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/settings_internal.js b/src/settings_internal.js index e656c83fd455f..1e558e4afd668 100644 --- a/src/settings_internal.js +++ b/src/settings_internal.js @@ -189,10 +189,14 @@ var EXPECT_MAIN = true; // If true, building against Emscripten's wasm heap memory profiler. var MEMORYPROFILER = false; -// Set automatically to : -// - 1 when using `-gsource-map` -// - 2 when using `gsource-map=inline` (embed sources content in source map) +// Source map related options. You can specify both like +// -gsource-map=inline,names +// -gsource-map var GENERATE_SOURCE_MAP = 0; +// -gsource-map=inline +var EMBED_SOURCE_MAP_SOURCE = 0; +// -gsource-map=names +var GENERATE_SOURCE_MAP_NAMES = 0; var GENERATE_DWARF = false; diff --git a/test/test_other.py b/test/test_other.py index a39eadcbd75d1..801f42849d810 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -9626,7 +9626,8 @@ 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): @@ -9634,8 +9635,8 @@ def do_tests(src): # 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. - self.run_process([EMCC, test_file(src), '-g', '-gsource-map', '-O1', '-o', - 'test_dwarf.js']) + self.run_process([EMCC, test_file(src), '-g', '-gsource-map=names', '-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], @@ -9650,13 +9651,20 @@ def do_tests(src): # 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', + self.run_process([EMCC, test_file(src), '-gsource-map=names', '-O1', '-o', 'test_dwarf.js']) check_source_map_loc_info(out_to_js_call_addr, out_to_js_call_func[0], out_to_js_call_loc[0]) check_source_map_loc_info(unreachable_addr, unreachable_func[0], unreachable_loc[0]) + # 2-1. Test source map only with names section turned off + self.run_process([EMCC, test_file(src), '-gsource-map', '-O1', '-o', + 'test_dwarf.js']) + check_source_map_loc_info(out_to_js_call_addr, None, + out_to_js_call_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', 'test_dwarf.js']) diff --git a/tools/building.py b/tools/building.py index dcdc03fc458ef..6e2911863f778 100644 --- a/tools/building.py +++ b/tools/building.py @@ -1172,8 +1172,10 @@ def emit_wasm_source_map(wasm_file, map_file, final_wasm): if settings.SOURCE_MAP_PREFIXES: sourcemap_cmd += ['--prefix', *settings.SOURCE_MAP_PREFIXES] - if settings.GENERATE_SOURCE_MAP == 2: + if settings.EMBED_SOURCE_MAP_SOURCE: sourcemap_cmd += ['--sources'] + if settings.GENERATE_SOURCE_MAP_NAMES: + sourcemap_cmd += ['--names'] # TODO(sbc): Convert to using library internal API instead of running `main` here rtn = wasm_sourcemap.main(sourcemap_cmd) diff --git a/tools/cmdline.py b/tools/cmdline.py index 8ce4faf2a7a12..765a14935a6af 100644 --- a/tools/cmdline.py +++ b/tools/cmdline.py @@ -401,9 +401,15 @@ def consume_arg_file(): settings.SEPARATE_DWARF = True settings.GENERATE_DWARF = 1 settings.DEBUG_LEVEL = 3 - elif debug_level in ['source-map', 'source-map=inline']: - settings.GENERATE_SOURCE_MAP = 1 if debug_level == 'source-map' else 2 + elif debug_level.startswith('source-map'): + settings.GENERATE_SOURCE_MAP = 1 newargs[i] = '-g' + if '=' in debug_level: + source_map_options = debug_level.split('=')[1].split(',') + if 'inline' in source_map_options: + settings.EMBED_SOURCE_MAP_SOURCE = 1 + if 'names' in source_map_options: + settings.GENERATE_SOURCE_MAP_NAMES = 1 elif debug_level == 'z': # Ignore `-gz`. We don't support debug info compression. pass diff --git a/tools/wasm-sourcemap.py b/tools/wasm-sourcemap.py index 2e52e136c5b63..33df172dcdad6 100755 --- a/tools/wasm-sourcemap.py +++ b/tools/wasm-sourcemap.py @@ -45,6 +45,7 @@ def parse_args(args): parser.add_argument('--dwarfdump', help="path to llvm-dwarfdump executable") parser.add_argument('--dwarfdump-output', nargs='?', help=argparse.SUPPRESS) parser.add_argument('--basepath', help='base path for source files, which will be relative to this') + parser.add_argument('--names', action='store_true', help='generate function names in the names field') return parser.parse_args(args) @@ -399,12 +400,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 options.names: + # 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: @@ -480,7 +486,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 options.names: + func_ranges = extract_func_ranges(debug_info) + else: + func_ranges = [] return entries, func_ranges From d6a3039cdfe50346cef1dbd7954dd66754750f6d Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 14 Jan 2026 22:23:36 +0000 Subject: [PATCH 2/2] Make internal settings have prefix 'SOURCE_MAP_' GENERATE_SOURCE_MAP -> SOURCE_MAP_GENERATE EMBED_SOURCE_MAP_SOURCE -> SOURCE_MAP_EMBED_SOURCE GENERATE_SOURCE_MAP_NAMES -> SOURCE_MAP_GENERATE_NAMES --- src/settings_internal.js | 6 +++--- tools/building.py | 8 ++++---- tools/cmdline.py | 12 ++++++------ tools/emscripten.py | 4 ++-- tools/link.py | 12 ++++++------ 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/settings_internal.js b/src/settings_internal.js index 1e558e4afd668..2c4bee36f3abe 100644 --- a/src/settings_internal.js +++ b/src/settings_internal.js @@ -192,11 +192,11 @@ var MEMORYPROFILER = false; // Source map related options. You can specify both like // -gsource-map=inline,names // -gsource-map -var GENERATE_SOURCE_MAP = 0; +var SOURCE_MAP_GENERATE = 0; // -gsource-map=inline -var EMBED_SOURCE_MAP_SOURCE = 0; +var SOURCE_MAP_EMBED_SOURCE = 0; // -gsource-map=names -var GENERATE_SOURCE_MAP_NAMES = 0; +var SOURCE_MAP_GENERATE_NAMES = 0; var GENERATE_DWARF = false; diff --git a/tools/building.py b/tools/building.py index 6e2911863f778..79f2706413c6c 100644 --- a/tools/building.py +++ b/tools/building.py @@ -184,7 +184,7 @@ def lld_flags_for_executable(external_symbols): # those things. if (not settings.GENERATE_DWARF and not settings.EMIT_SYMBOL_MAP and - not settings.GENERATE_SOURCE_MAP and + not settings.SOURCE_MAP_GENERATE and not settings.EMIT_NAME_SECTION and not settings.ASYNCIFY): cmd.append('--strip-debug') @@ -1172,9 +1172,9 @@ def emit_wasm_source_map(wasm_file, map_file, final_wasm): if settings.SOURCE_MAP_PREFIXES: sourcemap_cmd += ['--prefix', *settings.SOURCE_MAP_PREFIXES] - if settings.EMBED_SOURCE_MAP_SOURCE: + if settings.SOURCE_MAP_EMBED_SOURCE: sourcemap_cmd += ['--sources'] - if settings.GENERATE_SOURCE_MAP_NAMES: + if settings.SOURCE_MAP_GENERATE_NAMES: sourcemap_cmd += ['--names'] # TODO(sbc): Convert to using library internal API instead of running `main` here @@ -1259,7 +1259,7 @@ def run_binaryen_command(tool, infile, outfile=None, args=None, debug=False, std # we must tell binaryen to update it # TODO: all tools should support source maps; wasm-ctor-eval does not atm, # for example - if settings.GENERATE_SOURCE_MAP and outfile and tool in ['wasm-opt', 'wasm-emscripten-finalize', 'wasm-metadce']: + if settings.SOURCE_MAP_GENERATE and outfile and tool in ['wasm-opt', 'wasm-emscripten-finalize', 'wasm-metadce']: cmd += [f'--input-source-map={infile}.map'] cmd += [f'--output-source-map={outfile}.map'] if shared.SKIP_SUBPROCS: diff --git a/tools/cmdline.py b/tools/cmdline.py index 765a14935a6af..774893eaf5cb8 100644 --- a/tools/cmdline.py +++ b/tools/cmdline.py @@ -365,13 +365,13 @@ def consume_arg_file(): if debug_level == 0: # Set these explicitly so -g0 overrides previous -g on the cmdline settings.GENERATE_DWARF = 0 - settings.GENERATE_SOURCE_MAP = 0 + settings.SOURCE_MAP_GENERATE = 0 settings.EMIT_NAME_SECTION = 0 elif debug_level > 1: settings.EMIT_NAME_SECTION = 1 # if we don't need to preserve LLVM debug info, do not keep this flag # for clang - if debug_level < 3 and not (settings.GENERATE_SOURCE_MAP or settings.SEPARATE_DWARF): + if debug_level < 3 and not (settings.SOURCE_MAP_GENERATE or settings.SEPARATE_DWARF): newargs[i] = '-g0' else: if debug_level == 3: @@ -381,7 +381,7 @@ def consume_arg_file(): # Lower this to -g3, and report a warning. newargs[i] = '-g3' diagnostics.warning('deprecated', 'please replace -g4 with -gsource-map') - settings.GENERATE_SOURCE_MAP = 1 + settings.SOURCE_MAP_GENERATE = 1 elif debug_level > 4: exit_with_error("unknown argument: '%s'", arg) else: @@ -402,14 +402,14 @@ def consume_arg_file(): settings.GENERATE_DWARF = 1 settings.DEBUG_LEVEL = 3 elif debug_level.startswith('source-map'): - settings.GENERATE_SOURCE_MAP = 1 + settings.SOURCE_MAP_GENERATE = 1 newargs[i] = '-g' if '=' in debug_level: source_map_options = debug_level.split('=')[1].split(',') if 'inline' in source_map_options: - settings.EMBED_SOURCE_MAP_SOURCE = 1 + settings.SOURCE_MAP_EMBED_SOURCE = 1 if 'names' in source_map_options: - settings.GENERATE_SOURCE_MAP_NAMES = 1 + settings.SOURCE_MAP_GENERATE_NAMES = 1 elif debug_level == 'z': # Ignore `-gz`. We don't support debug info compression. pass diff --git a/tools/emscripten.py b/tools/emscripten.py index ce54b89db5b41..6e882b85726b8 100644 --- a/tools/emscripten.py +++ b/tools/emscripten.py @@ -565,7 +565,7 @@ def finalize_wasm(infile, outfile, js_syms): if infile != outfile: shutil.copy(infile, outfile) - if settings.GENERATE_SOURCE_MAP: + if settings.SOURCE_MAP_GENERATE: building.emit_wasm_source_map(infile, outfile + '.map', outfile) building.save_intermediate(outfile + '.map', 'base_wasm.map') base_url = settings.SOURCE_MAP_BASE + os.path.basename(outfile) + '.map' @@ -601,7 +601,7 @@ def finalize_wasm(infile, outfile, js_syms): metadata = get_metadata(outfile, outfile, modify_wasm, args) - if settings.GENERATE_SOURCE_MAP: + if settings.SOURCE_MAP_GENERATE: building.save_intermediate(outfile + '.map', 'post_finalize.map') expected_exports = set(settings.EXPORTED_FUNCTIONS) diff --git a/tools/link.py b/tools/link.py index 7ab005898ddf3..9ad7499129b3c 100644 --- a/tools/link.py +++ b/tools/link.py @@ -764,7 +764,7 @@ def setup_sanitizers(options): if settings.MEMORY64: exit_with_error('MEMORY64 does not yet work with ASAN') - if settings.GENERATE_SOURCE_MAP: + if settings.SOURCE_MAP_GENERATE: settings.LOAD_SOURCE_MAP = 1 @@ -875,9 +875,9 @@ def phase_linker_setup(options, linker_args): # noqa: C901, PLR0912, PLR0915 settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$addOnInit', '$addOnExit'] # TODO: support source maps with js_transform - if options.js_transform and settings.GENERATE_SOURCE_MAP: + if options.js_transform and settings.SOURCE_MAP_GENERATE: logger.warning('disabling source maps because a js transform is being done') - settings.GENERATE_SOURCE_MAP = 0 + settings.SOURCE_MAP_GENERATE = 0 # options.output_file is the user-specified one, target is what we will generate if options.output_file: @@ -1633,9 +1633,9 @@ def limit_incoming_module_api(): if settings.WASM_BIGINT: settings.LEGALIZE_JS_FFI = 0 - if settings.SINGLE_FILE and settings.GENERATE_SOURCE_MAP: + if settings.SINGLE_FILE and settings.SOURCE_MAP_GENERATE: diagnostics.warning('emcc', 'SINGLE_FILE disables source map support (which requires a .map file)') - settings.GENERATE_SOURCE_MAP = 0 + settings.SOURCE_MAP_GENERATE = 0 if options.use_closure_compiler == 2 and not settings.WASM2JS: exit_with_error('closure compiler mode 2 assumes the code is asm.js, so not meaningful for wasm') @@ -1760,7 +1760,7 @@ def get_full_import_name(name): diagnostics.warning('experimental', '-sJSPI (ASYNCIFY=2) is still experimental') if settings.WASM2JS: - if settings.GENERATE_SOURCE_MAP: + if settings.SOURCE_MAP_GENERATE: exit_with_error('wasm2js does not support source maps yet (debug in wasm for now)') if settings.MEMORY64: exit_with_error('wasm2js does not support MEMORY64')