diff --git a/docs/guide/languages.rst b/docs/guide/languages.rst index fdadd1828..6dd41879b 100644 --- a/docs/guide/languages.rst +++ b/docs/guide/languages.rst @@ -528,6 +528,12 @@ $readmemb, $readmemh specification do not include support for readmem to multi-dimensional arrays. +$stacktrace + The `$stacktrace` system call will show the C++ stack, not the Verilog + call stack, though the function names typically correlate. To get + symbolic names, the model must have debug symbols, e.g. compile with + `-CFLAGS -ggdb -LDFLAGS -ggdb -LDFLAGS -rdynamic`. + $test$plusargs, $value$plusargs Supported, but the instantiating C++/SystemC wrapper must call diff --git a/docs/spelling.txt b/docs/spelling.txt index 80752f3b4..b95eb4f9b 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -1102,6 +1102,7 @@ src srcdir srcfile sscanf +stacktrace stderr stdin stdout diff --git a/include/verilated.cpp b/include/verilated.cpp index cfec13cfd..1b21df6f2 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -69,6 +69,7 @@ # include // mkdir #endif #ifdef __GLIBC__ +# include # include # define _VL_HAVE_STACKTRACE #endif @@ -1918,6 +1919,40 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi return read_count; } +#ifdef _VL_HAVE_STACKTRACE +static std::string _vl_stacktrace_demangle(const std::string& input) { + std::string result; + result.reserve(input.size()); + + std::string word; + for (const char c : input) { + if (std::isalpha(c) || c == '_') { + word += c; + } else if (!word.empty() && std::isdigit(c)) { + word += c; + } else { + if (!word.empty()) { + // abi::__cxa_demangle mallocs demangled_name + int status = 0; + char* const demangled_name + = abi::__cxa_demangle(word.c_str(), NULL, NULL, &status); + if (status == 0) { + result += std::string{demangled_name}; + std::free(demangled_name); // Free the allocated memory + } else { + result += word; + } + word.clear(); + } + result += c; + } + } + // input requires final newline, so last word can't be symbol + result += word; + return result; +} +#endif + std::string VL_STACKTRACE_N() VL_MT_SAFE { static VerilatedMutex s_stackTraceMutex; const VerilatedLockGuard lock{s_stackTraceMutex}; @@ -1935,8 +1970,12 @@ std::string VL_STACKTRACE_N() VL_MT_SAFE { // cppcheck-suppress knownConditionTrueFalse if (!strings) return "Unable to backtrace\n"; +#ifdef _VL_HAVE_STACKTRACE std::string result = "Backtrace:\n"; - for (int j = 0; j < nptrs; ++j) result += std::string{strings[j]} + "\n"s; + for (int j = 0; j < nptrs; ++j) + result += _vl_stacktrace_demangle(std::string{strings[j]} + "\n"s); +#endif + free(strings); return result; } diff --git a/test_regress/t/t_stacktrace.py b/test_regress/t/t_stacktrace.py index 3cc73805c..09a3510fb 100755 --- a/test_regress/t/t_stacktrace.py +++ b/test_regress/t/t_stacktrace.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(verilator_flags2=['-CFLAGS -ggdb -LDFLAGS -ggdb -LDFLAGS -rdynamic']) test.execute() diff --git a/test_regress/t/t_stacktrace.v b/test_regress/t/t_stacktrace.v index da32abce1..1ce27843e 100644 --- a/test_regress/t/t_stacktrace.v +++ b/test_regress/t/t_stacktrace.v @@ -6,22 +6,22 @@ module t; - task automatic t; - // verilator no_inline_task - string trace; + task automatic t; + // verilator no_inline_task + string trace; - $display("== Trace Func"); - trace = $stacktrace(); - if (trace == "") $stop; - $display("%s", trace); + $display("== Trace Func"); + trace = $stacktrace(); + if (trace == "") $stop; + $display("%s", trace); - $display("== Trace Task"); - $stacktrace; + $display("== Trace Task"); + $stacktrace; - $write("*-* All Finished *-*\n"); - $finish; - endtask + $write("*-* All Finished *-*\n"); + $finish; + endtask - initial t(); + initial t(); endmodule