From b0f12d64f42dc8f5bbabf4cbcdf25178b3eb3749 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 23 Jun 2009 21:03:57 +0000 Subject: [PATCH] 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. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31214 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/kernel/debugger.h | 3 - headers/private/kernel/arch/user_debugger.h | 28 + .../kernel/arch/x86/arch_user_debugger.h | 16 +- headers/private/kernel/user_debugger.h | 4 + .../kernel/arch/x86/arch_user_debugger.cpp | 6 + src/system/kernel/debug/BreakpointManager.cpp | 783 ++++++++++++++++++ src/system/kernel/debug/BreakpointManager.h | 144 ++++ src/system/kernel/debug/Jamfile | 1 + src/system/kernel/debug/user_debugger.cpp | 335 +++----- 9 files changed, 1078 insertions(+), 242 deletions(-) create mode 100644 src/system/kernel/debug/BreakpointManager.cpp create mode 100644 src/system/kernel/debug/BreakpointManager.h diff --git a/headers/os/kernel/debugger.h b/headers/os/kernel/debugger.h index 2a203f9bff..65b9e0801a 100644 --- a/headers/os/kernel/debugger.h +++ b/headers/os/kernel/debugger.h @@ -455,9 +455,6 @@ typedef struct { 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 diff --git a/headers/private/kernel/arch/user_debugger.h b/headers/private/kernel/arch/user_debugger.h index 2cdd0eec00..93b1e87e50 100644 --- a/headers/private/kernel/arch/user_debugger.h +++ b/headers/private/kernel/arch/user_debugger.h @@ -50,6 +50,34 @@ status_t arch_clear_kernel_watchpoint(void *address); #include +// 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 diff --git a/headers/private/kernel/arch/x86/arch_user_debugger.h b/headers/private/kernel/arch/x86/arch_user_debugger.h index 533b07e060..d5327cade2 100644 --- a/headers/private/kernel/arch/x86/arch_user_debugger.h +++ b/headers/private/kernel/arch/x86/arch_user_debugger.h @@ -1,5 +1,5 @@ /* - * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. + * Copyright 2005-2009, Ingo Weinhold, bonefish@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 @@ struct arch_thread_debug_info { uint32 flags; }; +// The software breakpoint instruction (int3). +extern const uint8 kX86SoftwareBreakpoint[1]; + #ifdef __cplusplus extern "C" { #endif @@ -129,4 +130,11 @@ extern void x86_init_user_debug(); } #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 diff --git a/headers/private/kernel/user_debugger.h b/headers/private/kernel/user_debugger.h index bf87238b3c..3de07f2a3b 100644 --- a/headers/private/kernel/user_debugger.h +++ b/headers/private/kernel/user_debugger.h @@ -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 @@ struct team_debug_info { // 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; }; diff --git a/src/system/kernel/arch/x86/arch_user_debugger.cpp b/src/system/kernel/arch/x86/arch_user_debugger.cpp index 145949e3b4..7a707bd64f 100644 --- a/src/system/kernel/arch/x86/arch_user_debugger.cpp +++ b/src/system/kernel/arch/x86/arch_user_debugger.cpp @@ -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 @@ x86_handle_breakpoint_exception(struct iframe *frame) { 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; diff --git a/src/system/kernel/debug/BreakpointManager.cpp b/src/system/kernel/debug/BreakpointManager.cpp new file mode 100644 index 0000000000..331d9ce855 --- /dev/null +++ b/src/system/kernel/debug/BreakpointManager.cpp @@ -0,0 +1,783 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "BreakpointManager.h" + +#include + +#include + +#include +#include +#include +#include + + +//#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 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 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 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 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; +} diff --git a/src/system/kernel/debug/BreakpointManager.h b/src/system/kernel/debug/BreakpointManager.h new file mode 100644 index 0000000000..a20ea2a27a --- /dev/null +++ b/src/system/kernel/debug/BreakpointManager.h @@ -0,0 +1,144 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef BREAKPOINT_MANAGER_H +#define BREAKPOINT_MANAGER_H + +#include +#include + +#include +#include + + +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 { + addr_t address; + InstalledBreakpoint* installedBreakpoint; + bool used; + bool software; + uint8 softwareData[ + DEBUG_SOFTWARE_BREAKPOINT_SIZE]; + }; + + typedef DoublyLinkedList BreakpointList; + + struct InstalledBreakpoint : SplayTreeLink { + InstalledBreakpoint* splayNext; + Breakpoint* breakpoint; + addr_t address; + + InstalledBreakpoint(addr_t address); + }; + + struct InstalledWatchpoint + : DoublyLinkedListLinkImpl { + addr_t address; +#if DEBUG_SHARED_BREAK_AND_WATCHPOINTS + Breakpoint* breakpoint; +#endif + }; + + typedef DoublyLinkedList + InstalledWatchpointList; + + struct InstalledBreakpointSplayDefinition { + typedef addr_t KeyType; + typedef InstalledBreakpoint NodeType; + + static const KeyType& GetKey(const InstalledBreakpoint* node) + { + return node->address; + } + + static SplayTreeLink* GetLink( + InstalledBreakpoint* node) + { + return node; + } + + static int Compare(addr_t key, const InstalledBreakpoint* node) + { + if (key < node->address) + return -1; + return key == node->address ? 0 : 1; + } + + // for IteratableSplayTree only + static NodeType** GetListLink(InstalledBreakpoint* node) + { + return &node->splayNext; + } + }; + + typedef IteratableSplayTree + BreakpointTree; + +private: + Breakpoint* _GetUnusedHardwareBreakpoint(bool force); + + status_t _InstallSoftwareBreakpoint( + InstalledBreakpoint* installed, + addr_t address); + status_t _UninstallSoftwareBreakpoint( + Breakpoint* breakpoint); + + status_t _InstallHardwareBreakpoint( + Breakpoint* breakpoint, addr_t address); + status_t _UninstallHardwareBreakpoint( + Breakpoint* breakpoint); + + InstalledWatchpoint* _FindWatchpoint(addr_t address) const; + status_t _InstallWatchpoint( + InstalledWatchpoint* watchpoint, + addr_t address, uint32 type, int32 length); + status_t _UninstallWatchpoint( + InstalledWatchpoint* watchpoint); + + status_t _ReadMemory(const addr_t _address, + void* _buffer, size_t size, + size_t& bytesRead); + status_t _WriteMemory(addr_t _address, + const void* _buffer, size_t size, + size_t& bytesWritten); + +private: + rw_lock fLock; + BreakpointList fHardwareBreakpoints; + BreakpointTree fBreakpoints; + InstalledWatchpointList fWatchpoints; + int32 fBreakpointCount; + int32 fWatchpointCount; +}; + + +#endif // BREAKPOINT_MANAGER_H diff --git a/src/system/kernel/debug/Jamfile b/src/system/kernel/debug/Jamfile index a94a3dc84b..f1448362ae 100644 --- a/src/system/kernel/debug/Jamfile +++ b/src/system/kernel/debug/Jamfile @@ -7,6 +7,7 @@ UsePrivateHeaders shared ; KernelMergeObject kernel_debug.o : blue_screen.cpp + BreakpointManager.cpp debug.cpp debug_builtin_commands.cpp debug_commands.cpp diff --git a/src/system/kernel/debug/user_debugger.cpp b/src/system/kernel/debug/user_debugger.cpp index db8bf1aadc..719c03d6fe 100644 --- a/src/system/kernel/debug/user_debugger.cpp +++ b/src/system/kernel/debug/user_debugger.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -32,6 +31,9 @@ #include #include +#include "BreakpointManager.h" + + //#define TRACE_USER_DEBUGGER #ifdef TRACE_USER_DEBUGGER # define TRACE(x) dprintf x @@ -40,6 +42,12 @@ #endif +// TODO: Since the introduction of team_debug_info::debugger_changed_condition +// there's some potential for simplifications. E.g. clear_team_debug_info() and +// destroy_team_debug_info() are now only used in nub_thread_cleanup() (plus +// arch_clear_team_debug_info() in install_team_debugger_init_debug_infos()). + + static port_id sDefaultDebuggerPort = -1; // accessed atomically @@ -222,6 +230,7 @@ clear_team_debug_info(struct team_debug_info *info, bool initLock) info->debugger_write_lock = -1; info->causing_thread = -1; info->image_event = 0; + info->breakpoint_manager = NULL; if (initLock) { B_INITIALIZE_SPINLOCK(&info->lock); @@ -248,6 +257,10 @@ destroy_team_debug_info(struct team_debug_info *info) if (info) { arch_destroy_team_debug_info(&info->arch_info); + // delete the breakpoint manager + delete info->breakpoint_manager ; + info->breakpoint_manager = NULL; + // delete the debugger port write lock if (info->debugger_write_lock >= 0) { delete_sem(info->debugger_write_lock); @@ -777,6 +790,20 @@ thread_hit_debug_event(debug_debugger_message event, const void *message, requireDebugger, restart); } while (result >= 0 && restart); + // Prepare to continue -- we install a debugger change condition, so no-one + // will change the debugger while we're playing with the breakpoint manager. + // TODO: Maybe better use ref-counting and a flag in the breakpoint manager. + struct team* team = thread_get_current_thread()->team; + ConditionVariable debugChangeCondition; + prepare_debugger_change(team, debugChangeCondition); + + if (team->debug_info.breakpoint_manager != NULL) { + team->debug_info.breakpoint_manager->PrepareToContinue( + arch_debug_get_interrupt_pc()); + } + + finish_debugger_change(team); + return result; } @@ -966,7 +993,6 @@ user_debug_team_deleted(team_id teamID, port_id debuggerPort) message.origin.nub_port = -1; write_port_etc(debuggerPort, B_DEBUGGER_MESSAGE_TEAM_DELETED, &message, sizeof(message), B_RELATIVE_TIMEOUT, 0); - // TODO: Would it be OK to wait here? } } @@ -1217,7 +1243,6 @@ user_debug_breakpoint_hit(bool software) { // prepare the message debug_breakpoint_hit message; - message.software = software; arch_get_debug_cpu_state(&message.cpu_state); thread_hit_serious_debug_event(B_DEBUGGER_MESSAGE_BREAKPOINT_HIT, &message, @@ -1515,6 +1540,9 @@ nub_thread_cleanup(struct thread *nubThread) RELEASE_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info); restore_interrupts(state); + if (destroyDebugInfo) + teamDebugInfo.breakpoint_manager->RemoveAllBreakpoints(); + finish_debugger_change(nubThread->team); if (destroyDebugInfo) @@ -1526,183 +1554,6 @@ nub_thread_cleanup(struct thread *nubThread) } -/*! \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 -can_access_address(const void* address, bool write) -{ - // user addresses are always fine - if (IS_USER_ADDRESS(address)) - return true; - - // a commpage address can at least be read - if ((addr_t)address >= USER_COMMPAGE_ADDR - && (addr_t)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. - */ -static status_t -read_user_memory(const void *_address, void *_buffer, int32 size, - int32 &bytesRead) -{ - const char *address = (const char*)_address; - char *buffer = (char*)_buffer; - - // check the parameters - if (!can_access_address(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 (!can_access_address(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; -} - - -static status_t -write_user_memory(void *_address, const void *_buffer, int32 size, - int32 &bytesWritten) -{ - char *address = (char*)_address; - const char *buffer = (const char*)_buffer; - - // check the parameters - if (!can_access_address(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 (!can_access_address(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((void*)address); - if (area < 0) { - TRACE(("write_user_memory(): area not found for address: %p: " - "%lx\n", address, area)); - error = area; - break; - } - - area_info areaInfo; - status_t error = get_area_info(area, &areaInfo); - if (error != B_OK) { - TRACE(("write_user_memory(): 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 = (char*)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(("write_user_memory(): 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(("write_user_memory(): 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; -} - - /** \brief Debug nub thread helper function that returns the debug port of * a thread of the same team. */ @@ -1755,6 +1606,8 @@ debug_nub_thread(void *) port_id port = nubThread->team->debug_info.nub_port; sem_id writeLock = nubThread->team->debug_info.debugger_write_lock; + BreakpointManager* breakpointManager + = nubThread->team->debug_info.breakpoint_manager; RELEASE_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info); restore_interrupts(state); @@ -1811,16 +1664,16 @@ debug_nub_thread(void *) status_t result = B_OK; // check the parameters - if (!can_access_address(address, false)) + if (!BreakpointManager::CanAccessAddress(address, false)) result = B_BAD_ADDRESS; else if (size <= 0 || size > B_MAX_READ_WRITE_MEMORY_SIZE) result = B_BAD_VALUE; // read the memory - int32 bytesRead = 0; + size_t bytesRead = 0; if (result == B_OK) { - result = read_user_memory(address, reply.read_memory.data, - size, bytesRead); + result = breakpointManager->ReadMemory(address, + reply.read_memory.data, size, bytesRead); } reply.read_memory.error = result; @@ -1847,15 +1700,15 @@ debug_nub_thread(void *) status_t result = B_OK; // check the parameters - if (!can_access_address(address, true)) + if (!BreakpointManager::CanAccessAddress(address, true)) result = B_BAD_ADDRESS; else if (size <= 0 || size > realSize) result = B_BAD_VALUE; // write the memory - int32 bytesWritten = 0; + size_t bytesWritten = 0; if (result == B_OK) { - result = write_user_memory(address, data, size, + result = breakpointManager->WriteMemory(address, data, size, bytesWritten); } reply.write_memory.error = result; @@ -2031,12 +1884,14 @@ debug_nub_thread(void *) // check the address status_t result = B_OK; - if (address == NULL || !can_access_address(address, false)) + if (address == NULL + || !BreakpointManager::CanAccessAddress(address, false)) { result = B_BAD_ADDRESS; + } // set the breakpoint if (result == B_OK) - result = arch_set_breakpoint(address); + result = breakpointManager->InstallBreakpoint(address); if (result == B_OK) update_threads_breakpoints_flag(); @@ -2059,12 +1914,14 @@ debug_nub_thread(void *) // check the address status_t result = B_OK; - if (address == NULL || !can_access_address(address, false)) + if (address == NULL + || !BreakpointManager::CanAccessAddress(address, false)) { result = B_BAD_ADDRESS; + } // clear the breakpoint if (result == B_OK) - result = arch_clear_breakpoint(address); + result = breakpointManager->UninstallBreakpoint(address); if (result == B_OK) update_threads_breakpoints_flag(); @@ -2086,14 +1943,18 @@ debug_nub_thread(void *) // check the address and size status_t result = B_OK; - if (address == NULL || !can_access_address(address, false)) + if (address == NULL + || !BreakpointManager::CanAccessAddress(address, false)) { result = B_BAD_ADDRESS; + } if (length < 0) result = B_BAD_VALUE; // set the watchpoint - if (result == B_OK) - result = arch_set_watchpoint(address, type, length); + if (result == B_OK) { + result = breakpointManager->InstallWatchpoint(address, type, + length); + } if (result == B_OK) update_threads_breakpoints_flag(); @@ -2116,12 +1977,14 @@ debug_nub_thread(void *) // check the address status_t result = B_OK; - if (address == NULL || !can_access_address(address, false)) + if (address == NULL + || !BreakpointManager::CanAccessAddress(address, false)) { result = B_BAD_ADDRESS; + } // clear the watchpoint if (result == B_OK) - result = arch_clear_watchpoint(address); + result = breakpointManager->UninstallWatchpoint(address); if (result == B_OK) update_threads_breakpoints_flag(); @@ -2317,10 +2180,15 @@ debug_nub_thread(void *) atomic_or(&team->debug_info.flags, B_TEAM_DEBUG_DEBUGGER_HANDOVER); + BreakpointManager* breakpointManager + = team->debug_info.breakpoint_manager; RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); restore_interrupts(state); + // remove all installed breakpoints + breakpointManager->RemoveAllBreakpoints(); + release_sem(writeLock); } else { // We probably got a SIGKILL. If so, we will terminate when @@ -2545,7 +2413,7 @@ debug_nub_thread(void *) Interrupts must be disabled and the team debug info lock of the team to be debugged must be held. The function will release the lock, but leave - interrupts disabled. The team lock must be held, too. + interrupts disabled. The function also clears the arch specific team and thread debug infos (including among other things formerly set break/watchpoints). @@ -2655,13 +2523,6 @@ install_team_debugger(team_id teamID, port_id debuggerPort, error = B_OK; done = true; result = team->debug_info.nub_port; - } else if ( - teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_HANDING_OVER) { - // Another debugger is in the process of installing itself - // as the team's debugger. - error = (dontReplace ? B_OK : B_BAD_VALUE); - done = true; - result = team->debug_info.nub_port; } else { // a handover to another debugger is requested // Set the handing-over flag -- we'll clear both flags after @@ -2735,16 +2596,9 @@ install_team_debugger(team_id teamID, port_id debuggerPort, state = disable_interrupts(); GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info); - int32 teamDebugFlags = team->debug_info.flags; - - if ((teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) != 0 - && (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_HANDING_OVER) != 0 - && team->debug_info.debugger_port == debuggerPort) { - // Everything is as we left it above, so just clear the flags. - atomic_and(&team->debug_info.flags, - ~(B_TEAM_DEBUG_DEBUGGER_HANDOVER - | B_TEAM_DEBUG_DEBUGGER_HANDING_OVER)); - } + atomic_and(&team->debug_info.flags, + ~(B_TEAM_DEBUG_DEBUGGER_HANDOVER + | B_TEAM_DEBUG_DEBUGGER_HANDING_OVER)); RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); restore_interrupts(state); @@ -2800,6 +2654,16 @@ install_team_debugger(team_id teamID, port_id debuggerPort, if (error == B_OK) error = set_port_owner(nubPort, debuggerTeam); + // create the breakpoint manager + BreakpointManager* breakpointManager = NULL; + if (error == B_OK) { + breakpointManager = new(std::nothrow) BreakpointManager; + if (breakpointManager != NULL) + error = breakpointManager->Init(); + else + error = B_NO_MEMORY; + } + // spawn the nub thread thread_id nubThread = -1; if (error == B_OK) { @@ -2815,18 +2679,10 @@ install_team_debugger(team_id teamID, port_id debuggerPort, state = disable_interrupts(); GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info); - if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) { - // there's already a debugger installed - error = (dontReplace ? B_OK : B_BAD_VALUE); - done = true; - result = team->debug_info.nub_port; - - RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); - } else { - install_team_debugger_init_debug_infos(team, debuggerTeam, - debuggerPort, nubPort, nubThread, debuggerWriteLock, - causingThread); - } + team->debug_info.breakpoint_manager = breakpointManager; + install_team_debugger_init_debug_infos(team, debuggerTeam, + debuggerPort, nubPort, nubThread, debuggerWriteLock, + causingThread); restore_interrupts(state); } @@ -2834,7 +2690,7 @@ install_team_debugger(team_id teamID, port_id debuggerPort, finish_debugger_change(team); // if everything went fine, resume the nub thread, otherwise clean up - if (error == B_OK && !done) { + if (error == B_OK) { resume_thread(nubThread); } else { // delete port and terminate thread @@ -2846,6 +2702,8 @@ install_team_debugger(team_id teamID, port_id debuggerPort, int32 result; wait_for_thread(nubThread, &result); } + + delete breakpointManager; } TRACE(("install_team_debugger() done2: %ld\n", @@ -2961,11 +2819,13 @@ _user_remove_team_debugger(team_id teamID) InterruptsSpinLocker debugInfoLocker(team->debug_info.lock); - struct team_debug_info info; + thread_id nubThread = -1; + port_id nubPort = -1; + if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) { // there's a debugger installed - info = team->debug_info; - clear_team_debug_info(&team->debug_info, false); + nubThread = team->debug_info.nub_thread; + nubPort = team->debug_info.nub_port; } else { // no debugger installed error = B_BAD_VALUE; @@ -2973,11 +2833,16 @@ _user_remove_team_debugger(team_id teamID) debugInfoLocker.Unlock(); + // Delete the nub port -- this will cause the nub thread to terminate and + // remove the debugger. + if (nubPort >= 0) + delete_port(nubPort); + finish_debugger_change(team); - // clean up the info, if there was a debugger installed - if (error == B_OK) - destroy_team_debug_info(&info); + // wait for the nub thread + if (nubThread >= 0) + wait_for_thread(nubThread, NULL); return error; } @@ -3093,7 +2958,7 @@ _user_set_debugger_breakpoint(void *address, uint32 type, int32 length, bool watchpoint) { // check the address and size - if (address == NULL || !can_access_address(address, false)) + if (address == NULL || !BreakpointManager::CanAccessAddress(address, false)) return B_BAD_ADDRESS; if (watchpoint && length < 0) return B_BAD_VALUE; @@ -3126,7 +2991,7 @@ status_t _user_clear_debugger_breakpoint(void *address, bool watchpoint) { // check the address - if (address == NULL || !can_access_address(address, false)) + if (address == NULL || !BreakpointManager::CanAccessAddress(address, false)) return B_BAD_ADDRESS; // check whether a debugger is installed already