Skip to content

Commit

Permalink
-gside option: emit DWARF in a wasm on the side (#10568)
Browse files Browse the repository at this point in the history
When used, the main wasm file has no dwarf in it, but a
.debug.wasm file on the side does. This is similar to
split-dwarf in gcc/clang, but simpler. There is also a
custom section in the wasm that refers to the debug
file. We may evolve this further but for now it should be
useful.

Implemented internally using a `SIDE_DEBUG` flag.
  • Loading branch information
kripken authored Feb 26, 2020
1 parent 477709d commit 4a15aed
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 0 deletions.
8 changes: 8 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2819,6 +2819,11 @@ def check_bad_eq(arg):
newargs[i] = '-g'
shared.Settings.FULL_DWARF = 1
shared.warning('gforce_dwarf is a temporary option that will eventually disappear')
elif requested_level.startswith('side'):
# Emit full DWARF but also emit it in a file on the side
newargs[i] = '-g'
shared.Settings.FULL_DWARF = 1
shared.Settings.SIDE_DEBUG = 1
# a non-integer level can be something like -gline-tables-only. keep
# the flag for the clang frontend to emit the appropriate DWARF info.
# set the emscripten debug level to 3 so that we do not remove that
Expand Down Expand Up @@ -3337,6 +3342,9 @@ def run_closure_compiler(final):
with open(final, 'w') as f:
f.write(js)

if shared.Settings.FULL_DWARF and shared.Settings.SIDE_DEBUG:
shared.Building.emit_debug_on_side(wasm_binary_target)


def modularize():
global final
Expand Down
8 changes: 8 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1492,6 +1492,14 @@ var ELIMINATE_DUPLICATE_FUNCTIONS_PASSES = 5;
// the ctors.
var EVAL_CTORS = 0;

// Whether to emit DWARF in a wasm file on the side (this is not called
// "split"/"separate" because there is already a DWARF concept by that name).
// When DWARF is on the side, the main file has no DWARF info, while the side
// file, ending in .debug.wasm, has the same wasm binary + all the debug
// sections.
// This has no effect if DWARF is not being emitted.
var SIDE_DEBUG = 0;

// see http://kripken.github.io/emscripten-site/docs/debugging/CyberDWARF.html
// [fastcomp-only]
var CYBERDWARF = 0;
Expand Down
15 changes: 15 additions & 0 deletions tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -9033,6 +9033,21 @@ def test(infile, source_map_added_dir=''):
ensure_dir('inner')
test('inner/a.cpp', 'inner')

@no_fastcomp('dwarf')
def test_side_debug(self):
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-gforce_dwarf'])
self.assertExists('a.out.wasm')
self.assertNotExists('a.out.wasm.debug.wasm')
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-gside'])
self.assertExists('a.out.wasm')
self.assertExists('a.out.wasm.debug.wasm')
self.assertLess(os.path.getsize('a.out.wasm'), os.path.getsize('a.out.wasm.debug.wasm'))
# the special section should also exist, that refers to the side debug file
with open('a.out.wasm', 'rb') as f:
wasm = f.read()
self.assertIn(b'external_debug_info', wasm)
self.assertIn(b'a.out.wasm.debug.wasm', wasm)

def test_wasm_producers_section(self):
# no producers section by default
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c')])
Expand Down
22 changes: 22 additions & 0 deletions tools/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ def replace_or_append_suffix(filename, new_suffix):
LLVM_INTERPRETER = os.path.expanduser(build_llvm_tool_path(exe_suffix('lli')))
LLVM_COMPILER = os.path.expanduser(build_llvm_tool_path(exe_suffix('llc')))
LLVM_DWARFDUMP = os.path.expanduser(build_llvm_tool_path(exe_suffix('llvm-dwarfdump')))
LLVM_OBJCOPY = os.path.expanduser(build_llvm_tool_path(exe_suffix('llvm-objcopy')))
WASM_LD = os.path.expanduser(build_llvm_tool_path(exe_suffix('wasm-ld')))

EMSCRIPTEN = path_from_root('emscripten.py')
Expand Down Expand Up @@ -2756,6 +2757,27 @@ def wasm2js(js_file, wasm_file, opt_level, minify_whitespace, use_closure_compil
f.write(all_js)
return js_file

@staticmethod
def emit_debug_on_side(wasm_file):
# extract the DWARF info from the main file, and leave the wasm with
# debug into as a file on the side
# TODO: emit only debug sections in the side file, and not the entire
# wasm as well
wasm_file_with_dwarf = wasm_file + '.debug.wasm'
shutil.move(wasm_file, wasm_file_with_dwarf)
run_process([LLVM_OBJCOPY, '--remove-section=.debug*', wasm_file_with_dwarf, wasm_file])

# embed a section in the main wasm to point to the file with external DWARF,
# see https://yurydelendik.github.io/webassembly-dwarf/#external-DWARF
section_name = b'\x13external_debug_info' # section name, including prefixed size
contents = asbytes(wasm_file_with_dwarf)
section_size = len(section_name) + len(contents)
with open(wasm_file, 'ab') as f:
f.write(b'\0') # user section is code 0
f.write(WebAssembly.lebify(section_size))
f.write(section_name)
f.write(contents)

@staticmethod
def apply_wasm_memory_growth(js_file):
logger.debug('supporting wasm memory growth with pthreads')
Expand Down

0 comments on commit 4a15aed

Please sign in to comment.