| // Copyright 2013 The Emscripten Authors. All rights reserved. | |
| // Emscripten is available under two separate licenses, the MIT license and the | |
| // University of Illinois/NCSA Open Source License. Both these licenses can be | |
| // found in the LICENSE file. | |
| #include <emscripten.h> | |
| #include <stdio.h> | |
| #include <cstring> | |
| #define STRINGIZE_HELPER(x) #x | |
| #define STRINGIZE(x) STRINGIZE_HELPER(x) | |
| int failed = 0; // If 1, this test failed. | |
| // A custom assert macro to test varargs routing to emscripten_log(). | |
| #define MYASSERT(condition, msg, ...) \ | |
| do { \ | |
| if (!(condition)) { \ | |
| emscripten_log(EM_LOG_ERROR, "%s", "Condition '" #condition "' failed in file " __FILE__ ":" STRINGIZE(__LINE__) "!"); \ | |
| emscripten_log(EM_LOG_ERROR, msg, ##__VA_ARGS__); \ | |
| failed = 1; \ | |
| } \ | |
| } while(0) | |
| void __attribute__((noinline)) kitten() { | |
| // Log to Emscripten Module. | |
| emscripten_log(EM_LOG_NO_PATHS, "Print a log message: int: %d, string: %s.", 42, "hello"); | |
| emscripten_log(EM_LOG_NO_PATHS | EM_LOG_WARN, "Print a warning message"); | |
| emscripten_log(EM_LOG_NO_PATHS | EM_LOG_ERROR, "This is an error!"); | |
| // Log directly to Browser web inspector/console. | |
| emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE, "Info log to console: int: %d, string: %s", 42, "hello"); | |
| emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_WARN, "Warning message to console."); | |
| emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR, "Error message to console! This should appear in red!"); | |
| emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_INFO, "Info message to console."); | |
| emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_DEBUG, "Debug message to console."); | |
| // Log with full callstack information (both original C source and JS callstacks): | |
| emscripten_log(EM_LOG_C_STACK | EM_LOG_JS_STACK, "A message with as full call stack information as possible:"); | |
| // Log with just mangled JS callstacks: | |
| emscripten_log(EM_LOG_NO_PATHS | EM_LOG_JS_STACK, "This is a message with a mangled JS callstack:"); | |
| // Log only clean C callstack: | |
| emscripten_log(EM_LOG_NO_PATHS | EM_LOG_C_STACK, "This message should have a clean C callstack:"); | |
| } | |
| // Arbitrary function signature to add some content to callstack. | |
| void __attribute__((noinline)) bar(int = 0, char * = 0, double = 0) { | |
| if (1 == 2) | |
| MYASSERT(2 == 1, "World falls apart!"); | |
| else | |
| MYASSERT(1 == 1, ""); | |
| int flags = EM_LOG_NO_PATHS | EM_LOG_JS_STACK | EM_LOG_FUNC_PARAMS; | |
| // We can programmatically get the callstack. | |
| // 1. Ask for callstack length: | |
| int nbytes = emscripten_get_callstack(flags, 0, 0); | |
| // 2. Allocate temp memory to hold the callstack. | |
| char *callstack = new char[nbytes]; | |
| // 3. Obtain it. | |
| // 4. Do something with the callstack string. | |
| emscripten_get_callstack(flags, callstack, nbytes); | |
| /* The callstack should be something like | |
| at bar(int, char*, double) (src.cpp.o.js:5383:12) | |
| at void Foo<int>() (src.cpp.o.js:5417:4) | |
| at main() (src.cpp.o.js:5404:2) | |
| at Object.callMain (src.cpp.o.js:71344:30) | |
| at doRun (src.cpp.o.js:71383:25) | |
| at run (src.cpp.o.js:71396:5) | |
| at Object.<anonymous> (src.cpp.o.js:71439:1) | |
| at Module._compile (module.js:456:26) | |
| but the line numbers will greatly vary depending on the mode we are compiling in, so cannot test with direct string comparison. */ | |
| MYASSERT(!!strstr(callstack, ".js:"), "Callstack was %s!", callstack); | |
| MYASSERT(!!strstr(callstack, "bar(int, char*, double)"), "Callstack was %s!", callstack); | |
| MYASSERT(!!strstr(callstack, "void Foo<int>()"), "Callstack was %s!", callstack); | |
| // 5. Clean up. | |
| delete[] callstack; | |
| // Test that obtaining a truncated callstack works. (https://github.com/emscripten-core/emscripten/issues/2171) | |
| const size_t callstack_buf_len = 50; | |
| char *buffer = new char[callstack_buf_len+1]; | |
| buffer[callstack_buf_len] = 0x01; // Magic sentinel that should not change its value. | |
| emscripten_get_callstack(EM_LOG_C_STACK | EM_LOG_NO_PATHS | EM_LOG_FUNC_PARAMS, buffer, callstack_buf_len); | |
| MYASSERT(!!strstr(buffer, "bar(int,"), "Truncated callstack was %s!", buffer); | |
| MYASSERT(buffer[callstack_buf_len] == 0x01, ""); | |
| delete[] buffer; | |
| // Or alternatively use a fixed-size buffer for the callstack (and get a truncated output if it was too small). | |
| char str[1024]; | |
| emscripten_get_callstack(EM_LOG_NO_PATHS | EM_LOG_JS_STACK, str, 1024); | |
| // TODO(sbc): should we try to revive these checks? The callstacks don't look quite like | |
| // this in the wasm world and we already have coverage above I think? | |
| #if 0 | |
| /* With EM_LOG_JS_STACK, the callstack will be | |
| at __Z3bariPcd (src.cpp.o.js:5394:12) | |
| at __Z3FooIiEvv (src.cpp.o.js:5417:4) | |
| at Object._main (src.cpp.o.js:5404:2) | |
| at Object.callMain (src.cpp.o.js:71344:30) | |
| at doRun (src.cpp.o.js:71383:25) | |
| at run (src.cpp.o.js:71396:5) | |
| at Object.<anonymous> (src.cpp.o.js:71439:1) | |
| at Module._compile (module.js:456:26) */ | |
| #ifdef RUN_FROM_JS_SHELL | |
| MYASSERT(!!strstr(str, "at __Z3bariPcd (src.cpp"), "Callstack was %s!", str); | |
| MYASSERT(!!strstr(str, "at __Z3FooIiEvv (src.cpp"), "Callstack was %s!", str); | |
| #else | |
| MYASSERT(!!strstr(str, "at __Z3bariPcd (page.js"), "Callstack was %s!", str); | |
| MYASSERT(!!strstr(str, "at __Z3FooIiEvv (page.js"), "Callstack was %s!", str); | |
| #endif | |
| #endif | |
| } | |
| // Arbitrary function signature to add some content to callstack. | |
| template<typename T> | |
| void __attribute__((noinline)) Foo() { | |
| bar(); | |
| } | |
| #define TestLog(args...) emscripten_log(EM_LOG_CONSOLE, args) | |
| void PrintDoubleStuff(double first, double second) { | |
| double divided = first / second; | |
| TestLog("%f %f %f\n", first, second, divided); | |
| TestLog("%d %d %d\n", (int)(first * 1000000), (int)(second * 1000000), (int)(divided * 1000000)); | |
| TestLog("%f %d %d\n", first, (int)(second * 1000000), (int)(divided * 1000000)); | |
| TestLog("%d %f %d\n", (int)(first * 1000000), second, (int)(divided * 1000000)); | |
| TestLog("%d %d %f\n", (int)(first * 1000000), (int)(second * 1000000), divided); | |
| TestLog("%d %f %f\n", (int)(first * 1000000), second, divided); | |
| TestLog("%f %d %f\n", first, (int)(second * 1000000), divided); | |
| TestLog("%f %f %d\n", first, second, (int)(divided * 1000000)); | |
| } | |
| void DoubleTest() { | |
| PrintDoubleStuff(12.3456789, 9.12345678); | |
| } | |
| int main() { | |
| int test = 123; | |
| emscripten_log(EM_LOG_FUNC_PARAMS | EM_LOG_CONSOLE, "test print %d\n", test); | |
| Foo<int>(); | |
| DoubleTest(); | |
| if (failed) { | |
| printf("Failed!\n"); | |
| return 1; | |
| } | |
| printf("Success!\n"); | |
| return 0; | |
| } |