Support $stacktrace

This commit is contained in:
Wilson Snyder 2022-11-17 19:12:54 -05:00
parent 96306b7630
commit e8a1e4745c
11 changed files with 139 additions and 0 deletions

View File

@ -554,3 +554,14 @@ or "`ifdef`"'s may break other tools.
Re-enable waveform tracing for all future signals or instances that are Re-enable waveform tracing for all future signals or instances that are
declared. declared.
.. option:: $stacktrace
Called as a task, orint a stack trace. Called as a function, return a
string with a stack trace. This relies on the C++ system trace, which
may give less meaningful results if the model was not compiled with
debug symbols. Also the data represents the C++ stack, the
SystemVerilog functions/tasks involved may be renamed and/or inlined
before becoming the C++ functions that may be visible in the stack
trace. This extension is experimental and may be removed without
deprecation.

View File

@ -67,6 +67,10 @@
#if defined(_WIN32) || defined(__MINGW32__) #if defined(_WIN32) || defined(__MINGW32__)
# include <direct.h> // mkdir # include <direct.h> // mkdir
#endif #endif
#ifdef __linux__
# include <execinfo.h>
# define _VL_HAVE_STACKTRACE
#endif
#include "verilated_threads.h" #include "verilated_threads.h"
// clang-format on // clang-format on
@ -1623,6 +1627,33 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi
return read_count; return read_count;
} }
std::string VL_STACKTRACE_N() VL_MT_SAFE {
static VerilatedMutex s_stackTraceMutex;
const VerilatedLockGuard lock{s_stackTraceMutex};
constexpr int BT_BUF_SIZE = 100;
void *buffer[BT_BUF_SIZE];
int nptrs = 0;
char ** strings = nullptr;
#ifdef _VL_HAVE_STACKTRACE
nptrs = backtrace(buffer, BT_BUF_SIZE);
strings = backtrace_symbols(buffer, nptrs);
#endif
if (!strings) return "Unable to backtrace\n";
std::string out = "Backtrace:\n";
for (int j = 0; j < nptrs; j++) out += std::string{strings[j]} + std::string{"\n"};
free(strings);
return out;
}
void VL_STACKTRACE() VL_MT_SAFE {
const std::string out = VL_STACKTRACE_N();
VL_PRINTF("%s", out.c_str());
}
IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE { IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
VlWide<VL_WQ_WORDS_E> lhsw; VlWide<VL_WQ_WORDS_E> lhsw;
VL_SET_WQ(lhsw, lhs); VL_SET_WQ(lhsw, lhs);

View File

@ -138,6 +138,8 @@ extern void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...);
extern void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...); extern void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...);
extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...); extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...);
extern void VL_STACKTRACE() VL_MT_SAFE;
extern std::string VL_STACKTRACE_N() VL_MT_SAFE;
extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp); extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp);
extern IData VL_SYSTEM_IQ(QData lhs); extern IData VL_SYSTEM_IQ(QData lhs);
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); } inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }

View File

@ -1525,6 +1525,25 @@ public:
int instrCount() const override { return widthInstrs(); } int instrCount() const override { return widthInstrs(); }
bool same(const AstNode* /*samep*/) const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; }
}; };
class AstStackTraceF final : public AstNodeExpr {
// $stacktrace used as function
public:
AstStackTraceF(FileLine* fl)
: ASTGEN_SUPER_StackTraceF(fl) {
dtypeSetString();
}
ASTGEN_MEMBERS_AstStackTraceF;
string verilogKwd() const override { return "$stacktrace"; }
string emitVerilog() override { return verilogKwd(); }
string emitC() override { return "VL_STACKTRACE_N()"; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() const override { return false; }
bool isOutputter() const override { return true; }
bool isUnlikely() const override { return true; }
bool cleanOut() const override { return true; }
bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstSysIgnore final : public AstNodeExpr { class AstSysIgnore final : public AstNodeExpr {
// @astgen op1 := exprsp : List[AstNode] // Expressions to output (???) // @astgen op1 := exprsp : List[AstNode] // Expressions to output (???)
public: public:

View File

@ -3092,6 +3092,20 @@ public:
int instrCount() const override { return INSTR_COUNT_PLI; } int instrCount() const override { return INSTR_COUNT_PLI; }
bool same(const AstNode* /*samep*/) const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; }
}; };
class AstStackTraceT final : public AstNodeStmt {
// $stacktrace used as task
public:
AstStackTraceT(FileLine* fl)
: ASTGEN_SUPER_StackTraceT(fl) { }
ASTGEN_MEMBERS_AstStackTraceT;
string verilogKwd() const override { return "$stacktrace"; }
bool isGateOptimizable() const override { return false; }
bool isPredictOptimizable() const override { return false; }
bool isPure() const override { return false; }
bool isOutputter() const override { return true; }
bool isUnlikely() const override { return true; }
bool same(const AstNode* /*samep*/) const override { return true; }
};
class AstStmtExpr final : public AstNodeStmt { class AstStmtExpr final : public AstNodeStmt {
// Expression in statement position // Expression in statement position
// @astgen op1 := exprp : AstNodeExpr // @astgen op1 := exprp : AstNodeExpr

View File

@ -775,6 +775,12 @@ public:
iterateAndNextNull(nodep->lhsp()); iterateAndNextNull(nodep->lhsp());
if (!nodep->lhsp()->isWide()) puts(";"); if (!nodep->lhsp()->isWide()) puts(";");
} }
void visit(AstStackTraceF* nodep) override {
puts("VL_STACKTRACE_N()");
}
void visit(AstStackTraceT* nodep) override {
puts("VL_STACKTRACE();\n");
}
void visit(AstSystemT* nodep) override { void visit(AstSystemT* nodep) override {
puts("(void)VL_SYSTEM_I"); puts("(void)VL_SYSTEM_I");
emitIQW(nodep->lhsp()); emitIQW(nodep->lhsp());

View File

@ -4676,6 +4676,9 @@ private:
userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p()); userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p());
} }
} }
void visit(AstStackTraceF* nodep) override {
nodep->dtypeSetString();
}
void visit(AstSysIgnore* nodep) override { void visit(AstSysIgnore* nodep) override {
userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p()); userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p());
} }

