* 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
|
#define THREAD_FLAGS_DEBUG_THREAD 0x0002
|
||||||
// forces the thread into the debugger as soon as possible (set by
|
// forces the thread into the debugger as soon as possible (set by
|
||||||
// debug_thread())
|
// 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
|
// a debugger is installed for the current team (computed flag for
|
||||||
// optimization purposes)
|
// 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
|
// hardware breakpoints are defined for the current team (computed flag for
|
||||||
// optimization purposes)
|
// 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
|
// breakpoints are currently installed for the thread (i.e. the hardware is
|
||||||
// actually set up to trigger debug events for them)
|
// 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
|
// 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
|
// 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())
|
// 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
|
// force syscall restart, even if a signal handler without SA_RESTART was
|
||||||
// invoked (e.g. sigwait())
|
// invoked (e.g. sigwait())
|
||||||
#define THREAD_FLAGS_SYSCALL_RESTARTED 0x0200
|
#define THREAD_FLAGS_SYSCALL_RESTARTED 0x0400
|
||||||
// the current syscall has been restarted
|
// 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
|
// the thread is currently in a syscall; set/reset only for certain
|
||||||
// functions (e.g. ioctl()) to allow inner functions to discriminate
|
// functions (e.g. ioctl()) to allow inner functions to discriminate
|
||||||
// whether e.g. parameters were passed from userland or kernel
|
// whether e.g. parameters were passed from userland or kernel
|
||||||
|
|||||||
@@ -98,8 +98,9 @@
|
|||||||
original eax/edx values */ \
|
original eax/edx values */ \
|
||||||
iret
|
iret
|
||||||
|
|
||||||
#define DISABLE_BREAKPOINTS() \
|
#define STOP_USER_DEBUGGING() \
|
||||||
testl $THREAD_FLAGS_BREAKPOINTS_INSTALLED, THREAD_flags(%edi); \
|
testl $(THREAD_FLAGS_BREAKPOINTS_INSTALLED \
|
||||||
|
| THREAD_FLAGS_SINGLE_STEP), THREAD_flags(%edi); \
|
||||||
jz 1f; \
|
jz 1f; \
|
||||||
call x86_exit_user_debug_at_kernel_entry; \
|
call x86_exit_user_debug_at_kernel_entry; \
|
||||||
1:
|
1:
|
||||||
@@ -521,7 +522,7 @@ STATIC_FUNCTION(int_bottom_user):
|
|||||||
// disable breakpoints, if installed
|
// disable breakpoints, if installed
|
||||||
movl %dr3, %edi // thread pointer
|
movl %dr3, %edi // thread pointer
|
||||||
cli // disable interrupts
|
cli // disable interrupts
|
||||||
DISABLE_BREAKPOINTS()
|
STOP_USER_DEBUGGING()
|
||||||
|
|
||||||
// update the thread's user time
|
// update the thread's user time
|
||||||
UPDATE_THREAD_USER_TIME()
|
UPDATE_THREAD_USER_TIME()
|
||||||
@@ -615,7 +616,7 @@ STATIC_FUNCTION(handle_syscall):
|
|||||||
|
|
||||||
// disable breakpoints, if installed
|
// disable breakpoints, if installed
|
||||||
cli // disable interrupts
|
cli // disable interrupts
|
||||||
DISABLE_BREAKPOINTS()
|
STOP_USER_DEBUGGING()
|
||||||
|
|
||||||
// update the thread's user time
|
// update the thread's user time
|
||||||
UPDATE_THREAD_USER_TIME_PUSH_TIME()
|
UPDATE_THREAD_USER_TIME_PUSH_TIME()
|
||||||
|
|||||||
@@ -571,7 +571,8 @@ arch_update_thread_single_step()
|
|||||||
if (struct iframe* frame = i386_get_user_iframe()) {
|
if (struct iframe* frame = i386_get_user_iframe()) {
|
||||||
struct thread* thread = thread_get_current_thread();
|
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)
|
if (thread->debug_info.flags & B_THREAD_DEBUG_SINGLE_STEP)
|
||||||
frame->flags |= (1 << X86_EFLAGS_TF);
|
frame->flags |= (1 << X86_EFLAGS_TF);
|
||||||
else
|
else
|
||||||
@@ -803,15 +804,17 @@ x86_exit_user_debug_at_kernel_entry()
|
|||||||
{
|
{
|
||||||
struct thread *thread = thread_get_current_thread();
|
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,
|
// 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
|
// 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 %%dr6, %0" : "=r"(thread->cpu->arch.dr6));
|
||||||
asm("movl %%dr7, %0" : "=r"(thread->cpu->arch.dr7));
|
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();
|
GRAB_THREAD_LOCK();
|
||||||
|
|
||||||
// disable user breakpoints
|
// disable user breakpoints
|
||||||
@@ -910,7 +913,7 @@ x86_handle_debug_exception(struct iframe *frame)
|
|||||||
if (thread->team != team_get_kernel_team()
|
if (thread->team != team_get_kernel_team()
|
||||||
&& i386_get_user_iframe() == NULL) {
|
&& i386_get_user_iframe() == NULL) {
|
||||||
// TODO: This is not yet fully correct, since a newly created
|
// 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.
|
// property.
|
||||||
inKernel = false;
|
inKernel = false;
|
||||||
}
|
}
|
||||||
@@ -937,6 +940,9 @@ x86_handle_debug_exception(struct iframe *frame)
|
|||||||
atomic_or(&thread->debug_info.flags,
|
atomic_or(&thread->debug_info.flags,
|
||||||
B_THREAD_DEBUG_NOTIFY_SINGLE_STEP
|
B_THREAD_DEBUG_NOTIFY_SINGLE_STEP
|
||||||
| B_THREAD_DEBUG_STOP);
|
| 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) {
|
if (singleStep) {
|
||||||
atomic_or(&thread->debug_info.flags,
|
atomic_or(&thread->debug_info.flags,
|
||||||
B_THREAD_DEBUG_SINGLE_STEP);
|
B_THREAD_DEBUG_SINGLE_STEP);
|
||||||
|
atomic_or(&thread->flags, THREAD_FLAGS_SINGLE_STEP);
|
||||||
} else {
|
} else {
|
||||||
atomic_and(&thread->debug_info.flags,
|
atomic_and(&thread->debug_info.flags,
|
||||||
~B_THREAD_DEBUG_SINGLE_STEP);
|
~(int32)B_THREAD_DEBUG_SINGLE_STEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unset the "stopped" state
|
// unset the "stopped" state
|
||||||
@@ -1282,6 +1283,10 @@ user_debug_watchpoint_hit()
|
|||||||
void
|
void
|
||||||
user_debug_single_stepped()
|
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
|
// prepare the message
|
||||||
debug_single_step message;
|
debug_single_step message;
|
||||||
arch_get_debug_cpu_state(&message.cpu_state);
|
arch_get_debug_cpu_state(&message.cpu_state);
|
||||||
|
|||||||
Reference in New Issue
Block a user