From 6034234c87559532558c997cdb0ddfbedbafd840 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Wed, 30 Jul 2025 20:32:25 +0800 Subject: [PATCH 1/2] :bug: fix call trace issue #69 --- CMakeLists.txt | 2 +- src/nasal.h | 2 +- src/nasal_vm.cpp | 115 +++++++++++++++--------------------- src/nasal_vm.h | 1 - test/special_call_trace.nas | 15 +++++ 5 files changed, 64 insertions(+), 71 deletions(-) create mode 100644 test/special_call_trace.nas diff --git a/CMakeLists.txt b/CMakeLists.txt index c86cd55..f628ccf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,9 +128,9 @@ target_link_libraries(nasock module-used-object) if (NOT MSVC) add_library(nasal-web SHARED src/nasal_web.cpp - ${NASAL_OBJECT_SOURCE_FILE} ) target_include_directories(nasal-web PRIVATE ${CMAKE_SOURCE_DIR}/src) + target_link_libraries(nasal-web nasal-object) set_target_properties(nasal-web PROPERTIES C_VISIBILITY_PRESET hidden CXX_VISIBILITY_PRESET hidden diff --git a/src/nasal.h b/src/nasal.h index b75a274..c8ed818 100644 --- a/src/nasal.h +++ b/src/nasal.h @@ -1,7 +1,7 @@ #pragma once #ifndef __nasver__ -#define __nasver__ "11.3.2" +#define __nasver__ "11.3.3" #endif #include diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 746a89d..6884376 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -222,54 +222,33 @@ void vm::function_call_trace() { var* top = ctx.top; // generate trace back - std::stack functions; - std::stack callsite; + std::vector functions; + std::vector callsite; - // load call trace, from bottom to top - for (var* i = bottom; i <= top; ++i) { - // i-1 is the callsite program counter of this function - // +-------+----------------+ - // | func | func() {..} | <-- i + 2 - // +-------+----------------+ - // | ret | 0x3bf | <-- i + 1 (value should not be 0x0) - // +-------+----------------+ - // | addr | 0x7ff5f61ae020 | <-- i - // +-------+----------------+ - // TODO: op_cnt may destroy the order, so maybe this need refact - if (i + 2 > top) { - continue; + var* prev_func = &ctx.funcr; + functions.push_back(&prev_func->func()); + for (var* i = top; i >= bottom; i--) { + // +-------+------------------+ + // | ret | 0x3bf | <-- i + 1 (should not be 0, except coroutine) + // +-------+------------------+ + // | addr | 0x7ff5f61ae020 | <-- i + // +-------+------------------+ + // | upval | ... | <-- i - 1 + // +-------+------------------+ + // | locals| ... | + // +-------+------------------+ + // | func | function | <-- i - 1 - prev_func->local_size - 1 + if (i + 1 <= top && i[0].is_addr() && i[1].is_ret()) { + auto r_addr = i[1].ret(); + callsite.push_back(r_addr); + i--; + i -= prev_func->func().local_size; + i--; + if (i >= bottom && i[0].is_func()) { + prev_func = i; + functions.push_back(&prev_func->func()); + } } - if (!i->is_addr()) { - continue; - } - if ((i+1)->is_ret() && (i+1)->ret()>0 && (i+2)->is_func()) { - functions.push(&(i+2)->func()); - callsite.push((i+1)->ret()); - } - } - - // another condition may exist - // have ret pc on stack, but no function at the top of the ret pc - for (var * i = top; i >= bottom; --i) { - // +-------+----------------+ - // | xxxx | xxxx | <-- i + 2 (not function or not exist) - // +-------+----------------+ - // | ret | 0x3bf | <-- i + 1 (value should not be 0x0) - // +-------+----------------+ - // | addr | 0x7ff5f61ae020 | <-- i - // +-------+----------------+ - if ((i->is_addr() && i+2<=top && (i+1)->is_ret() && !(i+2)->is_func()) || - (i->is_addr() && i+1<=top && i+2>top && (i+1)->is_ret())) { - functions.push(&ctx.funcr.func()); - callsite.push((i+1)->ret()); - break; - } - } - - // function register stores the latest-called function - if (functions.empty() && !ctx.funcr.is_func()) { - cp.restore_code_page(); - return; } std::clog << "\ncall trace "; @@ -279,35 +258,35 @@ void vm::function_call_trace() { std::clog << " at " << files[bytecode[ctx.pc].fidx] << ":"; std::clog << bytecode[ctx.pc].line << "\n"; - const nas_func* last = nullptr; - u64 last_callsite = SIZE_MAX; - u64 same_count = 0; - for (; !functions.empty() && !callsite.empty(); functions.pop(), callsite.pop()) { - auto func = functions.top(); - auto place = callsite.top(); - - if (last==func && last_callsite==place) { - ++same_count; + const nas_func* prev = nullptr; + u64 prev_addr = 0; + u64 same_call_count = 0; + for (int i = 0; i < functions.size(); ++i) { + if (functions[i] == prev && callsite[i] == prev_addr) { + same_call_count++; continue; - } else if (same_count) { - std::clog << " `--> " << same_count << " same call(s)\n"; - same_count = 0; + } else if (same_call_count) { + std::clog << " `--> " << same_call_count << " same call(s)\n"; + same_call_count = 0; } - last = func; - last_callsite = place; + // in coroutine + if (callsite[i] == 0 && ngc.cort) { + std::clog << " call by coroutine\n"; + break; + } - // output called function std::clog << " call "; - function_detail_info(*func); + function_detail_info(*functions[i]); + auto r_addr = callsite[i]; + std::clog << " from " << files[bytecode[r_addr].fidx] << ":"; + std::clog << bytecode[r_addr].line << "\n"; - // output callsite - std::clog << " from "; - std::clog << files[bytecode[place].fidx] << ":"; - std::clog << bytecode[place].line << "\n"; + prev = functions[i]; + prev_addr = r_addr; } - if (same_count) { - std::clog << " `--> " << same_count << " same call(s)\n"; + if (same_call_count) { + std::clog << " `--> " << same_call_count << " same call(s)\n"; } cp.restore_code_page(); diff --git a/src/nasal_vm.h b/src/nasal_vm.h index 71f3e40..c8f3fde 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include diff --git a/test/special_call_trace.nas b/test/special_call_trace.nas new file mode 100644 index 0000000..42e1f6f --- /dev/null +++ b/test/special_call_trace.nas @@ -0,0 +1,15 @@ +var f = func() { + append({}, []); +} + +var b = func(f...) { + foreach(var i; f) { + i(); + } +} + +b(f); + +func(a, b, c) { + f() +}(0, 1, 2); \ No newline at end of file From 6b975b4a9cdb8da3897012ee797f793e8495c118 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Thu, 31 Jul 2025 20:21:27 +0800 Subject: [PATCH 2/2] :art: improve dump --- src/nasal.h | 2 +- src/nasal_vm.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nasal.h b/src/nasal.h index c8ed818..2c548c1 100644 --- a/src/nasal.h +++ b/src/nasal.h @@ -18,7 +18,7 @@ using usize = std::size_t; using f64 = double; // virtual machine stack depth, both global depth and value stack depth -const u32 VM_STACK_DEPTH = UINT16_MAX; +const u32 VM_STACK_DEPTH = UINT16_MAX + 1; // avoid error loading function bug in MSVC version nasal.exe #ifdef _MSC_VER diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 6884376..2e7e3f8 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -189,8 +189,7 @@ void vm::value_info(var& val) { } void vm::function_detail_info(const nas_func& func) { - std::clog << "func@"; - std::clog << std::hex << reinterpret_cast(&func) << std::dec; + std::clog << "func "; std::vector argument_list = {}; argument_list.resize(func.keys.size()); @@ -238,6 +237,7 @@ void vm::function_call_trace() { // | locals| ... | // +-------+------------------+ // | func | function | <-- i - 1 - prev_func->local_size - 1 + // +-------+------------------+ if (i + 1 <= top && i[0].is_addr() && i[1].is_ret()) { auto r_addr = i[1].ret(); callsite.push_back(r_addr);