Merge remote-tracking branch 'origin/topic/timw/4553-combine-call-stacks'
Some checks are pending
pre-commit / pre-commit (push) Waiting to run

* origin/topic/timw/4553-combine-call-stacks:
  Replace g_frame_stack with call_stack used by ScriptProfiler
  Clean up initialization in Func.h
This commit is contained in:
Tim Wojtulewicz 2025-10-13 09:49:10 -07:00
commit 5227e7ebb9
13 changed files with 135 additions and 116 deletions

18
CHANGES
View file

@ -1,3 +1,21 @@
8.1.0-dev.676 | 2025-10-13 09:54:47 -0700
* Replace g_frame_stack with call_stack used by ScriptProfiler (Tim Wojtulewicz, Corelight)
* Clean up initialization in Func.h (Tim Wojtulewicz, Corelight)
* Bump spicy-format (Benjamin Bannier, Corelight)
This brings in two changes:
- proper indention for blocks starting with multiple comments
- switch the hook from `language: rust` to `language: python` to avoid
nonsense due to pre-commit ignore Rust lock files,
https://github.com/pre-commit/pre-commit/issues/3162.
The second one should help avoid future instances of the hook randomly
breaking as its dependencies accidentially break semver.
8.1.0-dev.671 | 2025-10-11 12:38:21 +0200 8.1.0-dev.671 | 2025-10-11 12:38:21 +0200
* ZValElement: s to o renaming (Arne Welzel, Corelight) * ZValElement: s to o renaming (Arne Welzel, Corelight)

8
NEWS
View file

@ -26,6 +26,14 @@ Breaking Changes
to live in the ``auxil`` directory, after having moved there from an external plugin. to live in the ``auxil`` directory, after having moved there from an external plugin.
It is now built as part of main Zeek build whenever building on Linux. It is now built as part of main Zeek build whenever building on Linux.
- The global ``g_frame_stack`` C++ variable was removed. This variable was used to track
the current stack of script-function frames, mostly for the built-in script debugger. We
realized that we were also tracking this information in the global ``call_stack``
variable, used by the script profiler. These two have now been combined together into
``call_stack``. Plugins using ``g_frame_stack`` will need to be adapted to use the other
variable. We opted to not use a deprecation cycle for this due to the complication with
managing the data across both variables.
New Functionality New Functionality
----------------- -----------------

View file

@ -1 +1 @@
8.1.0-dev.671 8.1.0-dev.676

View file

