[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