From 13b81a3b8eed9750171419427cf6be4a8822c25c Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 30 Oct 2010 11:31:01 +0000 Subject: [PATCH] * Added new thread flag THREAD_FLAGS_SINGLE_STEP, which is set to indicate that userland single-stepping is enabled for the thread. * x86_exit_user_debug_at_kernel_entry(): Always store DR6 and DR7 in the CPU structure, not only when breakpoints are installed. * x86_handle_debug_exception(): When encountering a syscall single-step, also set the THREAD_FLAGS_DEBUG_THREAD thread flag. Otherwise the B_THREAD_DEBUG_STOP would be ignored. * x86 interrupt handling, DISABLE_BREAKPOINTS(): - Renamed to STOP_USER_DEBUGGING(). - Now it also call x86_exit_user_debug_at_kernel_entry() when THREAD_FLAGS_SINGLE_STEP is set, so that the debug registers are saved. Fixes #6751. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39201 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/thread_types.h | 20 ++++++++++--------- src/system/kernel/arch/x86/arch_interrupts.S | 9 +++++---- .../kernel/arch/x86/arch_user_debugger.cpp | 18 +++++++++++------ src/system/kernel/debug/user_debugger.cpp | 7 ++++++- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/headers/private/kernel/thread_types.h b/headers/private/kernel/thread_types.h index 8b339f21f7..8d2dde6943 100644 --- a/headers/private/kernel/thread_types.h +++ b/headers/private/kernel/thread_types.h @@ -352,27 +352,29 @@ struct thread_queue { #define THREAD_FLAGS_DEBUG_THREAD 0x0002 // forces the thread into the debugger as soon as possible (set by // debug_thread()) -#define THREAD_FLAGS_DEBUGGER_INSTALLED 0x0004 +#define THREAD_FLAGS_SINGLE_STEP 0x0004 + // indicates that the thread is in single-step mode (in userland) +#define THREAD_FLAGS_DEBUGGER_INSTALLED 0x0008 // a debugger is installed for the current team (computed flag for // optimization purposes) -#define THREAD_FLAGS_BREAKPOINTS_DEFINED 0x0008 +#define THREAD_FLAGS_BREAKPOINTS_DEFINED 0x0010 // hardware breakpoints are defined for the current team (computed flag for // optimization purposes) -#define THREAD_FLAGS_BREAKPOINTS_INSTALLED 0x0010 +#define THREAD_FLAGS_BREAKPOINTS_INSTALLED 0x0020 // breakpoints are currently installed for the thread (i.e. the hardware is // actually set up to trigger debug events for them) -#define THREAD_FLAGS_64_BIT_SYSCALL_RETURN 0x0020 +#define THREAD_FLAGS_64_BIT_SYSCALL_RETURN 0x0040 // set by 64 bit return value syscalls -#define THREAD_FLAGS_RESTART_SYSCALL 0x0040 +#define THREAD_FLAGS_RESTART_SYSCALL 0x0080 // set by handle_signals(), if the current syscall shall be restarted -#define THREAD_FLAGS_DONT_RESTART_SYSCALL 0x0080 +#define THREAD_FLAGS_DONT_RESTART_SYSCALL 0x0100 // explicitly disables automatic syscall restarts (e.g. resume_thread()) -#define THREAD_FLAGS_ALWAYS_RESTART_SYSCALL 0x0100 +#define THREAD_FLAGS_ALWAYS_RESTART_SYSCALL 0x0200 // force syscall restart, even if a signal handler without SA_RESTART was // invoked (e.g. sigwait()) -#define THREAD_FLAGS_SYSCALL_RESTARTED 0x0200 +#define THREAD_FLAGS_SYSCALL_RESTARTED 0x0400 // the current syscall has been restarted -#define THREAD_FLAGS_SYSCALL 0x0400 +#define THREAD_FLAGS_SYSCALL 0x0800 // the thread is currently in a syscall; set/reset only for certain // functions (e.g. ioctl()) to allow inner functions to discriminate // whether e.g. parameters were passed from userland or kernel diff --git a/src/system/kernel/arch/x86/arch_interrupts.S b/src/system/kernel/arch/x86/arch_interrupts.S index a7edfc514d..ac496d63b0 100644 --- a/src/system/kernel/arch/x86/arch_interrupts.S +++ b/src/system/kernel/arch/x86/arch_interrupts.S @@ -98,8 +98,9 @@ original eax/edx values */ \ iret -#define DISABLE_BREAKPOINTS() \ - testl $THREAD_FLAGS_BREAKPOINTS_INSTALLED, THREAD_flags(%edi); \ +#define STOP_USER_DEBUGGING() \ + testl $(THREAD_FLAGS_BREAKPOINTS_INSTALLED \ + | THREAD_FLAGS_SINGLE_STEP), THREAD_flags(%edi); \ jz 1f; \ call x86_exit_user_debug_at_kernel_entry; \ 1: @@ -521,7 +522,7 @@ STATIC_FUNCTION(int_bottom_user): // disable breakpoints, if installed movl %dr3, %edi // thread pointer cli // disable interrupts - DISABLE_BREAKPOINTS() + STOP_USER_DEBUGGING() // update the thread's user time UPDATE_THREAD_USER_TIME() @@ -615,7 +616,7 @@ STATIC_FUNCTION(handle_syscall): // disable breakpoints, if installed cli // disable interrupts - DISABLE_BREAKPOINTS() + STOP_USER_DEBUGGING() // update the thread's user time UPDATE_THREAD_USER_TIME_PUSH_TIME() diff --git a/src/system/kernel/arch/x86/arch_user_debugger.cpp b/src/system/kernel/arch/x86/arch_user_debugger.cpp index d0862b24f4..d032d0b45d 100644 --- a/src/system/kernel/arch/x86/arch_user_debugger.cpp +++ b/src/system/kernel/arch/x86/arch_user_debugger.cpp @@ -571,7 +571,8 @@ arch_update_thread_single_step() if (struct iframe* frame = i386_get_user_iframe()) { struct thread* thread = thread_get_current_thread(); - // set/clear TF in EFLAGS depending on if single stepping is desired + // set/clear TF in EFLAGS depending on whether single stepping is + // desired if (thread->debug_info.flags & B_THREAD_DEBUG_SINGLE_STEP) frame->flags |= (1 << X86_EFLAGS_TF); else @@ -803,15 +804,17 @@ x86_exit_user_debug_at_kernel_entry() { struct thread *thread = thread_get_current_thread(); - if (!(thread->flags & THREAD_FLAGS_BREAKPOINTS_INSTALLED)) - return; - // We need to save the current values of dr6 and dr7 in the CPU structure, // since in case of a debug exception we might overwrite them before - // x86_handle_debug_exception() is called. + // x86_handle_debug_exception() is called. Debug exceptions occur when + // hitting a hardware break/watchpoint or when single-stepping. asm("movl %%dr6, %0" : "=r"(thread->cpu->arch.dr6)); asm("movl %%dr7, %0" : "=r"(thread->cpu->arch.dr7)); + // The remainder needs only be done, when user breakpoints are installed. + if (!(thread->flags & THREAD_FLAGS_BREAKPOINTS_INSTALLED)) + return; + GRAB_THREAD_LOCK(); // disable user breakpoints @@ -910,7 +913,7 @@ x86_handle_debug_exception(struct iframe *frame) if (thread->team != team_get_kernel_team() && i386_get_user_iframe() == NULL) { // TODO: This is not yet fully correct, since a newly created - // thread that doesn't have entered userland yet also has this + // thread that hasn't entered userland yet also has this // property. inKernel = false; } @@ -937,6 +940,9 @@ x86_handle_debug_exception(struct iframe *frame) atomic_or(&thread->debug_info.flags, B_THREAD_DEBUG_NOTIFY_SINGLE_STEP | B_THREAD_DEBUG_STOP); + + // also set the respective thread flag + atomic_or(&thread->flags, THREAD_FLAGS_DEBUG_THREAD); } } } diff --git a/src/system/kernel/debug/user_debugger.cpp b/src/system/kernel/debug/user_debugger.cpp index 1c39f4ca69..2ddd89262e 100644 --- a/src/system/kernel/debug/user_debugger.cpp +++ b/src/system/kernel/debug/user_debugger.cpp @@ -749,9 +749,10 @@ thread_hit_debug_event_internal(debug_debugger_message event, if (singleStep) { atomic_or(&thread->debug_info.flags, B_THREAD_DEBUG_SINGLE_STEP); + atomic_or(&thread->flags, THREAD_FLAGS_SINGLE_STEP); } else { atomic_and(&thread->debug_info.flags, - ~B_THREAD_DEBUG_SINGLE_STEP); + ~(int32)B_THREAD_DEBUG_SINGLE_STEP); } // unset the "stopped" state @@ -1282,6 +1283,10 @@ user_debug_watchpoint_hit() void user_debug_single_stepped() { + // clear the single-step thread flag + struct thread* thread = thread_get_current_thread(); + atomic_and(&thread->flags, ~(int32)THREAD_FLAGS_SINGLE_STEP); + // prepare the message debug_single_step message; arch_get_debug_cpu_state(&message.cpu_state);