@ -303,17 +303,16 @@ void DbgBreakpoint::PrintHitMsg() {
case BP_FUNC: case BP_FUNC:
case BP_LINE: { case BP_LINE: {
ODesc d; ODesc d;
Frame* f = g_frame_stack.back(); const auto& f = call_stack.back().frame;
const ScriptFunc* func = f->GetFunction();
if ( func ) if ( const Func* func = f->GetFunction() )
func->DescribeDebug(&d, f->GetFuncArgs()); func->DescribeDebug(&d, f->GetFuncArgs());
const Location* loc = at_stmt->GetLocationInfo(); const Location* loc = at_stmt->GetLocationInfo();
debug_msg("Breakpoint %d, %s at %s:%d\n", GetID(), d.Description(), loc->FileName(), loc->FirstLine()); debug_msg("Breakpoint %d, %s at %s:%d\n", GetID(), d.Description(), loc->FileName(), loc->FirstLine());
}
return; return;
}
case BP_TIME: assert(false); case BP_TIME: assert(false);

View file

@ -143,15 +143,13 @@ int TraceState::LogTrace(const char* fmt, ...) {
const Stmt* stmt; const Stmt* stmt;
Location loc; Location loc;
if ( g_frame_stack.size() > 0 && g_frame_stack.back() ) { if ( ! call_stack.empty() ) {
stmt = g_frame_stack.back()->GetNextStmt(); const auto& frame = call_stack.back().frame;
stmt = frame->GetNextStmt();
if ( stmt ) if ( stmt )
loc = *stmt->GetLocationInfo(); loc = *stmt->GetLocationInfo();
else { else if ( const Func* f = frame->GetFunction() )
const ScriptFunc* f = g_frame_stack.back()->GetFunction(); loc = *f->GetLocationInfo();
if ( f )
loc = *f->GetLocationInfo();
}
} }
if ( ! loc.FileName() ) { if ( ! loc.FileName() ) {
@ -163,7 +161,7 @@ int TraceState::LogTrace(const char* fmt, ...) {
fprintf(trace_file, "%s:%d", loc.FileName(), loc.LastLine()); fprintf(trace_file, "%s:%d", loc.FileName(), loc.LastLine());
// Each stack frame is indented. // Each stack frame is indented.
for ( int i = 0; i < int(g_frame_stack.size()); ++i ) for ( const auto& call : call_stack )
fprintf(trace_file, "\t"); fprintf(trace_file, "\t");
retval = vfprintf(trace_file, fmt, args); retval = vfprintf(trace_file, fmt, args);
@ -589,16 +587,18 @@ static int dbg_dispatch_cmd(DebugCmd cmd_code, const vector<string>& args) {
case dcQuit: debug_msg("Program Terminating\n"); exit(0); case dcQuit: debug_msg("Program Terminating\n"); exit(0);
case dcNext: case dcNext: {
g_frame_stack.back()->BreakBeforeNextStmt(true); Frame* frame = call_stack.back().frame;
frame->BreakBeforeNextStmt(true);
step_or_next_pending = true; step_or_next_pending = true;
last_frame = g_frame_stack.back(); last_frame = frame;
break; break;
}
case dcStep: case dcStep:
g_debugger_state.BreakBeforeNextStmt(true); g_debugger_state.BreakBeforeNextStmt(true);
step_or_next_pending = true; step_or_next_pending = true;
last_frame = g_frame_stack.back(); last_frame = call_stack.back().frame;
break; break;
case dcContinue: case dcContinue:
@ -607,7 +607,7 @@ static int dbg_dispatch_cmd(DebugCmd cmd_code, const vector<string>& args) {
break; break;
case dcFinish: case dcFinish:
g_frame_stack.back()->BreakOnReturn(true); call_stack.back().frame->BreakOnReturn(true);
g_debugger_state.BreakBeforeNextStmt(false); g_debugger_state.BreakBeforeNextStmt(false);
break; break;
@ -663,7 +663,7 @@ static char* get_prompt(bool reset_counter = false) {
string get_context_description(const Stmt* stmt, const Frame* frame) { string get_context_description(const Stmt* stmt, const Frame* frame) {
ODesc d; ODesc d;
const ScriptFunc* func = frame ? frame->GetFunction() : nullptr; const Func* func = frame ? frame->GetFunction() : nullptr;
if ( func ) if ( func )
func->DescribeDebug(&d, frame->GetFuncArgs()); func->DescribeDebug(&d, frame->GetFuncArgs());
@ -698,9 +698,8 @@ int dbg_handle_debug_input() {
g_debugger_state.BreakFromSignal(false); g_debugger_state.BreakFromSignal(false);
} }
Frame* curr_frame = g_frame_stack.back(); auto curr_frame = call_stack.back().frame;
const ScriptFunc* func = curr_frame->GetFunction(); if ( const Func* func = curr_frame->GetFunction() )
if ( func )
current_module = extract_module_name(func->GetName().c_str()); current_module = extract_module_name(func->GetName().c_str());
else else
current_module = GLOBAL_MODULE_NAME; current_module = GLOBAL_MODULE_NAME;
@ -711,8 +710,8 @@ int dbg_handle_debug_input() {
const Location loc = *stmt->GetLocationInfo(); const Location loc = *stmt->GetLocationInfo();
if ( ! step_or_next_pending || g_frame_stack.back() != last_frame ) { if ( ! step_or_next_pending || call_stack.back().frame != last_frame ) {
string context = get_context_description(stmt, g_frame_stack.back()); string context = get_context_description(stmt, call_stack.back().frame);
debug_msg("%s\n", context.c_str()); debug_msg("%s\n", context.c_str());
} }
@ -848,16 +847,16 @@ ValPtr dbg_eval_expr(const char* expr) {
// Push the current frame's associated scope. // Push the current frame's associated scope.
// Note: g_debugger_state.curr_frame_idx is the user-visible number, // Note: g_debugger_state.curr_frame_idx is the user-visible number,
// while the array index goes in the opposite direction // while the array index goes in the opposite direction
int frame_idx = (g_frame_stack.size() - 1) - g_debugger_state.curr_frame_idx; int frame_idx = (call_stack.size() - 1) - g_debugger_state.curr_frame_idx;
if ( ! (frame_idx >= 0 && (unsigned)frame_idx < g_frame_stack.size()) ) if ( ! (frame_idx >= 0 && static_cast<size_t>(frame_idx) < call_stack.size()) )
reporter->InternalError("Assertion failed: frame_idx >= 0 && (unsigned) frame_idx < g_frame_stack.size()"); reporter->InternalError("Assertion failed: frame_idx >= 0 && (unsigned) frame_idx < call_stack.size()");
Frame* frame = g_frame_stack[frame_idx]; const auto& frame = call_stack[frame_idx].frame;
if ( ! (frame) ) if ( ! frame )
reporter->InternalError("Assertion failed: frame"); reporter->InternalError("Assertion failed: frame");
const ScriptFunc* func = frame->GetFunction(); const Func* func = frame->GetFunction();
if ( func ) if ( func )
push_existing_scope(func->GetScope()); push_existing_scope(func->GetScope());

View file

@ -173,7 +173,7 @@ int find_all_matching_cmds(const string& prefix, const char* array_of_matches[])
// Start, end bounds of which frame numbers to print // Start, end bounds of which frame numbers to print
static int dbg_backtrace_internal(int start, int end) { static int dbg_backtrace_internal(int start, int end) {
if ( start < 0 || end < 0 || (unsigned)start >= g_frame_stack.size() || (unsigned)end >= g_frame_stack.size() ) if ( start < 0 || end < 0 || (unsigned)start >= call_stack.size() || (unsigned)end >= call_stack.size() )
reporter->InternalError("Invalid stack frame index in DbgBacktraceInternal\n"); reporter->InternalError("Invalid stack frame index in DbgBacktraceInternal\n");
if ( start < end ) { if ( start < end ) {
@ -183,11 +183,11 @@ static int dbg_backtrace_internal(int start, int end) {
} }
for ( int i = start; i >= end; --i ) { for ( int i = start; i >= end; --i ) {
const Frame* f = g_frame_stack[i]; const auto& f = call_stack[i].frame;
const Stmt* stmt = f ? f->GetNextStmt() : nullptr; const Stmt* stmt = f ? f->GetNextStmt() : nullptr;
string context = get_context_description(stmt, f); string context = get_context_description(stmt, f);
debug_msg("#%d %s\n", int(g_frame_stack.size() - 1 - i), context.c_str()); debug_msg("#%d %s\n", int(call_stack.size() - 1 - i), context.c_str());
}; };
return 1; return 1;
@ -196,7 +196,7 @@ static int dbg_backtrace_internal(int start, int end) {
// Returns 0 for illegal arguments, or 1 on success. // Returns 0 for illegal arguments, or 1 on success.
int dbg_cmd_backtrace(DebugCmd cmd, const vector<string>& args) { int dbg_cmd_backtrace(DebugCmd cmd, const vector<string>& args) {
assert(cmd == dcBacktrace); assert(cmd == dcBacktrace);
assert(g_frame_stack.size() > 0); assert(! call_stack.empty());
unsigned int start_iter; unsigned int start_iter;
int end_iter; int end_iter;
@ -210,20 +210,20 @@ int dbg_cmd_backtrace(DebugCmd cmd, const vector<string>& args) {
} }
if ( how_many > 0 ) { // innermost N frames if ( how_many > 0 ) { // innermost N frames
start_iter = g_frame_stack.size() - 1; start_iter = call_stack.size() - 1;
end_iter = start_iter - how_many + 1; end_iter = start_iter - how_many + 1;
if ( end_iter < 0 ) if ( end_iter < 0 )
end_iter = 0; end_iter = 0;
} }
else { // outermost N frames else { // outermost N frames
start_iter = how_many - 1; start_iter = how_many - 1;
if ( start_iter + 1 > g_frame_stack.size() ) if ( start_iter + 1 > call_stack.size() )
start_iter = g_frame_stack.size() - 1; start_iter = call_stack.size() - 1;
end_iter = 0; end_iter = 0;
} }
} }
else { else {
start_iter = g_frame_stack.size() - 1; start_iter = call_stack.size() - 1;
end_iter = 0; end_iter = 0;
} }
@ -248,7 +248,7 @@ int dbg_cmd_frame(DebugCmd cmd, const vector<string>& args) {
return 0; return 0;
} }
if ( idx < 0 || (unsigned int)idx >= g_frame_stack.size() ) { if ( idx < 0 || (unsigned int)idx >= call_stack.size() ) {
debug_msg("No frame %d", idx); debug_msg("No frame %d", idx);
return 0; return 0;
} }
@ -267,7 +267,7 @@ int dbg_cmd_frame(DebugCmd cmd, const vector<string>& args) {
} }
else if ( cmd == dcUp ) { else if ( cmd == dcUp ) {
if ( (unsigned int)(g_debugger_state.curr_frame_idx + 1) == g_frame_stack.size() ) { if ( (unsigned int)(g_debugger_state.curr_frame_idx + 1) == call_stack.size() ) {
debug_msg("Outermost frame already selected\n"); debug_msg("Outermost frame already selected\n");
return 0; return 0;
} }
@ -275,11 +275,11 @@ int dbg_cmd_frame(DebugCmd cmd, const vector<string>& args) {
++g_debugger_state.curr_frame_idx; ++g_debugger_state.curr_frame_idx;
} }
int user_frame_number = g_frame_stack.size() - 1 - g_debugger_state.curr_frame_idx; int user_frame_number = call_stack.size() - 1 - g_debugger_state.curr_frame_idx;
// Set the current location to the new frame being looked at // Set the current location to the new frame being looked at
// for 'list', 'break', etc. // for 'list', 'break', etc.
const Stmt* stmt = g_frame_stack[user_frame_number]->GetNextStmt(); const Stmt* stmt = call_stack[user_frame_number].frame->GetNextStmt();
if ( ! stmt ) if ( ! stmt )
reporter->InternalError("Assertion failed: stmt is null"); reporter->InternalError("Assertion failed: stmt is null");
@ -310,9 +310,9 @@ int dbg_cmd_break(DebugCmd cmd, const vector<string>& args) {
int cond_index = -1; // at which argument pos. does bp condition start? int cond_index = -1; // at which argument pos. does bp condition start?
if ( args.empty() || args[0] == "if" ) { // break on next stmt if ( args.empty() || args[0] == "if" ) { // break on next stmt
int user_frame_number = g_frame_stack.size() - 1 - g_debugger_state.curr_frame_idx; int user_frame_number = call_stack.size() - 1 - g_debugger_state.curr_frame_idx;
Stmt* stmt = g_frame_stack[user_frame_number]->GetNextStmt(); Stmt* stmt = call_stack[user_frame_number].frame->GetNextStmt();
if ( ! stmt ) if ( ! stmt )
reporter->InternalError("Assertion failed: stmt is null"); reporter->InternalError("Assertion failed: stmt is null");

View file

@ -11,31 +11,25 @@
#include "zeek/Val.h" #include "zeek/Val.h"
#include "zeek/broker/Data.h" #include "zeek/broker/Data.h"
std::vector<zeek::detail::Frame*> g_frame_stack;
namespace zeek::detail { namespace zeek::detail {
Frame::Frame(int arg_size, const ScriptFunc* func, const zeek::Args* fn_args) { Frame::Frame(int arg_size, const Func* func, const zeek::Args* fn_args) {
size = arg_size; size = arg_size;
frame = std::make_unique<Element[]>(size); if ( size > 0 )
frame = std::make_unique<Element[]>(size);
function = func; function = func;
func_args = fn_args; func_args = fn_args;
next_stmt = nullptr;
break_before_next_stmt = false;
break_on_return = false;
call = nullptr;
delayed = false;
// We could Ref()/Unref() the captures frame, but there's really // We could Ref()/Unref() the captures frame, but there's really
// no need because by definition this current frame exists to // no need because by definition this current frame exists to
// enable execution of the function, and its captures frame won't // enable execution of the function, and its captures frame won't
// go away until the function itself goes away, which can only be // go away until the function itself goes away, which can only be
// after this frame does. // after this frame does.
captures = function ? function->GetCapturesFrame() : nullptr; if ( function && function->GetKind() == Func::SCRIPT_FUNC ) {
captures_offset_map = function ? function->GetCapturesOffsetMap() : nullptr; auto* sf = static_cast<const ScriptFunc*>(function);
current_offset = 0; captures = sf->GetCapturesFrame();
captures_offset_map = sf->GetCapturesOffsetMap();
}
} }
void Frame::SetElement(int n, ValPtr v) { void Frame::SetElement(int n, ValPtr v) {

View file

@ -20,11 +20,11 @@ using ValPtr = IntrusivePtr<Val>;
class BrokerListView; class BrokerListView;
class BrokerData; class BrokerData;
class Func;
namespace detail { namespace detail {
class CallExpr; class CallExpr;
class ScriptFunc;
using IDPtr = IntrusivePtr<ID>; using IDPtr = IntrusivePtr<ID>;
namespace trigger { namespace trigger {
@ -47,7 +47,7 @@ public:
* @param func the function that is creating this frame * @param func the function that is creating this frame
* @param fn_args the arguments being passed to that function. * @param fn_args the arguments being passed to that function.
*/ */
Frame(int size, const ScriptFunc* func, const zeek::Args* fn_args); Frame(int size, const Func* func, const zeek::Args* fn_args);
/** /**
* Returns the size of the frame. * Returns the size of the frame.
@ -117,7 +117,7 @@ public:
/** /**
* @return the function that the frame is associated with. * @return the function that the frame is associated with.
*/ */
const ScriptFunc* GetFunction() const { return function; } const Func* GetFunction() const { return function; }
/** /**
* @return the arguments passed to the function that this frame * @return the arguments passed to the function that this frame
@ -130,7 +130,7 @@ public:
* *
* @param func the function for the frame to be associated with. * @param func the function for the frame to be associated with.
*/ */
void SetFunction(ScriptFunc* func) { function = func; } void SetFunction(Func* func) { function = func; }
/** /**
* Sets the next statement to be executed in the context of the frame. * Sets the next statement to be executed in the context of the frame.
@ -216,9 +216,9 @@ private:
/** The number of vals that can be stored in this frame. */ /** The number of vals that can be stored in this frame. */
int size; int size;
bool break_before_next_stmt; bool break_before_next_stmt = false;
bool break_on_return; bool break_on_return = false;
bool delayed; bool delayed = false;
/** Associates ID's offsets with values. */ /** Associates ID's offsets with values. */
std::unique_ptr<Element[]> frame; std::unique_ptr<Element[]> frame;
@ -228,25 +228,25 @@ private:
* This is how we support inlined functions without having to * This is how we support inlined functions without having to
* alter the offsets associated with their local variables. * alter the offsets associated with their local variables.
*/ */
int current_offset; int current_offset = 0;
/** Frame used for lambda/when captures. */ /** Frame used for lambda/when captures. */
Frame* captures; Frame* captures = nullptr;
/** Maps IDs to offsets into the "captures" frame. If the ID /** Maps IDs to offsets into the "captures" frame. If the ID
* isn't present, then it's not a capture. * isn't present, then it's not a capture.
*/ */
const OffsetMap* captures_offset_map; const OffsetMap* captures_offset_map = nullptr;
/** The function this frame is associated with. */ /** The function this frame is associated with. */
const ScriptFunc* function; const Func* function = nullptr;
// The following is only needed for the debugger. // The following is only needed for the debugger.
/** The arguments to the function that this Frame is associated with. */ /** The arguments to the function that this Frame is associated with. */
const zeek::Args* func_args; const zeek::Args* func_args = nullptr;
/** The next statement to be evaluated in the context of this frame. */ /** The next statement to be evaluated in the context of this frame. */
Stmt* next_stmt; Stmt* next_stmt = nullptr;
trigger::TriggerPtr trigger; trigger::TriggerPtr trigger;
const CallExpr* call = nullptr; const CallExpr* call = nullptr;
@ -266,4 +266,4 @@ private:
* DebugFrame which provides the information that the debugger uses. See: * DebugFrame which provides the information that the debugger uses. See:
* https://stackoverflow.com/a/16211097 * https://stackoverflow.com/a/16211097
*/ */
extern std::vector<zeek::detail::Frame*> g_frame_stack; // extern std::vector<zeek::detail::Frame*> g_frame_stack;

View file

@ -93,10 +93,11 @@ std::string render_call_stack() {
if ( lvl > 0 ) if ( lvl > 0 )
rval += " | "; rval += " | ";
const auto& name = ci.func->GetName(); const auto& name = ci.frame->GetFunction()->GetName();
std::string arg_desc; std::string arg_desc;
for ( const auto& arg : ci.args ) { const auto& args = ci.frame->GetFuncArgs();
for ( const auto& arg : *args ) {
ODesc d; ODesc d;
d.SetShort(); d.SetShort();
arg->Describe(&d); arg->Describe(&d);
@ -331,17 +332,16 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
return Flavor() == FUNC_FLAVOR_HOOK ? val_mgr->True() : nullptr; return Flavor() == FUNC_FLAVOR_HOOK ? val_mgr->True() : nullptr;
} }
auto f = make_intrusive<Frame>(frame_size, this, args); Frame f{static_cast<int>(frame_size), this, args};
// Hand down any trigger. // Hand down any trigger.
if ( parent ) { if ( parent ) {
f->SetTrigger({NewRef{}, parent->GetTrigger()}); f.SetTrigger({NewRef{}, parent->GetTrigger()});
f->SetTriggerAssoc(parent->GetTriggerAssoc()); f.SetTriggerAssoc(parent->GetTriggerAssoc());
} }
g_frame_stack.push_back(f.get()); // used for backtracing
const CallExpr* call_expr = parent ? parent->GetCall() : nullptr; const CallExpr* call_expr = parent ? parent->GetCall() : nullptr;
call_stack.emplace_back(CallInfo{call_expr, this, *args}); call_stack.emplace_back(call_expr, &f);
// If a script function is ever invoked with more arguments than it has // If a script function is ever invoked with more arguments than it has
// parameters log an error and return. Most likely a "variadic function" // parameters log an error and return. Most likely a "variadic function"
@ -374,24 +374,23 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
for ( auto j = 0u; j < args->size(); ++j ) { for ( auto j = 0u; j < args->size(); ++j ) {
const auto& arg = (*args)[j]; const auto& arg = (*args)[j];
if ( f->GetElement(j) != arg ) if ( f.GetElement(j) != arg )
// Either not yet set, or somebody reassigned the frame slot. // Either not yet set, or somebody reassigned the frame slot.
f->SetElement(j, arg); f.SetElement(j, arg);
} }
if ( spm ) if ( spm )
spm->StartInvocation(this, body.stmts); spm->StartInvocation(this, body.stmts);
f->Reset(args->size()); f.Reset(args->size());
try { try {
result = body.stmts->Exec(f.get(), flow); result = body.stmts->Exec(&f, flow);
} }
catch ( InterpreterException& e ) { catch ( InterpreterException& e ) {
// Already reported, but now determine whether to unwind further. // Already reported, but now determine whether to unwind further.
if ( Flavor() == FUNC_FLAVOR_FUNCTION ) { if ( Flavor() == FUNC_FLAVOR_FUNCTION ) {
g_frame_stack.pop_back();
call_stack.pop_back(); call_stack.pop_back();
// Result not set b/c exception was thrown // Result not set b/c exception was thrown
throw; throw;
@ -404,7 +403,7 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
if ( spm ) if ( spm )
spm->EndInvocation(); spm->EndInvocation();
if ( f->HasDelayed() ) { if ( f.HasDelayed() ) {
assert(! result); assert(! result);
assert(parent); assert(parent);
parent->SetDelayed(); parent->SetDelayed();
@ -424,8 +423,6 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
} }
} }
call_stack.pop_back();
if ( Flavor() == FUNC_FLAVOR_HOOK ) { if ( Flavor() == FUNC_FLAVOR_HOOK ) {
if ( ! result ) if ( ! result )
result = val_mgr->True(); result = val_mgr->True();
@ -438,7 +435,7 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
// the function without an explicit return, or without a value. // the function without an explicit return, or without a value.
else if ( GetType()->Yield() && GetType()->Yield()->Tag() != TYPE_VOID && ! GetType()->ExpressionlessReturnOkay() && else if ( GetType()->Yield() && GetType()->Yield()->Tag() != TYPE_VOID && ! GetType()->ExpressionlessReturnOkay() &&
(flow != FLOW_RETURN /* we fell off the end */ || ! result /* explicit return with no result */) && (flow != FLOW_RETURN /* we fell off the end */ || ! result /* explicit return with no result */) &&
! f->HasDelayed() ) ! f.HasDelayed() )
reporter->Warning("non-void function returning without a value: %s", GetName().c_str()); reporter->Warning("non-void function returning without a value: %s", GetName().c_str());
if ( result && g_trace_state.DoTrace() ) { if ( result && g_trace_state.DoTrace() ) {
@ -448,7 +445,7 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
g_trace_state.LogTrace("Function return: %s\n", d.Description()); g_trace_state.LogTrace("Function return: %s\n", d.Description());
} }
g_frame_stack.pop_back(); call_stack.pop_back();
return result; return result;
} }
@ -733,8 +730,10 @@ ValPtr BuiltinFunc::Invoke(Args* args, Frame* parent) const {
g_trace_state.LogTrace("\tBuiltin Function called: %s\n", d.Description()); g_trace_state.LogTrace("\tBuiltin Function called: %s\n", d.Description());
} }
Frame f{0, this, args};
const CallExpr* call_expr = parent ? parent->GetCall() : nullptr; const CallExpr* call_expr = parent ? parent->GetCall() : nullptr;
call_stack.emplace_back(CallInfo{call_expr, this, *args}); call_stack.emplace_back(call_expr, &f);
auto result = func(parent, args); auto result = func(parent, args);
call_stack.pop_back(); call_stack.pop_back();
@ -937,15 +936,15 @@ zeek::VectorValPtr get_current_script_backtrace() {
auto cs_copy = zeek::detail::call_stack; auto cs_copy = zeek::detail::call_stack;
for ( const auto& ci : std::ranges::reverse_view(cs_copy) ) { for ( const auto& ci : std::ranges::reverse_view(cs_copy) ) {
if ( ! ci.func ) if ( ! ci.frame->GetFunction() )
// This happens for compiled code. // This happens for compiled code and BIFs.
continue; continue;
const auto& params = ci.func->GetType()->Params(); const auto& params = ci.frame->GetFunction()->GetType()->Params();
auto args = MakeCallArgumentVector(ci.args, params); auto args = MakeCallArgumentVector(*ci.frame->GetFuncArgs(), params);
auto elem = auto elem = make_backtrace_element(ci.frame->GetFunction()->GetName(), std::move(args),
make_backtrace_element(ci.func->GetName(), std::move(args), ci.call ? ci.call->GetLocationInfo() : nullptr); ci.call ? ci.call->GetLocationInfo() : nullptr);
rval->Append(std::move(elem)); rval->Append(std::move(elem));
} }
@ -990,7 +989,7 @@ static void emit_builtin_error_common(const char* msg, Obj* arg, bool unwind) {
return; return;
} }
auto last_call = call_stack.back(); const auto& last_call = call_stack.back();
if ( call_stack.size() < 2 ) { if ( call_stack.size() < 2 ) {
// Don't need to check for wrapper function like "<module>::__<func>" // Don't need to check for wrapper function like "<module>::__<func>"
@ -998,10 +997,13 @@ static void emit_builtin_error_common(const char* msg, Obj* arg, bool unwind) {
return; return;
} }
if ( ! last_call.frame->GetFunction() )
return;
auto starts_with_double_underscore = [](const std::string& name) -> bool { auto starts_with_double_underscore = [](const std::string& name) -> bool {
return name.size() > 2 && name[0] == '_' && name[1] == '_'; return name.size() > 2 && name[0] == '_' && name[1] == '_';
}; };
const std::string& last_func = last_call.func->GetName(); const std::string& last_func = last_call.frame->GetFunction()->GetName();
auto pos = last_func.find_first_of("::"); auto pos = last_func.find_first_of("::");
std::string wrapper_func; std::string wrapper_func;
@ -1027,7 +1029,10 @@ static void emit_builtin_error_common(const char* msg, Obj* arg, bool unwind) {
} }
auto parent_call = call_stack[call_stack.size() - 2]; auto parent_call = call_stack[call_stack.size() - 2];
const auto& parent_func = parent_call.func->GetName();
std::string parent_func;
if ( const auto* pcf = parent_call.frame->GetFunction() )
parent_func = pcf->GetName();
if ( wrapper_func == parent_func ) if ( wrapper_func == parent_func )
emit(parent_call.call); emit(parent_call.call);

View file

@ -339,21 +339,16 @@ public:
void Describe(ODesc* d) const override; void Describe(ODesc* d) const override;
protected: protected:
BuiltinFunc() { BuiltinFunc() = default;
func = nullptr; built_in_func func = nullptr;
is_pure = false; bool is_pure = false;
}
built_in_func func;
bool is_pure;
}; };
extern bool check_built_in_call(BuiltinFunc* f, CallExpr* call); extern bool check_built_in_call(BuiltinFunc* f, CallExpr* call);
struct CallInfo { struct CallInfo {
const CallExpr* call; const CallExpr* call = nullptr;
const Func* func; Frame* frame = nullptr;
const zeek::Args& args;
}; };
// Class that collects all the specifics defining a Func. // Class that collects all the specifics defining a Func.

View file

@ -7,6 +7,7 @@
#include "zeek/Desc.h" #include "zeek/Desc.h"
#include "zeek/Expr.h" #include "zeek/Expr.h"
#include "zeek/Frame.h"
#include "zeek/Func.h" #include "zeek/Func.h"
#include "zeek/IntrusivePtr.h" #include "zeek/IntrusivePtr.h"
#include "zeek/Reporter.h" #include "zeek/Reporter.h"
@ -136,7 +137,7 @@ std::string_view determine_script_location() {
ssize_t sidx = static_cast<ssize_t>(zeek::detail::call_stack.size()) - 1; ssize_t sidx = static_cast<ssize_t>(zeek::detail::call_stack.size()) - 1;
while ( sidx >= 0 ) { while ( sidx >= 0 ) {
const auto* func = zeek::detail::call_stack[sidx].func; const auto* func = zeek::detail::call_stack[sidx].frame->GetFunction();
const auto* ce = zeek::detail::call_stack[sidx].call; const auto* ce = zeek::detail::call_stack[sidx].call;
// Cached? // Cached?

View file

@ -41,12 +41,12 @@ eval auto& v = $$;
internal-op Load-Capture internal-op Load-Capture
class Vi class Vi
eval $$ = Z_FRAME->GetFunction()->GetCapturesVec()[$1]; eval $$ = static_cast<const ScriptFunc*>(Z_FRAME->GetFunction())->GetCapturesVec()[$1];
internal-op Load-Managed-Capture internal-op Load-Managed-Capture
class Vi class Vi
eval auto& lhs = $$; eval auto& lhs = $$;
auto& rhs = Z_FRAME->GetFunction()->GetCapturesVec()[$1]; auto& rhs = static_cast<const ScriptFunc*>(Z_FRAME->GetFunction())->GetCapturesVec()[$1];
zeek::Ref(rhs.ManagedVal()); zeek::Ref(rhs.ManagedVal());
ZVal::DeleteManagedType(lhs); ZVal::DeleteManagedType(lhs);
lhs = rhs; lhs = rhs;
@ -62,12 +62,12 @@ eval GlobalID($1)->SetVal(GlobalVal($1).ToVal(Z_TYPE));
internal-op Store-Capture internal-op Store-Capture
op1-read op1-read
class Vi class Vi
eval Z_FRAME->GetFunction()->GetCapturesVec()[$2] = $1; eval static_cast<const ScriptFunc*>(Z_FRAME->GetFunction())->GetCapturesVec()[$2] = $1;
internal-op Store-Managed-Capture internal-op Store-Managed-Capture
op1-read op1-read
class Vi class Vi
eval auto& lhs = Z_FRAME->GetFunction()->GetCapturesVec()[$2]; eval auto& lhs = static_cast<const ScriptFunc*>(Z_FRAME->GetFunction())->GetCapturesVec()[$2];
auto& rhs = $1; auto& rhs = $1;
zeek::Ref(rhs.ManagedVal()); zeek::Ref(rhs.ManagedVal());
ZVal::DeleteManagedType(lhs); ZVal::DeleteManagedType(lhs);

View file

@ -1054,7 +1054,7 @@ SetupResult setup(int argc, char** argv, Options* zopts) {
auto [body, scope] = get_global_stmts(); auto [body, scope] = get_global_stmts();
StmtFlowType flow; StmtFlowType flow;
Frame f(scope->Length(), nullptr, nullptr); Frame f(scope->Length(), nullptr, nullptr);
g_frame_stack.push_back(&f); call_stack.emplace_back(nullptr, &f);
try { try {
body->Exec(&f, flow); body->Exec(&f, flow);
@ -1062,7 +1062,7 @@ SetupResult setup(int argc, char** argv, Options* zopts) {
reporter->FatalError("failed to execute script statements at top-level scope"); reporter->FatalError("failed to execute script statements at top-level scope");
} }
g_frame_stack.pop_back(); call_stack.pop_back();
} }
clear_script_analysis(); clear_script_analysis();