// $Id: DbgBreakpoint.cc 1345 2005-09-08 07:42:11Z vern $ // Implementation of breakpoints. #include "config.h" #include #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 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"); } }