* New locking strategy for the team debug info. It's no longer protected
by the team lock, but by a lock in the structure. This is necessary since we can't acquire the team lock while thread switching, for then the thread lock is already held. * Added support for single stepping and break-/watchpoints. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11503 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -9,16 +9,39 @@
|
|||||||
|
|
||||||
#include <debugger.h>
|
#include <debugger.h>
|
||||||
|
|
||||||
|
#include <arch/user_debugger.h>
|
||||||
|
|
||||||
|
// Team related debugging data.
|
||||||
|
//
|
||||||
|
// Locking policy:
|
||||||
|
// 1) When accessing the structure it must be made sure, that the structure,
|
||||||
|
// (i.e. the struct team it lives in) isn't deleted. Thus one either needs to
|
||||||
|
// acquire the global team lock, or one accesses the structure from a thread
|
||||||
|
// of that team.
|
||||||
|
// 2) Access to the `flags' field is atomically. Reading via atomic_get()
|
||||||
|
// requires no further locks (in addition to 1) that is). Writing requires
|
||||||
|
// `lock' being held and must be done atomically, too
|
||||||
|
// (atomic_{set,and,or}()). Reading with `lock' being held doesn't need to
|
||||||
|
// be done atomically.
|
||||||
|
// 3) Access to all other fields (read or write) requires `lock' being held.
|
||||||
|
//
|
||||||
struct team_debug_info {
|
struct team_debug_info {
|
||||||
|
spinlock lock;
|
||||||
|
// Guards the remaining fields. Should always be the innermost lock
|
||||||
|
// to be acquired/released.
|
||||||
|
|
||||||
int32 flags;
|
int32 flags;
|
||||||
// Set atomically. So reading atomically is OK, even when the team
|
// Set atomically. So reading atomically is OK, even when the team
|
||||||
// lock is not held (at least if it is certain, that the team struct
|
// lock is not held (at least if it is certain, that the team struct
|
||||||
// won't go).
|
// won't go).
|
||||||
|
|
||||||
team_id debugger_team;
|
team_id debugger_team;
|
||||||
port_id debugger_port;
|
port_id debugger_port;
|
||||||
thread_id nub_thread;
|
thread_id nub_thread;
|
||||||
port_id nub_port;
|
port_id nub_port;
|
||||||
// the port the nub thread is waiting on for commands from the debugger
|
// the port the nub thread is waiting on for commands from the debugger
|
||||||
|
|
||||||
|
struct arch_team_debug_info arch_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct thread_debug_info {
|
struct thread_debug_info {
|
||||||
@@ -30,6 +53,9 @@ struct thread_debug_info {
|
|||||||
// the port the thread is waiting on for commands from the nub thread
|
// the port the thread is waiting on for commands from the nub thread
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define GRAB_TEAM_DEBUG_INFO_LOCK(info) acquire_spinlock(&(info).lock)
|
||||||
|
#define RELEASE_TEAM_DEBUG_INFO_LOCK(info) release_spinlock(&(info).lock)
|
||||||
|
|
||||||
// team debugging flags (user-specifiable flags are in <debugger.h>)
|
// team debugging flags (user-specifiable flags are in <debugger.h>)
|
||||||
enum {
|
enum {
|
||||||
B_TEAM_DEBUG_DEBUGGER_INSTALLED = 0x0001,
|
B_TEAM_DEBUG_DEBUGGER_INSTALLED = 0x0001,
|
||||||
@@ -47,6 +73,7 @@ enum {
|
|||||||
B_THREAD_DEBUG_DYING = 0x0002,
|
B_THREAD_DEBUG_DYING = 0x0002,
|
||||||
B_THREAD_DEBUG_STOP = 0x0004,
|
B_THREAD_DEBUG_STOP = 0x0004,
|
||||||
B_THREAD_DEBUG_STOPPED = 0x0008,
|
B_THREAD_DEBUG_STOPPED = 0x0008,
|
||||||
|
B_THREAD_DEBUG_SINGLE_STEP = 0x0010,
|
||||||
|
|
||||||
B_THREAD_DEBUG_KERNEL_FLAG_MASK = 0xffff,
|
B_THREAD_DEBUG_KERNEL_FLAG_MASK = 0xffff,
|
||||||
|
|
||||||
@@ -57,14 +84,21 @@ enum {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
B_DEBUGGED_THREAD_MESSAGE_CONTINUE = 0,
|
B_DEBUGGED_THREAD_MESSAGE_CONTINUE = 0,
|
||||||
B_DEBUGGED_THREAD_GET_WHY_STOPPED,
|
B_DEBUGGED_THREAD_GET_WHY_STOPPED,
|
||||||
|
B_DEBUGGED_THREAD_SET_CPU_STATE,
|
||||||
} debugged_thread_message;
|
} debugged_thread_message;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32 handle_event;
|
uint32 handle_event;
|
||||||
} debugged_thread_run;
|
bool single_step;
|
||||||
|
} debugged_thread_continue;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
debug_cpu_state cpu_state;
|
||||||
|
} debugged_thread_set_cpu_state;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
debugged_thread_run run;
|
debugged_thread_continue continue_thread;
|
||||||
|
debugged_thread_set_cpu_state set_cpu_state;
|
||||||
} debugged_thread_message_data;
|
} debugged_thread_message_data;
|
||||||
|
|
||||||
|
|
||||||
@@ -74,7 +108,7 @@ extern "C" {
|
|||||||
|
|
||||||
// service calls
|
// service calls
|
||||||
|
|
||||||
void clear_team_debug_info(struct team_debug_info *info);
|
void clear_team_debug_info(struct team_debug_info *info, bool initLock);
|
||||||
void destroy_team_debug_info(struct team_debug_info *info);
|
void destroy_team_debug_info(struct team_debug_info *info);
|
||||||
|
|
||||||
void clear_thread_debug_info(struct thread_debug_info *info,
|
void clear_thread_debug_info(struct thread_debug_info *info,
|
||||||
|
|||||||
@@ -53,36 +53,44 @@ kill_interruptable_write_port(port_id port, int32 code, const void *buffer,
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The team lock must be held.
|
* For the first initialization the function must be called with \a initLock
|
||||||
|
* set to \c true. If it would be possible that another thread accesses the
|
||||||
|
* structure at the same time, `lock' must be held when calling the function.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
clear_team_debug_info(struct team_debug_info *info)
|
clear_team_debug_info(struct team_debug_info *info, bool initLock)
|
||||||
{
|
{
|
||||||
if (info) {
|
if (info) {
|
||||||
|
arch_clear_team_debug_info(&info->arch_info);
|
||||||
atomic_set(&info->flags, B_TEAM_DEBUG_DEFAULT_FLAGS);
|
atomic_set(&info->flags, B_TEAM_DEBUG_DEFAULT_FLAGS);
|
||||||
info->debugger_team = -1;
|
info->debugger_team = -1;
|
||||||
info->debugger_port = -1;
|
info->debugger_port = -1;
|
||||||
info->nub_thread = -1;
|
info->nub_thread = -1;
|
||||||
info->nub_port = -1;
|
info->nub_port = -1;
|
||||||
|
|
||||||
|
if (initLock)
|
||||||
|
info->lock = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The team lock must not be held. \a info must not be a member of a
|
* `lock' must not be held nor may interrupts be disabled.
|
||||||
* team struct (or the team struct not longer be accessible, i.e. the team
|
* \a info must not be a member of a team struct (or the team struct must no
|
||||||
* should already be removed).
|
* longer be accessible, i.e. the team should already be removed).
|
||||||
*
|
*
|
||||||
* In case the team is still accessible, the procedure is:
|
* In case the team is still accessible, the procedure is:
|
||||||
* 1. get the team lock
|
* 1. get `lock'
|
||||||
* 2. copy the team debug info on stack
|
* 2. copy the team debug info on stack
|
||||||
* 3. call clear_team_debug_info() on the team debug info
|
* 3. call clear_team_debug_info() on the team debug info
|
||||||
* 4. release the team lock
|
* 4. release `lock'
|
||||||
* 5. call destroy_team_debug_info() on the copied team debug info
|
* 5. call destroy_team_debug_info() on the copied team debug info
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
destroy_team_debug_info(struct team_debug_info *info)
|
destroy_team_debug_info(struct team_debug_info *info)
|
||||||
{
|
{
|
||||||
if (info) {
|
if (info) {
|
||||||
|
arch_destroy_team_debug_info(&info->arch_info);
|
||||||
|
|
||||||
// delete the nub port
|
// delete the nub port
|
||||||
if (info->nub_port >= 0) {
|
if (info->nub_port >= 0) {
|
||||||
set_port_owner(info->nub_port, B_CURRENT_TEAM);
|
set_port_owner(info->nub_port, B_CURRENT_TEAM);
|
||||||
@@ -129,17 +137,17 @@ destroy_thread_debug_info(struct thread_debug_info *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
get_team_debug_info(team_debug_info &teamDebugInfo)
|
get_team_debug_info(team_debug_info &teamDebugInfo)
|
||||||
{
|
{
|
||||||
struct thread *thread = thread_get_current_thread();
|
struct thread *thread = thread_get_current_thread();
|
||||||
|
|
||||||
cpu_status state = disable_interrupts();
|
cpu_status state = disable_interrupts();
|
||||||
GRAB_TEAM_LOCK();
|
GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
|
||||||
|
|
||||||
memcpy(&teamDebugInfo, &thread->team->debug_info, sizeof(team_debug_info));
|
memcpy(&teamDebugInfo, &thread->team->debug_info, sizeof(team_debug_info));
|
||||||
|
|
||||||
RELEASE_TEAM_LOCK();
|
RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,8 +203,8 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
|||||||
port_id debuggerPort = -1;
|
port_id debuggerPort = -1;
|
||||||
status_t error = B_OK;
|
status_t error = B_OK;
|
||||||
cpu_status state = disable_interrupts();
|
cpu_status state = disable_interrupts();
|
||||||
GRAB_TEAM_LOCK();
|
|
||||||
GRAB_THREAD_LOCK();
|
GRAB_THREAD_LOCK();
|
||||||
|
GRAB_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
|
||||||
|
|
||||||
uint32 threadFlags = thread->debug_info.flags;
|
uint32 threadFlags = thread->debug_info.flags;
|
||||||
threadFlags &= ~B_THREAD_DEBUG_STOP;
|
threadFlags &= ~B_THREAD_DEBUG_STOP;
|
||||||
@@ -229,8 +237,8 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
|||||||
threadFlags |= B_THREAD_DEBUG_STOPPED;
|
threadFlags |= B_THREAD_DEBUG_STOPPED;
|
||||||
atomic_set(&thread->debug_info.flags, threadFlags);
|
atomic_set(&thread->debug_info.flags, threadFlags);
|
||||||
|
|
||||||
|
RELEASE_TEAM_DEBUG_INFO_LOCK(thread->team->debug_info);
|
||||||
RELEASE_THREAD_LOCK();
|
RELEASE_THREAD_LOCK();
|
||||||
RELEASE_TEAM_LOCK();
|
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
// delete the superfluous port
|
// delete the superfluous port
|
||||||
@@ -262,17 +270,34 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
|||||||
&command, &commandMessage, sizeof(commandMessage));
|
&command, &commandMessage, sizeof(commandMessage));
|
||||||
if (commandMessageSize < 0) {
|
if (commandMessageSize < 0) {
|
||||||
error = commandMessageSize;
|
error = commandMessageSize;
|
||||||
TRACE(("thread_hit_debug_event(): thread: %ld, failed to "
|
TRACE(("thread_hit_debug_event(): thread: %ld, failed "
|
||||||
"receive message from port %ld: %lx\n", thread->id, port,
|
"to receive message from port %ld: %lx\n",
|
||||||
error));
|
thread->id, port, error));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case B_DEBUGGED_THREAD_MESSAGE_CONTINUE:
|
case B_DEBUGGED_THREAD_MESSAGE_CONTINUE:
|
||||||
TRACE(("thread_hit_debug_event(): thread: %ld: "
|
TRACE(("thread_hit_debug_event(): thread: %ld: "
|
||||||
"B_DEBUGGED_THREAD_MESSAGE_CONTINUE\n", thread->id));
|
"B_DEBUGGED_THREAD_MESSAGE_CONTINUE\n",
|
||||||
result = commandMessage.run.handle_event;
|
thread->id));
|
||||||
|
result = commandMessage.continue_thread.handle_event;
|
||||||
|
|
||||||
|
// update the single-step flag
|
||||||
|
state = disable_interrupts();
|
||||||
|
GRAB_THREAD_LOCK();
|
||||||
|
|
||||||
|
if (commandMessage.continue_thread.single_step) {
|
||||||
|
atomic_or(&thread->debug_info.flags,
|
||||||
|
B_THREAD_DEBUG_SINGLE_STEP);
|
||||||
|
} else {
|
||||||
|
atomic_and(&thread->debug_info.flags,
|
||||||
|
~B_THREAD_DEBUG_SINGLE_STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
RELEASE_THREAD_LOCK();
|
||||||
|
restore_interrupts(state);
|
||||||
|
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -299,11 +324,23 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case B_DEBUGGED_THREAD_SET_CPU_STATE:
|
||||||
|
{
|
||||||
|
TRACE(("thread_hit_debug_event(): thread: %ld: "
|
||||||
|
"B_DEBUGGED_THREAD_SET_CPU_STATE\n",
|
||||||
|
thread->id));
|
||||||
|
arch_set_debug_cpu_state(
|
||||||
|
&commandMessage.set_cpu_state.cpu_state);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TRACE(("thread_hit_debug_event(): thread: %ld, failed to send message "
|
TRACE(("thread_hit_debug_event(): thread: %ld, failed to send "
|
||||||
"to debugger port %ld: %lx\n", thread->id, debuggerPort, error));
|
"message to debugger port %ld: %lx\n", thread->id,
|
||||||
|
debuggerPort, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
// unset the "stopped" state
|
// unset the "stopped" state
|
||||||
@@ -537,8 +574,12 @@ user_debug_thread_deleted(team_id teamID, thread_id threadID)
|
|||||||
int32 teamDebugFlags = 0;
|
int32 teamDebugFlags = 0;
|
||||||
port_id debuggerPort = -1;
|
port_id debuggerPort = -1;
|
||||||
if (team) {
|
if (team) {
|
||||||
|
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
|
|
||||||
teamDebugFlags = atomic_get(&team->debug_info.flags);
|
teamDebugFlags = atomic_get(&team->debug_info.flags);
|
||||||
debuggerPort = team->debug_info.debugger_port;
|
debuggerPort = team->debug_info.debugger_port;
|
||||||
|
|
||||||
|
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
RELEASE_TEAM_LOCK();
|
RELEASE_TEAM_LOCK();
|
||||||
@@ -617,15 +658,17 @@ nub_thread_cleanup(struct thread *nubThread)
|
|||||||
|
|
||||||
cpu_status state = disable_interrupts();
|
cpu_status state = disable_interrupts();
|
||||||
GRAB_TEAM_LOCK();
|
GRAB_TEAM_LOCK();
|
||||||
|
GRAB_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info);
|
||||||
|
|
||||||
team_debug_info &info = nubThread->team->debug_info;
|
team_debug_info &info = nubThread->team->debug_info;
|
||||||
if (info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED
|
if (info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED
|
||||||
&& info.nub_thread == nubThread->id) {
|
&& info.nub_thread == nubThread->id) {
|
||||||
teamDebugInfo = info;
|
teamDebugInfo = info;
|
||||||
clear_team_debug_info(&info);
|
clear_team_debug_info(&info, false);
|
||||||
destroyDebugInfo = true;
|
destroyDebugInfo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RELEASE_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info);
|
||||||
RELEASE_TEAM_LOCK();
|
RELEASE_TEAM_LOCK();
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
@@ -637,17 +680,19 @@ nub_thread_cleanup(struct thread *nubThread)
|
|||||||
static status_t
|
static status_t
|
||||||
debug_nub_thread(void *)
|
debug_nub_thread(void *)
|
||||||
{
|
{
|
||||||
|
struct thread *nubThread = thread_get_current_thread();
|
||||||
|
|
||||||
// check, if we're still the current nub thread and get our port
|
// check, if we're still the current nub thread and get our port
|
||||||
cpu_status state = disable_interrupts();
|
cpu_status state = disable_interrupts();
|
||||||
GRAB_TEAM_LOCK();
|
|
||||||
|
|
||||||
struct thread *nubThread = thread_get_current_thread();
|
GRAB_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info);
|
||||||
|
|
||||||
if (nubThread->team->debug_info.nub_thread != nubThread->id)
|
if (nubThread->team->debug_info.nub_thread != nubThread->id)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
port_id port = nubThread->team->debug_info.nub_port;
|
port_id port = nubThread->team->debug_info.nub_port;
|
||||||
|
|
||||||
RELEASE_TEAM_LOCK();
|
RELEASE_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
TRACE(("debug_nub_thread() thread: %ld, team %ld, nub port: %ld\n",
|
TRACE(("debug_nub_thread() thread: %ld, team %ld, nub port: %ld\n",
|
||||||
@@ -676,6 +721,8 @@ debug_nub_thread(void *)
|
|||||||
union {
|
union {
|
||||||
debug_nub_read_memory_reply read_memory;
|
debug_nub_read_memory_reply read_memory;
|
||||||
debug_nub_write_memory_reply write_memory;
|
debug_nub_write_memory_reply write_memory;
|
||||||
|
debug_nub_set_breakpoint_reply set_breakpoint;
|
||||||
|
debug_nub_set_watchpoint_reply set_watchpoint;
|
||||||
} reply;
|
} reply;
|
||||||
int32 replySize = 0;
|
int32 replySize = 0;
|
||||||
port_id replyPort = -1;
|
port_id replyPort = -1;
|
||||||
@@ -755,12 +802,12 @@ debug_nub_thread(void *)
|
|||||||
|
|
||||||
// set the flags
|
// set the flags
|
||||||
cpu_status state = disable_interrupts();
|
cpu_status state = disable_interrupts();
|
||||||
GRAB_TEAM_LOCK();
|
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
|
|
||||||
flags |= team->debug_info.flags & B_TEAM_DEBUG_KERNEL_FLAG_MASK;
|
flags |= team->debug_info.flags & B_TEAM_DEBUG_KERNEL_FLAG_MASK;
|
||||||
atomic_set(&team->debug_info.flags, flags);
|
atomic_set(&team->debug_info.flags, flags);
|
||||||
|
|
||||||
RELEASE_TEAM_LOCK();
|
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -796,14 +843,27 @@ debug_nub_thread(void *)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case B_DEBUG_MESSAGE_RUN_THREAD:
|
case B_DEBUG_MESSAGE_RUN_THREAD:
|
||||||
|
case B_DEBUG_MESSAGE_STEP_THREAD:
|
||||||
{
|
{
|
||||||
// get the parameters
|
// get the parameters
|
||||||
thread_id threadID = message.run_thread.thread;
|
thread_id threadID;
|
||||||
uint32 handleEvent = message.run_thread.handle_event;
|
uint32 handleEvent;
|
||||||
|
bool singleStep;
|
||||||
|
|
||||||
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_RUN_THREAD: "
|
if (command == B_DEBUG_MESSAGE_RUN_THREAD) {
|
||||||
|
threadID = message.run_thread.thread;
|
||||||
|
handleEvent = message.run_thread.handle_event;
|
||||||
|
singleStep = false;
|
||||||
|
} else {
|
||||||
|
threadID = message.step_thread.thread;
|
||||||
|
handleEvent = message.step_thread.handle_event;
|
||||||
|
singleStep = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_%s_THREAD: "
|
||||||
"thread: %ld, handle event: %lu\n", nubThread->id,
|
"thread: %ld, handle event: %lu\n", nubThread->id,
|
||||||
threadID, handleEvent));
|
(singleStep ? "STEP" : "RUN"), threadID,
|
||||||
|
handleEvent));
|
||||||
|
|
||||||
// find the thread and get its debug port
|
// find the thread and get its debug port
|
||||||
state = disable_interrupts();
|
state = disable_interrupts();
|
||||||
@@ -822,12 +882,13 @@ debug_nub_thread(void *)
|
|||||||
|
|
||||||
// send a message to the debugged thread
|
// send a message to the debugged thread
|
||||||
if (threadDebugPort >= 0) {
|
if (threadDebugPort >= 0) {
|
||||||
debugged_thread_run commandMessage;
|
debugged_thread_continue commandMessage;
|
||||||
commandMessage.handle_event = handleEvent;
|
commandMessage.handle_event = handleEvent;
|
||||||
|
commandMessage.single_step = singleStep;
|
||||||
|
|
||||||
write_port(threadDebugPort,
|
write_port(threadDebugPort,
|
||||||
B_DEBUGGED_THREAD_MESSAGE_CONTINUE, &commandMessage,
|
B_DEBUGGED_THREAD_MESSAGE_CONTINUE,
|
||||||
sizeof(commandMessage));
|
&commandMessage, sizeof(commandMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -864,6 +925,141 @@ debug_nub_thread(void *)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case B_DEBUG_MESSAGE_SET_CPU_STATE:
|
||||||
|
{
|
||||||
|
// get the parameters
|
||||||
|
thread_id threadID = message.set_cpu_state.thread;
|
||||||
|
const debug_cpu_state &cpuState
|
||||||
|
= message.set_cpu_state.cpu_state;
|
||||||
|
|
||||||
|
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_SET_CPU_STATE: "
|
||||||
|
"thread: %ld\n", nubThread->id, threadID));
|
||||||
|
|
||||||
|
// find the thread and get its debug port
|
||||||
|
state = disable_interrupts();
|
||||||
|
GRAB_THREAD_LOCK();
|
||||||
|
|
||||||
|
port_id threadDebugPort = -1;
|
||||||
|
struct thread *thread
|
||||||
|
= thread_get_thread_struct_locked(threadID);
|
||||||
|
if (thread && thread->team == nubThread->team
|
||||||
|
&& thread->debug_info.flags & B_THREAD_DEBUG_STOPPED) {
|
||||||
|
threadDebugPort = thread->debug_info.debug_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
RELEASE_THREAD_LOCK();
|
||||||
|
restore_interrupts(state);
|
||||||
|
|
||||||
|
// send a message to the debugged thread
|
||||||
|
if (threadDebugPort >= 0) {
|
||||||
|
debugged_thread_set_cpu_state commandMessage;
|
||||||
|
memcpy(&commandMessage.cpu_state, &cpuState,
|
||||||
|
sizeof(debug_cpu_state));
|
||||||
|
write_port(threadDebugPort,
|
||||||
|
B_DEBUGGED_THREAD_SET_CPU_STATE,
|
||||||
|
&commandMessage, sizeof(commandMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_DEBUG_MESSAGE_SET_BREAKPOINT:
|
||||||
|
{
|
||||||
|
// get the parameters
|
||||||
|
replyPort = message.set_breakpoint.reply_port;
|
||||||
|
void *address = message.set_breakpoint.address;
|
||||||
|
|
||||||
|
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_SET_BREAKPOINT: "
|
||||||
|
"address: %p\n", nubThread->id, address));
|
||||||
|
|
||||||
|
// check the address
|
||||||
|
status_t result = B_OK;
|
||||||
|
if (address == NULL || !IS_USER_ADDRESS(address))
|
||||||
|
result = B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
// set the breakpoint
|
||||||
|
if (result == B_OK)
|
||||||
|
result = arch_set_breakpoint(address);
|
||||||
|
|
||||||
|
// prepare the reply
|
||||||
|
reply.set_breakpoint.error = result;
|
||||||
|
replySize = sizeof(reply.set_breakpoint);
|
||||||
|
sendReply = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_DEBUG_MESSAGE_CLEAR_BREAKPOINT:
|
||||||
|
{
|
||||||
|
// get the parameters
|
||||||
|
void *address = message.clear_breakpoint.address;
|
||||||
|
|
||||||
|
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_CLEAR_BREAKPOINT: "
|
||||||
|
"address: %p\n", nubThread->id, address));
|
||||||
|
|
||||||
|
// check the address
|
||||||
|
status_t result = B_OK;
|
||||||
|
if (address == NULL || !IS_USER_ADDRESS(address))
|
||||||
|
result = B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
// clear the breakpoint
|
||||||
|
if (result == B_OK)
|
||||||
|
result = arch_clear_breakpoint(address);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_DEBUG_MESSAGE_SET_WATCHPOINT:
|
||||||
|
{
|
||||||
|
// get the parameters
|
||||||
|
replyPort = message.set_watchpoint.reply_port;
|
||||||
|
void *address = message.set_watchpoint.address;
|
||||||
|
uint32 type = message.set_watchpoint.type;
|
||||||
|
int32 length = message.set_watchpoint.length;
|
||||||
|
|
||||||
|
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_SET_WATCHPOINT: "
|
||||||
|
"address: %p, type: %lu, length: %ld\n", nubThread->id,
|
||||||
|
address, type, length));
|
||||||
|
|
||||||
|
// check the address and size
|
||||||
|
status_t result = B_OK;
|
||||||
|
if (address == NULL || !IS_USER_ADDRESS(address))
|
||||||
|
result = B_BAD_ADDRESS;
|
||||||
|
if (length < 0)
|
||||||
|
result = B_BAD_VALUE;
|
||||||
|
|
||||||
|
// set the watchpoint
|
||||||
|
if (result == B_OK)
|
||||||
|
result = arch_set_watchpoint(address, type, length);
|
||||||
|
|
||||||
|
// prepare the reply
|
||||||
|
reply.set_watchpoint.error = result;
|
||||||
|
replySize = sizeof(reply.set_watchpoint);
|
||||||
|
sendReply = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case B_DEBUG_MESSAGE_CLEAR_WATCHPOINT:
|
||||||
|
{
|
||||||
|
// get the parameters
|
||||||
|
void *address = message.clear_watchpoint.address;
|
||||||
|
|
||||||
|
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_CLEAR_WATCHPOINT: "
|
||||||
|
"address: %p\n", nubThread->id, address));
|
||||||
|
|
||||||
|
// check the address
|
||||||
|
status_t result = B_OK;
|
||||||
|
if (address == NULL || !IS_USER_ADDRESS(address))
|
||||||
|
result = B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
// clear the watchpoint
|
||||||
|
if (result == B_OK)
|
||||||
|
result = arch_clear_watchpoint(address);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// send the reply, if necessary
|
// send the reply, if necessary
|
||||||
@@ -901,13 +1097,16 @@ TRACE(("install_team_debugger(team: %ld, port: %ld, default: %d, "
|
|||||||
cpu_status state = disable_interrupts();
|
cpu_status state = disable_interrupts();
|
||||||
GRAB_TEAM_LOCK();
|
GRAB_TEAM_LOCK();
|
||||||
|
|
||||||
struct team *team = (teamID == B_CURRENT_TEAM
|
// get a real team ID
|
||||||
? thread_get_current_thread()->team
|
// We look up the team by ID, even in case of the current team, so we can be
|
||||||
: team_get_team_struct_locked(teamID));
|
// sure, that the team is not already dying.
|
||||||
|
if (teamID == B_CURRENT_TEAM)
|
||||||
|
teamID = thread_get_current_thread()->team->id;
|
||||||
|
|
||||||
|
struct team *team = team_get_team_struct_locked(teamID);
|
||||||
|
|
||||||
if (team) {
|
if (team) {
|
||||||
// get a real team ID
|
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
teamID = team->id;
|
|
||||||
|
|
||||||
if (team == team_get_kernel_team()) {
|
if (team == team_get_kernel_team()) {
|
||||||
// don't allow to debug the kernel
|
// don't allow to debug the kernel
|
||||||
@@ -918,6 +1117,8 @@ TRACE(("install_team_debugger(team: %ld, port: %ld, default: %d, "
|
|||||||
done = true;
|
done = true;
|
||||||
result = team->debug_info.nub_port;
|
result = team->debug_info.nub_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
} else
|
} else
|
||||||
error = B_BAD_TEAM_ID;
|
error = B_BAD_TEAM_ID;
|
||||||
|
|
||||||
@@ -977,16 +1178,19 @@ TRACE(("install_team_debugger(team: %ld, port: %ld, default: %d, "
|
|||||||
state = disable_interrupts();
|
state = disable_interrupts();
|
||||||
GRAB_TEAM_LOCK();
|
GRAB_TEAM_LOCK();
|
||||||
|
|
||||||
team = (teamID == B_CURRENT_TEAM
|
// look up again, to make sure the team isn't dying
|
||||||
? thread_get_current_thread()->team
|
team = team_get_team_struct_locked(teamID);
|
||||||
: team_get_team_struct_locked(teamID));
|
|
||||||
|
|
||||||
if (team) {
|
if (team) {
|
||||||
|
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
|
|
||||||
if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
||||||
// there's already a debugger installed
|
// there's already a debugger installed
|
||||||
error = (dontFail ? B_OK : B_BAD_VALUE);
|
error = (dontFail ? B_OK : B_BAD_VALUE);
|
||||||
done = true;
|
done = true;
|
||||||
result = team->debug_info.nub_port;
|
result = team->debug_info.nub_port;
|
||||||
|
|
||||||
|
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
} else {
|
} else {
|
||||||
atomic_set(&team->debug_info.flags,
|
atomic_set(&team->debug_info.flags,
|
||||||
B_TEAM_DEBUG_DEFAULT_FLAGS
|
B_TEAM_DEBUG_DEFAULT_FLAGS
|
||||||
@@ -996,6 +1200,8 @@ TRACE(("install_team_debugger(team: %ld, port: %ld, default: %d, "
|
|||||||
team->debug_info.debugger_team = debuggerTeam;
|
team->debug_info.debugger_team = debuggerTeam;
|
||||||
team->debug_info.debugger_port = debuggerPort;
|
team->debug_info.debugger_port = debuggerPort;
|
||||||
|
|
||||||
|
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
|
|
||||||
// set the user debug flags of all threads to the default
|
// set the user debug flags of all threads to the default
|
||||||
GRAB_THREAD_LOCK();
|
GRAB_THREAD_LOCK();
|
||||||
|
|
||||||
@@ -1076,18 +1282,18 @@ _user_debugger(const char *message)
|
|||||||
int
|
int
|
||||||
_user_disable_debugger(int state)
|
_user_disable_debugger(int state)
|
||||||
{
|
{
|
||||||
cpu_status cpuState = disable_interrupts();
|
struct team *team = thread_get_current_thread()->team;
|
||||||
GRAB_TEAM_LOCK();
|
|
||||||
|
|
||||||
team_debug_info &info = thread_get_current_thread()->team->debug_info;
|
cpu_status cpuState = disable_interrupts();
|
||||||
|
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
|
|
||||||
int32 oldFlags;
|
int32 oldFlags;
|
||||||
if (state)
|
if (state)
|
||||||
oldFlags = atomic_and(&info.flags, ~B_TEAM_DEBUG_SIGNALS);
|
oldFlags = atomic_and(&team->debug_info.flags, ~B_TEAM_DEBUG_SIGNALS);
|
||||||
else
|
else
|
||||||
oldFlags = atomic_or(&info.flags, B_TEAM_DEBUG_SIGNALS);
|
oldFlags = atomic_or(&team->debug_info.flags, B_TEAM_DEBUG_SIGNALS);
|
||||||
|
|
||||||
RELEASE_TEAM_LOCK();
|
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
restore_interrupts(cpuState);
|
restore_interrupts(cpuState);
|
||||||
|
|
||||||
// TODO: Check, if the return value is really the old state.
|
// TODO: Check, if the return value is really the old state.
|
||||||
@@ -1138,14 +1344,18 @@ _user_remove_team_debugger(team_id teamID)
|
|||||||
: team_get_team_struct_locked(teamID));
|
: team_get_team_struct_locked(teamID));
|
||||||
|
|
||||||
if (team) {
|
if (team) {
|
||||||
|
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
|
|
||||||
if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
||||||
// there's a debugger installed
|
// there's a debugger installed
|
||||||
info = team->debug_info;
|
info = team->debug_info;
|
||||||
clear_team_debug_info(&team->debug_info);
|
clear_team_debug_info(&team->debug_info, false);
|
||||||
} else {
|
} else {
|
||||||
// no debugger installed
|
// no debugger installed
|
||||||
error = B_BAD_VALUE;
|
error = B_BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||||
} else
|
} else
|
||||||
error = B_BAD_TEAM_ID;
|
error = B_BAD_TEAM_ID;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user