mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
355 lines
6.5 KiB
C++
355 lines
6.5 KiB
C++
// $Id: DbgBreakpoint.cc 1345 2005-09-08 07:42:11Z vern $
|
|
|
|
// Implementation of breakpoints.
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include "ID.h"
|
|
#include "Queue.h"
|
|
#include "Debug.h"
|
|
#include "Scope.h"
|
|
#include "Func.h"
|
|
#include "Stmt.h"
|
|
#include "DbgBreakpoint.h"
|
|
#include "Timer.h"
|
|
|
|
|
|
// BreakpointTimer used for time-based breakpoints
|
|
class BreakpointTimer : public Timer {
|
|
public:
|
|
BreakpointTimer(DbgBreakpoint* arg_bp, double arg_t)
|
|
: Timer(arg_t, TIMER_BREAKPOINT)
|
|
{ bp = arg_bp; }
|
|
|
|
void Dispatch(double t, int is_expire);
|
|
|
|
protected:
|
|
DbgBreakpoint* bp;
|
|
};
|
|
|
|
void BreakpointTimer::Dispatch(double t, int is_expire)
|
|
{
|
|
if ( is_expire )
|
|
return;
|
|
|
|
bp->ShouldBreak(t);
|
|
}
|
|
|
|
|
|
DbgBreakpoint::DbgBreakpoint()
|
|
{
|
|
kind = BP_STMT;
|
|
|
|
enabled = temporary = false;
|
|
BPID = -1;
|
|
|
|
at_stmt = 0;
|
|
at_time = -1.0;
|
|
|
|
repeat_count = hit_count = 0;
|
|
|
|
description[0] = 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()
|
|
{
|
|
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; ++i )
|
|
{
|
|
if ( i->second == this )
|
|
g_debugger_state.breakpoint_map.erase(i);
|
|
}
|
|
}
|
|
|
|
void DbgBreakpoint::AddToStmt()
|
|
{
|
|
if ( at_stmt )
|
|
at_stmt->IncrBPCount();
|
|
}
|
|
|
|
void DbgBreakpoint::RemoveFromStmt()
|
|
{
|
|
if ( at_stmt )
|
|
at_stmt->DecrBPCount();
|
|
}
|
|
|
|
|
|
bool DbgBreakpoint::SetLocation(ParseLocationRec plr, string loc_str)
|
|
{
|
|
if ( plr.type == plrUnknown )
|
|
{
|
|
debug_msg("Breakpoint specifier invalid or operation canceled.\n");
|
|
return false;
|
|
}
|
|
|
|
if ( plr.type == plrFileAndLine )
|
|
{
|
|
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;
|
|
safe_snprintf(description, sizeof(description), "%s:%d",
|
|
source_filename, source_line);
|
|
|
|
debug_msg("Breakpoint %d set at %s\n", GetID(), Description());
|
|
}
|
|
|
|
else if ( plr.type == plrFunction )
|
|
{
|
|
kind = BP_FUNC;
|
|
function_name = make_full_var_name(current_module.c_str(),
|
|
loc_str.c_str());
|
|
at_stmt = plr.stmt;
|
|
const Location* loc = at_stmt->GetLocationInfo();
|
|
safe_snprintf(description, sizeof(description), "%s at %s:%d",
|
|
function_name.c_str(), loc->filename, loc->last_line);
|
|
|
|
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();
|
|
safe_snprintf(description, sizeof(description), "%s:%d",
|
|
loc->filename, loc->last_line);
|
|
|
|
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 = plrFunction;
|
|
//### How to deal with wildcards?
|
|
//### perhaps save user choices?--tough...
|
|
break;
|
|
}
|
|
|
|
internal_error("DbgBreakpoint::Reset function incomplete.");
|
|
}
|
|
|
|
bool DbgBreakpoint::SetCondition(const 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 bcHitAndDelete;
|
|
}
|
|
|
|
if ( condition.size() )
|
|
{
|
|
// TODO: ### evaluate using debugger frame too
|
|
Val* yes = dbg_eval_expr(condition.c_str());
|
|
|
|
if ( ! yes )
|
|
{
|
|
debug_msg("Breakpoint condition '%s' invalid, removing condition.\n",
|
|
condition.c_str());
|
|
SetCondition("");
|
|
PrintHitMsg();
|
|
return bcHit;
|
|
}
|
|
|
|
if ( ! IsIntegral(yes->Type()->Tag()) &&
|
|
! IsBool(yes->Type()->Tag()) )
|
|
{
|
|
PrintHitMsg();
|
|
debug_msg("Breakpoint condition should return an integral type");
|
|
return bcHitAndDelete;
|
|
}
|
|
|
|
yes->CoerceToInt();
|
|
if ( yes->IsZero() )
|
|
return bcNoHit;
|
|
}
|
|
|
|
int repcount = GetRepeatCount();
|
|
if ( repcount )
|
|
{
|
|
if ( ++hit_count == repcount )
|
|
{
|
|
hit_count = 0;
|
|
PrintHitMsg();
|
|
return bcHit;
|
|
}
|
|
|
|
return bcNoHit;
|
|
}
|
|
|
|
PrintHitMsg();
|
|
return bcHit;
|
|
}
|
|
|
|
BreakCode DbgBreakpoint::ShouldBreak(Stmt* s)
|
|
{
|
|
if ( ! IsEnabled() )
|
|
return bcNoHit;
|
|
|
|
switch ( kind ) {
|
|
case BP_STMT:
|
|
case BP_FUNC:
|
|
if ( at_stmt != s )
|
|
return bcNoHit;
|
|
break;
|
|
|
|
case BP_LINE:
|
|
assert(s->GetLocationInfo()->first_line <= source_line &&
|
|
s->GetLocationInfo()->last_line >= source_line);
|
|
break;
|
|
|
|
case BP_TIME:
|
|
assert(false);
|
|
|
|
default:
|
|
internal_error("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 )
|
|
internal_error("Calling ShouldBreak(time) on a non-time breakpoint");
|
|
|
|
if ( t < at_time )
|
|
return bcNoHit;
|
|
|
|
if ( ! IsEnabled() )
|
|
return bcNoHit;
|
|
|
|
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 BroFunc* 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->first_line);
|
|
}
|
|
return;
|
|
|
|
case BP_TIME:
|
|
assert(false);
|
|
|
|
default:
|
|
internal_error("Missed a case in DbgBreakpoint::PrintHitMsg\n");
|
|
}
|
|
}
|