zeek/src/DbgBreakpoint.cc

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");
}
}