diff --git a/src/Func.cc b/src/Func.cc index f56719ee00..fa08d81709 100644 --- a/src/Func.cc +++ b/src/Func.cc @@ -903,6 +903,59 @@ FunctionIngredients::FunctionIngredients(ScopePtr _scope, StmtPtr _body, } } +zeek::RecordValPtr make_backtrace_element(std::string_view name, const VectorValPtr args, + const zeek::detail::Location* loc) + { + static auto elem_type = id::find_type("BacktraceElement"); + static auto function_name_idx = elem_type->FieldOffset("function_name"); + static auto function_args_idx = elem_type->FieldOffset("function_args"); + static auto file_location_idx = elem_type->FieldOffset("file_location"); + static auto line_location_idx = elem_type->FieldOffset("line_location"); + + auto elem = make_intrusive(elem_type); + elem->Assign(function_name_idx, name.data()); + elem->Assign(function_args_idx, std::move(args)); + + if ( loc ) + { + elem->Assign(file_location_idx, loc->filename); + elem->Assign(line_location_idx, loc->first_line); + } + + return elem; + } + +zeek::VectorValPtr get_current_script_backtrace() + { + static auto backtrace_type = id::find_type("Backtrace"); + + auto rval = make_intrusive(backtrace_type); + + // The body of the following loop can wind up adding items to + // the call stack (because MakeCallArgumentVector() evaluates + // default arguments, which can in turn involve calls to script + // functions), so we work from a copy of the current call stack + // to prevent problems with iterator invalidation. + auto cs_copy = zeek::detail::call_stack; + + for ( auto it = cs_copy.rbegin(); it != cs_copy.rend(); ++it ) + { + const auto& ci = *it; + if ( ! ci.func ) + // This happens for compiled code. + continue; + + const auto& params = ci.func->GetType()->Params(); + auto args = MakeCallArgumentVector(ci.args, params); + + auto elem = make_backtrace_element(ci.func->Name(), std::move(args), + ci.call ? ci.call->GetLocationInfo() : nullptr); + rval->Append(std::move(elem)); + } + + return rval; + } + static void emit_builtin_error_common(const char* msg, Obj* arg, bool unwind) { auto emit = [=](const CallExpr* ce) diff --git a/src/Func.h b/src/Func.h index a27eb892b7..06f2d26306 100644 --- a/src/Func.h +++ b/src/Func.h @@ -364,6 +364,25 @@ private: extern std::vector call_stack; +/** + * Create a single BacktraceElement record val. + * + * @param name the name of the function. + * @param args call argument vector created by MakeCallArgumentVector(). + * @param loc optional location information of the caller. + * + * @return record value representing a BacktraceElement. + */ +zeek::RecordValPtr make_backtrace_element(std::string_view name, const VectorValPtr args, + const zeek::detail::Location* loc); + +/** + * Create a Zeek script Backtrace of the current script call_stack. + * + * @return VectorValPtr containing BacktraceElement entries. + */ +zeek::VectorValPtr get_current_script_backtrace(); + // This is set to true after the built-in functions have been initialized. extern bool did_builtin_init; extern std::vector bif_initializers; diff --git a/src/zeek.bif b/src/zeek.bif index c97e6e7d9c..1ad2324e04 100644 --- a/src/zeek.bif +++ b/src/zeek.bif @@ -2368,6 +2368,10 @@ function is_v6_subnet%(s: subnet%): bool return zeek::val_mgr->False(); %} +%%{ +#include "zeek/Func.h" +%%} + ## Returns a representation of the call stack as a vector of call stack ## elements, each containing call location information. ## @@ -2375,49 +2379,7 @@ function is_v6_subnet%(s: subnet%): bool ## location information. function backtrace%(%): Backtrace %{ - using zeek::detail::call_stack; - static auto backtrace_type = id::find_type("Backtrace"); - static auto elem_type = id::find_type("BacktraceElement"); - static auto function_name_idx = elem_type->FieldOffset("function_name"); - static auto function_args_idx = elem_type->FieldOffset("function_args"); - static auto file_location_idx = elem_type->FieldOffset("file_location"); - static auto line_location_idx = elem_type->FieldOffset("line_location"); - - auto rval = make_intrusive(backtrace_type); - - // The body of the following loop can wind up adding items to - // the call stack (because MakeCallArgumentVector() evaluates - // default arguments, which can in turn involve calls to script - // functions), so we work from a copy of the current call stack - // to prevent problems with iterator invalidation. - auto cs_copy = call_stack; - - for ( auto it = cs_copy.rbegin(); it != cs_copy.rend(); ++it ) - { - const auto& ci = *it; - if ( ! ci.func ) - // This happens for compiled code. - continue; - - auto elem = make_intrusive(elem_type); - - const auto& params = ci.func->GetType()->Params(); - auto args = MakeCallArgumentVector(ci.args, params); - - elem->Assign(function_name_idx, ci.func->Name()); - elem->Assign(function_args_idx, std::move(args)); - - if ( ci.call ) - { - auto loc = ci.call->GetLocationInfo(); - elem->Assign(file_location_idx, loc->filename); - elem->Assign(line_location_idx, loc->first_line); - } - - rval->Assign(rval->Size(), std::move(elem)); - } - - return rval; + return zeek::detail::get_current_script_backtrace(); %} # ===========================================================================