From 8b3d3d8a1574eed1d59d052651fefa9af6b1fa6d Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 25 Feb 2010 20:20:16 +0000 Subject: [PATCH] Correctly handle cases when a thread single-steps into the kernel as it can happen on syscalls or "int" instructions. The debug exception handler sets the thread debug flags B_THREAD_DEBUG_STOP and B_THREAD_DEBUG_NOTIFY_SINGLE_STEP (new) and lets the thread continue. Before leaving the kernel the thread is stopped and a single-step notification is sent. Fixes #3487. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35620 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/user_debugger.h | 17 +++++---- .../kernel/arch/x86/arch_user_debugger.cpp | 37 ++++++++++++++++++- src/system/kernel/debug/user_debugger.cpp | 22 +++++++++-- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/headers/private/kernel/user_debugger.h b/headers/private/kernel/user_debugger.h index dc95f36edf..43d5e9d977 100644 --- a/headers/private/kernel/user_debugger.h +++ b/headers/private/kernel/user_debugger.h @@ -154,17 +154,18 @@ enum { // thread debugging flags (user-specifiable flags are in ) enum { - B_THREAD_DEBUG_INITIALIZED = 0x0001, - B_THREAD_DEBUG_DYING = 0x0002, - B_THREAD_DEBUG_STOP = 0x0004, - B_THREAD_DEBUG_STOPPED = 0x0008, - B_THREAD_DEBUG_SINGLE_STEP = 0x0010, + B_THREAD_DEBUG_INITIALIZED = 0x0001, + B_THREAD_DEBUG_DYING = 0x0002, + B_THREAD_DEBUG_STOP = 0x0004, + B_THREAD_DEBUG_STOPPED = 0x0008, + B_THREAD_DEBUG_SINGLE_STEP = 0x0010, + B_THREAD_DEBUG_NOTIFY_SINGLE_STEP = 0x0020, - B_THREAD_DEBUG_NUB_THREAD = 0x0020, // marks the nub thread + B_THREAD_DEBUG_NUB_THREAD = 0x0040, // marks the nub thread - B_THREAD_DEBUG_KERNEL_FLAG_MASK = 0xffff, + B_THREAD_DEBUG_KERNEL_FLAG_MASK = 0xffff, - B_THREAD_DEBUG_DEFAULT_FLAGS = 0, + B_THREAD_DEBUG_DEFAULT_FLAGS = 0, }; // messages sent from the debug nub thread to a debugged thread diff --git a/src/system/kernel/arch/x86/arch_user_debugger.cpp b/src/system/kernel/arch/x86/arch_user_debugger.cpp index f8dc9fa7b0..2c784069a4 100644 --- a/src/system/kernel/arch/x86/arch_user_debugger.cpp +++ b/src/system/kernel/arch/x86/arch_user_debugger.cpp @@ -886,7 +886,42 @@ x86_handle_debug_exception(struct iframe *frame) // it, but we don't want it when continuing otherwise. frame->flags &= ~(1 << X86_EFLAGS_TF); - panic("kernel single step"); + // Determine whether the exception occurred at a syscall/trap + // kernel entry or whether this is genuine kernel single-stepping. + bool inKernel = true; + struct thread* thread = thread_get_current_thread(); + 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 + // property. + inKernel = false; + } + + if (inKernel) { + panic("kernel single step"); + } else { + // The thread is a userland thread and it just entered the + // kernel when the single-step exception occurred. This happens + // e.g. when sysenter is called with single-stepping enabled. + // We need to ignore the exception now and send a single-step + // notification later, when the thread wants to return from the + // kernel. + InterruptsSpinLocker threadLocker(gThreadSpinlock); + + // Check whether the team is still being debugged and set + // the B_THREAD_DEBUG_NOTIFY_SINGLE_STEP and + // B_THREAD_DEBUG_STOP flags, so that the thread will be + // stopped when it is going to leave the kernel and notify the + // debugger about the single-step event. + int32 teamDebugFlags + = atomic_get(&thread->team->debug_info.flags); + if (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) { + atomic_or(&thread->debug_info.flags, + B_THREAD_DEBUG_NOTIFY_SINGLE_STEP + | B_THREAD_DEBUG_STOP); + } + } } } else if (dr6 & (1 << X86_DR6_BT)) { // task switch diff --git a/src/system/kernel/debug/user_debugger.cpp b/src/system/kernel/debug/user_debugger.cpp index 26fc94dacc..f77693a71e 100644 --- a/src/system/kernel/debug/user_debugger.cpp +++ b/src/system/kernel/debug/user_debugger.cpp @@ -954,11 +954,25 @@ user_debug_handle_signal(int signal, struct sigaction *handler, bool deadly) void user_debug_stop_thread() { - // prepare the message - debug_thread_debugged message; + // check whether this is actually an emulated single-step notification + InterruptsSpinLocker threadsLocker(gThreadSpinlock); + struct thread* thread = thread_get_current_thread(); + bool singleStepped = false; + if ((atomic_and(&thread->debug_info.flags, + ~B_THREAD_DEBUG_NOTIFY_SINGLE_STEP) + & B_THREAD_DEBUG_NOTIFY_SINGLE_STEP) != 0) { + singleStepped = true; + } - thread_hit_serious_debug_event(B_DEBUGGER_MESSAGE_THREAD_DEBUGGED, &message, - sizeof(message)); + threadsLocker.Unlock(); + + if (singleStepped) { + user_debug_single_stepped(); + } else { + debug_thread_debugged message; + thread_hit_serious_debug_event(B_DEBUGGER_MESSAGE_THREAD_DEBUGGED, + &message, sizeof(message)); + } }