[Haiku-commits] r31214 - in haiku/trunk: headers/os/kernel headers/private/kernel headers/private/kernel/arch headers/private/kernel/arch/x86 src/system/kernel/arch/x86 src/system/kernel/debug
bonefish at mail.berlios.de
bonefish at mail.berlios.de
Tue Jun 23 23:04:01 CEST 2009
Author: bonefish
Date: 2009-06-23 23:03:57 +0200 (Tue, 23 Jun 2009)
New Revision: 31214
ViewCVS: http://svn.berlios.de/viewcvs/haiku?rev=31214&view=rev
Added:
haiku/trunk/src/system/kernel/debug/BreakpointManager.cpp
haiku/trunk/src/system/kernel/debug/BreakpointManager.h
Modified:
haiku/trunk/headers/os/kernel/debugger.h
haiku/trunk/headers/private/kernel/arch/user_debugger.h
haiku/trunk/headers/private/kernel/arch/x86/arch_user_debugger.h
haiku/trunk/headers/private/kernel/user_debugger.h
haiku/trunk/src/system/kernel/arch/x86/arch_user_debugger.cpp
haiku/trunk/src/system/kernel/debug/Jamfile
haiku/trunk/src/system/kernel/debug/user_debugger.cpp
Log:
Added transparent software breakpoint support for user debuggers:
* The bulk of the work -- i.e. juggling the software and hardware breakpoints,
watchpoints, and memory reads/writes -- is done in the new class
BreakpointManager.
* For the architectures a few capability macros have to be defined, one
pointing to the software breakpoint instruction opcode. Done for x86.
* Some more simplifications in the user debugger code, made possible by the
recently introduced debugger_changed_condition attribute.
Modified: haiku/trunk/headers/os/kernel/debugger.h
===================================================================
--- haiku/trunk/headers/os/kernel/debugger.h 2009-06-23 20:52:30 UTC (rev 31213)
+++ haiku/trunk/headers/os/kernel/debugger.h 2009-06-23 21:03:57 UTC (rev 31214)
@@ -455,9 +455,6 @@
typedef struct {
debug_origin origin;
debug_cpu_state cpu_state; // cpu state
- bool software; // true, if the is a software breakpoint
- // (i.e. caused by a respective trap
- // instruction)
} debug_breakpoint_hit;
// B_DEBUGGER_MESSAGE_WATCHPOINT_HIT
Modified: haiku/trunk/headers/private/kernel/arch/user_debugger.h
===================================================================
--- haiku/trunk/headers/private/kernel/arch/user_debugger.h 2009-06-23 20:52:30 UTC (rev 31213)
+++ haiku/trunk/headers/private/kernel/arch/user_debugger.h 2009-06-23 21:03:57 UTC (rev 31214)
@@ -50,6 +50,34 @@
#include <arch_user_debugger.h>
+// Defaults for macros defined by the architecture specific header:
+
+// maximum number of instruction breakpoints
+#ifndef DEBUG_MAX_BREAKPOINTS
+# define DEBUG_MAX_BREAKPOINTS 0
+#endif
+
+// maximum number of data watchpoints
+#ifndef DEBUG_MAX_WATCHPOINTS
+# define DEBUG_MAX_WATCHPOINTS 0
+#endif
+
+// the software breakpoint instruction
+#if !defined(DEBUG_SOFTWARE_BREAKPOINT) \
+ || !defined(DEBUG_SOFTWARE_BREAKPOINT_SIZE)
+# undef DEBUG_SOFTWARE_BREAKPOINT
+# undef DEBUG_SOFTWARE_BREAKPOINT_SIZE
+# define DEBUG_SOFTWARE_BREAKPOINT NULL
+# define DEBUG_SOFTWARE_BREAKPOINT_SIZE 0
+#endif
+
+// Boolean whether break- and watchpoints use the same registers. If != 0, then
+// DEBUG_MAX_BREAKPOINTS == DEBUG_MAX_WATCHPOINTS and either specifies the
+// total count of break- plus watchpoints.
+#ifndef DEBUG_SHARED_BREAK_AND_WATCHPOINTS
+# define DEBUG_SHARED_BREAK_AND_WATCHPOINTS 0
+#endif
+
#endif // _ASSEMBLER
#endif // KERNEL_ARCH_USER_DEBUGGER_H
Modified: haiku/trunk/headers/private/kernel/arch/x86/arch_user_debugger.h
===================================================================
--- haiku/trunk/headers/private/kernel/arch/x86/arch_user_debugger.h 2009-06-23 20:52:30 UTC (rev 31213)
+++ haiku/trunk/headers/private/kernel/arch/x86/arch_user_debugger.h 2009-06-23 21:03:57 UTC (rev 31214)
@@ -1,5 +1,5 @@
/*
- * Copyright 2005, Ingo Weinhold, bonefish at users.sf.net.
+ * Copyright 2005-2009, Ingo Weinhold, bonefish at users.sf.net.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_ARCH_X86_USER_DEBUGGER_H
@@ -9,9 +9,7 @@
// number of breakpoints the CPU supports
// Actually it supports 4, but DR3 is used to hold the struct thread*.
-enum {
- X86_BREAKPOINT_COUNT = 3,
-};
+#define X86_BREAKPOINT_COUNT 3
// debug status register DR6
enum {
@@ -110,6 +108,9 @@
uint32 flags;
};
+// The software breakpoint instruction (int3).
+extern const uint8 kX86SoftwareBreakpoint[1];
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -129,4 +130,11 @@
}
#endif
+// Feature macros we're supposed to define.
+#define DEBUG_MAX_BREAKPOINTS X86_BREAKPOINT_COUNT
+#define DEBUG_MAX_WATCHPOINTS X86_BREAKPOINT_COUNT
+#define DEBUG_SOFTWARE_BREAKPOINT kX86SoftwareBreakpoint
+#define DEBUG_SOFTWARE_BREAKPOINT_SIZE 1
+#define DEBUG_SHARED_BREAK_AND_WATCHPOINTS 1
+
#endif // _KERNEL_ARCH_X86_USER_DEBUGGER_H
Modified: haiku/trunk/headers/private/kernel/user_debugger.h
===================================================================
--- haiku/trunk/headers/private/kernel/user_debugger.h 2009-06-23 20:52:30 UTC (rev 31213)
+++ haiku/trunk/headers/private/kernel/user_debugger.h 2009-06-23 21:03:57 UTC (rev 31214)
@@ -20,6 +20,7 @@
#define B_DEBUG_PROFILE_BUFFER_FLUSH_THRESHOLD 70 /* in % */
+struct BreakpointManager;
struct ConditionVariable;
struct function_profile_info;
struct thread;
@@ -70,6 +71,9 @@
// variable the thread won't be deleted (until unsetting it) -- it might
// be removed from the team hash table, though.
+ struct BreakpointManager* breakpoint_manager;
+ // manages hard- and software breakpoints
+
struct arch_team_debug_info arch_info;
};
Modified: haiku/trunk/src/system/kernel/arch/x86/arch_user_debugger.cpp
===================================================================
--- haiku/trunk/src/system/kernel/arch/x86/arch_user_debugger.cpp 2009-06-23 20:52:30 UTC (rev 31213)
+++ haiku/trunk/src/system/kernel/arch/x86/arch_user_debugger.cpp 2009-06-23 21:03:57 UTC (rev 31214)
@@ -30,6 +30,9 @@
#define B_WATCHPOINT_NOT_FOUND B_NAME_NOT_FOUND
// ToDo: Make those real error codes.
+// The software breakpoint instruction (int3).
+const uint8 kX86SoftwareBreakpoint[1] = { 0xcc };
+
// maps breakpoint slot index to LEN_i LSB number
static const uint32 sDR7Len[4] = {
X86_DR7_LEN0_LSB, X86_DR7_LEN1_LSB, X86_DR7_LEN2_LSB, X86_DR7_LEN3_LSB
@@ -902,6 +905,9 @@
{
TRACE(("i386_handle_breakpoint_exception()\n"));
+ // reset eip to the int3 instruction
+ frame->eip--;
+
if (!IFRAME_IS_USER(frame)) {
panic("breakpoint exception in kernel mode");
return;
Added: haiku/trunk/src/system/kernel/debug/BreakpointManager.cpp
===================================================================
--- haiku/trunk/src/system/kernel/debug/BreakpointManager.cpp 2009-06-23 20:52:30 UTC (rev 31213)
+++ haiku/trunk/src/system/kernel/debug/BreakpointManager.cpp 2009-06-23 21:03:57 UTC (rev 31214)
@@ -0,0 +1,783 @@
+/*
+ * Copyright 2009, Ingo Weinhold, ingo_weinhold at gmx.de.
+ * Distributed under the terms of the MIT License.
+ */
+
+#include "BreakpointManager.h"
+
+#include <algorithm>
+
+#include <AutoDeleter.h>
+
+#include <commpage_defs.h>
+#include <kernel.h>
+#include <util/AutoLock.h>
+#include <vm.h>
+
+
+//#define TRACE_BREAKPOINT_MANAGER
+#ifdef TRACE_BREAKPOINT_MANAGER
+# define TRACE(x...) dprintf(x)
+#else
+# define TRACE(x...) do {} while (false)
+#endif
+
+
+// soft limit for the number of breakpoints
+const int32 kMaxBreakpointCount = 10240;
+
+
+BreakpointManager::InstalledBreakpoint::InstalledBreakpoint(addr_t address)
+ :
+ breakpoint(NULL),
+ address(address)
+{
+}
+
+
+// #pragma mark -
+
+
+BreakpointManager::BreakpointManager()
+ :
+ fBreakpointCount(0),
+ fWatchpointCount(0)
+{
+ rw_lock_init(&fLock, "breakpoint manager");
+}
+
+
+BreakpointManager::~BreakpointManager()
+{
+ WriteLocker locker(fLock);
+
+ // delete the installed breakpoint objects
+ BreakpointTree::Iterator it = fBreakpoints.GetIterator();
+ while (InstalledBreakpoint* installedBreakpoint = it.Next()) {
+ it.Remove();
+
+ // delete underlying software breakpoint
+ if (installedBreakpoint->breakpoint->software)
+ delete installedBreakpoint->breakpoint;
+
+ delete installedBreakpoint;
+ }
+
+ // delete the watchpoints
+ while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead())
+ delete watchpoint;
+
+ // delete the hardware breakpoint objects
+ while (Breakpoint* breakpoint = fHardwareBreakpoints.RemoveHead())
+ delete breakpoint;
+
+ rw_lock_destroy(&fLock);
+}
+
+
+status_t
+BreakpointManager::Init()
+{
+ // create objects for the hardware breakpoints
+ for (int32 i = 0; i < DEBUG_MAX_BREAKPOINTS; i++) {
+ Breakpoint* breakpoint = new(std::nothrow) Breakpoint;
+ if (breakpoint == NULL)
+ return B_NO_MEMORY;
+
+ breakpoint->address = 0;
+ breakpoint->installedBreakpoint = NULL;
+ breakpoint->used = false;
+ breakpoint->software = false;
+
+ fHardwareBreakpoints.Add(breakpoint);
+ }
+
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::InstallBreakpoint(void* _address)
+{
+ const addr_t address = (addr_t)_address;
+
+ WriteLocker locker(fLock);
+
+ if (fBreakpointCount >= kMaxBreakpointCount)
+ return B_BUSY;
+
+ // check whether there's already a breakpoint at the address
+ InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
+ if (installed != NULL)
+ return B_BAD_VALUE;
+
+ // create the breakpoint object
+ installed = new(std::nothrow) InstalledBreakpoint(address);
+ if (installed == NULL)
+ return B_NO_MEMORY;
+ ObjectDeleter<InstalledBreakpoint> installedDeleter(installed);
+
+ // If we still have enough hardware breakpoints left, install a hardware
+ // breakpoint.
+ Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(false);
+ if (breakpoint != NULL) {
+ status_t error = _InstallHardwareBreakpoint(breakpoint, address);
+ if (error != B_OK)
+ return error;
+
+ breakpoint->installedBreakpoint = installed;
+ installed->breakpoint = breakpoint;
+ } else {
+ // install a software breakpoint
+ status_t error = _InstallSoftwareBreakpoint(installed, address);
+ if (error != B_OK)
+ return error;
+ }
+
+ fBreakpoints.Insert(installed);
+ installedDeleter.Detach();
+ fBreakpointCount++;
+
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::UninstallBreakpoint(void* _address)
+{
+ const addr_t address = (addr_t)_address;
+
+ WriteLocker locker(fLock);
+
+ InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
+ if (installed == NULL)
+ return B_BAD_VALUE;
+
+ if (installed->breakpoint->software)
+ _UninstallSoftwareBreakpoint(installed->breakpoint);
+ else
+ _UninstallHardwareBreakpoint(installed->breakpoint);
+
+ fBreakpoints.Remove(installed);
+ delete installed;
+ fBreakpointCount--;
+
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::InstallWatchpoint(void* _address, uint32 type, int32 length)
+{
+ const addr_t address = (addr_t)_address;
+
+ WriteLocker locker(fLock);
+
+ InstalledWatchpoint* watchpoint = _FindWatchpoint(address);
+ if (watchpoint != NULL)
+ return B_BAD_VALUE;
+
+#if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
+ // We need at least one hardware breakpoint for our breakpoint management.
+ if (fWatchpointCount + 1 >= DEBUG_MAX_WATCHPOINTS)
+ return B_BUSY;
+#else
+ if (fWatchpointCount >= DEBUG_MAX_WATCHPOINTS)
+ return B_BUSY;
+#endif
+
+ watchpoint = new(std::nothrow) InstalledWatchpoint;
+ if (watchpoint == NULL)
+ return B_NO_MEMORY;
+ ObjectDeleter<InstalledWatchpoint> watchpointDeleter(watchpoint);
+
+ status_t error = _InstallWatchpoint(watchpoint, address, type, length);
+ if (error != B_OK)
+ return error;
+
+ fWatchpoints.Add(watchpointDeleter.Detach());
+ fWatchpointCount++;
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::UninstallWatchpoint(void* address)
+{
+ WriteLocker locker(fLock);
+
+ InstalledWatchpoint* watchpoint = _FindWatchpoint((addr_t)address);
+ if (watchpoint == NULL)
+ return B_BAD_VALUE;
+
+ ObjectDeleter<InstalledWatchpoint> deleter(watchpoint);
+ fWatchpoints.Remove(watchpoint);
+ fWatchpointCount--;
+
+ return _UninstallWatchpoint(watchpoint);
+}
+
+
+void
+BreakpointManager::RemoveAllBreakpoints()
+{
+ WriteLocker locker(fLock);
+
+ // remove the breakpoints
+ BreakpointTree::Iterator it = fBreakpoints.GetIterator();
+ while (InstalledBreakpoint* installedBreakpoint = it.Next()) {
+ it.Remove();
+
+ // uninstall underlying hard/software breakpoint
+ if (installedBreakpoint->breakpoint->software)
+ _UninstallSoftwareBreakpoint(installedBreakpoint->breakpoint);
+ else
+ _UninstallHardwareBreakpoint(installedBreakpoint->breakpoint);
+
+ delete installedBreakpoint;
+ }
+
+ // remove the watchpoints
+ while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead()) {
+ _UninstallWatchpoint(watchpoint);
+ delete watchpoint;
+ }
+}
+
+
+/*! \brief Returns whether the given address can be accessed in principle.
+ No check whether there's an actually accessible area is performed, though.
+*/
+/*static*/ bool
+BreakpointManager::CanAccessAddress(const void* _address, bool write)
+{
+ const addr_t address = (addr_t)_address;
+
+ // user addresses are always fine
+ if (IS_USER_ADDRESS(address))
+ return true;
+
+ // a commpage address can at least be read
+ if (address >= USER_COMMPAGE_ADDR
+ && address < USER_COMMPAGE_ADDR + COMMPAGE_SIZE) {
+ return !write;
+ }
+
+ return false;
+}
+
+
+/*! \brief Reads data from user memory.
+
+ Tries to read \a size bytes of data from user memory address \a address
+ into the supplied buffer \a buffer. If only a part could be read the
+ function won't fail. The number of bytes actually read is return through
+ \a bytesRead.
+
+ \param address The user memory address from which to read.
+ \param buffer The buffer into which to write.
+ \param size The number of bytes to read.
+ \param bytesRead Will be set to the number of bytes actually read.
+ \return \c B_OK, if reading went fine. Then \a bytesRead will be set to
+ the amount of data actually read. An error indicates that nothing
+ has been read.
+*/
+status_t
+BreakpointManager::ReadMemory(const void* _address, void* buffer, size_t size,
+ size_t& bytesRead)
+{
+ const addr_t address = (addr_t)_address;
+
+ ReadLocker locker(fLock);
+
+ status_t error = _ReadMemory(address, buffer, size, bytesRead);
+ if (error != B_OK)
+ return error;
+
+ // If we have software breakpoints installed, fix the buffer not to contain
+ // any of them.
+
+ // address of the first possibly intersecting software breakpoint
+ const addr_t startAddress
+ = std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1)
+ - (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1);
+
+ BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true,
+ true);
+ while (InstalledBreakpoint* installed = it.Next()) {
+ Breakpoint* breakpoint = installed->breakpoint;
+ if (breakpoint->address >= address + size)
+ break;
+
+ if (breakpoint->software) {
+ // Software breakpoint intersects -- replace the read data with
+ // the data saved in the breakpoint object.
+ addr_t minAddress = std::max(breakpoint->address, address);
+ size_t toCopy = std::min(address + size,
+ breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE)
+ - minAddress;
+ memcpy((uint8*)buffer + (minAddress - address),
+ breakpoint->softwareData + (minAddress - breakpoint->address),
+ toCopy);
+ }
+ }
+
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::WriteMemory(void* _address, const void* _buffer, size_t size,
+ size_t& bytesWritten)
+{
+ bytesWritten = 0;
+
+ if (size == 0)
+ return B_OK;
+
+ addr_t address = (addr_t)_address;
+ const uint8* buffer = (uint8*)_buffer;
+
+ WriteLocker locker(fLock);
+
+ // We don't want to overwrite software breakpoints, so things are a bit more
+ // complicated. We iterate through the intersecting software breakpoints,
+ // writing the memory between them normally, but skipping the breakpoints
+ // itself. We write into their softwareData instead.
+
+ // Get the first breakpoint -- if it starts before the address, we'll
+ // handle it separately to make things in the main loop simpler.
+ const addr_t startAddress
+ = std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1)
+ - (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1);
+
+ BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true,
+ true);
+ InstalledBreakpoint* installed = it.Next();
+ while (installed != NULL) {
+ Breakpoint* breakpoint = installed->breakpoint;
+ if (breakpoint->address >= address)
+ break;
+
+ if (breakpoint->software) {
+ // We've got a breakpoint that is partially intersecting with the
+ // beginning of the address range to write.
+ size_t toCopy = std::min(address + size,
+ breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE)
+ - address;
+ memcpy(breakpoint->softwareData + (address - breakpoint->address),
+ buffer, toCopy);
+
+ address += toCopy;
+ size -= toCopy;
+ bytesWritten += toCopy;
+ buffer += toCopy;
+ }
+
+ installed = it.Next();
+ }
+
+ // loop through the breakpoints intersecting with the range
+ while (installed != NULL) {
+ Breakpoint* breakpoint = installed->breakpoint;
+ if (breakpoint->address >= address + size)
+ break;
+
+ if (breakpoint->software) {
+ // write the data up to the breakpoint (if any)
+ size_t toCopy = breakpoint->address - address;
+ if (toCopy > 0) {
+ size_t chunkWritten;
+ status_t error = _WriteMemory(address, buffer, toCopy,
+ chunkWritten);
+ if (error != B_OK)
+ return bytesWritten > 0 ? B_OK : error;
+
+ address += chunkWritten;
+ size -= chunkWritten;
+ bytesWritten += chunkWritten;
+ buffer += chunkWritten;
+
+ if (chunkWritten < toCopy)
+ return B_OK;
+ }
+
+ // write to the breakpoint data
+ toCopy = std::min(size, (size_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE);
+ memcpy(breakpoint->softwareData, buffer, toCopy);
+
+ address += toCopy;
+ size -= toCopy;
+ bytesWritten += toCopy;
+ buffer += toCopy;
+ }
+
+ installed = it.Next();
+ }
+
+ // write remaining data
+ if (size > 0) {
+ size_t chunkWritten;
+ status_t error = _WriteMemory(address, buffer, size, chunkWritten);
+ if (error != B_OK)
+ return bytesWritten > 0 ? B_OK : error;
+
+ bytesWritten += chunkWritten;
+ }
+
+ return B_OK;
+}
+
+
+void
+BreakpointManager::PrepareToContinue(void* _address)
+{
+ const addr_t address = (addr_t)_address;
+
+ WriteLocker locker(fLock);
+
+ // Check whether there's a software breakpoint at the continuation address.
+ InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
+ if (installed == NULL || !installed->breakpoint->software)
+ return;
+
+ // We need to replace the software breakpoint by a hardware one, or
+ // we can't continue the thread.
+ Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(true);
+ if (breakpoint == NULL) {
+ dprintf("Failed to allocate a hardware breakpoint.\n");
+ return;
+ }
+
+ status_t error = _InstallHardwareBreakpoint(breakpoint, address);
+ if (error != B_OK)
+ return;
+
+ _UninstallSoftwareBreakpoint(installed->breakpoint);
+
+ breakpoint->installedBreakpoint = installed;
+ installed->breakpoint = breakpoint;
+}
+
+
+BreakpointManager::Breakpoint*
+BreakpointManager::_GetUnusedHardwareBreakpoint(bool force)
+{
+ // try to find a free one first
+ for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator();
+ Breakpoint* breakpoint = it.Next();) {
+ if (!breakpoint->used)
+ return breakpoint;
+ }
+
+ if (!force)
+ return NULL;
+
+ // replace one by a software breakpoint
+ for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator();
+ Breakpoint* breakpoint = it.Next();) {
+ if (breakpoint->installedBreakpoint == NULL)
+ continue;
+
+ status_t error = _InstallSoftwareBreakpoint(
+ breakpoint->installedBreakpoint, breakpoint->address);
+ if (error != B_OK)
+ continue;
+
+ if (_UninstallHardwareBreakpoint(breakpoint) == B_OK)
+ return breakpoint;
+ }
+
+ return NULL;
+}
+
+
+status_t
+BreakpointManager::_InstallSoftwareBreakpoint(InstalledBreakpoint* installed,
+ addr_t address)
+{
+ Breakpoint* breakpoint = new(std::nothrow) Breakpoint;
+ if (breakpoint == NULL)
+ return B_NO_MEMORY;
+ ObjectDeleter<Breakpoint> breakpointDeleter(breakpoint);
+
+ breakpoint->address = address;
+ breakpoint->installedBreakpoint = installed;
+ breakpoint->used = true;
+ breakpoint->software = true;
+
+ // save the memory where the software breakpoint shall be installed
+ size_t bytesTransferred;
+ status_t error = _ReadMemory(address, breakpoint->softwareData,
+ DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred);
+ if (error != B_OK)
+ return error;
+ if (bytesTransferred != DEBUG_SOFTWARE_BREAKPOINT_SIZE)
+ return B_BAD_ADDRESS;
+
+ // write the breakpoint code
+ error = _WriteMemory(address, DEBUG_SOFTWARE_BREAKPOINT,
+ DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred);
+ if (error != B_OK)
+ return error;
+
+ if (bytesTransferred < DEBUG_SOFTWARE_BREAKPOINT_SIZE) {
+ // breakpoint written partially only -- undo the written part
+ if (bytesTransferred > 0) {
+ size_t dummy;
+ _WriteMemory(address, breakpoint->softwareData, bytesTransferred,
+ dummy);
+ }
+ return B_BAD_ADDRESS;
+ }
+
+ installed->breakpoint = breakpoint;
+ breakpointDeleter.Detach();
+
+ TRACE("installed software breakpoint at %#lx\n", address);
+
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::_UninstallSoftwareBreakpoint(Breakpoint* breakpoint)
+{
+ size_t bytesWritten;
+ _WriteMemory(breakpoint->address, breakpoint->softwareData,
+ DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesWritten);
+
+ TRACE("uninstalled software breakpoint at %#lx\n", breakpoint->address);
+
+ delete breakpoint;
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::_InstallHardwareBreakpoint(Breakpoint* breakpoint,
+ addr_t address)
+{
+ status_t error = arch_set_breakpoint((void*)address);
+ if (error != B_OK)
+ return error;
+
+ // move to the tail of the list
+ fHardwareBreakpoints.Remove(breakpoint);
+ fHardwareBreakpoints.Add(breakpoint);
+
+ TRACE("installed hardware breakpoint at %#lx\n", address);
+
+ breakpoint->address = address;
+ breakpoint->used = true;
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::_UninstallHardwareBreakpoint(Breakpoint* breakpoint)
+{
+ status_t error = arch_clear_breakpoint((void*)breakpoint->address);
+ if (error != B_OK)
+ return error;
+
+ TRACE("uninstalled hardware breakpoint at %#lx\n", breakpoint->address);
+
+ breakpoint->used = false;
+ breakpoint->installedBreakpoint = NULL;
+ return B_OK;
+}
+
+
+BreakpointManager::InstalledWatchpoint*
+BreakpointManager::_FindWatchpoint(addr_t address) const
+{
+ for (InstalledWatchpointList::ConstIterator it = fWatchpoints.GetIterator();
+ InstalledWatchpoint* watchpoint = it.Next();) {
+ if (address == watchpoint->address)
+ return watchpoint;
+ }
+
+ return NULL;
+}
+
+
+status_t
+BreakpointManager::_InstallWatchpoint(InstalledWatchpoint* watchpoint,
+ addr_t address, uint32 type, int32 length)
+{
+#if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
+ // We need a hardware breakpoint.
+ watchpoint->breakpoint = _GetUnusedHardwareBreakpoint(true);
+ if (watchpoint->breakpoint == NULL) {
+ dprintf("Failed to allocate a hardware breakpoint for watchpoint.\n");
+ return B_BUSY;
+ }
+#endif
+
+ status_t error = arch_set_watchpoint((void*)address, type, length);
+ if (error != B_OK)
+ return error;
+
+ watchpoint->address = address;
+
+#if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
+ watchpoint->breakpoint->used = true;
+#endif
+
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::_UninstallWatchpoint(InstalledWatchpoint* watchpoint)
+{
+#if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
+ watchpoint->breakpoint->used = false;
+#endif
+
+ return arch_clear_watchpoint((void*)watchpoint->address);
+}
+
+
+status_t
+BreakpointManager::_ReadMemory(const addr_t _address, void* _buffer,
+ size_t size, size_t& bytesRead)
+{
+ const uint8* address = (const uint8*)_address;
+ uint8* buffer = (uint8*)_buffer;
+
+ // check the parameters
+ if (!CanAccessAddress(address, false))
+ return B_BAD_ADDRESS;
+ if (size <= 0)
+ return B_BAD_VALUE;
+
+ // If the region to be read crosses page boundaries, we split it up into
+ // smaller chunks.
+ status_t error = B_OK;
+ bytesRead = 0;
+ while (size > 0) {
+ // check whether we're still in user address space
+ if (!CanAccessAddress(address, false)) {
+ error = B_BAD_ADDRESS;
+ break;
+ }
+
+ // don't cross page boundaries in a single read
+ int32 toRead = size;
+ int32 maxRead = B_PAGE_SIZE - (addr_t)address % B_PAGE_SIZE;
+ if (toRead > maxRead)
+ toRead = maxRead;
+
+ error = user_memcpy(buffer, address, toRead);
+ if (error != B_OK)
+ break;
+
+ bytesRead += toRead;
+ address += toRead;
+ buffer += toRead;
+ size -= toRead;
+ }
+
+ // If reading fails, we only fail, if we haven't read anything yet.
+ if (error != B_OK) {
+ if (bytesRead > 0)
+ return B_OK;
+ return error;
+ }
+
+ return B_OK;
+}
+
+
+status_t
+BreakpointManager::_WriteMemory(addr_t _address, const void* _buffer,
+ size_t size, size_t& bytesWritten)
+{
+ uint8* address = (uint8*)_address;
+ const uint8* buffer = (const uint8*)_buffer;
+
+ // check the parameters
+ if (!CanAccessAddress(address, true))
+ return B_BAD_ADDRESS;
+ if (size <= 0)
+ return B_BAD_VALUE;
+
+ // If the region to be written crosses area boundaries, we split it up into
+ // smaller chunks.
+ status_t error = B_OK;
+ bytesWritten = 0;
+ while (size > 0) {
+ // check whether we're still in user address space
+ if (!CanAccessAddress(address, true)) {
+ error = B_BAD_ADDRESS;
+ break;
+ }
+
+ // get the area for the address (we need to use _user_area_for(), since
+ // we're looking for a user area)
+ area_id area = _user_area_for(address);
+ if (area < 0) {
+ TRACE("BreakpointManager::_WriteMemory(): area not found for "
+ "address: %#lx: %lx\n", address, area);
+ error = area;
+ break;
+ }
+
+ area_info areaInfo;
+ status_t error = get_area_info(area, &areaInfo);
+ if (error != B_OK) {
+ TRACE("BreakpointManager::_WriteMemory(): failed to get info for "
+ "area %ld: %lx\n", area, error);
+ error = B_BAD_ADDRESS;
+ break;
+ }
+
+ // restrict this round of writing to the found area
+ int32 toWrite = size;
+ int32 maxWrite = (uint8*)areaInfo.address + areaInfo.size - address;
+ if (toWrite > maxWrite)
+ toWrite = maxWrite;
+
+ // if the area is read-only, we temporarily need to make it writable
+ bool protectionChanged = false;
+ if (!(areaInfo.protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA))) {
+ error = set_area_protection(area,
+ areaInfo.protection | B_WRITE_AREA);
+ if (error != B_OK) {
+ TRACE("BreakpointManager::_WriteMemory(): failed to set new "
+ "protection for area %ld: %lx\n", area, error);
+ break;
+ }
+ protectionChanged = true;
+ }
+
+ // copy the memory
+ error = user_memcpy(address, buffer, toWrite);
+
+ // reset the area protection
+ if (protectionChanged)
+ set_area_protection(area, areaInfo.protection);
+
+ if (error != B_OK) {
+ TRACE("BreakpointManager::_WriteMemory(): user_memcpy() failed: "
+ "%lx\n", error);
+ break;
+ }
+
+ bytesWritten += toWrite;
+ address += toWrite;
+ buffer += toWrite;
+ size -= toWrite;
+ }
+
+ // If writing fails, we only fail, if we haven't written anything yet.
+ if (error != B_OK) {
+ if (bytesWritten > 0)
+ return B_OK;
+ return error;
+ }
+
+ return B_OK;
+}
Added: haiku/trunk/src/system/kernel/debug/BreakpointManager.h
===================================================================
--- haiku/trunk/src/system/kernel/debug/BreakpointManager.h 2009-06-23 20:52:30 UTC (rev 31213)
+++ haiku/trunk/src/system/kernel/debug/BreakpointManager.h 2009-06-23 21:03:57 UTC (rev 31214)
@@ -0,0 +1,144 @@
+/*
+ * 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 <util/DoublyLinkedList.h>
+#include <util/SplayTree.h>
+
+#include <arch/user_debugger.h>
+#include <lock.h>
+
+
+struct BreakpointManager {
+public:
+ BreakpointManager();
+ ~BreakpointManager();
+
+ status_t Init();
+
+ status_t InstallBreakpoint(void* address);
+ status_t UninstallBreakpoint(void* address);
+
+ status_t InstallWatchpoint(void* address, uint32 type,
+ int32 length);
+ status_t UninstallWatchpoint(void* address);
+
+ void RemoveAllBreakpoints();
+ // break- and watchpoints
+
+ static bool CanAccessAddress(const void* address,
+ bool write);
+ status_t ReadMemory(const void* _address, void* _buffer,
+ size_t size, size_t& bytesRead);
+ status_t WriteMemory(void* _address, const void* _buffer,
+ size_t size, size_t& bytesWritten);
+
+ void PrepareToContinue(void* address);
+
+private:
+ struct InstalledBreakpoint;
+
+ struct Breakpoint : DoublyLinkedListLinkImpl<Breakpoint> {
+ addr_t address;
+ InstalledBreakpoint* installedBreakpoint;
+ bool used;
+ bool software;
+ uint8 softwareData[
+ DEBUG_SOFTWARE_BREAKPOINT_SIZE];
+ };
+
+ typedef DoublyLinkedList<Breakpoint> BreakpointList;
+
+ struct InstalledBreakpoint : SplayTreeLink<InstalledBreakpoint> {
+ InstalledBreakpoint* splayNext;
+ Breakpoint* breakpoint;
+ addr_t address;
[... truncated: 681 lines follow ...]
More information about the Haiku-commits
mailing list