mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
324 lines
7.8 KiB
C++
324 lines
7.8 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
// Implementation of breakpoints.
|
|
|
|
#include "zeek/DbgBreakpoint.h"
|
|
|
|
#include <cassert>
|
|
|
|
#include "zeek/Debug.h"
|
|
#include "zeek/Desc.h"
|
|
#include "zeek/Frame.h"
|
|
#include "zeek/Func.h"
|
|
#include "zeek/ID.h"
|
|
#include "zeek/Reporter.h"
|
|
#include "zeek/Scope.h"
|
|
#include "zeek/Timer.h"
|
|
#include "zeek/Val.h"
|
|
#include "zeek/module_util.h"
|
|
|
|
namespace zeek::detail {
|
|
|
|
// BreakpointTimer used for time-based breakpoints
|
|
class BreakpointTimer final : public Timer {
|
|
public:
|
|
BreakpointTimer(DbgBreakpoint* arg_bp, double arg_t) : Timer(arg_t, TIMER_BREAKPOINT) { bp = arg_bp; }
|
|
|
|
void Dispatch(double t, bool is_expire) override;
|
|
|
|
protected:
|
|
DbgBreakpoint* bp;
|
|
};
|
|
|
|
void BreakpointTimer::Dispatch(double t, bool is_expire) {
|
|
if ( is_expire )
|
|
return;
|
|
|
|
bp->ShouldBreak(t);
|
|
}
|
|
|
|
DbgBreakpoint::DbgBreakpoint() {
|
|
kind = BP_STMT;
|
|
|
|
enabled = temporary = false;
|
|
BPID = -1;
|
|
|
|
at_stmt = nullptr;
|
|
at_time = -1.0;
|
|
|
|
repeat_count = hit_count = 0;
|
|
|
|
description[0] = 0;
|
|
source_filename = nullptr;
|
|
source_line = 0;
|
|
}
|
|
|
|
DbgBreakpoint::~DbgBreakpoint() {
|
|
SetEnable(false); // clean up any active state
|
|
RemoveFromGlobalMap();
|
|
}
|
|
|
|
bool DbgBreakpoint::SetEnable(bool do_enable) {
|
|
bool old_value = enabled;
|
|
enabled = do_enable;
|
|
|
|
// Update statement counts.
|
|
if ( do_enable && ! old_value )
|
|
AddToStmt();
|
|
|
|
else if ( ! do_enable && old_value )
|
|
RemoveFromStmt();
|
|
|
|
return old_value;
|
|
}
|
|
|
|
void DbgBreakpoint::AddToGlobalMap() {
|
|
// Make sure it's not there already.
|
|
RemoveFromGlobalMap();
|
|
|
|
g_debugger_state.breakpoint_map.insert(BPMapType::value_type(at_stmt, this));
|
|
}
|
|
|
|
void DbgBreakpoint::RemoveFromGlobalMap() {
|
|
std::pair<BPMapType::iterator, BPMapType::iterator> p;
|
|
p = g_debugger_state.breakpoint_map.equal_range(at_stmt);
|
|
|
|
for ( BPMapType::iterator i = p.first; i != p.second; ) {
|
|
if ( i->second == this ) {
|
|
BPMapType::iterator next = i;
|
|
++next;
|
|
g_debugger_state.breakpoint_map.erase(i);
|
|
i = next;
|
|
}
|
|
else
|
|
++i;
|
|
}
|
|
}
|
|
|
|
void DbgBreakpoint::AddToStmt() {
|
|
if ( at_stmt )
|
|
at_stmt->IncrBPCount();
|
|
}
|
|
|
|
void DbgBreakpoint::RemoveFromStmt() {
|
|
if ( at_stmt )
|
|
at_stmt->DecrBPCount();
|
|
}
|
|
|
|
bool DbgBreakpoint::SetLocation(ParseLocationRec plr, std::string_view loc_str) {
|
|
if ( plr.type == PLR_UNKNOWN ) {
|
|
debug_msg("Breakpoint specifier invalid or operation canceled.\n");
|
|
return false;
|
|
}
|
|
|
|
if ( plr.type == PLR_FILE_AND_LINE ) {
|
|
kind = BP_LINE;
|
|
source_filename = plr.filename;
|
|
source_line = plr.line;
|
|
|
|
if ( ! plr.stmt ) {
|
|
debug_msg("No statement at that line.\n");
|
|
return false;
|
|
}
|
|
|
|
at_stmt = plr.stmt;
|
|
snprintf(description, sizeof(description), "%s:%d", source_filename, source_line);
|
|
|
|
debug_msg("Breakpoint %d set at %s\n", GetID(), Description());
|
|
}
|
|
|
|
else if ( plr.type == PLR_FUNCTION ) {
|
|
std::string loc_s(loc_str);
|
|
kind = BP_FUNC;
|
|
function_name = make_full_var_name(current_module.c_str(), loc_s.c_str());
|
|
at_stmt = plr.stmt;
|
|
const Location* loc = at_stmt->GetLocationInfo();
|
|
snprintf(description, sizeof(description), "%s at %s:%d", function_name.c_str(), loc->FileName(),
|
|
loc->LastLine());
|
|
|
|
debug_msg("Breakpoint %d set at %s\n", GetID(), Description());
|
|
}
|
|
|
|
SetEnable(true);
|
|
AddToGlobalMap();
|
|
return true;
|
|
}
|
|
|
|
bool DbgBreakpoint::SetLocation(Stmt* stmt) {
|
|
if ( ! stmt )
|
|
return false;
|
|
|
|
kind = BP_STMT;
|
|
at_stmt = stmt;
|
|
|
|
SetEnable(true);
|
|
AddToGlobalMap();
|
|
|
|
const Location* loc = stmt->GetLocationInfo();
|
|
snprintf(description, sizeof(description), "%s:%d", loc->FileName(), loc->LastLine());
|
|
|
|
debug_msg("Breakpoint %d set at %s\n", GetID(), Description());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DbgBreakpoint::SetLocation(double t) {
|
|
debug_msg("SetLocation(time) has not been debugged.");
|
|
return false;
|
|
|
|
kind = BP_TIME;
|
|
at_time = t;
|
|
|
|
timer_mgr->Add(new BreakpointTimer(this, t));
|
|
|
|
debug_msg("Time-based breakpoints not yet supported.\n");
|
|
return false;
|
|
}
|
|
|
|
bool DbgBreakpoint::Reset() {
|
|
ParseLocationRec plr;
|
|
|
|
switch ( kind ) {
|
|
case BP_TIME: debug_msg("Time-based breakpoints not yet supported.\n"); break;
|
|
|
|
case BP_FUNC:
|
|
case BP_STMT:
|
|
case BP_LINE:
|
|
plr.type = PLR_FUNCTION;
|
|
// ### How to deal with wildcards?
|
|
// ### perhaps save user choices?--tough...
|
|
break;
|
|
}
|
|
|
|
reporter->InternalError("DbgBreakpoint::Reset function incomplete.");
|
|
|
|
// Cannot be reached.
|
|
return false;
|
|
}
|
|
|
|
bool DbgBreakpoint::SetCondition(const std::string& new_condition) {
|
|
condition = new_condition;
|
|
return true;
|
|
}
|
|
|
|
bool DbgBreakpoint::SetRepeatCount(int count) {
|
|
repeat_count = count;
|
|
return true;
|
|
}
|
|
|
|
BreakCode DbgBreakpoint::HasHit() {
|
|
if ( temporary ) {
|
|
SetEnable(false);
|
|
return BC_HIT_AND_DELETE;
|
|
}
|
|
|
|
if ( condition.size() ) {
|
|
// TODO: ### evaluate using debugger frame too
|
|
auto yes = dbg_eval_expr(condition.c_str());
|
|
|
|
if ( ! yes ) {
|
|
debug_msg("Breakpoint condition '%s' invalid, removing condition.\n", condition.c_str());
|
|
SetCondition("");
|
|
PrintHitMsg();
|
|
return BC_HIT;
|
|
}
|
|
|
|
if ( ! IsIntegral(yes->GetType()->Tag()) && ! IsBool(yes->GetType()->Tag()) ) {
|
|
PrintHitMsg();
|
|
debug_msg("Breakpoint condition should return an integral type");
|
|
return BC_HIT_AND_DELETE;
|
|
}
|
|
|
|
yes->CoerceToInt();
|
|
if ( yes->IsZero() ) {
|
|
return BC_NO_HIT;
|
|
}
|
|
}
|
|
|
|
int repcount = GetRepeatCount();
|
|
if ( repcount ) {
|
|
if ( ++hit_count == repcount ) {
|
|
hit_count = 0;
|
|
PrintHitMsg();
|
|
return BC_HIT;
|
|
}
|
|
|
|
return BC_NO_HIT;
|
|
}
|
|
|
|
PrintHitMsg();
|
|
return BC_HIT;
|
|
}
|
|
|
|
BreakCode DbgBreakpoint::ShouldBreak(Stmt* s) {
|
|
if ( ! IsEnabled() )
|
|
return BC_NO_HIT;
|
|
|
|
switch ( kind ) {
|
|
case BP_STMT:
|
|
case BP_FUNC:
|
|
if ( at_stmt != s )
|
|
return BC_NO_HIT;
|
|
break;
|
|
|
|
case BP_LINE:
|
|
assert(s->GetLocationInfo()->FirstLine() <= source_line && s->GetLocationInfo()->LastLine() >= source_line);
|
|
break;
|
|
|
|
case BP_TIME: assert(false);
|
|
|
|
default: reporter->InternalError("Invalid breakpoint type in DbgBreakpoint::ShouldBreak");
|
|
}
|
|
|
|
// If we got here, that means that the breakpoint could hit,
|
|
// except potentially if it has a special condition or a repeat count.
|
|
|
|
BreakCode code = HasHit();
|
|
if ( code )
|
|
g_debugger_state.BreakBeforeNextStmt(true);
|
|
|
|
return code;
|
|
}
|
|
|
|
BreakCode DbgBreakpoint::ShouldBreak(double t) {
|
|
if ( kind != BP_TIME )
|
|
reporter->InternalError("Calling ShouldBreak(time) on a non-time breakpoint");
|
|
|
|
if ( t < at_time )
|
|
return BC_NO_HIT;
|
|
|
|
if ( ! IsEnabled() )
|
|
return BC_NO_HIT;
|
|
|
|
BreakCode code = HasHit();
|
|
if ( code )
|
|
g_debugger_state.BreakBeforeNextStmt(true);
|
|
|
|
return code;
|
|
}
|
|
|
|
void DbgBreakpoint::PrintHitMsg() {
|
|
switch ( kind ) {
|
|
case BP_STMT:
|
|
case BP_FUNC:
|
|
case BP_LINE: {
|
|
ODesc d;
|
|
Frame* f = g_frame_stack.back();
|
|
const ScriptFunc* func = f->GetFunction();
|
|
|
|
if ( func )
|
|
func->DescribeDebug(&d, f->GetFuncArgs());
|
|
|
|
const Location* loc = at_stmt->GetLocationInfo();
|
|
|
|
debug_msg("Breakpoint %d, %s at %s:%d\n", GetID(), d.Description(), loc->FileName(), loc->FirstLine());
|
|
}
|
|
return;
|
|
|
|
case BP_TIME: assert(false);
|
|
|
|
default: reporter->InternalError("Missed a case in DbgBreakpoint::PrintHitMsg\n");
|
|
}
|
|
}
|
|
|
|
} // namespace zeek::detail
|