[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