Argh! This change should have been checked in two days ago!
* Added support for debugger handover. The current debugger need to send a B_DEBUG_MESSAGE_HANDED_OVER message. The rest is completely transparent to the new debugger. It simply calls install_team_debugger(). * Implemented _user_wait_for_debugger(). * Reworked a few bits to ensure that after a remove_team_debugger() or after requesting a debugger handover the debugger doesn't get any more messages. * When the debugger is removed or dies, the debugged threads should now cleanup their debug info and continue, instead of waiting at their debug port forever. All the new features are not tested. Will happen, when gdb will be debugged. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11697 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
4d5289b7f0
commit
0b5637ed3a
@ -32,6 +32,7 @@ static port_id sDefaultDebuggerPort = -1;
|
||||
|
||||
|
||||
static status_t ensure_debugger_installed(team_id teamID, port_id *port = NULL);
|
||||
static void get_team_debug_info(team_debug_info &teamDebugInfo);
|
||||
|
||||
|
||||
static ssize_t
|
||||
@ -52,6 +53,58 @@ kill_interruptable_write_port(port_id port, int32 code, const void *buffer,
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
debugger_write(port_id port, int32 code, const void *buffer, size_t bufferSize,
|
||||
bool dontWait)
|
||||
{
|
||||
TRACE(("debugger_write(): thread: %ld, team %ld, port: %ld, code: %lx, message: %p, "
|
||||
"size: %lu, dontWait: %d\n", thread_get_current_thread()->id,
|
||||
thread_get_current_thread()->team->id, port, code, buffer, bufferSize,
|
||||
dontWait));
|
||||
|
||||
status_t error = B_OK;
|
||||
|
||||
// get the team debug info
|
||||
team_debug_info teamDebugInfo;
|
||||
get_team_debug_info(teamDebugInfo);
|
||||
sem_id writeLock = teamDebugInfo.debugger_write_lock;
|
||||
|
||||
// get the write lock
|
||||
TRACE(("debugger_write(): acquiring write lock...\n"));
|
||||
error = acquire_sem_etc(writeLock, 1,
|
||||
(dontWait ? B_RELATIVE_TIMEOUT : B_KILL_CAN_INTERRUPT), 0);
|
||||
if (error != B_OK) {
|
||||
TRACE(("debugger_write() done1: %lx\n", error));
|
||||
return error;
|
||||
}
|
||||
|
||||
// re-get the team debug info
|
||||
get_team_debug_info(teamDebugInfo);
|
||||
|
||||
if (teamDebugInfo.debugger_port != port
|
||||
|| (teamDebugInfo.flags & B_TEAM_DEBUG_DEBUGGER_HANDOVER)) {
|
||||
// The debugger has changed in the meantime or we are about to be
|
||||
// handed over to a new debugger. In either case we don't send the
|
||||
// message.
|
||||
TRACE(("debugger_write(): %s\n",
|
||||
(teamDebugInfo.debugger_port != port ? "debugger port changed"
|
||||
: "handover flag set")));
|
||||
} else {
|
||||
TRACE(("debugger_write(): writing to port...\n"));
|
||||
|
||||
error = write_port_etc(port, code, buffer, bufferSize,
|
||||
(dontWait ? B_RELATIVE_TIMEOUT : B_KILL_CAN_INTERRUPT), 0);
|
||||
}
|
||||
|
||||
// release the write lock
|
||||
release_sem(writeLock);
|
||||
|
||||
TRACE(("debugger_write() done: %lx\n", error));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -67,6 +120,7 @@ clear_team_debug_info(struct team_debug_info *info, bool initLock)
|
||||
info->debugger_port = -1;
|
||||
info->nub_thread = -1;
|
||||
info->nub_port = -1;
|
||||
info->debugger_write_lock = -1;
|
||||
|
||||
if (initLock)
|
||||
info->lock = 0;
|
||||
@ -91,6 +145,12 @@ destroy_team_debug_info(struct team_debug_info *info)
|
||||
if (info) {
|
||||
arch_destroy_team_debug_info(&info->arch_info);
|
||||
|
||||
// delete the debugger port write lock
|
||||
if (info->debugger_write_lock >= 0) {
|
||||
delete_sem(info->debugger_write_lock);
|
||||
info->debugger_write_lock = -1;
|
||||
}
|
||||
|
||||
// delete the nub port
|
||||
if (info->nub_port >= 0) {
|
||||
set_port_owner(info->nub_port, B_CURRENT_TEAM);
|
||||
@ -171,9 +231,11 @@ prepare_thread_stopped_message(debug_thread_stopped &message,
|
||||
|
||||
|
||||
static status_t
|
||||
thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
||||
debug_why_stopped whyStopped, void *additionalData)
|
||||
thread_hit_debug_event_internal(uint32 event, const void *message, int32 size,
|
||||
debug_why_stopped whyStopped, void *additionalData, bool requireDebugger,
|
||||
bool &restart)
|
||||
{
|
||||
restart = false;
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
|
||||
TRACE(("thread_hit_debug_event(): thread: %ld, event: %lu, message: %p, "
|
||||
@ -204,6 +266,7 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
||||
// the thread's debug port, and update the thread's debug flags
|
||||
port_id deletePort = port;
|
||||
port_id debuggerPort = -1;
|
||||
port_id nubPort = -1;
|
||||
status_t error = B_OK;
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
@ -211,8 +274,13 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
||||
|
||||
uint32 threadFlags = thread->debug_info.flags;
|
||||
threadFlags &= ~B_THREAD_DEBUG_STOP;
|
||||
if (thread->team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
||||
debuggerPort = thread->team->debug_info.debugger_port;
|
||||
bool debuggerInstalled
|
||||
= (thread->team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED);
|
||||
if (debuggerInstalled || !requireDebugger) {
|
||||
if (debuggerInstalled) {
|
||||
debuggerPort = thread->team->debug_info.debugger_port;
|
||||
nubPort = thread->team->debug_info.nub_port;
|
||||
}
|
||||
|
||||
if (setPort) {
|
||||
if (threadFlags & B_THREAD_DEBUG_INITIALIZED) {
|
||||
@ -255,13 +323,14 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
||||
}
|
||||
|
||||
// send a message to the debugger port
|
||||
TRACE(("thread_hit_debug_event(): thread: %ld, sending message to debugger "
|
||||
"port %ld\n", thread->id, debuggerPort));
|
||||
do {
|
||||
error = write_port(debuggerPort, event, message, size);
|
||||
} while (error == B_INTERRUPTED);
|
||||
if (debuggerInstalled) {
|
||||
TRACE(("thread_hit_debug_event(): thread: %ld, sending message to "
|
||||
"debugger port %ld\n", thread->id, debuggerPort));
|
||||
error = debugger_write(debuggerPort, event, message, size, false);
|
||||
}
|
||||
|
||||
status_t result = B_THREAD_DEBUG_HANDLE_EVENT;
|
||||
bool singleStep = false;
|
||||
|
||||
if (error == B_OK) {
|
||||
bool done = false;
|
||||
@ -286,21 +355,7 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
||||
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);
|
||||
|
||||
singleStep = commandMessage.continue_thread.single_step;
|
||||
done = true;
|
||||
break;
|
||||
|
||||
@ -309,20 +364,10 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
||||
port_id replyPort
|
||||
= commandMessage.get_why_stopped.reply_port;
|
||||
|
||||
// get the team debug info (just in case it has changed)
|
||||
team_debug_info teamDebugInfo;
|
||||
get_team_debug_info(teamDebugInfo);
|
||||
if (!(teamDebugInfo.flags
|
||||
& B_TEAM_DEBUG_DEBUGGER_INSTALLED)) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
debuggerPort = teamDebugInfo.debugger_port;
|
||||
|
||||
// prepare the message
|
||||
debug_thread_stopped stoppedMessage;
|
||||
prepare_thread_stopped_message(stoppedMessage, whyStopped,
|
||||
teamDebugInfo.nub_port, additionalData);
|
||||
nubPort, additionalData);
|
||||
|
||||
// send it
|
||||
error = kill_interruptable_write_port(replyPort, event,
|
||||
@ -341,6 +386,31 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case B_DEBUGGED_THREAD_DEBUGGER_CHANGED:
|
||||
{
|
||||
// Check, if the debugger really changed, i.e. is different
|
||||
// than the one we know.
|
||||
team_debug_info teamDebugInfo;
|
||||
get_team_debug_info(teamDebugInfo);
|
||||
|
||||
if (teamDebugInfo.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
||||
if (!debuggerInstalled
|
||||
|| teamDebugInfo.debugger_port != debuggerPort) {
|
||||
// debugger was installed or has changed: restart
|
||||
// this function
|
||||
restart = true;
|
||||
done = true;
|
||||
}
|
||||
} else {
|
||||
if (debuggerInstalled) {
|
||||
// debugger is gone: continue the thread normally
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -349,19 +419,61 @@ thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
||||
debuggerPort, error));
|
||||
}
|
||||
|
||||
// unset the "stopped" state
|
||||
// update the thread debug info
|
||||
bool destroyThreadInfo = false;
|
||||
thread_debug_info threadDebugInfo;
|
||||
|
||||
state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
atomic_and(&thread->debug_info.flags, ~B_THREAD_DEBUG_STOPPED);
|
||||
// check, if the team is still being debugged
|
||||
int32 teamDebugFlags = atomic_get(&thread->team->debug_info.flags);
|
||||
if (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
||||
// update the single-step flag
|
||||
if (singleStep) {
|
||||
atomic_or(&thread->debug_info.flags,
|
||||
B_THREAD_DEBUG_SINGLE_STEP);
|
||||
} else {
|
||||
atomic_and(&thread->debug_info.flags,
|
||||
~B_THREAD_DEBUG_SINGLE_STEP);
|
||||
}
|
||||
|
||||
// unset the "stopped" state
|
||||
atomic_and(&thread->debug_info.flags, ~B_THREAD_DEBUG_STOPPED);
|
||||
|
||||
} else {
|
||||
// the debugger is gone: cleanup our info completely
|
||||
threadDebugInfo = thread->debug_info;
|
||||
clear_thread_debug_info(&thread->debug_info, false);
|
||||
destroyThreadInfo = true;
|
||||
}
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
if (destroyThreadInfo)
|
||||
destroy_thread_debug_info(&threadDebugInfo);
|
||||
|
||||
return (error == B_OK ? result : error);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
thread_hit_debug_event(uint32 event, const void *message, int32 size,
|
||||
debug_why_stopped whyStopped, void *additionalData, bool requireDebugger)
|
||||
{
|
||||
status_t result;
|
||||
bool restart;
|
||||
do {
|
||||
restart = false;
|
||||
result = thread_hit_debug_event_internal(event, message, size,
|
||||
whyStopped, additionalData, requireDebugger, restart);
|
||||
} while (result >= 0 && restart);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
user_debug_pre_syscall(uint32 syscall, void *args)
|
||||
{
|
||||
@ -392,7 +504,7 @@ user_debug_pre_syscall(uint32 syscall, void *args)
|
||||
}
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_PRE_SYSCALL, &message,
|
||||
sizeof(message), B_PRE_SYSCALL_HIT, NULL);
|
||||
sizeof(message), B_PRE_SYSCALL_HIT, NULL, true);
|
||||
}
|
||||
|
||||
|
||||
@ -430,7 +542,7 @@ user_debug_post_syscall(uint32 syscall, void *args, uint64 returnValue,
|
||||
}
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_POST_SYSCALL, &message,
|
||||
sizeof(message), B_POST_SYSCALL_HIT, NULL);
|
||||
sizeof(message), B_POST_SYSCALL_HIT, NULL, true);
|
||||
}
|
||||
|
||||
|
||||
@ -453,7 +565,7 @@ stop_thread(debug_why_stopped whyStopped, void *additionalData)
|
||||
additionalData);
|
||||
|
||||
return thread_hit_debug_event(B_DEBUGGER_MESSAGE_THREAD_STOPPED, &message,
|
||||
sizeof(message), whyStopped, additionalData);
|
||||
sizeof(message), whyStopped, additionalData, true);
|
||||
}
|
||||
|
||||
|
||||
@ -494,7 +606,7 @@ user_debug_handle_signal(int signal, struct sigaction *handler, bool deadly)
|
||||
message.deadly = deadly;
|
||||
|
||||
status_t result = thread_hit_debug_event(B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED,
|
||||
&message, sizeof(message), B_SIGNAL_RECEIVED, NULL);
|
||||
&message, sizeof(message), B_SIGNAL_RECEIVED, NULL, true);
|
||||
return (result != B_THREAD_DEBUG_IGNORE_EVENT);
|
||||
}
|
||||
|
||||
@ -526,7 +638,7 @@ user_debug_team_created(team_id teamID)
|
||||
message.new_team = teamID;
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_TEAM_CREATED, &message,
|
||||
sizeof(message), B_TEAM_CREATED, NULL);
|
||||
sizeof(message), B_TEAM_CREATED, NULL, true);
|
||||
}
|
||||
|
||||
|
||||
@ -564,7 +676,7 @@ user_debug_thread_created(thread_id threadID)
|
||||
message.new_thread = threadID;
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_THREAD_CREATED, &message,
|
||||
sizeof(message), B_THREAD_CREATED, NULL);
|
||||
sizeof(message), B_THREAD_CREATED, NULL, true);
|
||||
}
|
||||
|
||||
|
||||
@ -603,8 +715,8 @@ user_debug_thread_deleted(team_id teamID, thread_id threadID)
|
||||
message.origin.thread = threadID;
|
||||
message.origin.team = teamID;
|
||||
message.origin.nub_port = -1;
|
||||
write_port_etc(debuggerPort, B_DEBUGGER_MESSAGE_THREAD_DELETED,
|
||||
&message, sizeof(message), B_RELATIVE_TIMEOUT, 0);
|
||||
debugger_write(debuggerPort, B_DEBUGGER_MESSAGE_THREAD_DELETED,
|
||||
&message, sizeof(message), true);
|
||||
// TODO: Would it be OK to wait here?
|
||||
}
|
||||
}
|
||||
@ -629,7 +741,7 @@ user_debug_image_created(const image_info *imageInfo)
|
||||
memcpy(&message.info, imageInfo, sizeof(image_info));
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_IMAGE_CREATED, &message,
|
||||
sizeof(message), B_IMAGE_CREATED, NULL);
|
||||
sizeof(message), B_IMAGE_CREATED, NULL, true);
|
||||
}
|
||||
|
||||
|
||||
@ -652,7 +764,7 @@ user_debug_image_deleted(const image_info *imageInfo)
|
||||
memcpy(&message.info, imageInfo, sizeof(image_info));
|
||||
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_IMAGE_CREATED, &message,
|
||||
sizeof(message), B_IMAGE_DELETED, NULL);
|
||||
sizeof(message), B_IMAGE_DELETED, NULL, true);
|
||||
}
|
||||
|
||||
|
||||
@ -670,6 +782,48 @@ user_debug_single_stepped()
|
||||
}
|
||||
|
||||
|
||||
/** \brief Called by the debug nub thread of a team to broadcast a message
|
||||
* that are initialized for debugging (and thus have a debug port).
|
||||
*/
|
||||
static void
|
||||
broadcast_debugged_thread_message(struct thread *nubThread, int32 code,
|
||||
const void *message, int32 size)
|
||||
{
|
||||
// iterate through the threads
|
||||
thread_info threadInfo;
|
||||
int32 cookie = 0;
|
||||
while (get_next_thread_info(nubThread->team->id, &cookie, &threadInfo)
|
||||
== B_OK) {
|
||||
// find the thread and get its debug port
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
port_id threadDebugPort = -1;
|
||||
thread_id threadID = -1;
|
||||
struct thread *thread
|
||||
= thread_get_thread_struct_locked(threadInfo.thread);
|
||||
if (thread && thread != nubThread && thread->team == nubThread->team
|
||||
&& thread->debug_info.flags & B_THREAD_DEBUG_INITIALIZED) {
|
||||
threadDebugPort = thread->debug_info.debug_port;
|
||||
threadID = thread->id;
|
||||
}
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
// send the message to the thread
|
||||
if (threadDebugPort >= 0) {
|
||||
status_t error = kill_interruptable_write_port(threadDebugPort,
|
||||
code, message, size);
|
||||
if (error != B_OK) {
|
||||
TRACE(("broadcast_debugged_thread_message(): Failed to send "
|
||||
"message to thread %ld: %lx\n", threadID, error));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nub_thread_cleanup(struct thread *nubThread)
|
||||
{
|
||||
@ -694,6 +848,10 @@ nub_thread_cleanup(struct thread *nubThread)
|
||||
|
||||
if (destroyDebugInfo)
|
||||
destroy_team_debug_info(&teamDebugInfo);
|
||||
|
||||
// notify all threads that the debugger is gone
|
||||
broadcast_debugged_thread_message(nubThread,
|
||||
B_DEBUGGED_THREAD_DEBUGGER_CHANGED, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -766,6 +924,7 @@ debug_nub_thread(void *)
|
||||
return 0;
|
||||
|
||||
port_id port = nubThread->team->debug_info.nub_port;
|
||||
sem_id writeLock = nubThread->team->debug_info.debugger_write_lock;
|
||||
|
||||
RELEASE_TEAM_DEBUG_INFO_LOCK(nubThread->team->debug_info);
|
||||
restore_interrupts(state);
|
||||
@ -773,6 +932,10 @@ debug_nub_thread(void *)
|
||||
TRACE(("debug_nub_thread() thread: %ld, team %ld, nub port: %ld\n",
|
||||
nubThread->id, nubThread->team->id, port));
|
||||
|
||||
// notify all threads that a debugger has been installed
|
||||
broadcast_debugged_thread_message(nubThread,
|
||||
B_DEBUGGED_THREAD_DEBUGGER_CHANGED, NULL, 0);
|
||||
|
||||
// command processing loop
|
||||
while (true) {
|
||||
int32 command;
|
||||
@ -794,10 +957,11 @@ debug_nub_thread(void *)
|
||||
|
||||
bool sendReply = false;
|
||||
union {
|
||||
debug_nub_read_memory_reply read_memory;
|
||||
debug_nub_write_memory_reply write_memory;
|
||||
debug_nub_set_breakpoint_reply set_breakpoint;
|
||||
debug_nub_set_watchpoint_reply set_watchpoint;
|
||||
debug_nub_read_memory_reply read_memory;
|
||||
debug_nub_write_memory_reply write_memory;
|
||||
debug_nub_set_breakpoint_reply set_breakpoint;
|
||||
debug_nub_set_watchpoint_reply set_watchpoint;
|
||||
debug_nub_prepare_handover_reply prepare_handover;
|
||||
} reply;
|
||||
int32 replySize = 0;
|
||||
port_id replyPort = -1;
|
||||
@ -1142,6 +1306,52 @@ debug_nub_thread(void *)
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case B_DEBUG_MESSAGE_PREPARE_HANDOVER:
|
||||
{
|
||||
TRACE(("nub thread %ld: B_DEBUG_MESSAGE_PREPARE_HANDOVER\n",
|
||||
nubThread->id));
|
||||
|
||||
struct team *team = nubThread->team;
|
||||
|
||||
// Acquire the debugger write lock. As soon as we have it and
|
||||
// have set the B_TEAM_DEBUG_DEBUGGER_HANDOVER flag, no thread
|
||||
// will write anything to the debugger port anymore.
|
||||
status_t result = acquire_sem_etc(writeLock, 1,
|
||||
B_KILL_CAN_INTERRUPT, 0);
|
||||
if (result == B_OK) {
|
||||
// set the respective team debug flag
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||
|
||||
atomic_or(&team->debug_info.flags,
|
||||
B_TEAM_DEBUG_DEBUGGER_HANDOVER);
|
||||
|
||||
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||
restore_interrupts(state);
|
||||
|
||||
release_sem(writeLock);
|
||||
} else {
|
||||
// We probably got a SIGKILL. If so, we will terminate when
|
||||
// sending the reply fails.
|
||||
}
|
||||
|
||||
// prepare the reply
|
||||
reply.prepare_handover.error = result;
|
||||
replySize = sizeof(reply.prepare_handover);
|
||||
sendReply = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case B_DEBUG_MESSAGE_HANDED_OVER:
|
||||
{
|
||||
// notify all threads that the debugger has changed
|
||||
broadcast_debugged_thread_message(nubThread,
|
||||
B_DEBUGGED_THREAD_DEBUGGER_CHANGED, NULL, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// send the reply, if necessary
|
||||
@ -1160,62 +1370,59 @@ debug_nub_thread(void *)
|
||||
}
|
||||
|
||||
|
||||
/** \brief Helper function for install_team_debugger(), that sets up the team
|
||||
* and thread debug infos.
|
||||
*
|
||||
* Interrupts must be enabled and the team debug info lock of the team to be
|
||||
* debugged must be held. The function will release the lock, but leave
|
||||
* interrupts disabled.
|
||||
*/
|
||||
static void
|
||||
install_team_debugger_init_debug_infos(struct team *team, team_id debuggerTeam,
|
||||
port_id debuggerPort, port_id nubPort, thread_id nubThread,
|
||||
sem_id debuggerPortWriteLock)
|
||||
{
|
||||
atomic_set(&team->debug_info.flags,
|
||||
B_TEAM_DEBUG_DEFAULT_FLAGS | B_TEAM_DEBUG_DEBUGGER_INSTALLED);
|
||||
team->debug_info.nub_port = nubPort;
|
||||
team->debug_info.nub_thread = nubThread;
|
||||
team->debug_info.debugger_team = debuggerTeam;
|
||||
team->debug_info.debugger_port = debuggerPort;
|
||||
team->debug_info.debugger_write_lock = debuggerPortWriteLock;
|
||||
|
||||
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||
|
||||
// set the user debug flags of all threads to the default
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
for (struct thread *thread = team->thread_list;
|
||||
thread;
|
||||
thread = thread->team_next) {
|
||||
if (thread->id != nubThread) {
|
||||
int32 flags = thread->debug_info.flags
|
||||
& ~B_THREAD_DEBUG_USER_FLAG_MASK;
|
||||
atomic_set(&thread->debug_info.flags,
|
||||
flags | B_THREAD_DEBUG_DEFAULT_FLAGS);
|
||||
}
|
||||
}
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
}
|
||||
|
||||
|
||||
static port_id
|
||||
install_team_debugger(team_id teamID, port_id debuggerPort, bool useDefault,
|
||||
bool dontFail)
|
||||
{
|
||||
TRACE(("install_team_debugger(team: %ld, port: %ld, default: %d, "
|
||||
"dontFail: %d)\n", teamID, debuggerPort, useDefault, dontFail));
|
||||
TRACE(("install_team_debugger(team: %ld, port: %ld, default: %d, "
|
||||
"dontFail: %d)\n", teamID, debuggerPort, useDefault, dontFail));
|
||||
|
||||
if (useDefault)
|
||||
debuggerPort = atomic_get(&sDefaultDebuggerPort);
|
||||
|
||||
// check, if a debugger is already installed
|
||||
|
||||
status_t error = B_OK;
|
||||
bool done = false;
|
||||
port_id result = B_ERROR;
|
||||
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_TEAM_LOCK();
|
||||
|
||||
// get a real team ID
|
||||
// We look up the team by ID, even in case of the current team, so we can be
|
||||
// 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) {
|
||||
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||
|
||||
if (team == team_get_kernel_team()) {
|
||||
// don't allow to debug the kernel
|
||||
error = B_NOT_ALLOWED;
|
||||
} else if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
||||
// there's already a debugger installed
|
||||
error = (dontFail ? B_OK : B_BAD_VALUE);
|
||||
done = true;
|
||||
result = team->debug_info.nub_port;
|
||||
}
|
||||
|
||||
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||
} else
|
||||
error = B_BAD_TEAM_ID;
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
if (done || error != B_OK) {
|
||||
TRACE(("install_team_debugger() done1: %ld\n",
|
||||
(error == B_OK ? result : error)));
|
||||
return (error == B_OK ? result : error);
|
||||
}
|
||||
|
||||
// get the debugger team
|
||||
port_info debuggerPortInfo;
|
||||
error = get_port_info(debuggerPort, &debuggerPortInfo);
|
||||
status_t error = get_port_info(debuggerPort, &debuggerPortInfo);
|
||||
if (error != B_OK) {
|
||||
TRACE(("install_team_debugger(): Failed to get debugger port info: "
|
||||
"%lx\n", error));
|
||||
@ -1231,14 +1438,99 @@ TRACE(("install_team_debugger(team: %ld, port: %ld, default: %d, "
|
||||
return B_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
// create the nub port
|
||||
// check, if a debugger is already installed
|
||||
|
||||
bool done = false;
|
||||
port_id result = B_ERROR;
|
||||
bool handOver = false;
|
||||
port_id nubPort = -1;
|
||||
|
||||
cpu_status state = disable_interrupts();
|
||||
GRAB_TEAM_LOCK();
|
||||
|
||||
// get a real team ID
|
||||
// We look up the team by ID, even in case of the current team, so we can be
|
||||
// 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) {
|
||||
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||
|
||||
int32 teamDebugFlags = team->debug_info.flags;
|
||||
|
||||
if (team == team_get_kernel_team()) {
|
||||
// don't allow to debug the kernel
|
||||
error = B_NOT_ALLOWED;
|
||||
} else if (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
|
||||
if (teamDebugFlags & B_TEAM_DEBUG_DEBUGGER_HANDOVER) {
|
||||
// a handover to another debugger is requested
|
||||
// clear the flag
|
||||
atomic_and(& team->debug_info.flags,
|
||||
~B_TEAM_DEBUG_DEBUGGER_HANDOVER);
|
||||
|
||||
result = nubPort = team->debug_info.nub_port;
|
||||
|
||||
// set the new debugger
|
||||
install_team_debugger_init_debug_infos(team, debuggerTeam,
|
||||
debuggerPort, nubPort, team->debug_info.nub_thread,
|
||||
team->debug_info.debugger_write_lock);
|
||||
|
||||
handOver = true;
|
||||
done = true;
|
||||
} else {
|
||||
// there's already a debugger installed
|
||||
error = (dontFail ? B_OK : B_BAD_VALUE);
|
||||
done = true;
|
||||
result = team->debug_info.nub_port;
|
||||
}
|
||||
}
|
||||
|
||||
// in case of a handover the lock is already released
|
||||
if (!handOver)
|
||||
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||
} else
|
||||
error = B_BAD_TEAM_ID;
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
if (handOver) {
|
||||
// notify the nub thread
|
||||
kill_interruptable_write_port(nubPort, B_DEBUG_MESSAGE_HANDED_OVER,
|
||||
NULL, 0);
|
||||
|
||||
TRACE(("install_team_debugger() done: handed over to debugger: team: "
|
||||
"%ld, port: %ld\n", debuggerTeam, debuggerPort));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (done || error != B_OK) {
|
||||
TRACE(("install_team_debugger() done1: %ld\n",
|
||||
(error == B_OK ? result : error)));
|
||||
return (error == B_OK ? result : error);
|
||||
}
|
||||
|
||||
// create the debugger write lock semaphore
|
||||
char nameBuffer[B_OS_NAME_LENGTH];
|
||||
snprintf(nameBuffer, sizeof(nameBuffer), "team %ld debugger port write",
|
||||
teamID);
|
||||
sem_id debuggerWriteLock = create_sem(1, nameBuffer);
|
||||
if (debuggerWriteLock < 0)
|
||||
error = debuggerWriteLock;
|
||||
|
||||
// create the nub port
|
||||
snprintf(nameBuffer, sizeof(nameBuffer), "team %ld debug", teamID);
|
||||
port_id nubPort = create_port(1, nameBuffer);
|
||||
if (nubPort < 0)
|
||||
error = nubPort;
|
||||
else
|
||||
result = nubPort;
|
||||
if (error == B_OK) {
|
||||
nubPort = create_port(1, nameBuffer);
|
||||
if (nubPort < 0)
|
||||
error = nubPort;
|
||||
else
|
||||
result = nubPort;
|
||||
}
|
||||
|
||||
// make the debugger team the port owner; thus we know, if the debugger is
|
||||
// gone and can cleanup
|
||||
@ -1274,31 +1566,8 @@ TRACE(("install_team_debugger(team: %ld, port: %ld, default: %d, "
|
||||
|
||||
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
||||
} else {
|
||||
atomic_set(&team->debug_info.flags,
|
||||
B_TEAM_DEBUG_DEFAULT_FLAGS
|
||||
| B_TEAM_DEBUG_DEBUGGER_INSTALLED);
|
||||
team->debug_info.nub_port = nubPort;
|
||||
team->debug_info.nub_thread = nubThread;
|
||||
team->debug_info.debugger_team = debuggerTeam;
|
||||
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
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
for (struct thread *thread = team->thread_list;
|
||||
thread;
|
||||
thread = thread->team_next) {
|
||||
if (thread->id != nubThread) {
|
||||
int32 flags = thread->debug_info.flags
|
||||
& ~B_THREAD_DEBUG_USER_FLAG_MASK;
|
||||
atomic_set(&thread->debug_info.flags,
|
||||
flags | B_THREAD_DEBUG_DEFAULT_FLAGS);
|
||||
}
|
||||
}
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
install_team_debugger_init_debug_infos(team, debuggerTeam,
|
||||
debuggerPort, nubPort, nubThread, debuggerWriteLock);
|
||||
}
|
||||
} else
|
||||
error = B_BAD_TEAM_ID;
|
||||
@ -1448,15 +1717,6 @@ _user_remove_team_debugger(team_id teamID)
|
||||
if (error == B_OK)
|
||||
destroy_team_debug_info(&info);
|
||||
|
||||
// TODO: There's more to do! There might still be stopped threads blocking on
|
||||
// their ports. They should continue now. The same goes, if the debugger just
|
||||
// terminates or gets killed, i.e. the nub thread should best do the cleanup.
|
||||
// It could just broadcast a new message to all threads and the threads will
|
||||
// do the necessary cleanup. To support _user_wait_for_debugger() the thread
|
||||
// should enter debugged state (thread_hit_debug_event()) and just wait for
|
||||
// the nub thread to broadcast a special message that indicates the begin of
|
||||
// debugging.
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1506,6 +1766,7 @@ _user_debug_thread(thread_id threadID)
|
||||
void
|
||||
_user_wait_for_debugger(void)
|
||||
{
|
||||
// TODO: Implement!
|
||||
thread_hit_debug_event(B_DEBUGGER_MESSAGE_THREAD_STOPPED, NULL, 0,
|
||||
B_THREAD_NOT_RUNNING, NULL, false);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user