diff --git a/src/settings_internal.js b/src/settings_internal.js index e656c83fd455f..2c4bee36f3abe 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) -var GENERATE_SOURCE_MAP = 0; +// Source map related options. You can specify both like +// -gsource-map=inline,names +// -gsource-map +var SOURCE_MAP_GENERATE = 0; +// -gsource-map=inline +var SOURCE_MAP_EMBED_SOURCE = 0; +// -gsource-map=names +var SOURCE_MAP_GENERATE_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..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,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.SOURCE_MAP_EMBED_SOURCE: sourcemap_cmd += ['--sources'] + if settings.SOURCE_MAP_GENERATE_NAMES: + sourcemap_cmd += ['--names'] # TODO(sbc): Convert to using library internal API instead of running `main` here rtn = wasm_sourcemap.main(sourcemap_cmd) @@ -1257,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 8ce4faf2a7a12..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: @@ -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.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.SOURCE_MAP_EMBED_SOURCE = 1 + if 'names' in source_map_options: + 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') 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