* Moved breakpoint management into new class BreakpointManager and added

support for temporary breakpoints.
* TeamDebugger: No longer handle debug events in the listener thread. Instead
  post a message to the looper thread. Makes the locking a bit easier.
* Architecture: Added virtual GetInstructionInfo() and GetStatement() returning
  information on the instruction respectively a statement at a given address.
  Implemented for x86.
* DebugInfo: Added virtual GetStatement() and implemented it for
  DebuggerDebugInfo by means of using the Architecture.
* Implemented step over/into/out support. Works in principle, but has no
  handling for PLTs yet, i.e. stepping into functions of other libraries
  requires two steps ATM.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31244 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-06-25 23:51:09 +00:00
parent 1a90a58f48
commit 43b0f7e0d7
16 changed files with 919 additions and 123 deletions
+235
View File
@@ -0,0 +1,235 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "BreakpointManager.h"
#include <stdio.h>
#include <new>
#include <AutoLocker.h>
#include "DebuggerInterface.h"
#include "TeamDebugModel.h"
BreakpointManager::BreakpointManager(TeamDebugModel* debugModel,
DebuggerInterface* debuggerInterface)
:
fLock("breakpoint manager"),
fDebugModel(debugModel),
fDebuggerInterface(debuggerInterface)
{
}
BreakpointManager::~BreakpointManager()
{
}
status_t
BreakpointManager::Init()
{
return fLock.InitCheck();
}
status_t
BreakpointManager::InstallUserBreakpoint(target_addr_t address,
bool enabled)
{
user_breakpoint_state state = enabled
? USER_BREAKPOINT_ENABLED : USER_BREAKPOINT_DISABLED;
AutoLocker<TeamDebugModel> modelLocker(fDebugModel);
// If there already is a breakpoint, it might already have the requested
// state.
Breakpoint* breakpoint = fDebugModel->BreakpointAtAddress(address);
if (breakpoint != NULL && breakpoint->UserState() == state)
return B_OK;
// create a breakpoint, if it doesn't exist yet
if (breakpoint == NULL) {
Image* image = fDebugModel->GetTeam()->ImageByAddress(address);
if (image == NULL)
return B_BAD_ADDRESS;
breakpoint = new(std::nothrow) Breakpoint(image, address);
if (breakpoint == NULL)
return B_NO_MEMORY;
if (!fDebugModel->AddBreakpoint(breakpoint))
return B_NO_MEMORY;
}
user_breakpoint_state oldState = breakpoint->UserState();
// set the breakpoint state
breakpoint->SetUserState(state);
fDebugModel->NotifyUserBreakpointChanged(breakpoint);
AutoLocker<BLocker> installLocker(fLock);
// We need to make the installation decision with both locks held, and
// we keep this lock until we have the breakpoint installed/uninstalled.
bool install = breakpoint->ShouldBeInstalled();
if (breakpoint->IsInstalled() == install)
return B_OK;
// The breakpoint needs to be installed/uninstalled.
Reference<Breakpoint> breakpointReference(breakpoint);
modelLocker.Unlock();
status_t error = install
? fDebuggerInterface->InstallBreakpoint(address)
: fDebuggerInterface->UninstallBreakpoint(address);
// Mark the breakpoint installed/uninstalled, if everything went fine.
if (error == B_OK) {
breakpoint->SetInstalled(install);
return B_OK;
}
// revert on error
installLocker. Unlock();
modelLocker.Lock();
breakpoint->SetUserState(oldState);
fDebugModel->NotifyUserBreakpointChanged(breakpoint);
if (breakpoint->IsUnused())
fDebugModel->RemoveBreakpoint(breakpoint);
return error;
}
void
BreakpointManager::UninstallUserBreakpoint(target_addr_t address)
{
AutoLocker<TeamDebugModel> modelLocker(fDebugModel);
Breakpoint* breakpoint = fDebugModel->BreakpointAtAddress(address);
if (breakpoint == NULL || breakpoint->UserState() == USER_BREAKPOINT_NONE)
return;
// set the breakpoint state
breakpoint->SetUserState(USER_BREAKPOINT_NONE);
fDebugModel->NotifyUserBreakpointChanged(breakpoint);
AutoLocker<BLocker> installLocker(fLock);
// We need to make the uninstallation decision with both locks held, and
// we keep this lock until we have the breakpoint uninstalled.
// check whether the breakpoint needs to be uninstalled
bool uninstall = !breakpoint->ShouldBeInstalled()
&& breakpoint->IsInstalled();
// if unused remove it
Reference<Breakpoint> breakpointReference(breakpoint);
if (breakpoint->IsUnused())
fDebugModel->RemoveBreakpoint(breakpoint);
modelLocker.Unlock();
if (uninstall) {
fDebuggerInterface->UninstallBreakpoint(address);
breakpoint->SetInstalled(false);
}
}
status_t
BreakpointManager::InstallTemporaryBreakpoint(target_addr_t address,
BreakpointClient* client)
{
AutoLocker<TeamDebugModel> modelLocker(fDebugModel);
// create a breakpoint, if it doesn't exist yet
Breakpoint* breakpoint = fDebugModel->BreakpointAtAddress(address);
if (breakpoint == NULL) {
Image* image = fDebugModel->GetTeam()->ImageByAddress(address);
if (image == NULL)
return B_BAD_ADDRESS;
breakpoint = new(std::nothrow) Breakpoint(image, address);
if (breakpoint == NULL)
return B_NO_MEMORY;
if (!fDebugModel->AddBreakpoint(breakpoint))
return B_NO_MEMORY;
}
Reference<Breakpoint> breakpointReference(breakpoint);
// add the client
status_t error;
if (breakpoint->AddClient(client)) {
AutoLocker<BLocker> installLocker(fLock);
// We need to make the installation decision with both locks held,
// and we keep this lock until we have the breakpoint installed.
if (breakpoint->IsInstalled())
return B_OK;
// install
modelLocker.Unlock();
error = fDebuggerInterface->InstallBreakpoint(address);
if (error == B_OK) {
breakpoint->SetInstalled(true);
return B_OK;
}
installLocker.Unlock();
modelLocker.Lock();
breakpoint->RemoveClient(client);
} else
error = B_NO_MEMORY;
// clean up on error
if (breakpoint->IsUnused())
fDebugModel->RemoveBreakpoint(breakpoint);
return error;
}
void
BreakpointManager::UninstallTemporaryBreakpoint(target_addr_t address,
BreakpointClient* client)
{
AutoLocker<TeamDebugModel> modelLocker(fDebugModel);
Breakpoint* breakpoint = fDebugModel->BreakpointAtAddress(address);
if (breakpoint == NULL)
return;
// remove the client
breakpoint->RemoveClient(client);
AutoLocker<BLocker> installLocker(fLock);
// We need to make the uninstallation decision with both locks held, and
// we keep this lock until we have the breakpoint uninstalled.
// check whether the breakpoint needs to be uninstalled
bool uninstall = !breakpoint->ShouldBeInstalled()
&& breakpoint->IsInstalled();
// if unused remove it
Reference<Breakpoint> breakpointReference(breakpoint);
if (breakpoint->IsUnused())
fDebugModel->RemoveBreakpoint(breakpoint);
modelLocker.Unlock();
if (uninstall) {
fDebuggerInterface->UninstallBreakpoint(address);
breakpoint->SetInstalled(false);
}
}
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef BREAKPOINT_MANAGER_H
#define BREAKPOINT_MANAGER_H
#include <Locker.h>
#include "Breakpoint.h"
class DebuggerInterface;
class TeamDebugModel;
class BreakpointManager {
public:
BreakpointManager(TeamDebugModel* debugModel,
DebuggerInterface* debuggerInterface);
~BreakpointManager();
status_t Init();
status_t InstallUserBreakpoint(target_addr_t address,
bool enabled);
void UninstallUserBreakpoint(target_addr_t address);
status_t InstallTemporaryBreakpoint(
target_addr_t address,
BreakpointClient* client);
void UninstallTemporaryBreakpoint(
target_addr_t address,
BreakpointClient* client);
private:
BLocker fLock; // used to synchronize un-/installing
TeamDebugModel* fDebugModel;
DebuggerInterface* fDebuggerInterface;
};
#endif // BREAKPOINT_MANAGER_H
+2
View File
@@ -21,6 +21,7 @@ SubDirHdrs [ FDirName $(HAIKU_TOP) src bin debug ] ;
SubDirHdrs [ FDirName $(debugAnalyzerSources) gui ] ;
Application Debugger :
BreakpointManager.cpp
Debugger.cpp
# ElfFile.cpp
Jobs.cpp
@@ -31,6 +32,7 @@ Application Debugger :
# arch
Architecture.cpp
CpuState.cpp
InstructionInfo.cpp
Register.cpp
# arch/x86
+1
View File
@@ -20,6 +20,7 @@ enum {
MSG_THREAD_STACK_TRACE_CHANGED = 'tstc',
MSG_STACK_FRAME_SOURCE_CODE_CHANGED = 'sfsc',
MSG_USER_BREAKPOINT_CHANGED = 'ubrc',
MSG_DEBUGGER_EVENT = 'dbge',
MSG_TEAM_DEBUGGER_QUIT = 'dbqt'
};
+36 -99
View File
@@ -17,6 +17,7 @@
#include "debug_utils.h"
#include "BreakpointManager.h"
#include "CpuState.h"
#include "DebuggerInterface.h"
#include "Jobs.h"
@@ -34,6 +35,7 @@ TeamDebugger::TeamDebugger(Listener* listener)
fTeamID(-1),
fDebuggerInterface(NULL),
fWorker(NULL),
fBreakpointManager(NULL),
fDebugEventListener(-1),
fTeamWindow(NULL),
fTerminating(false),
@@ -74,6 +76,7 @@ TeamDebugger::~TeamDebugger()
threadHandler = next;
}
delete fBreakpointManager;
delete fDebuggerInterface;
delete fWorker;
delete fDebugModel;
@@ -140,6 +143,16 @@ TeamDebugger::Init(team_id teamID, thread_id threadID, bool stopInMain)
if (error != B_OK)
return error;
// create the breakpoint manager
fBreakpointManager = new(std::nothrow) BreakpointManager(fDebugModel,
fDebuggerInterface);
if (fBreakpointManager == NULL)
return B_NO_MEMORY;
error = fBreakpointManager->Init();
if (error != B_OK)
return error;
// set team debugging flags
fDebuggerInterface->SetTeamDebuggingFlags(
B_TEAM_DEBUG_THREADS | B_TEAM_DEBUG_IMAGES);
@@ -157,7 +170,8 @@ TeamDebugger::Init(team_id teamID, thread_id threadID, bool stopInMain)
return error;
ThreadHandler* handler = new(std::nothrow) ThreadHandler(
fDebugModel, thread, fWorker, fDebuggerInterface);
fDebugModel, thread, fWorker, fDebuggerInterface,
fBreakpointManager);
if (handler == NULL)
return B_NO_MEMORY;
@@ -293,6 +307,16 @@ TeamDebugger::MessageReceived(BMessage* message)
break;
}
case MSG_DEBUGGER_EVENT:
{
DebugEvent* event;
if (message->FindPointer("event", (void**)&event) != B_OK)
break;
_HandleDebuggerMessage(event);
delete event;
}
default:
BLooper::MessageReceived(message);
break;
@@ -468,13 +492,12 @@ event->Team());
continue;
}
_HandleDebuggerMessage(event);
// if (event->EventType() == B_DEBUGGER_MESSAGE_TEAM_DELETED
// || event->EventType() == B_DEBUGGER_MESSAGE_TEAM_EXEC) {
// // TODO:...
// break;
// }
BMessage message(MSG_DEBUGGER_EVENT);
if (message.AddPointer("event", event) != B_OK
|| PostMessage(&message) != B_OK) {
// TODO: Continue thread if necessary!
delete event;
}
}
return B_OK;
@@ -591,7 +614,8 @@ TeamDebugger::_HandleThreadCreated(ThreadCreatedEvent* event)
fTeam->AddThread(info, &thread);
ThreadHandler* handler = new(std::nothrow) ThreadHandler(
fDebugModel, thread, fWorker, fDebuggerInterface);
fDebugModel, thread, fWorker, fDebuggerInterface,
fBreakpointManager);
if (handler != NULL) {
fThreadHandlers.Insert(handler);
handler->Init();
@@ -633,78 +657,12 @@ TeamDebugger::_HandleImageDeleted(ImageDeletedEvent* event)
}
status_t
TeamDebugger::_SetUserBreakpoint(target_addr_t address, bool enabled)
{
user_breakpoint_state state = enabled
? USER_BREAKPOINT_ENABLED : USER_BREAKPOINT_DISABLED;
AutoLocker<TeamDebugModel> locker(fDebugModel);
// If there already is a breakpoint, it might already have the requested
// state.
Breakpoint* breakpoint = fDebugModel->BreakpointAtAddress(address);
if (breakpoint != NULL && breakpoint->UserState() == state)
return B_OK;
// create a breakpoint, if it doesn't exist yet
if (breakpoint == NULL) {
Image* image = fTeam->ImageByAddress(address);
if (image == NULL)
return B_OK;
breakpoint = new(std::nothrow) Breakpoint(image, address);
if (breakpoint == NULL)
return B_NO_MEMORY;
if (!fDebugModel->AddBreakpoint(breakpoint))
return B_NO_MEMORY;
}
user_breakpoint_state oldState = breakpoint->UserState();
// set the breakpoint state
breakpoint->SetUserState(state);
fDebugModel->NotifyUserBreakpointChanged(breakpoint);
bool install = breakpoint->ShouldBeInstalled();
if (breakpoint->IsInstalled() == install)
return B_OK;
// The breakpoint needs to be installed/uninstalled.
locker.Unlock();
status_t error = install
? fDebuggerInterface->InstallBreakpoint(address)
: fDebuggerInterface->UninstallBreakpoint(address);
locker.Lock();
breakpoint = fDebugModel->BreakpointAtAddress(address);
// Mark the breakpoint installed/uninstalled, if everything went fine.
if (error == B_OK) {
breakpoint->SetInstalled(install);
printf("-> breakpoint %sinstalled successfully!\n", install ? "" : "un");
return B_OK;
}
// revert on error
breakpoint->SetUserState(oldState);
fDebugModel->NotifyUserBreakpointChanged(breakpoint);
if (breakpoint->IsUnused())
fDebugModel->RemoveBreakpoint(breakpoint);
return error;
}
void
TeamDebugger::_HandleSetUserBreakpoint(target_addr_t address, bool enabled)
{
printf("TeamDebugger::_HandleSetUserBreakpoint(%#llx, %d)\n", address, enabled);
status_t error = _SetUserBreakpoint(address, enabled);
status_t error = fBreakpointManager->InstallUserBreakpoint(address,
enabled);
if (error != B_OK) {
_NotifyUser("Install Breakpoint", "Failed to install breakpoint: %s",
strerror(error));
@@ -716,28 +674,7 @@ void
TeamDebugger::_HandleClearUserBreakpoint(target_addr_t address)
{
printf("TeamDebugger::_HandleClearUserBreakpoint(%#llx)\n", address);
AutoLocker<TeamDebugModel> locker(fDebugModel);
Breakpoint* breakpoint = fDebugModel->BreakpointAtAddress(address);
if (breakpoint == NULL || breakpoint->UserState() == USER_BREAKPOINT_NONE)
return;
// set the breakpoint state
breakpoint->SetUserState(USER_BREAKPOINT_NONE);
fDebugModel->NotifyUserBreakpointChanged(breakpoint);
// check whether the breakpoint needs to be uninstalled
bool uninstall = !breakpoint->ShouldBeInstalled()
&& breakpoint->IsInstalled();
// if unused remove it
if (breakpoint->IsUnused())
fDebugModel->RemoveBreakpoint(breakpoint);
locker.Unlock();
if (uninstall)
fDebuggerInterface->UninstallBreakpoint(address);
fBreakpointManager->UninstallUserBreakpoint(address);
}
+1 -4
View File
@@ -76,10 +76,6 @@ private:
bool _HandleImageDeleted(
ImageDeletedEvent* event);
status_t _SetUserBreakpoint(target_addr_t address,
bool enabled);
void _HandleSetUserBreakpoint(target_addr_t address,
bool enabled);
void _HandleClearUserBreakpoint(
@@ -100,6 +96,7 @@ private:
// protected by the team lock
DebuggerInterface* fDebuggerInterface;
Worker* fWorker;
BreakpointManager* fBreakpointManager;
thread_id fDebugEventListener;
TeamWindow* fTeamWindow;
volatile bool fTerminating;
+361 -18
View File
@@ -11,28 +11,54 @@
#include <AutoLocker.h>
#include "Architecture.h"
#include "BreakpointManager.h"
#include "CpuState.h"
#include "DebuggerInterface.h"
#include "DebugInfo.h"
#include "FunctionDebugInfo.h"
#include "ImageDebugInfo.h"
#include "InstructionInfo.h"
#include "Jobs.h"
#include "MessageCodes.h"
#include "SourceCode.h"
#include "StackTrace.h"
#include "Statement.h"
#include "Team.h"
#include "TeamDebugModel.h"
#include "Worker.h"
// step modes
enum {
STEP_NONE,
STEP_OVER,
STEP_INTO,
STEP_OUT
};
ThreadHandler::ThreadHandler(TeamDebugModel* debugModel, Thread* thread,
Worker* worker, DebuggerInterface* debuggerInterface)
Worker* worker, DebuggerInterface* debuggerInterface,
BreakpointManager* breakpointManager)
:
fDebugModel(debugModel),
fThread(thread),
fWorker(worker),
fDebuggerInterface(debuggerInterface)
fDebuggerInterface(debuggerInterface),
fBreakpointManager(breakpointManager),
fStepMode(STEP_NONE),
fStepStatement(NULL),
fBreakpointAddress(0),
fPreviousInstructionPointer(0),
fSingleStepping(false)
{
}
ThreadHandler::~ThreadHandler()
{
_ClearContinuationState();
}
@@ -61,7 +87,52 @@ ThreadHandler::HandleDebuggerCall(DebuggerCallEvent* event)
bool
ThreadHandler::HandleBreakpointHit(BreakpointHitEvent* event)
{
return _HandleThreadStopped(event->GetCpuState());
CpuState* cpuState = event->GetCpuState();
target_addr_t instructionPointer = cpuState->InstructionPointer();
// check whether this is a temporary breakpoint we're waiting for
if (fBreakpointAddress != 0 && instructionPointer == fBreakpointAddress
&& fStepMode != STEP_NONE) {
if (_HandleBreakpointHitStep(cpuState))
return true;
} else {
// Might be a user breakpoint, but could as well be a temporary
// breakpoint of another thread.
AutoLocker<TeamDebugModel> locker(fDebugModel);
Breakpoint* breakpoint = fDebugModel->BreakpointAtAddress(
cpuState->InstructionPointer());
bool continueThread = false;
if (breakpoint == NULL) {
// spurious breakpoint -- might be a temporary breakpoint, that has
// already been uninstalled
continueThread = true;
} else if (breakpoint->UserState() != USER_BREAKPOINT_ENABLED) {
// breakpoint of another thread or one that has been disabled in
// the meantime
continueThread = true;
}
if (continueThread) {
if (fSingleStepping) {
// We might have hit a just-installed software breakpoint and
// thus haven't stepped at all. Just try again.
if (fPreviousInstructionPointer == instructionPointer) {
fDebuggerInterface->SingleStepThread(ThreadID());
return true;
}
// That shouldn't happen. Try something reasonable anyway.
if (fStepMode != STEP_NONE) {
if (_HandleSingleStepStep(cpuState))
return true;
}
}
return false;
}
}
return _HandleThreadStopped(cpuState);
}
@@ -75,6 +146,12 @@ ThreadHandler::HandleWatchpointHit(WatchpointHitEvent* event)
bool
ThreadHandler::HandleSingleStep(SingleStepEvent* event)
{
// Check whether we're stepping automatically.
if (fStepMode != STEP_NONE) {
if (_HandleSingleStepStep(event->GetCpuState()))
return true;
}
return _HandleThreadStopped(event->GetCpuState());
}
@@ -101,6 +178,12 @@ ThreadHandler::HandleThreadAction(uint32 action)
return;
}
// When stepping we need a stack trace. Save it before unsetting the state.
CpuState* cpuState = fThread->GetCpuState();
StackTrace* stackTrace = fThread->GetStackTrace();
Reference<CpuState> cpuStateReference(cpuState);
Reference<StackTrace> stackTraceReference(stackTrace);
// When continuing the thread update thread state before actually issuing
// the command, since we need to unlock.
if (action != MSG_THREAD_STOP)
@@ -110,27 +193,87 @@ ThreadHandler::HandleThreadAction(uint32 action)
switch (action) {
case MSG_THREAD_RUN:
printf("MSG_THREAD_RUN\n");
fDebuggerInterface->ContinueThread(ThreadID());
break;
fStepMode = STEP_NONE;
_RunThread(0);
return;
case MSG_THREAD_STOP:
printf("MSG_THREAD_STOP\n");
fStepMode = STEP_NONE;
fDebuggerInterface->StopThread(ThreadID());
break;
return;
case MSG_THREAD_STEP_OVER:
printf("MSG_THREAD_STEP_OVER\n");
fDebuggerInterface->SingleStepThread(ThreadID());
break;
case MSG_THREAD_STEP_INTO:
printf("MSG_THREAD_STEP_INTO\n");
fDebuggerInterface->SingleStepThread(ThreadID());
break;
case MSG_THREAD_STEP_OUT:
printf("MSG_THREAD_STEP_OUT\n");
fDebuggerInterface->SingleStepThread(ThreadID());
break;
}
// TODO: Handle stepping correctly!
// We want to step. We need a stack trace for that purpose. If we don't
// have one yet, get it. Start with the CPU state.
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) == B_OK) {
stackTraceReference.SetTo(stackTrace, true);
}
}
if (stackTrace == NULL || stackTrace->CountFrames() == 0) {
_StepFallback();
return;
}
StackFrame* frame = stackTrace->FrameAt(0);
// When the thread is in a syscall, do the same for all step kinds: Stop it
// when it return by means of a breakpoint.
if (frame->Type() == STACK_FRAME_TYPE_SYSCALL) {
// set a breakpoint at the CPU state's instruction pointer (points to
// the return address, unlike the stack frame's instruction pointer)
status_t error = _InstallTemporaryBreakpoint(
frame->GetCpuState()->InstructionPointer());
if (error != B_OK) {
_StepFallback();
return;
}
fStepMode = STEP_OUT;
_RunThread(frame->GetCpuState()->InstructionPointer());
return;
}
// For "step out" just set a temporary breakpoint on the return address.
if (action == MSG_THREAD_STEP_OUT) {
status_t error = _InstallTemporaryBreakpoint(frame->ReturnAddress());
if (error != B_OK) {
_StepFallback();
return;
}
fStepMode = STEP_OUT;
_RunThread(frame->GetCpuState()->InstructionPointer());
return;
}
// For "step in" and "step over" we also need the source code statement at
// the current instruction pointer.
fStepStatement = _GetStatementAtInstructionPointer(frame);
if (fStepStatement == NULL) {
_StepFallback();
return;
}
if (action == MSG_THREAD_STEP_INTO) {
// step into
fStepMode = STEP_INTO;
_SingleStepThread(frame->GetCpuState()->InstructionPointer());
} else {
// step over
fStepMode = STEP_OVER;
if (!_DoStepOver(frame->GetCpuState()))
_StepFallback();
}
}
@@ -173,13 +316,31 @@ ThreadHandler::HandleCpuStateChanged()
void
ThreadHandler::HandleStackTraceChanged()
{
printf("ThreadHandler::_HandleStackTraceChanged()\n");
}
status_t
ThreadHandler::GetImageDebugInfo(Image* image, ImageDebugInfo*& _info)
{
AutoLocker<Team> teamLocker(fThread->GetTeam());
if (image->GetImageDebugInfo() != NULL) {
_info = image->GetImageDebugInfo();
_info->AddReference();
return B_OK;
}
// Let's be lazy. If the image debug info has not been loaded yet, the user
// can't have seen any source code either.
return B_ENTRY_NOT_FOUND;
}
bool
ThreadHandler::_HandleThreadStopped(CpuState* cpuState)
{
_ClearContinuationState();
AutoLocker<TeamDebugModel> locker(fDebugModel);
_SetThreadState(THREAD_STATE_STOPPED, cpuState);
@@ -194,3 +355,185 @@ ThreadHandler::_SetThreadState(uint32 state, CpuState* cpuState)
fThread->SetState(state);
fThread->SetCpuState(cpuState);
}
Statement*
ThreadHandler::_GetStatementAtInstructionPointer(StackFrame* frame)
{
AutoLocker<TeamDebugModel> locker(fDebugModel);
// If there's source code attached to the stack frame, we can just get the
// statement.
SourceCode* sourceCode = frame->GetSourceCode();
if (sourceCode != NULL) {
Statement* statement = sourceCode->StatementAtAddress(
frame->InstructionPointer());
if (statement != NULL)
statement->AddReference();
return statement;
}
locker.Unlock();
// We need to get the statement from the debug info of the function (if
// any).
FunctionDebugInfo* function = frame->Function();
if (function == NULL)
return NULL;
Statement* statement;
if (function->GetDebugInfo()->GetStatement(function,
frame->InstructionPointer(), statement) != B_OK) {
return NULL;
}
return statement;
}
void
ThreadHandler::_StepFallback()
{
fStepMode = STEP_NONE;
_SingleStepThread(0);
}
bool
ThreadHandler::_DoStepOver(CpuState* cpuState)
{
// The basic strategy is to single-step out of the statement like for
// "step into", only we have to avoid stepping into subroutines. Hence we
// check whether the current instruction is a subroutine call. If not, we
// just single-step, otherwise we set a breakpoint after the instruction.
InstructionInfo info;
if (fDebuggerInterface->GetArchitecture()->GetInstructionInfo(
cpuState->InstructionPointer(), info) != B_OK) {
return false;
}
if (info.Type() != INSTRUCTION_TYPE_SUBROUTINE_CALL) {
_SingleStepThread(cpuState->InstructionPointer());
return true;
}
if (_InstallTemporaryBreakpoint(info.Address() + info.Size()) != B_OK)
return false;
_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->RemoveReference();
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)
{
// in any case uninstall the temporary breakpoint
_UninstallTemporaryBreakpoint();
switch (fStepMode) {
case STEP_OVER:
// If we're still in the statement, we continue single-stepping,
// otherwise we're done.
if (fStepStatement->ContainsAddress(
cpuState->InstructionPointer())) {
_SingleStepThread(cpuState->InstructionPointer());
return true;
}
return false;
case STEP_INTO:
// Should never happen -- we don't set a breakpoint in this case.
case STEP_OUT:
// That's the return address, so we're done.
default:
return false;
}
}
bool
ThreadHandler::_HandleSingleStepStep(CpuState* cpuState)
{
switch (fStepMode) {
case STEP_INTO:
{
// We continue stepping as long as we're in the statement.
if (fStepStatement->ContainsAddress(cpuState->InstructionPointer())) {
_SingleStepThread(cpuState->InstructionPointer());
return true;
}
return false;
}
case STEP_OVER:
{
// If we have stepped out of the statement, we're done.
if (!fStepStatement->ContainsAddress(cpuState->InstructionPointer()))
return false;
return _DoStepOver(cpuState);
}
case STEP_OUT:
// We never single-step in this case.
default:
return false;
}
}
+39 -2
View File
@@ -8,21 +8,28 @@
#include <Referenceable.h>
#include <util/OpenHashTable.h>
#include "Breakpoint.h"
#include "DebugEvent.h"
#include "ImageDebugInfoProvider.h"
#include "Thread.h"
class BreakpointManager;
class DebuggerInterface;
class StackFrame;
class Statement;
class TeamDebugModel;
class Worker;
class ThreadHandler : public Referenceable,
public HashTableLink<ThreadHandler> {
public HashTableLink<ThreadHandler>, private ImageDebugInfoProvider,
private BreakpointClient {
public:
ThreadHandler(TeamDebugModel* debugModel,
Thread* thread, Worker* worker,
DebuggerInterface* debuggerInterface);
DebuggerInterface* debuggerInterface,
BreakpointManager* breakpointManager);
~ThreadHandler();
void Init();
@@ -30,6 +37,8 @@ public:
thread_id ThreadID() const { return fThread->ID(); }
Thread* GetThread() const { return fThread; }
// All Handle*() methods are invoked in team debugger thread,
// looper lock held.
bool HandleThreadDebugged(
ThreadDebuggedEvent* event);
bool HandleDebuggerCall(
@@ -50,16 +59,44 @@ public:
void HandleStackTraceChanged();
private:
// ImageDebugInfoProvider
virtual status_t GetImageDebugInfo(Image* image,
ImageDebugInfo*& _info);
bool _HandleThreadStopped(CpuState* cpuState);
void _SetThreadState(uint32 state,
CpuState* cpuState);
Statement* _GetStatementAtInstructionPointer(
StackFrame* frame);
void _StepFallback();
bool _DoStepOver(CpuState* cpuState);
status_t _InstallTemporaryBreakpoint(
target_addr_t address);
void _UninstallTemporaryBreakpoint();
void _ClearContinuationState();
void _RunThread(target_addr_t instructionPointer);
void _SingleStepThread(
target_addr_t instructionPointer);
bool _HandleBreakpointHitStep(CpuState* cpuState);
bool _HandleSingleStepStep(CpuState* cpuState);
private:
TeamDebugModel* fDebugModel;
Thread* fThread;
Worker* fWorker;
DebuggerInterface* fDebuggerInterface;
BreakpointManager* fBreakpointManager;
uint32 fStepMode;
Statement* fStepStatement;
target_addr_t fBreakpointAddress;
target_addr_t fPreviousInstructionPointer;
bool fSingleStepping;
};
+10
View File
@@ -9,16 +9,20 @@
#include <Referenceable.h>
#include "ArchitectureTypes.h"
class CpuState;
class DebuggerInterface;
class FunctionDebugInfo;
class Image;
class ImageDebugInfoProvider;
class InstructionInfo;
class Register;
class SourceCode;
class StackFrame;
class StackTrace;
class Statement;
class Team;
@@ -51,9 +55,15 @@ public:
// Called after a CreateStackFrame()
// with the image/function corresponding
// to the CPU state.
virtual status_t DisassembleCode(FunctionDebugInfo* function,
const void* buffer, size_t bufferSize,
SourceCode*& _sourceCode) = 0;
virtual status_t GetStatement(FunctionDebugInfo* function,
target_addr_t address,
Statement*& _statement) = 0;
virtual status_t GetInstructionInfo(target_addr_t address,
InstructionInfo& _info) = 0;
status_t CreateStackTrace(Team* team,
ImageDebugInfoProvider* imageInfoProvider,
@@ -0,0 +1,44 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "InstructionInfo.h"
InstructionInfo::InstructionInfo()
:
fAddress(0),
fSize(0),
fType(INSTRUCTION_TYPE_OTHER),
fBreakpointAllowed(false),
fDisassembledLine()
{
}
InstructionInfo::InstructionInfo(target_addr_t address, target_size_t size,
instruction_type type, bool breakpointAllowed,
const BString& disassembledLine)
:
fAddress(address),
fSize(size),
fType(type),
fBreakpointAllowed(breakpointAllowed),
fDisassembledLine(disassembledLine)
{
}
bool
InstructionInfo::SetTo(target_addr_t address, target_size_t size,
instruction_type type, bool breakpointAllowed,
const BString& disassembledLine)
{
fAddress = address;
fSize = size;
fType = type;
fBreakpointAllowed = breakpointAllowed;
fDisassembledLine = disassembledLine;
return disassembledLine.Length() == 0 || fDisassembledLine.Length() > 0;
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef INSTRUCTION_INFO_H
#define INSTRUCTION_INFO_H
#include <String.h>
#include "ArchitectureTypes.h"
enum instruction_type {
INSTRUCTION_TYPE_SUBROUTINE_CALL,
INSTRUCTION_TYPE_OTHER
};
class InstructionInfo {
public:
InstructionInfo();
InstructionInfo(target_addr_t address,
target_size_t size, instruction_type type,
bool breakpointAllowed,
const BString& disassembledLine);
bool SetTo(target_addr_t address, target_size_t size,
instruction_type type,
bool breakpointAllowed,
const BString& disassembledLine);
target_addr_t Address() const { return fAddress; }
target_size_t Size() const { return fSize; }
instruction_type Type() const { return fType; }
bool IsBreakpointAllowed() const
{ return fBreakpointAllowed; }
const char* DisassembledLine() const
{ return fDisassembledLine.String(); }
private:
target_addr_t fAddress;
target_size_t fSize;
instruction_type fType;
bool fBreakpointAllowed;
BString fDisassembledLine;
};
#endif // INSTRUCTION_INFO_H
@@ -15,6 +15,7 @@
#include "DebuggerInterface.h"
#include "DisassembledCode.h"
#include "FunctionDebugInfo.h"
#include "InstructionInfo.h"
#include "StackFrame.h"
#include "Statement.h"
@@ -307,6 +308,80 @@ ArchitectureX86::DisassembleCode(FunctionDebugInfo* function,
}
status_t
ArchitectureX86::GetStatement(FunctionDebugInfo* function,
target_addr_t address, Statement*& _statement)
{
// TODO: This is not architecture dependent anymore!
// get the instruction info
InstructionInfo info;
status_t error = GetInstructionInfo(address, info);
if (error != B_OK)
return error;
// create a statement
ContiguousStatement* statement = new(std::nothrow) ContiguousStatement(
SourceLocation(0), SourceLocation(1),
TargetAddressRange(info.Address(), info.Size()),
info.IsBreakpointAllowed());
if (statement == NULL)
return B_NO_MEMORY;
_statement = statement;
return B_OK;
}
status_t
ArchitectureX86::GetInstructionInfo(target_addr_t address,
InstructionInfo& _info)
{
// read the code
uint8 buffer[16];
// TODO: What's the maximum instruction size?
ssize_t bytesRead = fDebuggerInterface->ReadMemory(address, buffer,
sizeof(buffer));
if (bytesRead < 0)
return bytesRead;
// init disassembler
DisassemblerX86 disassembler;
status_t error = disassembler.Init(address, buffer, bytesRead);
if (error != B_OK)
return error;
// disassemble the instruction
BString line;
target_addr_t instructionAddress;
target_size_t instructionSize;
bool breakpointAllowed;
error = disassembler.GetNextInstruction(line, instructionAddress,
instructionSize, breakpointAllowed);
if (error != B_OK)
return error;
instruction_type instructionType = INSTRUCTION_TYPE_OTHER;
if (buffer[0] == 0xff) {
// absolute call with r/m32
instructionType = INSTRUCTION_TYPE_SUBROUTINE_CALL;
} else if (buffer[0] == 0xe8 && instructionSize == 5) {
// relative call with rel32 -- don't categorize the call with 0 as
// subroutine call, since it is only used to get the address of the GOT
if (buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0
|| buffer[4] != 0) {
instructionType = INSTRUCTION_TYPE_SUBROUTINE_CALL;
}
}
if (!_info.SetTo(instructionAddress, instructionSize, instructionType,
breakpointAllowed, line)) {
return B_NO_MEMORY;
}
return B_OK;
}
void
ArchitectureX86::_AddRegister(int32 index, const char* name,
register_format format, uint32 bitSize, register_type type)
@@ -33,9 +33,15 @@ public:
Image* previousImage,
FunctionDebugInfo* previousFunction,
CpuState* previousCpuState);
virtual status_t DisassembleCode(FunctionDebugInfo* function,
const void* buffer, size_t bufferSize,
SourceCode*& _sourceCode);
virtual status_t GetStatement(FunctionDebugInfo* function,
target_addr_t address,
Statement*& _statement);
virtual status_t GetInstructionInfo(target_addr_t address,
InstructionInfo& _info);
private:
void _AddRegister(int32 index, const char* name,
+5
View File
@@ -17,6 +17,7 @@ class FunctionDebugInfo;
class Image;
class SourceCode;
class StackFrame;
class Statement;
class DebugInfo : public Referenceable {
@@ -37,6 +38,10 @@ public:
virtual status_t LoadSourceCode(FunctionDebugInfo* function,
SourceCode*& _sourceCode) = 0;
// returns reference
virtual status_t GetStatement(FunctionDebugInfo* function,
target_addr_t address,
Statement*& _statement) = 0;
// returns reference
};
@@ -95,6 +95,14 @@ DebuggerDebugInfo::LoadSourceCode(FunctionDebugInfo* function,
}
status_t
DebuggerDebugInfo::GetStatement(FunctionDebugInfo* function,
target_addr_t address, Statement*& _statement)
{
return fArchitecture->GetStatement(function, address, _statement);
}
SymbolInfo*
DebuggerDebugInfo::_FindSymbol(target_addr_t address)
{
@@ -36,6 +36,9 @@ public:
CpuState*& _previousCpuState);
virtual status_t LoadSourceCode(FunctionDebugInfo* function,
SourceCode*& _sourceCode);
virtual status_t GetStatement(FunctionDebugInfo* function,
target_addr_t address,
Statement*& _statement);
private:
typedef BObjectList<SymbolInfo> SymbolList;