From a5ce4678c054489520b8e2a7c0910da48a5d1521 Mon Sep 17 00:00:00 2001 From: Rene Gollent Date: Thu, 30 Oct 2014 16:49:16 -0400 Subject: [PATCH] Debugger: Implement conditional breakpoints. ThreadHandler: - When a breakpoint event is hit, we now check if there is an associated UserBreakpoint with a condition attached. If so, we schedule an evaluation request, and only stop the thread if that one evaluates to true (or if evaluation fails in some way). This implements #9713. --- .../debugger/controllers/ThreadHandler.cpp | 168 +++++++++++++++++- src/apps/debugger/controllers/ThreadHandler.h | 11 ++ 2 files changed, 178 insertions(+), 1 deletion(-) diff --git a/src/apps/debugger/controllers/ThreadHandler.cpp b/src/apps/debugger/controllers/ThreadHandler.cpp index a9653c200b..e44bbbbf8f 100644 --- a/src/apps/debugger/controllers/ThreadHandler.cpp +++ b/src/apps/debugger/controllers/ThreadHandler.cpp @@ -11,7 +11,9 @@ #include +#include #include +#include #include "Architecture.h" #include "BreakpointManager.h" @@ -24,11 +26,13 @@ #include "MessageCodes.h" #include "Register.h" #include "SourceCode.h" +#include "SourceLanguage.h" #include "SpecificImageDebugInfo.h" #include "StackTrace.h" #include "Statement.h" #include "Team.h" #include "Tracing.h" +#include "Value.h" #include "Worker.h" @@ -42,6 +46,43 @@ enum { }; +class ExpressionJobListener : public JobListener { +public: + ExpressionJobListener(ThreadHandler* handler) + : + JobListener(), + fHandler(handler) + { + fHandler->AcquireReference(); + } + + ~ExpressionJobListener() + { + fHandler->ReleaseReference(); + } + + virtual void JobDone(Job* job) + { + Value* resultValue = dynamic_cast(job) + ->GetResultValue(); + + fHandler->_HandleBreakpointConditionEvaluated(resultValue); + } + + virtual void JobFailed(Job* job) + { + fHandler->_HandleBreakpointConditionEvaluated(NULL); + } + + virtual void JobAborted(Job* job) + { + fHandler->_HandleBreakpointConditionEvaluated(NULL); + } +private: + ThreadHandler* fHandler; +}; + + ThreadHandler::ThreadHandler(Thread* thread, Worker* worker, DebuggerInterface* debuggerInterface, ImageDebugInfoJobListener* listener, @@ -58,7 +99,10 @@ ThreadHandler::ThreadHandler(Thread* thread, Worker* worker, fSteppedOverFunctionAddress(0), fPreviousInstructionPointer(0), fPreviousFrameAddress(0), - fSingleStepping(false) + fSingleStepping(false), + fHasPendingConditionEvaluation(false), + fConditionWaitSem(-1), + fConditionResult(NULL) { fDebuggerInterface->AcquireReference(); } @@ -68,6 +112,9 @@ ThreadHandler::~ThreadHandler() { _ClearContinuationState(); fDebuggerInterface->ReleaseReference(); + + if (fConditionWaitSem > 0) + delete_sem(fConditionWaitSem); } @@ -76,6 +123,7 @@ ThreadHandler::Init() { fWorker->ScheduleJob(new(std::nothrow) GetThreadStateJob(fDebuggerInterface, fThread)); + fConditionWaitSem = create_sem(0, "breakpoint condition waiter"); } @@ -164,6 +212,12 @@ ThreadHandler::HandleBreakpointHit(BreakpointHitEvent* event) } return false; + } else { + locker.Unlock(); + if (_HandleBreakpointConditionIfNeeded(cpuState)) + return true; + + locker.Lock(); } } @@ -387,6 +441,13 @@ ThreadHandler::HandleCpuStateChanged() void ThreadHandler::HandleStackTraceChanged() { + AutoLocker< ::Team> teamLocker(fThread->GetTeam()); + if (fHasPendingConditionEvaluation && fThread->GetStackTrace() != NULL) { + fHasPendingConditionEvaluation = false; + teamLocker.Unlock(); + + _HandleBreakpointConditionIfNeeded(fThread->GetCpuState()); + } } @@ -788,6 +849,111 @@ ThreadHandler::_HandleSingleStepStep(CpuState* cpuState) } +bool +ThreadHandler::_HandleBreakpointConditionIfNeeded(CpuState* cpuState) +{ + AutoLocker< ::Team> teamLocker(fThread->GetTeam()); + Breakpoint* breakpoint = fThread->GetTeam()->BreakpointAtAddress( + cpuState->InstructionPointer()); + + if (breakpoint == NULL) + return false; + + if (!breakpoint->HasEnabledUserBreakpoint()) + return false; + + const UserBreakpointInstanceList& breakpoints + = breakpoint->UserBreakpoints(); + + for (UserBreakpointInstanceList::ConstIterator it + = breakpoints.GetIterator(); it.HasNext();) { + UserBreakpoint* userBreakpoint = it.Next()->GetUserBreakpoint(); + if (!userBreakpoint->IsValid()) + continue; + if (!userBreakpoint->IsEnabled()) + continue; + if (!userBreakpoint->HasCondition()) + continue; + + StackTrace* stackTrace = fThread->GetStackTrace(); + if (stackTrace == NULL) { + fThread->SetCpuState(cpuState); + fHasPendingConditionEvaluation = true; + return true; + } + + StackFrame* frame = stackTrace->FrameAt(0); + FunctionDebugInfo* info = frame->Function()->GetFunctionDebugInfo(); + if (info == NULL) + return false; + + SpecificImageDebugInfo* specificInfo + = info->GetSpecificImageDebugInfo(); + if (specificInfo == NULL) + return false; + + SourceLanguage* language; + if (specificInfo->GetSourceLanguage(info, language) != B_OK) + return false; + + BReference reference(language, true); + ExpressionJobListener* listener + = new(std::nothrow) ExpressionJobListener(this); + if (listener == NULL) + return false; + + status_t error = fWorker->ScheduleJob( + new(std::nothrow) ExpressionEvaluationJob(fThread->GetTeam(), + fDebuggerInterface, language, userBreakpoint->Condition(), + B_UINT64_TYPE, frame, fThread), listener); + + BPrivate::ObjectDeleter deleter(listener); + if (error == B_OK) { + teamLocker.Unlock(); + + do { + error = acquire_sem(fConditionWaitSem); + } while (error == B_INTERRUPTED); + + teamLocker.Lock(); + + bool stop = false; + if (fConditionResult == NULL) + stop = true; + else { + BVariant value; + if (!fConditionResult->ToVariant(value)) + stop = true; + if (!value.TypeIsInteger(value.Type())) + stop = true; + stop = value.ToBool(); + fConditionResult->ReleaseReference(); + fConditionResult = NULL; + } + + if (stop) + return false; + else { + fDebuggerInterface->ContinueThread(fThread->ID()); + return true; + } + } + } + + return false; +} + + +void +ThreadHandler::_HandleBreakpointConditionEvaluated(Value* value) +{ + fConditionResult = value; + if (fConditionResult != NULL) + fConditionResult->AcquireReference(); + release_sem(fConditionWaitSem); +} + + bool ThreadHandler::_HasExitedFrame(target_addr_t framePointer) const { diff --git a/src/apps/debugger/controllers/ThreadHandler.h b/src/apps/debugger/controllers/ThreadHandler.h index eb80905d21..f29f539b79 100644 --- a/src/apps/debugger/controllers/ThreadHandler.h +++ b/src/apps/debugger/controllers/ThreadHandler.h @@ -21,6 +21,7 @@ class DebuggerInterface; class ImageDebugInfoJobListener; class StackFrame; class Statement; +class Value; class Worker; @@ -64,6 +65,9 @@ public: void HandleCpuStateChanged(); void HandleStackTraceChanged(); +private: + friend class ExpressionJobListener; + private: // ImageDebugInfoProvider virtual status_t GetImageDebugInfo(Image* image, @@ -95,6 +99,10 @@ private: void _SingleStepThread( target_addr_t instructionPointer); + bool _HandleBreakpointConditionIfNeeded( + CpuState* cpuState); + void _HandleBreakpointConditionEvaluated( + Value* value); bool _HandleBreakpointHitStep(CpuState* cpuState); bool _HandleSingleStepStep(CpuState* cpuState); @@ -115,6 +123,9 @@ private: target_addr_t fPreviousInstructionPointer; target_addr_t fPreviousFrameAddress; bool fSingleStepping; + bool fHasPendingConditionEvaluation; + sem_id fConditionWaitSem; + Value* fConditionResult; public: ThreadHandler* fNext;