Skip to content

Commit 89ff6c9

Browse files
authored
Move callstack handling code to library_stack_trace (#21575)
Also move unused `stackTrace` function to `library_legacy.js` since we don't use it anywhere.
1 parent 1082728 commit 89ff6c9

8 files changed

+350
-350
lines changed

src/library.js

Lines changed: 0 additions & 339 deletions
Original file line numberDiff line numberDiff line change
@@ -2370,110 +2370,6 @@ addToLibrary({
23702370
}
23712371
},
23722372

2373-
$getCallstack__deps: ['$jsStackTrace', '$warnOnce'],
2374-
$getCallstack__docs: '/** @param {number=} flags */',
2375-
$getCallstack: function(flags) {
2376-
var callstack = jsStackTrace();
2377-
2378-
// Find the symbols in the callstack that corresponds to the functions that
2379-
// report callstack information, and remove everything up to these from the
2380-
// output.
2381-
var iThisFunc = callstack.lastIndexOf('_emscripten_log');
2382-
var iThisFunc2 = callstack.lastIndexOf('_emscripten_get_callstack');
2383-
var iNextLine = callstack.indexOf('\n', Math.max(iThisFunc, iThisFunc2))+1;
2384-
callstack = callstack.slice(iNextLine);
2385-
2386-
// If user requested to see the original source stack, but no source map
2387-
// information is available, just fall back to showing the JS stack.
2388-
if (flags & {{{ cDefs.EM_LOG_C_STACK }}} && typeof emscripten_source_map == 'undefined') {
2389-
warnOnce('Source map information is not available, emscripten_log with EM_LOG_C_STACK will be ignored. Build with "--pre-js $EMSCRIPTEN/src/emscripten-source-map.min.js" linker flag to add source map loading to code.');
2390-
flags ^= {{{ cDefs.EM_LOG_C_STACK }}};
2391-
flags |= {{{ cDefs.EM_LOG_JS_STACK }}};
2392-
}
2393-
2394-
// Process all lines:
2395-
var lines = callstack.split('\n');
2396-
callstack = '';
2397-
// New FF30 with column info: extract components of form:
2398-
// ' Object._main@http://server.com:4324:12'
2399-
var newFirefoxRe = new RegExp('\\s*(.*?)@(.*?):([0-9]+):([0-9]+)');
2400-
// Old FF without column info: extract components of form:
2401-
// ' Object._main@http://server.com:4324'
2402-
var firefoxRe = new RegExp('\\s*(.*?)@(.*):(.*)(:(.*))?');
2403-
// Extract components of form:
2404-
// ' at Object._main (http://server.com/file.html:4324:12)'
2405-
var chromeRe = new RegExp('\\s*at (.*?) \\\((.*):(.*):(.*)\\\)');
2406-
2407-
for (var l in lines) {
2408-
var line = lines[l];
2409-
2410-
var symbolName = '';
2411-
var file = '';
2412-
var lineno = 0;
2413-
var column = 0;
2414-
2415-
var parts = chromeRe.exec(line);
2416-
if (parts && parts.length == 5) {
2417-
symbolName = parts[1];
2418-
file = parts[2];
2419-
lineno = parts[3];
2420-
column = parts[4];
2421-
} else {
2422-
parts = newFirefoxRe.exec(line);
2423-
if (!parts) parts = firefoxRe.exec(line);
2424-
if (parts && parts.length >= 4) {
2425-
symbolName = parts[1];
2426-
file = parts[2];
2427-
lineno = parts[3];
2428-
// Old Firefox doesn't carry column information, but in new FF30, it
2429-
// is present. See https://bugzilla.mozilla.org/show_bug.cgi?id=762556
2430-
column = parts[4]|0;
2431-
} else {
2432-
// Was not able to extract this line for demangling/sourcemapping
2433-
// purposes. Output it as-is.
2434-
callstack += line + '\n';
2435-
continue;
2436-
}
2437-
}
2438-
2439-
var haveSourceMap = false;
2440-
2441-
if (flags & {{{ cDefs.EM_LOG_C_STACK }}}) {
2442-
var orig = emscripten_source_map.originalPositionFor({line: lineno, column: column});
2443-
haveSourceMap = orig?.source;
2444-
if (haveSourceMap) {
2445-
if (flags & {{{ cDefs.EM_LOG_NO_PATHS }}}) {
2446-
orig.source = orig.source.substring(orig.source.replace(/\\/g, "/").lastIndexOf('/')+1);
2447-
}
2448-
callstack += ` at ${symbolName} (${orig.source}:${orig.line}:${orig.column})\n`;
2449-
}
2450-
}
2451-
if ((flags & {{{ cDefs.EM_LOG_JS_STACK }}}) || !haveSourceMap) {
2452-
if (flags & {{{ cDefs.EM_LOG_NO_PATHS }}}) {
2453-
file = file.substring(file.replace(/\\/g, "/").lastIndexOf('/')+1);
2454-
}
2455-
callstack += (haveSourceMap ? (` = ${symbolName}`) : (` at ${symbolName}`)) + ` (${file}:${lineno}:${column})\n`;
2456-
}
2457-
}
2458-
// Trim extra whitespace at the end of the output.
2459-
callstack = callstack.replace(/\s+$/, '');
2460-
return callstack;
2461-
},
2462-
2463-
emscripten_get_callstack__deps: ['$getCallstack', '$lengthBytesUTF8', '$stringToUTF8'],
2464-
emscripten_get_callstack: function(flags, str, maxbytes) {
2465-
var callstack = getCallstack(flags);
2466-
// User can query the required amount of bytes to hold the callstack.
2467-
if (!str || maxbytes <= 0) {
2468-
return lengthBytesUTF8(callstack)+1;
2469-
}
2470-
// Output callstack string as C string to HEAP.
2471-
var bytesWrittenExcludingNull = stringToUTF8(callstack, str, maxbytes);
2472-
2473-
// Return number of bytes written, including null.
2474-
return bytesWrittenExcludingNull+1;
2475-
},
2476-
24772373
$emscriptenLog__deps: ['$getCallstack'],
24782374
$emscriptenLog: (flags, str) => {
24792375
if (flags & {{{ cDefs.EM_LOG_C_STACK | cDefs.EM_LOG_JS_STACK }}}) {
@@ -2541,241 +2437,6 @@ addToLibrary({
25412437
else return lengthBytesUTF8(str);
25422438
},
25432439

2544-
// Generates a representation of the program counter from a line of stack trace.
2545-
// The exact return value depends in whether we are running WASM or JS, and whether
2546-
// the engine supports offsets into WASM. See the function body for details.
2547-
$convertFrameToPC__docs: '/** @returns {number} */',
2548-
$convertFrameToPC__internal: true,
2549-
$convertFrameToPC: (frame) => {
2550-
#if !USE_OFFSET_CONVERTER
2551-
abort('Cannot use convertFrameToPC (needed by __builtin_return_address) without -sUSE_OFFSET_CONVERTER');
2552-
#else
2553-
#if ASSERTIONS
2554-
assert(wasmOffsetConverter);
2555-
#endif
2556-
var match;
2557-
2558-
if (match = /\bwasm-function\[\d+\]:(0x[0-9a-f]+)/.exec(frame)) {
2559-
// some engines give the binary offset directly, so we use that as return address
2560-
return +match[1];
2561-
} else if (match = /\bwasm-function\[(\d+)\]:(\d+)/.exec(frame)) {
2562-
// other engines only give function index and offset in the function,
2563-
// so we try using the offset converter. If that doesn't work,
2564-
// we pack index and offset into a "return address"
2565-
return wasmOffsetConverter.convert(+match[1], +match[2]);
2566-
} else if (match = /:(\d+):\d+(?:\)|$)/.exec(frame)) {
2567-
// If we are in js, we can use the js line number as the "return address".
2568-
// This should work for wasm2js. We tag the high bit to distinguish this
2569-
// from wasm addresses.
2570-
return 0x80000000 | +match[1];
2571-
}
2572-
#endif
2573-
// return 0 if we can't find any
2574-
return 0;
2575-
},
2576-
2577-
// Returns a representation of a call site of the caller of this function, in a manner
2578-
// similar to __builtin_return_address. If level is 0, we return the call site of the
2579-
// caller of this function.
2580-
emscripten_return_address__deps: ['$convertFrameToPC', '$jsStackTrace'],
2581-
emscripten_return_address: (level) => {
2582-
var callstack = jsStackTrace().split('\n');
2583-
if (callstack[0] == 'Error') {
2584-
callstack.shift();
2585-
}
2586-
// skip this function and the caller to get caller's return address
2587-
#if MEMORY64
2588-
// MEMORY64 injects and extra wrapper within emscripten_return_address
2589-
// to handle BigInt conversions.
2590-
var caller = callstack[level + 4];
2591-
#else
2592-
var caller = callstack[level + 3];
2593-
#endif
2594-
return convertFrameToPC(caller);
2595-
},
2596-
2597-
$UNWIND_CACHE: {},
2598-
2599-
// This function pulls the JavaScript stack trace and updates UNWIND_CACHE so
2600-
// that our representation of the program counter is mapped to the line of the
2601-
// stack trace for every line in the stack trace. This allows
2602-
// emscripten_pc_get_* to lookup the line of the stack trace from the PC and
2603-
// return meaningful information.
2604-
//
2605-
// Additionally, it saves a copy of the entire stack trace and the return
2606-
// address of the caller. This is because there are two common forms of a
2607-
// stack trace. The first form starts the stack trace at the caller of the
2608-
// function requesting a stack trace. In this case, the function can simply
2609-
// walk down the stack from the return address using emscripten_return_address
2610-
// with increasing values for level. The second form starts the stack trace
2611-
// at the current function. This requires a helper function to get the program
2612-
// counter. This helper function will return the return address. This is the
2613-
// program counter at the call site. But there is a problem: when calling into
2614-
// code that performs stack unwinding, the program counter has changed since
2615-
// execution continued from calling the helper function. So we can't just walk
2616-
// down the stack and expect to see the PC value we got. By caching the call
2617-
// stack, we can call emscripten_stack_unwind with the PC value and use that
2618-
// to unwind the cached stack. Naturally, the PC helper function will have to
2619-
// call emscripten_stack_snapshot to cache the stack. We also return the
2620-
// return address of the caller so the PC helper function does not need to
2621-
// call emscripten_return_address, saving a lot of time.
2622-
//
2623-
// One might expect that a sensible solution is to call the stack unwinder and
2624-
// explicitly tell it how many functions to skip from the stack. However,
2625-
// existing libraries do not work this way. For example, compiler-rt's
2626-
// sanitizer_common library has macros GET_CALLER_PC_BP_SP and
2627-
// GET_CURRENT_PC_BP_SP, which obtains the PC value for the two common cases
2628-
// stated above, respectively. Then, it passes the PC, BP, SP values along
2629-
// until some other function uses them to unwind. On standard machines, the
2630-
// stack can be unwound by treating BP as a linked list. This makes PC
2631-
// unnecessary to walk the stack, since walking is done with BP, which remains
2632-
// valid until the function returns. But on Emscripten, BP does not exist, at
2633-
// least in JavaScript frames, so we have to rely on PC values. Therefore, we
2634-
// must be able to unwind from a PC value that may no longer be on the
2635-
// execution stack, and so we are forced to cache the entire call stack.
2636-
emscripten_stack_snapshot__deps: ['$convertFrameToPC', '$UNWIND_CACHE', '$saveInUnwindCache', '$jsStackTrace'],
2637-
emscripten_stack_snapshot: function() {
2638-
var callstack = jsStackTrace().split('\n');
2639-
if (callstack[0] == 'Error') {
2640-
callstack.shift();
2641-
}
2642-
saveInUnwindCache(callstack);
2643-
2644-
// Caches the stack snapshot so that emscripten_stack_unwind_buffer() can
2645-
// unwind from this spot.
2646-
UNWIND_CACHE.last_addr = convertFrameToPC(callstack[3]);
2647-
UNWIND_CACHE.last_stack = callstack;
2648-
return UNWIND_CACHE.last_addr;
2649-
},
2650-
2651-
$saveInUnwindCache__deps: ['$UNWIND_CACHE', '$convertFrameToPC'],
2652-
$saveInUnwindCache__internal: true,
2653-
$saveInUnwindCache: (callstack) => {
2654-
callstack.forEach((frame) => {
2655-
var pc = convertFrameToPC(frame);
2656-
if (pc) {
2657-
UNWIND_CACHE[pc] = frame;
2658-
}
2659-
});
2660-
},
2661-
2662-
// Unwinds the stack from a cached PC value. See emscripten_stack_snapshot for
2663-
// how this is used. addr must be the return address of the last call to
2664-
// emscripten_stack_snapshot, or this function will instead use the current
2665-
// call stack.
2666-
emscripten_stack_unwind_buffer__deps: ['$UNWIND_CACHE', '$saveInUnwindCache', '$convertFrameToPC', '$jsStackTrace'],
2667-
emscripten_stack_unwind_buffer: (addr, buffer, count) => {
2668-
var stack;
2669-
if (UNWIND_CACHE.last_addr == addr) {
2670-
stack = UNWIND_CACHE.last_stack;
2671-
} else {
2672-
stack = jsStackTrace().split('\n');
2673-
if (stack[0] == 'Error') {
2674-
stack.shift();
2675-
}
2676-
saveInUnwindCache(stack);
2677-
}
2678-
2679-
var offset = 3;
2680-
while (stack[offset] && convertFrameToPC(stack[offset]) != addr) {
2681-
++offset;
2682-
}
2683-
2684-
for (var i = 0; i < count && stack[i+offset]; ++i) {
2685-
{{{ makeSetValue('buffer', 'i*4', 'convertFrameToPC(stack[i + offset])', 'i32') }}};
2686-
}
2687-
return i;
2688-
},
2689-
2690-
// Look up the function name from our stack frame cache with our PC representation.
2691-
#if USE_OFFSET_CONVERTER
2692-
emscripten_pc_get_function__deps: ['$UNWIND_CACHE', 'free', '$stringToNewUTF8'],
2693-
// Don't treat allocation of _emscripten_pc_get_function.ret as a leak
2694-
emscripten_pc_get_function__noleakcheck: true,
2695-
#endif
2696-
emscripten_pc_get_function: (pc) => {
2697-
#if !USE_OFFSET_CONVERTER
2698-
abort('Cannot use emscripten_pc_get_function without -sUSE_OFFSET_CONVERTER');
2699-
return 0;
2700-
#else
2701-
var name;
2702-
if (pc & 0x80000000) {
2703-
// If this is a JavaScript function, try looking it up in the unwind cache.
2704-
var frame = UNWIND_CACHE[pc];
2705-
if (!frame) return 0;
2706-
2707-
var match;
2708-
if (match = /^\s+at (.*) \(.*\)$/.exec(frame)) {
2709-
name = match[1];
2710-
} else if (match = /^(.+?)@/.exec(frame)) {
2711-
name = match[1];
2712-
} else {
2713-
return 0;
2714-
}
2715-
} else {
2716-
name = wasmOffsetConverter.getName(pc);
2717-
}
2718-
if (_emscripten_pc_get_function.ret) _free(_emscripten_pc_get_function.ret);
2719-
_emscripten_pc_get_function.ret = stringToNewUTF8(name);
2720-
return _emscripten_pc_get_function.ret;
2721-
#endif
2722-
},
2723-
2724-
$convertPCtoSourceLocation__deps: ['$UNWIND_CACHE', '$convertFrameToPC'],
2725-
$convertPCtoSourceLocation: (pc) => {
2726-
if (UNWIND_CACHE.last_get_source_pc == pc) return UNWIND_CACHE.last_source;
2727-
2728-
var match;
2729-
var source;
2730-
#if LOAD_SOURCE_MAP
2731-
if (wasmSourceMap) {
2732-
source = wasmSourceMap.lookup(pc);
2733-
}
2734-
#endif
2735-
2736-
if (!source) {
2737-
var frame = UNWIND_CACHE[pc];
2738-
if (!frame) return null;
2739-
// Example: at callMain (a.out.js:6335:22)
2740-
if (match = /\((.*):(\d+):(\d+)\)$/.exec(frame)) {
2741-
source = {file: match[1], line: match[2], column: match[3]};
2742-
// Example: main@a.out.js:1337:42
2743-
} else if (match = /@(.*):(\d+):(\d+)/.exec(frame)) {
2744-
source = {file: match[1], line: match[2], column: match[3]};
2745-
}
2746-
}
2747-
UNWIND_CACHE.last_get_source_pc = pc;
2748-
UNWIND_CACHE.last_source = source;
2749-
return source;
2750-
},
2751-
2752-
// Look up the file name from our stack frame cache with our PC representation.
2753-
emscripten_pc_get_file__deps: ['$convertPCtoSourceLocation', 'free', '$stringToNewUTF8'],
2754-
// Don't treat allocation of _emscripten_pc_get_file.ret as a leak
2755-
emscripten_pc_get_file__noleakcheck: true,
2756-
emscripten_pc_get_file: (pc) => {
2757-
var result = convertPCtoSourceLocation(pc);
2758-
if (!result) return 0;
2759-
2760-
if (_emscripten_pc_get_file.ret) _free(_emscripten_pc_get_file.ret);
2761-
_emscripten_pc_get_file.ret = stringToNewUTF8(result.file);
2762-
return _emscripten_pc_get_file.ret;
2763-
},
2764-
2765-
// Look up the line number from our stack frame cache with our PC representation.
2766-
emscripten_pc_get_line__deps: ['$convertPCtoSourceLocation'],
2767-
emscripten_pc_get_line: (pc) => {
2768-
var result = convertPCtoSourceLocation(pc);
2769-
return result ? result.line : 0;
2770-
},
2771-
2772-
// Look up the column number from our stack frame cache with our PC representation.
2773-
emscripten_pc_get_column__deps: ['$convertPCtoSourceLocation'],
2774-
emscripten_pc_get_column: (pc) => {
2775-
var result = convertPCtoSourceLocation(pc);
2776-
return result ? result.column || 0 : 0;
2777-
},
2778-
27792440
emscripten_get_module_name__deps: ['$stringToUTF8'],
27802441
emscripten_get_module_name: (buf, length) => {
27812442
#if MINIMAL_RUNTIME

src/library_legacy.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,13 @@ legacyFuncs = {
120120
});
121121
},
122122
#endif
123+
124+
$stackTrace__deps: ['$jsStackTrace'],
125+
$stackTrace: function() {
126+
var js = jsStackTrace();
127+
if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace']();
128+
return js;
129+
}
123130
};
124131

125132
if (WARN_DEPRECATED && !INCLUDE_FULL_LIBRARY) {

0 commit comments

Comments
 (0)