diff --git a/headers/os/kernel/debugger.h b/headers/os/kernel/debugger.h index d2a96841e2..5084f5a1c2 100644 --- a/headers/os/kernel/debugger.h +++ b/headers/os/kernel/debugger.h @@ -29,6 +29,16 @@ extern status_t remove_team_debugger(team_id team); extern status_t debug_thread(thread_id thread); extern void wait_for_debugger(void); +// EXPERIMENTAL: Self-debugging functions. Will fail when a team debugger is +// installed. A breakpoint/watchpoint hit will cause the default debugger to +// be installed for the team. +extern status_t set_debugger_breakpoint(void *address); +extern status_t clear_debugger_breakpoint(void *address); +extern status_t set_debugger_watchpoint(void *address, uint32 type, + int32 length); +extern status_t clear_debugger_watchpoint(void *address); + + // team debugging flags enum { // event mask: If a flag is set, any of the team's threads will stop when diff --git a/headers/private/kernel/syscalls.h b/headers/private/kernel/syscalls.h index 26c40e395b..683793357c 100644 --- a/headers/private/kernel/syscalls.h +++ b/headers/private/kernel/syscalls.h @@ -263,6 +263,10 @@ extern status_t _kern_remove_team_debugger(team_id team); extern status_t _kern_debug_thread(thread_id thread); extern void _kern_wait_for_debugger(void); +extern status_t _kern_set_debugger_breakpoint(void *address, uint32 type, + int32 length, bool watchpoint); +extern status_t _kern_clear_debugger_breakpoint(void *address, + bool watchpoint); /* atomic_* ops (needed for CPUs that don't support them directly) */ #ifdef ATOMIC_FUNCS_ARE_SYSCALLS diff --git a/headers/private/kernel/user_debugger.h b/headers/private/kernel/user_debugger.h index fbce350d71..11bfbf5c38 100644 --- a/headers/private/kernel/user_debugger.h +++ b/headers/private/kernel/user_debugger.h @@ -177,6 +177,10 @@ status_t _user_remove_team_debugger(team_id team); status_t _user_debug_thread(thread_id thread); void _user_wait_for_debugger(void); +status_t _user_set_debugger_breakpoint(void *address, uint32 type, + int32 length, bool watchpoint); +status_t _user_clear_debugger_breakpoint(void *address, bool watchpoint); + #ifdef __cplusplus } // extern "C" diff --git a/src/system/kernel/debug/user_debugger.cpp b/src/system/kernel/debug/user_debugger.cpp index bbceb7268c..fd0ea28173 100644 --- a/src/system/kernel/debug/user_debugger.cpp +++ b/src/system/kernel/debug/user_debugger.cpp @@ -1786,11 +1786,14 @@ debug_nub_thread(void *) /** \brief Helper function for install_team_debugger(), that sets up the team - * and thread debug infos. - * - * Interrupts must be enabled 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. + and thread debug infos. + + 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 function also clears the arch specific team and thread debug infos + (including among other things formerly set break/watchpoints). */ static void install_team_debugger_init_debug_infos(struct team *team, team_id debuggerTeam, @@ -1805,6 +1808,8 @@ install_team_debugger_init_debug_infos(struct team *team, team_id debuggerTeam, team->debug_info.debugger_port = debuggerPort; team->debug_info.debugger_write_lock = debuggerPortWriteLock; + arch_clear_team_debug_info(&team->debug_info.arch_info); + RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info); // set the user debug flags and signal masks of all threads to the default @@ -1822,6 +1827,8 @@ install_team_debugger_init_debug_infos(struct team *team, team_id debuggerTeam, flags | B_THREAD_DEBUG_DEFAULT_FLAGS); thread->debug_info.ignore_signals = 0; thread->debug_info.ignore_signals_once = 0; + + arch_clear_thread_debug_info(&thread->debug_info.arch_info); } } @@ -2229,6 +2236,7 @@ _user_debug_thread(thread_id threadID) return error; } + void _user_wait_for_debugger(void) { @@ -2238,3 +2246,54 @@ _user_wait_for_debugger(void) } +status_t +_user_set_debugger_breakpoint(void *address, uint32 type, int32 length, + bool watchpoint) +{ + // check the address and size + if (address == NULL || !IS_USER_ADDRESS(address)) + return B_BAD_ADDRESS; + if (watchpoint && length < 0) + return B_BAD_VALUE; + + // check whether a debugger is installed already + team_debug_info teamDebugInfo; + get_team_debug_info(teamDebugInfo); + if (teamDebugInfo.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) + return B_BAD_VALUE; + + // We can't help it, here's a small but relatively harmless race condition, + // since a debugger could be installed in the meantime. The worst case is + // that we install a break/watchpoint the debugger doesn't know about. + + // set the break/watchpoint + if (watchpoint) + return arch_set_watchpoint(address, type, length); + else + return arch_set_breakpoint(address); +} + + +status_t +_user_clear_debugger_breakpoint(void *address, bool watchpoint) +{ + // check the address + if (address == NULL || !IS_USER_ADDRESS(address)) + return B_BAD_ADDRESS; + + // check whether a debugger is installed already + team_debug_info teamDebugInfo; + get_team_debug_info(teamDebugInfo); + if (teamDebugInfo.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) + return B_BAD_VALUE; + + // We can't help it, here's a small but relatively harmless race condition, + // since a debugger could be installed in the meantime. The worst case is + // that we clear a break/watchpoint the debugger has just installed. + + // clear the break/watchpoint + if (watchpoint) + return arch_clear_watchpoint(address); + else + return arch_clear_breakpoint(address); +} diff --git a/src/system/libroot/os/debug.c b/src/system/libroot/os/debug.c index ced7da95e1..9041c6a48a 100644 --- a/src/system/libroot/os/debug.c +++ b/src/system/libroot/os/debug.c @@ -114,6 +114,34 @@ wait_for_debugger(void) } +status_t +set_debugger_breakpoint(void *address) +{ + return _kern_set_debugger_breakpoint(address, 0, 0, false); +} + + +status_t +clear_debugger_breakpoint(void *address) +{ + return _kern_clear_debugger_breakpoint(address, false); +} + + +status_t +set_debugger_watchpoint(void *address, uint32 type, int32 length) +{ + return _kern_set_debugger_breakpoint(address, type, length, true); +} + + +status_t +clear_debugger_watchpoint(void *address) +{ + return _kern_clear_debugger_breakpoint(address, true); +} + + static void get_debug_string(const debug_string_entry *stringEntries, const char *defaultString, uint32 code, char *buffer, int32 bufferSize)