[Haiku-commits] r31244 - in haiku/trunk/src/apps/debugger: . arch arch/x86 debug_info
bonefish at BerliOS
bonefish at mail.berlios.de
Fri Jun 26 01:51:10 CEST 2009
Author: bonefish
Date: 2009-06-26 01:51:09 +0200 (Fri, 26 Jun 2009)
New Revision: 31244
ViewCVS: http://svn.berlios.de/viewcvs/haiku?rev=31244&view=rev
Added:
haiku/trunk/src/apps/debugger/BreakpointManager.cpp
haiku/trunk/src/apps/debugger/BreakpointManager.h
haiku/trunk/src/apps/debugger/arch/InstructionInfo.cpp
haiku/trunk/src/apps/debugger/arch/InstructionInfo.h
Modified:
haiku/trunk/src/apps/debugger/Jamfile
haiku/trunk/src/apps/debugger/MessageCodes.h
haiku/trunk/src/apps/debugger/TeamDebugger.cpp
haiku/trunk/src/apps/debugger/TeamDebugger.h
haiku/trunk/src/apps/debugger/ThreadHandler.cpp
haiku/trunk/src/apps/debugger/ThreadHandler.h
haiku/trunk/src/apps/debugger/arch/Architecture.h
haiku/trunk/src/apps/debugger/arch/x86/ArchitectureX86.cpp
haiku/trunk/src/apps/debugger/arch/x86/ArchitectureX86.h
haiku/trunk/src/apps/debugger/debug_info/DebugInfo.h
haiku/trunk/src/apps/debugger/debug_info/DebuggerDebugInfo.cpp
haiku/trunk/src/apps/debugger/debug_info/DebuggerDebugInfo.h
Log:
* 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.
Added: haiku/trunk/src/apps/debugger/BreakpointManager.cpp
===================================================================
--- haiku/trunk/src/apps/debugger/BreakpointManager.cpp 2009-06-25 22:49:07 UTC (rev 31243)
+++ haiku/trunk/src/apps/debugger/BreakpointManager.cpp 2009-06-25 23:51:09 UTC (rev 31244)
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2009, Ingo Weinhold, ingo_weinhold at gmx.de.
+ * 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);
+ }
+}
Added: haiku/trunk/src/apps/debugger/BreakpointManager.h
===================================================================
--- haiku/trunk/src/apps/debugger/BreakpointManager.h 2009-06-25 22:49:07 UTC (rev 31243)
+++ haiku/trunk/src/apps/debugger/BreakpointManager.h 2009-06-25 23:51:09 UTC (rev 31244)
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2009, Ingo Weinhold, ingo_weinhold at gmx.de.
+ * 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
Modified: haiku/trunk/src/apps/debugger/Jamfile
===================================================================
--- haiku/trunk/src/apps/debugger/Jamfile 2009-06-25 22:49:07 UTC (rev 31243)
+++ haiku/trunk/src/apps/debugger/Jamfile 2009-06-25 23:51:09 UTC (rev 31244)
@@ -21,6 +21,7 @@
SubDirHdrs [ FDirName $(debugAnalyzerSources) gui ] ;
Application Debugger :
+ BreakpointManager.cpp
Debugger.cpp
# ElfFile.cpp
Jobs.cpp
@@ -31,6 +32,7 @@
# arch
Architecture.cpp
CpuState.cpp
+ InstructionInfo.cpp
Register.cpp
# arch/x86
Modified: haiku/trunk/src/apps/debugger/MessageCodes.h
===================================================================
--- haiku/trunk/src/apps/debugger/MessageCodes.h 2009-06-25 22:49:07 UTC (rev 31243)
+++ haiku/trunk/src/apps/debugger/MessageCodes.h 2009-06-25 23:51:09 UTC (rev 31244)
@@ -20,6 +20,7 @@
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'
};
Modified: haiku/trunk/src/apps/debugger/TeamDebugger.cpp
===================================================================
--- haiku/trunk/src/apps/debugger/TeamDebugger.cpp 2009-06-25 22:49:07 UTC (rev 31243)
+++ haiku/trunk/src/apps/debugger/TeamDebugger.cpp 2009-06-25 23:51:09 UTC (rev 31244)
@@ -17,6 +17,7 @@
#include "debug_utils.h"
+#include "BreakpointManager.h"
#include "CpuState.h"
#include "DebuggerInterface.h"
#include "Jobs.h"
@@ -34,6 +35,7 @@
fTeamID(-1),
fDebuggerInterface(NULL),
fWorker(NULL),
+ fBreakpointManager(NULL),
fDebugEventListener(-1),
fTeamWindow(NULL),
fTerminating(false),
@@ -74,6 +76,7 @@
threadHandler = next;
}
+ delete fBreakpointManager;
delete fDebuggerInterface;
delete fWorker;
delete fDebugModel;
@@ -140,6 +143,16 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
-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 @@
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);
}
Modified: haiku/trunk/src/apps/debugger/TeamDebugger.h
===================================================================
--- haiku/trunk/src/apps/debugger/TeamDebugger.h 2009-06-25 22:49:07 UTC (rev 31243)
+++ haiku/trunk/src/apps/debugger/TeamDebugger.h 2009-06-25 23:51:09 UTC (rev 31244)
@@ -76,10 +76,6 @@
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 @@
// protected by the team lock
DebuggerInterface* fDebuggerInterface;
Worker* fWorker;
+ BreakpointManager* fBreakpointManager;
thread_id fDebugEventListener;
TeamWindow* fTeamWindow;
volatile bool fTerminating;
Modified: haiku/trunk/src/apps/debugger/ThreadHandler.cpp
===================================================================
--- haiku/trunk/src/apps/debugger/ThreadHandler.cpp 2009-06-25 22:49:07 UTC (rev 31243)
+++ haiku/trunk/src/apps/debugger/ThreadHandler.cpp 2009-06-25 23:51:09 UTC (rev 31244)
@@ -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 @@
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 @@
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 @@
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,28 +193,88 @@
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 @@
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 @@
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)
+{
[... truncated: 438 lines follow ...]
More information about the Haiku-commits
mailing list