* Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2010-2016, Rene Gollent, rene@gollent.com.
* Distributed under the terms of the MIT License.
*/
#include "ThreadHandler.h"
#include <stdio.h>
#include <new>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Variant.h>
#include "Architecture.h"
#include "BreakpointManager.h"
#include "CpuState.h"
#include "DebugEvent.h"
#include "DebuggerInterface.h"
#include "ExpressionInfo.h"
#include "FunctionInstance.h"
#include "ImageDebugInfo.h"
#include "InstructionInfo.h"
#include "Jobs.h"
#include "MessageCodes.h"
#include "Register.h"
#include "SignalDispositionTypes.h"
#include "SourceCode.h"
#include "SourceLanguage.h"
#include "SpecificImageDebugInfo.h"
#include "StackTrace.h"
#include "Statement.h"
#include "SyntheticPrimitiveType.h"
#include "Team.h"
#include "Tracing.h"
#include "Value.h"
#include "ValueLocation.h"
#include "Worker.h"
enum {
STEP_NONE,
STEP_OVER,
STEP_INTO,
STEP_OUT,
STEP_UNTIL
};
class ExpressionEvaluationListener : public ExpressionInfo::Listener {
public:
ExpressionEvaluationListener(ThreadHandler* handler)
:
fHandler(handler)
{
fHandler->AcquireReference();
}
~ExpressionEvaluationListener()
{
fHandler->ReleaseReference();
}
virtual void ExpressionEvaluated(ExpressionInfo* info, status_t result,
ExpressionResult* value)
{
fHandler->_HandleBreakpointConditionEvaluated(value);
}
private:
ThreadHandler* fHandler;
};
ThreadHandler::ThreadHandler(::Thread* thread, Worker* worker,
DebuggerInterface* debuggerInterface, JobListener* jobListener,
BreakpointManager* breakpointManager)
:
fThread(thread),
fWorker(worker),
fDebuggerInterface(debuggerInterface),
fJobListener(jobListener),
fBreakpointManager(breakpointManager),
fStepMode(STEP_NONE),
fStepStatement(NULL),
fBreakpointAddress(0),
fSteppedOverFunctionAddress(0),
fPreviousInstructionPointer(0),
fPreviousFrameAddress(0),
fSingleStepping(false),
fConditionWaitSem(-1),
fConditionResult(NULL)
{
fDebuggerInterface->AcquireReference();
}
ThreadHandler::~ThreadHandler()
{
_ClearContinuationState();
fDebuggerInterface->ReleaseReference();
if (fConditionWaitSem > 0)
delete_sem(fConditionWaitSem);
}
void
ThreadHandler::Init()
{
fWorker->ScheduleJob(new(std::nothrow) GetThreadStateJob(fDebuggerInterface,
fThread), fJobListener);
fConditionWaitSem = create_sem(0, "breakpoint condition waiter");
}
status_t
ThreadHandler::SetBreakpointAndRun(target_addr_t address)
{
status_t error = _InstallTemporaryBreakpoint(address);
if (error != B_OK)
return error;
fPreviousInstructionPointer = 0;
fDebuggerInterface->ContinueThread(ThreadID());
fStepMode = STEP_OUT;
fSingleStepping = false;
return B_OK;
}
bool
ThreadHandler::HandleThreadDebugged(ThreadDebuggedEvent* event,
const BString& stoppedReason)
{
return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGED, stoppedReason);
}
bool
ThreadHandler::HandleDebuggerCall(DebuggerCallEvent* event)
{
BString message;
fDebuggerInterface->ReadMemoryString(event->Message(), 1024, message);
return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGER_CALL, message);
}
bool
ThreadHandler::HandleBreakpointHit(BreakpointHitEvent* event)
{
CpuState* cpuState = event->GetCpuState();
target_addr_t instructionPointer = cpuState->InstructionPointer();
TRACE_EVENTS("ThreadHandler::HandleBreakpointHit(): ip: %" B_PRIx64 "\n",
instructionPointer);
if (fBreakpointAddress != 0 && instructionPointer == fBreakpointAddress
&& fStepMode != STEP_NONE) {
if (fStepMode != STEP_UNTIL && _HandleBreakpointHitStep(cpuState))
return true;
} else {
AutoLocker<Team> locker(fThread->GetTeam());
Breakpoint* breakpoint = fThread->GetTeam()->BreakpointAtAddress(
cpuState->InstructionPointer());
bool continueThread = false;
if (breakpoint == NULL) {
continueThread = true;
} else if (!breakpoint->HasEnabledUserBreakpoint()) {
continueThread = true;
}
if (continueThread) {
if (fSingleStepping) {
if (fPreviousInstructionPointer == instructionPointer) {
fDebuggerInterface->SingleStepThread(ThreadID());
return true;
}
if (fStepMode != STEP_NONE) {
if (_HandleSingleStepStep(cpuState))
return true;
}
}
return false;
} else {
locker.Unlock();
if (_HandleBreakpointConditionIfNeeded(cpuState))
return true;
locker.Lock();
}
}
return _HandleThreadStopped(cpuState, THREAD_STOPPED_BREAKPOINT);
}
bool
ThreadHandler::HandleWatchpointHit(WatchpointHitEvent* event)
{
return _HandleThreadStopped(event->GetCpuState(),
THREAD_STOPPED_WATCHPOINT);
}
bool
ThreadHandler::HandleSingleStep(SingleStepEvent* event)
{
if (fStepMode != STEP_NONE) {
if (_HandleSingleStepStep(event->GetCpuState()))
return true;
}
return _HandleThreadStopped(event->GetCpuState(),
THREAD_STOPPED_SINGLE_STEP);
}
bool
ThreadHandler::HandleExceptionOccurred(ExceptionOccurredEvent* event)
{
char buffer[256];
get_debug_exception_string(event->Exception(), buffer, sizeof(buffer));
return _HandleThreadStopped(NULL, THREAD_STOPPED_EXCEPTION, buffer);
}
bool
ThreadHandler::HandleSignalReceived(SignalReceivedEvent* event)
{
::Team* team = fThread->GetTeam();
AutoLocker<Team> locker(team);
const SignalInfo& info = event->GetSignalInfo();
int32 signal = info.Signal();
int32 disposition = team->SignalDispositionFor(signal);
switch (disposition) {
case SIGNAL_DISPOSITION_IGNORE:
return false;
case SIGNAL_DISPOSITION_STOP_AT_SIGNAL_HANDLER:
{
const struct sigaction& handlerInfo = info.Handler();
target_addr_t address = 0;
if ((handlerInfo.sa_flags & SA_SIGINFO) != 0)
address = (target_addr_t)handlerInfo.sa_sigaction;
else
address = (target_addr_t)handlerInfo.sa_handler;
if (address == (target_addr_t)SIG_DFL
|| address == (target_addr_t)SIG_IGN
|| address == (target_addr_t)SIG_HOLD) {
address = 0;
}
if (address != 0 && _InstallTemporaryBreakpoint(address) == B_OK
&& fDebuggerInterface->ContinueThread(ThreadID()) == B_OK) {
fStepMode = STEP_UNTIL;
return true;
}
}
case SIGNAL_DISPOSITION_STOP_AT_RECEIPT:
{
BString stopReason;
stopReason.SetToFormat("Received signal %" B_PRId32 " (%s)",
signal, strsignal(signal));
return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGED,
stopReason);
}
default:
break;
}
return false;
}
void
ThreadHandler::HandleThreadAction(uint32 action, target_addr_t address)
{
AutoLocker<Team> locker(fThread->GetTeam());
if (fThread->State() == THREAD_STATE_UNKNOWN)
return;
if (action == MSG_THREAD_STOP
? fThread->State() != THREAD_STATE_RUNNING
: fThread->State() != THREAD_STATE_STOPPED) {
return;
}
CpuState* cpuState = fThread->GetCpuState();
StackTrace* stackTrace = fThread->GetStackTrace();
BReference<CpuState> cpuStateReference(cpuState);
BReference<StackTrace> stackTraceReference(stackTrace);
if (action == MSG_THREAD_SET_ADDRESS) {
_HandleSetAddress(cpuState, address);
return;
}
if (action != MSG_THREAD_STOP) {
_SetThreadState(THREAD_STATE_RUNNING, NULL, THREAD_STOPPED_UNKNOWN,
BString());
}
locker.Unlock();
switch (action) {
case MSG_THREAD_RUN:
fStepMode = address != 0 ? STEP_UNTIL : STEP_NONE;
if (address != 0)
_InstallTemporaryBreakpoint(address);
_RunThread(0);
return;
case MSG_THREAD_STOP:
fStepMode = STEP_NONE;
if (fDebuggerInterface->StopThread(ThreadID()) == B_OK)
fThread->SetStopRequestPending();
return;
case MSG_THREAD_STEP_OVER:
case MSG_THREAD_STEP_INTO:
case MSG_THREAD_STEP_OUT:
break;
}
TRACE_CONTROL("ThreadHandler::HandleThreadAction(MSG_THREAD_STEP_*)\n");
if (stackTrace == NULL && cpuState == NULL) {
if (fDebuggerInterface->GetCpuState(fThread->ID(), cpuState) == B_OK)
cpuStateReference.SetTo(cpuState, true);
}
if (stackTrace == NULL && cpuState != NULL) {
if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
fThread->GetTeam(), this, cpuState, stackTrace, NULL, 1,
false, false) == B_OK) {
stackTraceReference.SetTo(stackTrace, true);
}
}
if (stackTrace == NULL || stackTrace->CountFrames() == 0) {
_StepFallback();
return;
}
StackFrame* frame = stackTrace->FrameAt(0);
TRACE_CONTROL(" ip: %#" B_PRIx64 "\n", frame->InstructionPointer());
target_addr_t frameIP = frame->GetCpuState()->InstructionPointer();
if (frame->Type() == STACK_FRAME_TYPE_SYSCALL) {
status_t error = _InstallTemporaryBreakpoint(frameIP);
if (error != B_OK) {
_StepFallback();
return;
}
fStepMode = STEP_OUT;
_RunThread(frameIP);
return;
}
if (action == MSG_THREAD_STEP_OUT) {
status_t error = _InstallTemporaryBreakpoint(frame->ReturnAddress());
if (error != B_OK) {
_StepFallback();
return;
}
fPreviousInstructionPointer = frameIP;
fPreviousFrameAddress = frame->FrameAddress();
fStepMode = STEP_OUT;
_RunThread(frameIP);
return;
}
fStepStatement = _GetStatementAtInstructionPointer(frame);
if (fStepStatement == NULL) {
_StepFallback();
return;
}
TRACE_CONTROL(" statement: %#" B_PRIx64 " - %#" B_PRIx64 "\n",
fStepStatement->CoveringAddressRange().Start(),
fStepStatement->CoveringAddressRange().End());
if (action == MSG_THREAD_STEP_INTO) {
fStepMode = STEP_INTO;
_SingleStepThread(frameIP);
} else {
fPreviousFrameAddress = frame->FrameAddress();
fStepMode = STEP_OVER;
if (!_DoStepOver(frame->GetCpuState()))
_StepFallback();
}
}
void
ThreadHandler::HandleThreadStateChanged()
{
AutoLocker<Team> locker(fThread->GetTeam());
fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_CPU_STATE));
fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_STACK_TRACE));
if (fThread->State() == THREAD_STATE_STOPPED
&& fThread->GetCpuState() == NULL) {
fWorker->ScheduleJob(
new(std::nothrow) GetCpuStateJob(fDebuggerInterface, fThread),
fJobListener);
}
}
void
ThreadHandler::HandleCpuStateChanged()
{
AutoLocker<Team> locker(fThread->GetTeam());
fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_STACK_TRACE));
if (fThread->GetCpuState() != NULL && fThread->GetStackTrace() == NULL) {
fWorker->ScheduleJob(
new(std::nothrow) GetStackTraceJob(fDebuggerInterface,
fJobListener, fDebuggerInterface->GetArchitecture(),
fThread), fJobListener);
}
}
void
ThreadHandler::HandleStackTraceChanged()
{
}
status_t
ThreadHandler::GetImageDebugInfo(Image* image, ImageDebugInfo*& _info)
{
AutoLocker<Team> teamLocker(fThread->GetTeam());
if (image->GetImageDebugInfo() != NULL) {
_info = image->GetImageDebugInfo();
_info->AcquireReference();
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
bool
ThreadHandler::_HandleThreadStopped(CpuState* cpuState, uint32 stoppedReason,
const BString& stoppedReasonInfo)
{
_ClearContinuationState();
AutoLocker<Team> locker(fThread->GetTeam());
_SetThreadState(THREAD_STATE_STOPPED, cpuState, stoppedReason,
stoppedReasonInfo);
return true;
}
bool
ThreadHandler::_HandleSetAddress(CpuState* state, target_addr_t address)
{
CpuState* newState = NULL;
if (state->Clone(newState) != B_OK)
return false;
BReference<CpuState> stateReference(newState, true);
newState->SetInstructionPointer(address);
if (fDebuggerInterface->SetCpuState(fThread->ID(), newState) != B_OK)
return false;
AutoLocker<Team> locker(fThread->GetTeam());
fThread->SetStackTrace(NULL);
fThread->SetCpuState(newState);
return true;
}
void
ThreadHandler::_SetThreadState(uint32 state, CpuState* cpuState,
uint32 stoppedReason, const BString& stoppedReasonInfo)
{
fThread->SetState(state, stoppedReason, stoppedReasonInfo);
fThread->SetCpuState(cpuState);
}
Statement*
ThreadHandler::_GetStatementAtInstructionPointer(StackFrame* frame)
{
AutoLocker<Team> locker(fThread->GetTeam());
FunctionInstance* functionInstance = frame->Function();
if (functionInstance == NULL)
return NULL;
FunctionDebugInfo* function = functionInstance->GetFunctionDebugInfo();
locker.Unlock();
Statement* statement;
if (function->GetSpecificImageDebugInfo()->GetStatement(function,
frame->InstructionPointer(), statement) != B_OK) {
return NULL;
}
return statement;
}
void
ThreadHandler::_StepFallback()
{
fStepMode = STEP_NONE;
_SingleStepThread(0);
}
bool
ThreadHandler::_DoStepOver(CpuState* cpuState)
{
TRACE_CONTROL("ThreadHandler::_DoStepOver()\n");
InstructionInfo info;
if (fDebuggerInterface->GetArchitecture()->GetInstructionInfo(
cpuState->InstructionPointer(), info, cpuState) != B_OK) {
TRACE_CONTROL(" failed to get instruction info\n");
return false;
}
if (info.Type() != INSTRUCTION_TYPE_SUBROUTINE_CALL) {
_SingleStepThread(cpuState->InstructionPointer());
TRACE_CONTROL(" not a subroutine call\n");
return true;
}
TRACE_CONTROL(" subroutine call -- installing breakpoint at address "
"%#" B_PRIx64 "\n", info.Address() + info.Size());
if (_InstallTemporaryBreakpoint(info.Address() + info.Size()) != B_OK)
return false;
fSteppedOverFunctionAddress = info.TargetAddress();
_RunThread(cpuState->InstructionPointer());
return true;
}
status_t
ThreadHandler::_InstallTemporaryBreakpoint(target_addr_t address)
{
_UninstallTemporaryBreakpoint();
status_t error = fBreakpointManager->InstallTemporaryBreakpoint(address,
this);
if (error != B_OK)
return error;
fBreakpointAddress = address;
return B_OK;
}
void
ThreadHandler::_UninstallTemporaryBreakpoint()
{
if (fBreakpointAddress == 0)
return;
fBreakpointManager->UninstallTemporaryBreakpoint(fBreakpointAddress, this);
fBreakpointAddress = 0;
}
void
ThreadHandler::_ClearContinuationState()
{
_UninstallTemporaryBreakpoint();
if (fStepStatement != NULL) {
fStepStatement->ReleaseReference();
fStepStatement = NULL;
}
fStepMode = STEP_NONE;
fSingleStepping = false;
}
void
ThreadHandler::_RunThread(target_addr_t instructionPointer)
{
fPreviousInstructionPointer = instructionPointer;
fDebuggerInterface->ContinueThread(ThreadID());
fSingleStepping = false;
}
void
ThreadHandler::_SingleStepThread(target_addr_t instructionPointer)
{
fPreviousInstructionPointer = instructionPointer;
fDebuggerInterface->SingleStepThread(ThreadID());
fSingleStepping = true;
}
bool
ThreadHandler::_HandleBreakpointHitStep(CpuState* cpuState)
{
_UninstallTemporaryBreakpoint();
switch (fStepMode) {
case STEP_OVER:
{
StackTrace* stackTrace = fThread->GetStackTrace();
BReference<StackTrace> stackTraceReference(stackTrace);
if (stackTrace == NULL && cpuState != NULL) {
if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
fThread->GetTeam(), this, cpuState, stackTrace, NULL,
1, false, false) == B_OK) {
stackTraceReference.SetTo(stackTrace, true);
}
}
if (stackTrace != NULL) {
StackFrame* frame = stackTrace->FrameAt(0);
if (frame != NULL && fPreviousFrameAddress
!= frame->FrameAddress()) {
status_t error = _InstallTemporaryBreakpoint(
cpuState->InstructionPointer());
if (error != B_OK)
_StepFallback();
else
_RunThread(cpuState->InstructionPointer());
return true;
}
}
if (fPreviousFrameAddress != 0 && fSteppedOverFunctionAddress != 0
&& fSteppedOverFunctionAddress != cpuState->InstructionPointer()) {
TRACE_CONTROL("STEP_OVER: called function address %#" B_PRIx64
", previous frame address: %#" B_PRIx64 ", frame address: %#"
B_PRIx64 ", adding return info\n", fSteppedOverFunctionAddress,
fPreviousFrameAddress, stackTrace->FrameAt(0)->FrameAddress());
ReturnValueInfo* returnInfo = new(std::nothrow) ReturnValueInfo(
fSteppedOverFunctionAddress, cpuState);
if (returnInfo == NULL)
return false;
BReference<ReturnValueInfo> returnInfoReference(returnInfo, true);
if (fThread->AddReturnValueInfo(returnInfo) != B_OK)
return false;
returnInfoReference.Detach();
fSteppedOverFunctionAddress = 0;
}
if (fStepStatement->ContainsAddress(
cpuState->InstructionPointer())) {
if (!_DoStepOver(cpuState))
_StepFallback();
return true;
}
fPreviousFrameAddress = 0;
return false;
}
case STEP_INTO:
return false;
case STEP_OUT:
{
if (!_HasExitedFrame(cpuState->StackFramePointer())) {
status_t error = _InstallTemporaryBreakpoint(
cpuState->InstructionPointer());
if (error != B_OK)
_StepFallback();
else
_RunThread(cpuState->InstructionPointer());
return true;
}
if (fPreviousFrameAddress == 0)
return false;
TRACE_CONTROL("ThreadHandler::_HandleBreakpointHitStep() - "
"frame pointer 0x%#" B_PRIx64 ", previous: 0x%#" B_PRIx64
" - step out adding return value\n", cpuState
->StackFramePointer(), fPreviousFrameAddress);
ReturnValueInfo* info = new(std::nothrow) ReturnValueInfo(
fPreviousInstructionPointer, cpuState);
if (info == NULL)
return false;
BReference<ReturnValueInfo> infoReference(info, true);
if (fThread->AddReturnValueInfo(info) != B_OK)
return false;
infoReference.Detach();
fPreviousFrameAddress = 0;
}
default:
return false;
}
}
bool
ThreadHandler::_HandleSingleStepStep(CpuState* cpuState)
{
TRACE_CONTROL("ThreadHandler::_HandleSingleStepStep(): ip: %" B_PRIx64 "\n",
cpuState->InstructionPointer());
switch (fStepMode) {
case STEP_INTO:
{
if (fStepStatement->ContainsAddress(cpuState->InstructionPointer())) {
_SingleStepThread(cpuState->InstructionPointer());
return true;
}
StackTrace* stackTrace = fThread->GetStackTrace();
BReference<StackTrace> stackTraceReference(stackTrace);
if (stackTrace == NULL && cpuState != NULL) {
if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
fThread->GetTeam(), this, cpuState, stackTrace, NULL,
1, false, false) == B_OK) {
stackTraceReference.SetTo(stackTrace, true);
}
}
if (stackTrace != NULL) {
StackFrame* frame = stackTrace->FrameAt(0);
Image* image = frame->GetImage();
if (image == NULL)
return false;
ImageDebugInfo* info = NULL;
if (GetImageDebugInfo(image, info) != B_OK)
return false;
BReference<ImageDebugInfo>(info, true);
if (info->GetAddressSectionType(
cpuState->InstructionPointer())
== ADDRESS_SECTION_TYPE_PLT) {
_SingleStepThread(cpuState->InstructionPointer());
return true;
}
}
return false;
}
case STEP_OVER:
{
if (!fStepStatement->ContainsAddress(cpuState->InstructionPointer())) {
StackTrace* stackTrace = fThread->GetStackTrace();
BReference<StackTrace> stackTraceReference(stackTrace);
if (stackTrace == NULL && cpuState != NULL) {
if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
fThread->GetTeam(), this, cpuState, stackTrace,
NULL, 1, false, false) == B_OK) {
stackTraceReference.SetTo(stackTrace, true);
}
}
if (stackTrace != NULL) {
if (_HasExitedFrame(stackTrace->FrameAt(0)
->FrameAddress())) {
TRACE_CONTROL("ThreadHandler::_HandleSingleStepStep() "
" - adding return value for STEP_OVER\n");
ReturnValueInfo* info = new(std::nothrow)
ReturnValueInfo(fStepStatement
->CoveringAddressRange().Start(), cpuState);
if (info == NULL)
return false;
BReference<ReturnValueInfo> infoReference(info, true);
if (fThread->AddReturnValueInfo(info) != B_OK)
return false;
infoReference.Detach();
}
}
return false;
}
return _DoStepOver(cpuState);
}
case STEP_OUT:
default:
return false;
}
}
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();
BReference<StackTrace> stackTraceReference;
if (stackTrace == NULL) {
if (fDebuggerInterface->GetArchitecture()->CreateStackTrace(
fThread->GetTeam(), this, cpuState, stackTrace, NULL, 1,
false, true) == B_OK) {
stackTraceReference.SetTo(stackTrace, true);
} else
return false;
}
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);
ExpressionEvaluationListener* listener
= new(std::nothrow) ExpressionEvaluationListener(this);
if (listener == NULL)
return false;
ExpressionInfo* expressionInfo = new(std::nothrow) ExpressionInfo(
userBreakpoint->Condition());
if (expressionInfo == NULL) {
delete listener;
return false;
}
BReference<ExpressionInfo> expressionReference(expressionInfo, true);
expressionInfo->AddListener(listener);
status_t error = fWorker->ScheduleJob(
new(std::nothrow) ExpressionEvaluationJob(fThread->GetTeam(),
fDebuggerInterface, language, expressionInfo, frame, fThread),
fJobListener);
BPrivate::ObjectDeleter<ExpressionEvaluationListener> deleter(
listener);
if (error == B_OK) {
teamLocker.Unlock();
do {
error = acquire_sem(fConditionWaitSem);
} while (error == B_INTERRUPTED);
teamLocker.Lock();
if (_CheckStopCondition()) {
if (fConditionResult != NULL) {
fConditionResult->ReleaseReference();
fConditionResult = NULL;
}
_SetThreadState(THREAD_STATE_STOPPED, cpuState,
THREAD_STOPPED_BREAKPOINT, BString());
return false;
} else {
fDebuggerInterface->ContinueThread(ThreadID());
return true;
}
}
}
return false;
}
void
ThreadHandler::_HandleBreakpointConditionEvaluated(ExpressionResult* value)
{
fConditionResult = value;
if (fConditionResult != NULL)
fConditionResult->AcquireReference();
release_sem(fConditionWaitSem);
}
bool
ThreadHandler::_CheckStopCondition()
{
if (fConditionResult == NULL)
return true;
if (fConditionResult->Kind() != EXPRESSION_RESULT_KIND_PRIMITIVE)
return true;
BVariant value;
if (!fConditionResult->PrimitiveValue()->ToVariant(value))
return true;
return value.ToBool();
}
bool
ThreadHandler::_HasExitedFrame(target_addr_t framePointer) const
{
return fDebuggerInterface->GetArchitecture()->StackGrowthDirection()
== STACK_GROWTH_DIRECTION_POSITIVE
? framePointer < fPreviousFrameAddress
: framePointer > fPreviousFrameAddress;
}