* 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
This commit is contained in:
Ingo Weinhold
2010-10-30 11:31:01 +00:00
parent ee7817042b
commit 13b81a3b8e
4 changed files with 34 additions and 20 deletions
+11 -9
View File
@@ -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
+5 -4
View File
@@ -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()
@@ -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);
}
}
}
+6 -1
View File
@@ -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);