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 <new>
|
||||||
|
|
||||||
|
#include <AutoDeleter.h>
|
||||||
#include <AutoLocker.h>
|
#include <AutoLocker.h>
|
||||||
|
#include <Variant.h>
|
||||||
|
|
||||||
#include "Architecture.h"
|
#include "Architecture.h"
|
||||||
#include "BreakpointManager.h"
|
#include "BreakpointManager.h"
|
||||||
|
@ -24,11 +26,13 @@
|
||||||
#include "MessageCodes.h"
|
#include "MessageCodes.h"
|
||||||
#include "Register.h"
|
#include "Register.h"
|
||||||
#include "SourceCode.h"
|
#include "SourceCode.h"
|
||||||
|
#include "SourceLanguage.h"
|
||||||
#include "SpecificImageDebugInfo.h"
|
#include "SpecificImageDebugInfo.h"
|
||||||
#include "StackTrace.h"
|
#include "StackTrace.h"
|
||||||
#include "Statement.h"
|
#include "Statement.h"
|
||||||
#include "Team.h"
|
#include "Team.h"
|
||||||
#include "Tracing.h"
|
#include "Tracing.h"
|
||||||
|
#include "Value.h"
|
||||||
#include "Worker.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,
|
ThreadHandler::ThreadHandler(Thread* thread, Worker* worker,
|
||||||
DebuggerInterface* debuggerInterface,
|
DebuggerInterface* debuggerInterface,
|
||||||
ImageDebugInfoJobListener* listener,
|
ImageDebugInfoJobListener* listener,
|
||||||
|
@ -58,7 +99,10 @@ ThreadHandler::ThreadHandler(Thread* thread, Worker* worker,
|
||||||
fSteppedOverFunctionAddress(0),
|
fSteppedOverFunctionAddress(0),
|
||||||
fPreviousInstructionPointer(0),
|
fPreviousInstructionPointer(0),
|
||||||
fPreviousFrameAddress(0),
|
fPreviousFrameAddress(0),
|
||||||
fSingleStepping(false)
|
fSingleStepping(false),
|
||||||
|
fHasPendingConditionEvaluation(false),
|
||||||
|
fConditionWaitSem(-1),
|
||||||
|
fConditionResult(NULL)
|
||||||
{
|
{
|
||||||
fDebuggerInterface->AcquireReference();
|
fDebuggerInterface->AcquireReference();
|
||||||
}
|
}
|
||||||
|
@ -68,6 +112,9 @@ ThreadHandler::~ThreadHandler()
|
||||||
{
|
{
|
||||||
_ClearContinuationState();
|
_ClearContinuationState();
|
||||||
fDebuggerInterface->ReleaseReference();
|
fDebuggerInterface->ReleaseReference();
|
||||||
|
|
||||||
|
if (fConditionWaitSem > 0)
|
||||||
|
delete_sem(fConditionWaitSem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,6 +123,7 @@ ThreadHandler::Init()
|
||||||
{
|
{
|
||||||
fWorker->ScheduleJob(new(std::nothrow) GetThreadStateJob(fDebuggerInterface,
|
fWorker->ScheduleJob(new(std::nothrow) GetThreadStateJob(fDebuggerInterface,
|
||||||
fThread));
|
fThread));
|
||||||
|
fConditionWaitSem = create_sem(0, "breakpoint condition waiter");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,6 +212,12 @@ ThreadHandler::HandleBreakpointHit(BreakpointHitEvent* event)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
locker.Unlock();
|
||||||
|
if (_HandleBreakpointConditionIfNeeded(cpuState))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
locker.Lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,6 +441,13 @@ ThreadHandler::HandleCpuStateChanged()
|
||||||
void
|
void
|
||||||
ThreadHandler::HandleStackTraceChanged()
|
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
|
bool
|
||||||
ThreadHandler::_HasExitedFrame(target_addr_t framePointer) const
|
ThreadHandler::_HasExitedFrame(target_addr_t framePointer) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@ class DebuggerInterface;
|
||||||
class ImageDebugInfoJobListener;
|
class ImageDebugInfoJobListener;
|
||||||
class StackFrame;
|
class StackFrame;
|
||||||
class Statement;
|
class Statement;
|
||||||
|
class Value;
|
||||||
class Worker;
|
class Worker;
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +65,9 @@ public:
|
||||||
void HandleCpuStateChanged();
|
void HandleCpuStateChanged();
|
||||||
void HandleStackTraceChanged();
|
void HandleStackTraceChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ExpressionJobListener;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// ImageDebugInfoProvider
|
// ImageDebugInfoProvider
|
||||||
virtual status_t GetImageDebugInfo(Image* image,
|
virtual status_t GetImageDebugInfo(Image* image,
|
||||||
|
@ -95,6 +99,10 @@ private:
|
||||||
void _SingleStepThread(
|
void _SingleStepThread(
|
||||||
target_addr_t instructionPointer);
|
target_addr_t instructionPointer);
|
||||||
|
|
||||||
|
bool _HandleBreakpointConditionIfNeeded(
|
||||||
|
CpuState* cpuState);
|
||||||
|
void _HandleBreakpointConditionEvaluated(
|
||||||
|
Value* value);
|
||||||
|
|
||||||
bool _HandleBreakpointHitStep(CpuState* cpuState);
|
bool _HandleBreakpointHitStep(CpuState* cpuState);
|
||||||
bool _HandleSingleStepStep(CpuState* cpuState);
|
bool _HandleSingleStepStep(CpuState* cpuState);
|
||||||
|
@ -115,6 +123,9 @@ private:
|
||||||
target_addr_t fPreviousInstructionPointer;
|
target_addr_t fPreviousInstructionPointer;
|
||||||
target_addr_t fPreviousFrameAddress;
|
target_addr_t fPreviousFrameAddress;
|
||||||
bool fSingleStepping;
|
bool fSingleStepping;
|
||||||
|
bool fHasPendingConditionEvaluation;
|
||||||
|
sem_id fConditionWaitSem;
|
||||||
|
Value* fConditionResult;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ThreadHandler* fNext;
|
ThreadHandler* fNext;
|
||||||
|
|
Loading…
Reference in New Issue