[Haiku-commits] r31045 - in haiku/trunk: headers/private/kernel headers/private/kernel/arch headers/private/kernel/arch/x86 headers/private/system src/system/kernel/arch/m68k src/system/kernel/arch/ppc src/system/kernel/arch/x86 src/system/kernel/debug
bonefish at mail.berlios.de
bonefish at mail.berlios.de
Sun Jun 14 14:14:12 CEST 2009
Author: bonefish
Date: 2009-06-14 14:14:06 +0200 (Sun, 14 Jun 2009)
New Revision: 31045
ViewCVS: http://svn.berlios.de/viewcvs/haiku?rev=31045&view=rev
Modified:
haiku/trunk/headers/private/kernel/arch/user_debugger.h
haiku/trunk/headers/private/kernel/arch/x86/arch_thread.h
haiku/trunk/headers/private/kernel/user_debugger.h
haiku/trunk/headers/private/system/syscalls.h
haiku/trunk/src/system/kernel/arch/m68k/arch_user_debugger.cpp
haiku/trunk/src/system/kernel/arch/ppc/arch_user_debugger.cpp
haiku/trunk/src/system/kernel/arch/x86/arch_interrupts.S
haiku/trunk/src/system/kernel/arch/x86/arch_thread.cpp
haiku/trunk/src/system/kernel/arch/x86/arch_user_debugger.cpp
haiku/trunk/src/system/kernel/debug/user_debugger.cpp
Log:
User debugger support:
* Generalized address checks. The debugger can now also read the commpage.
* Added new syscall _kern_get_thread_cpu_state() to get the CPU state of a
not running thread. Introduced arch_get_thread_debug_cpu_state() for that
purpose, which is only implemented for x86 ATM (uses the new
i386_get_thread_user_iframe()).
* Don't allow a debugger to change a thread's "esp" anymore. That's the esp
register in the kernel. "user_esp" can still be changed.
* Generally set RF (resume flag) in eflags in interrupt handlers, not only
after a instruction breakpoint debug exception. This should prevent
breakpoints from being triggered more than once (e.g. when the breakpoint is
on an instruction that can cause a page fault). I still saw those with bdb
in VMware, but that might be a VMware bug.
Modified: haiku/trunk/headers/private/kernel/arch/user_debugger.h
===================================================================
--- haiku/trunk/headers/private/kernel/arch/user_debugger.h 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/headers/private/kernel/arch/user_debugger.h 2009-06-14 12:14:06 UTC (rev 31045)
@@ -17,6 +17,7 @@
struct arch_team_debug_info;
struct arch_thread_debug_info;
+struct thread;
void arch_clear_team_debug_info(struct arch_team_debug_info *info);
void arch_destroy_team_debug_info(struct arch_team_debug_info *info);
@@ -27,6 +28,8 @@
void arch_set_debug_cpu_state(const struct debug_cpu_state *cpuState);
void arch_get_debug_cpu_state(struct debug_cpu_state *cpuState);
+status_t arch_get_thread_debug_cpu_state(struct thread *thread,
+ struct debug_cpu_state *cpuState);
status_t arch_set_breakpoint(void *address);
status_t arch_clear_breakpoint(void *address);
Modified: haiku/trunk/headers/private/kernel/arch/x86/arch_thread.h
===================================================================
--- haiku/trunk/headers/private/kernel/arch/x86/arch_thread.h 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/headers/private/kernel/arch/x86/arch_thread.h 2009-06-14 12:14:06 UTC (rev 31045)
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2005, The Haiku Team. All rights reserved.
+ * Copyright 2002-2009, The Haiku Team. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2002, Travis Geiselbrecht. All rights reserved.
@@ -18,6 +18,8 @@
struct iframe *i386_get_user_iframe(void);
struct iframe *i386_get_current_iframe(void);
+struct iframe *i386_get_thread_user_iframe(struct thread *thread);
+
void *x86_next_page_directory(struct thread *from, struct thread *to);
void x86_restart_syscall(struct iframe* frame);
Modified: haiku/trunk/headers/private/kernel/user_debugger.h
===================================================================
--- haiku/trunk/headers/private/kernel/user_debugger.h 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/headers/private/kernel/user_debugger.h 2009-06-14 12:14:06 UTC (rev 31045)
@@ -240,6 +240,8 @@
port_id _user_install_team_debugger(team_id team, port_id debuggerPort);
status_t _user_remove_team_debugger(team_id team);
status_t _user_debug_thread(thread_id thread);
+status_t _user_get_thread_cpu_state(thread_id thread,
+ struct debug_cpu_state *cpuState);
void _user_wait_for_debugger(void);
status_t _user_set_debugger_breakpoint(void *address, uint32 type,
Modified: haiku/trunk/headers/private/system/syscalls.h
===================================================================
--- haiku/trunk/headers/private/system/syscalls.h 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/headers/private/system/syscalls.h 2009-06-14 12:14:06 UTC (rev 31045)
@@ -19,6 +19,7 @@
extern "C" {
#endif
+struct debug_cpu_state;
struct dirent;
struct Elf32_Sym;
struct fd_info;
@@ -384,6 +385,8 @@
port_id debuggerPort);
extern status_t _kern_remove_team_debugger(team_id team);
extern status_t _kern_debug_thread(thread_id thread);
+extern status_t _kern_get_thread_cpu_state(thread_id threadID,
+ struct debug_cpu_state *userCPUState);
extern void _kern_wait_for_debugger(void);
extern status_t _kern_set_debugger_breakpoint(void *address, uint32 type,
Modified: haiku/trunk/src/system/kernel/arch/m68k/arch_user_debugger.cpp
===================================================================
--- haiku/trunk/src/system/kernel/arch/m68k/arch_user_debugger.cpp 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/src/system/kernel/arch/m68k/arch_user_debugger.cpp 2009-06-14 12:14:06 UTC (rev 31045)
@@ -45,14 +45,14 @@
{
if (struct iframe* frame = m68k_get_user_iframe()) {
struct thread* thread = thread_get_current_thread();
-
+
// set/clear T1 in SR depending on if single stepping is desired
// T1 T0
// 0 0 no tracing
// 0 1 trace on flow
// 1 0 single step
// 1 1 undef
- // note 060 and 020(?) only have T1 bit,
+ // note 060 and 020(?) only have T1 bit,
// but this should be compatible as well.
if (thread->debug_info.flags & B_THREAD_DEBUG_SINGLE_STEP) {
frame->cpu.sr &= ~(M68K_SR_T_MASK);
@@ -77,6 +77,14 @@
status_t
+arch_get_thread_debug_cpu_state(struct thread *thread,
+ struct debug_cpu_state *cpuState)
+{
+ return B_UNSUPPORTED;
+}
+
+
+status_t
arch_set_breakpoint(void *address)
{
return B_ERROR;
Modified: haiku/trunk/src/system/kernel/arch/ppc/arch_user_debugger.cpp
===================================================================
--- haiku/trunk/src/system/kernel/arch/ppc/arch_user_debugger.cpp 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/src/system/kernel/arch/ppc/arch_user_debugger.cpp 2009-06-14 12:14:06 UTC (rev 31045)
@@ -55,6 +55,14 @@
status_t
+arch_get_thread_debug_cpu_state(struct thread *thread,
+ struct debug_cpu_state *cpuState)
+{
+ return B_UNSUPPORTED;
+}
+
+
+status_t
arch_set_breakpoint(void *address)
{
return B_ERROR;
Modified: haiku/trunk/src/system/kernel/arch/x86/arch_interrupts.S
===================================================================
--- haiku/trunk/src/system/kernel/arch/x86/arch_interrupts.S 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/src/system/kernel/arch/x86/arch_interrupts.S 2009-06-14 12:14:06 UTC (rev 31045)
@@ -253,6 +253,11 @@
movl %esp, %ebp // frame pointer is the iframe
+ // Set the RF (resume flag) in EFLAGS. This prevents an instruction
+ // breakpoint on the instruction we're returning to to trigger a debug
+ // exception.
+ orl $0x10000, IFRAME_flags(%ebp);
+
testl $0x20000, IFRAME_flags(%ebp) // VM86 mode
jnz int_bottom_vm86
cmp $USER_CODE_SEG, IFRAME_cs(%ebp) // user mode
Modified: haiku/trunk/src/system/kernel/arch/x86/arch_thread.cpp
===================================================================
--- haiku/trunk/src/system/kernel/arch/x86/arch_thread.cpp 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/src/system/kernel/arch/x86/arch_thread.cpp 2009-06-14 12:14:06 UTC (rev 31045)
@@ -90,10 +90,8 @@
static struct iframe *
-find_previous_iframe(addr_t frame)
+find_previous_iframe(struct thread *thread, addr_t frame)
{
- struct thread *thread = thread_get_current_thread();
-
// iterate backwards through the stack frames, until we hit an iframe
while (frame >= thread->kernel_stack_base
&& frame < thread->kernel_stack_top) {
@@ -117,7 +115,7 @@
if (frame == NULL)
return NULL;
- return find_previous_iframe(frame->ebp);
+ return find_previous_iframe(thread_get_current_thread(), frame->ebp);
}
@@ -130,7 +128,7 @@
static struct iframe*
get_current_iframe(void)
{
- return find_previous_iframe(x86_read_ebp());
+ return find_previous_iframe(thread_get_current_thread(), x86_read_ebp());
}
@@ -156,12 +154,38 @@
}
+/*! \brief Like i386_get_user_iframe(), just for the given thread.
+ The thread must not be running and the threads spinlock must be held.
+*/
struct iframe *
+i386_get_thread_user_iframe(struct thread *thread)
+{
+ if (thread->state == B_THREAD_RUNNING)
+ return NULL;
+
+ // read %ebp from the thread's stack stored by a pushad
+ addr_t ebp = thread->arch_info.current_stack.esp[2];
+
+ // find the user iframe
+ struct iframe *frame = find_previous_iframe(thread, ebp);
+
+ while (frame != NULL) {
+ if (IFRAME_IS_USER(frame))
+ return frame;
+ frame = get_previous_iframe(frame);
+ }
+
+ return NULL;
+}
+
+
+struct iframe *
i386_get_current_iframe(void)
{
return get_current_iframe();
}
+
void *
x86_next_page_directory(struct thread *from, struct thread *to)
{
Modified: haiku/trunk/src/system/kernel/arch/x86/arch_user_debugger.cpp
===================================================================
--- haiku/trunk/src/system/kernel/arch/x86/arch_user_debugger.cpp 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/src/system/kernel/arch/x86/arch_user_debugger.cpp 2009-06-14 12:14:06 UTC (rev 31045)
@@ -1,5 +1,5 @@
/*
- * Copyright 2005, Ingo Weinhold, bonefish at users.sf.net.
+ * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold at gmx.de.
* Distributed under the terms of the MIT License.
*/
@@ -60,6 +60,31 @@
static bool sQEmuSingleStepHack = false;
+static void
+get_iframe_registers(struct iframe *frame, struct debug_cpu_state *cpuState)
+{
+ cpuState->gs = frame->gs;
+ cpuState->fs = frame->fs;
+ cpuState->es = frame->es;
+ cpuState->ds = frame->ds;
+ cpuState->edi = frame->edi;
+ cpuState->esi = frame->esi;
+ cpuState->ebp = frame->ebp;
+ cpuState->esp = frame->esp;
+ cpuState->ebx = frame->ebx;
+ cpuState->edx = frame->orig_edx;
+ cpuState->ecx = frame->ecx;
+ cpuState->eax = frame->orig_eax;
+ cpuState->vector = frame->vector;
+ cpuState->error_code = frame->error_code;
+ cpuState->eip = frame->eip;
+ cpuState->cs = frame->cs;
+ cpuState->eflags = frame->flags;
+ cpuState->user_esp = frame->user_esp;
+ cpuState->user_ss = frame->user_ss;
+}
+
+
static inline void
install_breakpoints(const arch_team_debug_info &teamInfo)
{
@@ -562,7 +587,7 @@
frame->edi = cpuState->edi;
frame->esi = cpuState->esi;
frame->ebp = cpuState->ebp;
- frame->esp = cpuState->esp;
+// frame->esp = cpuState->esp;
frame->ebx = cpuState->ebx;
frame->edx = cpuState->edx;
frame->ecx = cpuState->ecx;
@@ -586,31 +611,31 @@
i386_fnsave(cpuState->extended_regs);
// For this to be correct the calling function must not use these
// registers (not even indirectly).
-
- cpuState->gs = frame->gs;
- cpuState->fs = frame->fs;
- cpuState->es = frame->es;
- cpuState->ds = frame->ds;
- cpuState->edi = frame->edi;
- cpuState->esi = frame->esi;
- cpuState->ebp = frame->ebp;
- cpuState->esp = frame->esp;
- cpuState->ebx = frame->ebx;
- cpuState->edx = frame->orig_edx;
- cpuState->ecx = frame->ecx;
- cpuState->eax = frame->orig_eax;
- cpuState->vector = frame->vector;
- cpuState->error_code = frame->error_code;
- cpuState->eip = frame->eip;
- cpuState->cs = frame->cs;
- cpuState->eflags = frame->flags;
- cpuState->user_esp = frame->user_esp;
- cpuState->user_ss = frame->user_ss;
+ get_iframe_registers(frame, cpuState);
}
}
+/*! \brief Returns the CPU state for the given thread.
+ The thread must not be running and the threads spinlock must be held.
+*/
status_t
+arch_get_thread_debug_cpu_state(struct thread *thread,
+ struct debug_cpu_state *cpuState)
+{
+ struct iframe *frame = i386_get_thread_user_iframe(thread);
+ if (frame == NULL)
+ return B_BAD_VALUE;
+
+ get_iframe_registers(frame, cpuState);
+ memcpy(cpuState->extended_regs, thread->arch_info.fpu_state,
+ sizeof(cpuState->extended_regs));
+
+ return B_OK;
+}
+
+
+status_t
arch_set_breakpoint(void *address)
{
return set_breakpoint(address, X86_INSTRUCTION_BREAKPOINT,
@@ -801,15 +826,9 @@
bool watchpoint = true;
for (int32 i = 0; i < X86_BREAKPOINT_COUNT; i++) {
if (dr6 & (1 << sDR6B[i])) {
- // If it is an instruction breakpoint, we need to set RF in
- // EFLAGS to prevent triggering the same exception
- // again (breakpoint instructions are triggered *before*
- // executing the instruction).
uint32 type = (dr7 >> sDR7RW[i]) & 0x3;
- if (type == X86_INSTRUCTION_BREAKPOINT) {
- frame->flags |= (1 << X86_EFLAGS_RF);
+ if (type == X86_INSTRUCTION_BREAKPOINT)
watchpoint = false;
- }
}
}
Modified: haiku/trunk/src/system/kernel/debug/user_debugger.cpp
===================================================================
--- haiku/trunk/src/system/kernel/debug/user_debugger.cpp 2009-06-14 11:55:24 UTC (rev 31044)
+++ haiku/trunk/src/system/kernel/debug/user_debugger.cpp 2009-06-14 12:14:06 UTC (rev 31045)
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2008, Ingo Weinhold, ingo_weinhold at gmx.de.
+ * Copyright 2005-2009, Ingo Weinhold, ingo_weinhold at gmx.de.
* Distributed under the terms of the MIT License.
*/
@@ -12,6 +12,7 @@
#include <arch/debug.h>
#include <arch/user_debugger.h>
+#include <commpage_defs.h>
#include <cpu.h>
#include <debugger.h>
#include <kernel.h>
@@ -1412,6 +1413,26 @@
}
+/*! \brief Returns whether the given address can be accessed in principle.
+ No check whether there's an actually accessible area is performed, though.
+*/
+static bool
+can_access_address(const void* address, bool write)
+{
+ // user addresses are always fine
+ if (IS_USER_ADDRESS(address))
+ return true;
+
+ // a commpage address can at least be read
+ if ((addr_t)address >= USER_COMMPAGE_ADDR
+ && (addr_t)address < USER_COMMPAGE_ADDR + COMMPAGE_SIZE) {
+ return !write;
+ }
+
+ return false;
+}
+
+
/** \brief Reads data from user memory.
*
* Tries to read \a size bytes of data from user memory address \a address
@@ -1435,7 +1456,7 @@
char *buffer = (char*)_buffer;
// check the parameters
- if (!IS_USER_ADDRESS(address))
+ if (!can_access_address(address, false))
return B_BAD_ADDRESS;
if (size <= 0)
return B_BAD_VALUE;
@@ -1446,7 +1467,7 @@
bytesRead = 0;
while (size > 0) {
// check whether we're still in user address space
- if (!IS_USER_ADDRESS(address)) {
+ if (!can_access_address(address, false)) {
error = B_BAD_ADDRESS;
break;
}
@@ -1486,7 +1507,7 @@
const char *buffer = (const char*)_buffer;
// check the parameters
- if (!IS_USER_ADDRESS(address))
+ if (!can_access_address(address, true))
return B_BAD_ADDRESS;
if (size <= 0)
return B_BAD_VALUE;
@@ -1497,7 +1518,7 @@
bytesWritten = 0;
while (size > 0) {
// check whether we're still in user address space
- if (!IS_USER_ADDRESS(address)) {
+ if (!can_access_address(address, true)) {
error = B_BAD_ADDRESS;
break;
}
@@ -1589,7 +1610,7 @@
else if (thread->debug_info.flags & B_THREAD_DEBUG_STOPPED)
threadDebugPort = thread->debug_info.debug_port;
else
- result = B_BAD_VALUE;
+ result = B_BAD_THREAD_STATE;
} else
result = B_BAD_THREAD_ID;
@@ -1677,7 +1698,7 @@
status_t result = B_OK;
// check the parameters
- if (!IS_USER_ADDRESS(address))
+ if (!can_access_address(address, false))
result = B_BAD_ADDRESS;
else if (size <= 0 || size > B_MAX_READ_WRITE_MEMORY_SIZE)
result = B_BAD_VALUE;
@@ -1713,7 +1734,7 @@
status_t result = B_OK;
// check the parameters
- if (!IS_USER_ADDRESS(address))
+ if (!can_access_address(address, true))
result = B_BAD_ADDRESS;
else if (size <= 0 || size > realSize)
result = B_BAD_VALUE;
@@ -1897,7 +1918,7 @@
// check the address
status_t result = B_OK;
- if (address == NULL || !IS_USER_ADDRESS(address))
+ if (address == NULL || !can_access_address(address, false))
result = B_BAD_ADDRESS;
// set the breakpoint
@@ -1925,7 +1946,7 @@
// check the address
status_t result = B_OK;
- if (address == NULL || !IS_USER_ADDRESS(address))
+ if (address == NULL || !can_access_address(address, false))
result = B_BAD_ADDRESS;
// clear the breakpoint
@@ -1952,7 +1973,7 @@
// check the address and size
status_t result = B_OK;
- if (address == NULL || !IS_USER_ADDRESS(address))
+ if (address == NULL || !can_access_address(address, false))
result = B_BAD_ADDRESS;
if (length < 0)
result = B_BAD_VALUE;
@@ -1982,7 +2003,7 @@
// check the address
status_t result = B_OK;
- if (address == NULL || !IS_USER_ADDRESS(address))
+ if (address == NULL || !can_access_address(address, false))
result = B_BAD_ADDRESS;
// clear the watchpoint
@@ -2928,6 +2949,49 @@
}
+status_t
+_user_get_thread_cpu_state(thread_id threadID,
+ struct debug_cpu_state *userCPUState)
+{
+ TRACE(("[%ld] _user_get_thread_cpu_state(%ld, %p)\n", find_thread(NULL),
+ threadID, userCPUState));
+
+ if (userCPUState == NULL || !IS_USER_ADDRESS(userCPUState))
+ return B_BAD_ADDRESS;
+
+ InterruptsSpinLocker locker(gThreadSpinlock);
+
+ // get and check the thread
+ struct thread *thread = thread_get_thread_struct_locked(threadID);
+ if (thread == NULL) {
+ // thread doesn't exist any longer
+ return B_BAD_THREAD_ID;
+ } else if (thread->team == team_get_kernel_team()) {
+ // we can't debug the kernel team
+ return B_NOT_ALLOWED;
+ } else if (thread->debug_info.flags & B_THREAD_DEBUG_DYING) {
+ // the thread is already dying
+ return B_BAD_THREAD_ID;
+ } else if (thread->debug_info.flags & B_THREAD_DEBUG_NUB_THREAD) {
+ // don't play with the nub thread
+ return B_NOT_ALLOWED;
+ } else if (thread->state == B_THREAD_RUNNING) {
+ // thread is running -- no way to get its CPU state
+ return B_BAD_THREAD_STATE;
+ }
+
+ // get the CPU state
+ debug_cpu_state cpuState;
+ status_t error = arch_get_thread_debug_cpu_state(thread, &cpuState);
+ if (error != B_OK)
+ return error;
+
+ locker.Unlock();
+
+ return user_memcpy(userCPUState, &cpuState, sizeof(cpuState));
+}
+
+
void
_user_wait_for_debugger(void)
{
@@ -2942,7 +3006,7 @@
bool watchpoint)
{
// check the address and size
- if (address == NULL || !IS_USER_ADDRESS(address))
+ if (address == NULL || !can_access_address(address, false))
return B_BAD_ADDRESS;
if (watchpoint && length < 0)
return B_BAD_VALUE;
@@ -2975,7 +3039,7 @@
_user_clear_debugger_breakpoint(void *address, bool watchpoint)
{
// check the address
- if (address == NULL || !IS_USER_ADDRESS(address))
+ if (address == NULL || !can_access_address(address, false))
return B_BAD_ADDRESS;
// check whether a debugger is installed already
More information about the Haiku-commits
mailing list