* 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:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user