* 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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user