View File

@ -265,6 +265,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"$skew" { FL; return yaTIMINGSPEC; } "$skew" { FL; return yaTIMINGSPEC; }
"$sqrt" { FL; return yD_SQRT; } "$sqrt" { FL; return yD_SQRT; }
"$sscanf" { FL; return yD_SSCANF; } "$sscanf" { FL; return yD_SSCANF; }
"$stacktrace" { FL; return yD_STACKTRACE; }
"$stime" { FL; return yD_STIME; } "$stime" { FL; return yD_STIME; }
"$stop" { FL; return yD_STOP; } "$stop" { FL; return yD_STOP; }
"$strobe" { FL; return yD_STROBE; } "$strobe" { FL; return yD_STROBE; }

View File

@ -860,6 +860,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yD_SQRT "$sqrt" %token<fl> yD_SQRT "$sqrt"
%token<fl> yD_SSCANF "$sscanf" %token<fl> yD_SSCANF "$sscanf"
%token<fl> yD_STABLE "$stable" %token<fl> yD_STABLE "$stable"
%token<fl> yD_STACKTRACE "$stacktrace"
%token<fl> yD_STIME "$stime" %token<fl> yD_STIME "$stime"
%token<fl> yD_STOP "$stop" %token<fl> yD_STOP "$stop"
%token<fl> yD_STROBE "$strobe" %token<fl> yD_STROBE "$strobe"
@ -3788,6 +3789,7 @@ system_t_call<nodep>: // IEEE: system_tf_call (as task)
| yD_DUMPON '(' expr ')' { $$ = new AstDumpCtl($<fl>1, VDumpCtlType::ON); DEL($3); } | yD_DUMPON '(' expr ')' { $$ = new AstDumpCtl($<fl>1, VDumpCtlType::ON); DEL($3); }
// //
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCStmt($1,$3)); } | yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCStmt($1,$3)); }
| yD_STACKTRACE parenE { $$ = new AstStackTraceT{$1}; }
| yD_SYSTEM '(' expr ')' { $$ = new AstSystemT($1, $3); } | yD_SYSTEM '(' expr ')' { $$ = new AstSystemT($1, $3); }
// //
| yD_EXIT parenE { $$ = new AstFinish($1); } | yD_EXIT parenE { $$ = new AstFinish($1); }
@ -3897,6 +3899,7 @@ system_f_call<nodeExprp>: // IEEE: system_tf_call (as func)
// //
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCFunc($1,$3)); } | yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCFunc($1,$3)); }
| yD_CAST '(' expr ',' expr ')' { $$ = new AstCastDynamic($1, $5, $3); } | yD_CAST '(' expr ',' expr ')' { $$ = new AstCastDynamic($1, $5, $3); }
| yD_STACKTRACE parenE { $$ = new AstStackTraceF{$1}; }
| yD_SYSTEM '(' expr ')' { $$ = new AstSystemF($1,$3); } | yD_SYSTEM '(' expr ')' { $$ = new AstSystemF($1,$3); }
// //
| system_f_call_or_t { $$ = $1; } | system_f_call_or_t { $$ = $1; }

22
test_regress/t/t_stacktrace.pl Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,27 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under The Creative Commons Public Domain, for
// any use, without warranty, 2022 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t;
task t;
// verilator no_inline_task
string trace;
$display("== Trace Func");
trace = $stacktrace();
if (trace == "") $stop;
$display("%s", trace);
$display("== Trace Task");
$stacktrace;
$write("*-* All Finished *-*\n");
$finish;
endtask
initial t();
endmodule