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.
This commit is contained in:
parent
43060a58fa
commit
a5ce4678c0
@ -11,7 +11,9 @@
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <AutoLocker.h>
|
||||
#include <Variant.h>
|
||||
|
||||
#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<ExpressionEvaluationJob*>(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<SourceLanguage> 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<ExpressionJobListener> 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
|
||||
{
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user