Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions test/core/test_dwarf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <emscripten.h>

EM_JS(int, out_to_js, (int x), {})

class MyClass {
public:
void foo();
void bar();
};

void __attribute__((noinline)) MyClass::foo() {
out_to_js(0); // line 12
out_to_js(1);
out_to_js(2);
}

void __attribute__((always_inline)) MyClass::bar() {
out_to_js(3);
__builtin_trap(); // line 19
}

int main() {
MyClass mc;
mc.foo();
mc.bar();
}
88 changes: 63 additions & 25 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -9639,12 +9639,49 @@ def check_dwarf_loc_info(address, funcs, locs):
for loc in locs:
self.assertIn(loc, out)

def check_source_map_loc_info(address, loc):
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)
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.
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],
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])

# 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],
out_to_js_call_loc[0])
check_source_map_loc_info(unreachable_addr, unreachable_func[0],
unreachable_loc[0])

# 3. Test DWARF only
self.run_process([EMCC, test_file(src), '-g', '-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_dwarf_loc_info(unreachable_addr, unreachable_func, unreachable_loc)

# -- C program test --
# We test two locations within test_dwarf.c:
# out_to_js(0); // line 6
# __builtin_trap(); // line 13
Expand All @@ -9667,31 +9704,32 @@ def check_source_map_loc_info(address, loc):
# The first one corresponds to the innermost inlined location.
unreachable_loc = ['test_dwarf.c:13:3', 'test_dwarf.c:18:3']

# 1. Test DWARF + source map together
# For DWARF, we check for the full inlined info for both function names and
# source locations. Source maps provide neither function names nor inlined
# info. So we only check for the source location of the outermost function.
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_loc[0])
check_dwarf_loc_info(unreachable_addr, unreachable_func, unreachable_loc)
check_source_map_loc_info(unreachable_addr, 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('core/test_dwarf.c'),
'-gsource-map', '-O1', '-o', 'test_dwarf.js'])
check_source_map_loc_info(out_to_js_call_addr, out_to_js_call_loc[0])
check_source_map_loc_info(unreachable_addr, unreachable_loc[0])
do_tests('core/test_dwarf.c')

# 3. Test DWARF only
self.run_process([EMCC, test_file('core/test_dwarf.c'),
'-g', '-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_dwarf_loc_info(unreachable_addr, unreachable_func, unreachable_loc)
# -- C++ program test --
# We test two locations within test_dwarf.cpp:
# out_to_js(0); // line 12
# __builtin_trap(); // line 19
self.run_process([EMCC, test_file('core/test_dwarf.cpp'),
'-g', '-gsource-map', '-O1', '-o', 'test_dwarf.js'])
# Address of out_to_js(0) within MyClass::foo(), uninlined
out_to_js_call_addr = self.get_instr_addr('call\t0', 'test_dwarf.wasm')
# Address of __builtin_trap() within MyClass::bar(), inlined into main()
unreachable_addr = self.get_instr_addr('unreachable', 'test_dwarf.wasm')

# Function name of out_to_js(0) within MyClass::foo(), uninlined
out_to_js_call_func = ['MyClass::foo()']
# Function names of __builtin_trap() within MyClass::bar(), inlined into
# main(). The first one corresponds to the innermost inlined function.
unreachable_func = ['MyClass::bar()', 'main']

# Source location of out_to_js(0) within MyClass::foo(), uninlined
out_to_js_call_loc = ['test_dwarf.cpp:12:3']
# Source locations of __builtin_trap() within MyClass::bar(), inlined into
# main(). The first one corresponds to the innermost inlined location.
unreachable_loc = ['test_dwarf.cpp:19:3', 'test_dwarf.cpp:25:6']

do_tests('core/test_dwarf.cpp')

def test_emsymbolizer_functions(self):
'Test emsymbolizer use cases that only provide function-granularity info'
Expand Down
8 changes: 7 additions & 1 deletion tools/emsymbolizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class Location:
def __init__(self):
self.version = None
self.sources = []
self.funcs = []
self.mappings = {}
self.offsets = []

Expand All @@ -129,6 +130,7 @@ def parse(self, filename):

self.version = source_map_json['version']
self.sources = source_map_json['sources']
self.funcs = source_map_json['names']

chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
vlq_map = {c: i for i, c in enumerate(chars)}
Expand Down Expand Up @@ -156,6 +158,7 @@ def decodeVLQ(string):
src = 0
line = 1
col = 1
func = 0
for segment in source_map_json['mappings'].split(','):
data = decodeVLQ(segment)
info = []
Expand All @@ -170,7 +173,9 @@ def decodeVLQ(string):
if len(data) >= 4:
col += data[3]
info.append(col)
# TODO: see if we need the name, which is the next field (data[4])
if len(data) == 5:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be >= 5 like above on line 173?

func += data[4]
info.append(func)

self.mappings[offset] = WasmSourceMap.Location(*info)
self.offsets.append(offset)
Expand Down Expand Up @@ -208,6 +213,7 @@ def lookup(self, offset, lower_bound=None):
self.sources[info.source] if info.source is not None else None,
info.line,
info.column,
self.funcs[info.func] if info.func is not None else None,
)


Expand Down
Loading