void set_desired_inferior(int use_general) { struct thread_info *found; if (use_general == 1) { found = (struct thread_info *)find_inferior_id(&all_threads, general_thread); } else { found = NULL; /* If we are continuing any (all) thread(s), use step_thread * to decide which thread to step and/or send the specified * signal to. */ if (((step_thread != 0) && (step_thread != (unsigned long)-1L)) && ((cont_thread == 0) || (cont_thread == (unsigned long)-1L))) found = (struct thread_info *)find_inferior_id(&all_threads, step_thread); if (found == NULL) found = (struct thread_info *)find_inferior_id(&all_threads, cont_thread); } if (found == NULL) current_inferior = (struct thread_info *)all_threads.head; else current_inferior = found; }
void set_desired_inferior (int use_general) { struct thread_info *found; if (use_general == 1) { found = (struct thread_info *) find_inferior_id (&all_threads, general_thread); } else { found = NULL; /* If we are continuing any (all) thread(s), use step_thread to decide which thread to step and/or send the specified signal to. */ if ((step_thread != 0 && step_thread != -1) && (cont_thread == 0 || cont_thread == -1)) found = (struct thread_info *) find_inferior_id (&all_threads, step_thread); if (found == NULL) found = (struct thread_info *) find_inferior_id (&all_threads, cont_thread); } if (found == NULL) current_inferior = (struct thread_info *) all_threads.head; else current_inferior = found; { ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior); ThreadId tid = tst->tid; dlog(1, "set_desired_inferior use_general %d found %p tid %d lwpid %d\n", use_general, found, tid, tst->os_state.lwpid); } }
static void linux_wait_for_process (struct process_info **childp, int *wstatp) { int ret; int to_wait_for = -1; if (*childp != NULL) to_wait_for = (*childp)->lwpid; while (1) { ret = waitpid (to_wait_for, wstatp, WNOHANG); if (ret == -1) { if (errno != ECHILD) perror_with_name ("waitpid"); } else if (ret > 0) break; ret = waitpid (to_wait_for, wstatp, WNOHANG | __WCLONE); if (ret == -1) { if (errno != ECHILD) perror_with_name ("waitpid (WCLONE)"); } else if (ret > 0) break; usleep (1000); } if (debug_threads && (!WIFSTOPPED (*wstatp) || (WSTOPSIG (*wstatp) != 32 && WSTOPSIG (*wstatp) != 33))) fprintf (stderr, "Got an event from %d (%x)\n", ret, *wstatp); if (to_wait_for == -1) *childp = (struct process_info *) find_inferior_id (&all_processes, ret); (*childp)->stopped = 1; (*childp)->pending_is_breakpoint = 0; if (debug_threads && WIFSTOPPED (*wstatp)) { current_inferior = (struct thread_info *) find_inferior_id (&all_threads, (*childp)->tid); /* For testing only; i386_stop_pc prints out a diagnostic. */ if (the_low_target.get_pc != NULL) get_stop_pc (); } }
static void maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) { td_err_e err; struct thread_info *inferior; struct process_info *process; /* If we are attaching to our first thread, things are a little different. */ if (all_threads.head == all_threads.tail) { inferior = (struct thread_info *) all_threads.head; process = get_thread_process (inferior); if (process->thread_known == 0) { /* Switch to indexing the threads list by TID. */ change_inferior_id (&all_threads, ti_p->ti_tid); goto found; } } inferior = (struct thread_info *) find_inferior_id (&all_threads, ti_p->ti_tid); if (inferior != NULL) return; if (debug_threads) fprintf (stderr, "Attaching to thread %ld (LWP %d)\n", ti_p->ti_tid, ti_p->ti_lid); linux_attach_lwp (ti_p->ti_lid, ti_p->ti_tid); inferior = (struct thread_info *) find_inferior_id (&all_threads, ti_p->ti_tid); if (inferior == NULL) { warning ("Could not attach to thread %ld (LWP %d)\n", ti_p->ti_tid, ti_p->ti_lid); return; } process = inferior_target_data (inferior); found: new_thread_notify (ti_p->ti_tid); process->tid = ti_p->ti_tid; process->lwpid = ti_p->ti_lid; process->thread_known = 1; process->th = *th_p; err = td_thr_event_enable (th_p, 1); if (err != TD_OK) error ("Cannot enable thread event reporting for %d: %s", ti_p->ti_lid, thread_db_err_str (err)); }
/* Find a thread record given a thread id. If GET_CONTEXT is set then also retrieve the context for this thread. */ static win32_thread_info * thread_rec (ptid_t ptid, int get_context) { struct thread_info *thread; win32_thread_info *th; thread = (struct thread_info *) find_inferior_id (&all_threads, ptid); if (thread == NULL) return NULL; th = inferior_target_data (thread); if (get_context && th->context.ContextFlags == 0) { if (!th->suspended) { if (SuspendThread (th->h) == (DWORD) -1) { DWORD err = GetLastError (); OUTMSG (("warning: SuspendThread failed in thread_rec, " "(error %d): %s\n", (int) err, strwinerror (err))); } else th->suspended = 1; } win32_get_thread_context (th); } return th; }
struct thread_info * parse_thread_id (const char *tidstr, const char **end) { const char *number = tidstr; const char *dot, *p1; struct thread_info *tp; struct inferior *inf; int thr_num; int explicit_inf_id = 0; dot = strchr (number, '.'); if (dot != NULL) { /* Parse number to the left of the dot. */ int inf_num; p1 = number; inf_num = get_positive_number_trailer (&p1, '.', number); if (inf_num == 0) invalid_thread_id_error (number); inf = find_inferior_id (inf_num); if (inf == NULL) error (_("No inferior number '%d'"), inf_num); explicit_inf_id = 1; p1 = dot + 1; } else { inf = current_inferior (); p1 = number; } thr_num = get_positive_number_trailer (&p1, 0, number); if (thr_num == 0) invalid_thread_id_error (number); ALL_THREADS (tp) { if (ptid_get_pid (tp->ptid) == inf->pid && tp->per_inf_num == thr_num) break; } if (tp == NULL) { if (show_inferior_qualified_tids () || explicit_inf_id) error (_("Unknown thread %d.%d."), inf->num, thr_num); else error (_("Unknown thread %d."), thr_num); } if (end != NULL) *end = p1; return tp; }
ps_err_e ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset) { #ifdef HAVE_REGSETS struct process_info *process; struct thread_info *reg_inferior, *save_inferior; process = (struct process_info *) find_inferior_id (&all_processes, lwpid); if (process == NULL) return PS_ERR; reg_inferior = get_process_thread (process); save_inferior = current_inferior; current_inferior = reg_inferior; the_target->fetch_registers (0); gregset_info()->fill_function (gregset); current_inferior = save_inferior; return PS_OK; #else return PS_ERR; #endif }
/* Return nonzero if the given thread is still alive. */ static int fbsd_thread_alive (int tid) { if (find_inferior_id (&all_threads, tid) != NULL) return 1; else return 0; }
/* Return nonzero if the given thread is still alive. */ static int linux_thread_alive (unsigned long tid) { if (find_inferior_id (&all_threads, tid) != NULL) return 1; else return 0; }
static int find_one_thread (ptid_t ptid) { td_thrhandle_t th; td_thrinfo_t ti; td_err_e err; struct thread_info *inferior; struct lwp_info *lwp; struct thread_db *thread_db = current_process ()->priv->thread_db; int lwpid = ptid_get_lwp (ptid); inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid); lwp = get_thread_lwp (inferior); if (lwp->thread_known) return 1; /* Get information about this thread. */ err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, &th); if (err != TD_OK) error ("Cannot get thread handle for LWP %d: %s", lwpid, thread_db_err_str (err)); err = thread_db->td_thr_get_info_p (&th, &ti); if (err != TD_OK) error ("Cannot get thread info for LWP %d: %s", lwpid, thread_db_err_str (err)); if (debug_threads) debug_printf ("Found thread %ld (LWP %d)\n", ti.ti_tid, ti.ti_lid); if (lwpid != ti.ti_lid) { warning ("PID mismatch! Expected %ld, got %ld", (long) lwpid, (long) ti.ti_lid); return 0; } if (thread_db_use_events) { err = thread_db->td_thr_event_enable_p (&th, 1); if (err != TD_OK) error ("Cannot enable thread event reporting for %d: %s", ti.ti_lid, thread_db_err_str (err)); } /* If the new thread ID is zero, a final thread ID will be available later. Do not enable thread debugging yet. */ if (ti.ti_tid == 0) return 0; lwp->thread_known = 1; lwp->th = th; return 1; }
/* Return 1 iff the thread with thread ID TID is alive. */ static int win32_thread_alive (ptid_t ptid) { int res; /* Our thread list is reliable; don't bother to poll target threads. */ if (find_inferior_id (&all_threads, ptid) != NULL) res = 1; else res = 0; return res; }
int fbsd_attach (int pid) { struct process_info *process; fbsd_attach_lwp (pid, pid); /* Don't ignore the initial SIGSTOP if we just attached to this process. */ process = (struct process_info *) find_inferior_id (&all_processes, pid); process->stop_expected = 0; return 0; }
/* Find a thread record given a thread id. If GET_CONTEXT is set then also retrieve the context for this thread. */ static win32_thread_info * thread_rec (ptid_t ptid, int get_context) { struct thread_info *thread; win32_thread_info *th; thread = (struct thread_info *) find_inferior_id (&all_threads, ptid); if (thread == NULL) return NULL; th = inferior_target_data (thread); if (get_context) win32_require_context (th); return th; }
/* Delete a thread from the list of threads. */ static void child_delete_thread (DWORD id) { struct inferior_list_entry *thread; /* If the last thread is exiting, just return. */ if (all_threads.head == all_threads.tail) return; thread = find_inferior_id (&all_threads, id); if (thread == NULL) return; delete_thread_info (thread); }
int pull_pid_from_list (struct inferior_list *list, unsigned long pid) { struct inferior_list_entry *new_entry; new_entry = find_inferior_id (list, pid_to_ptid (pid)); if (new_entry == NULL) return 0; else { remove_inferior (list, new_entry); free (new_entry); return 1; } }
/* Delete a thread from the list of threads. */ static void child_delete_thread (DWORD pid, DWORD tid) { struct inferior_list_entry *thread; ptid_t ptid; /* If the last thread is exiting, just return. */ if (all_threads.head == all_threads.tail) return; ptid = ptid_build (pid, tid, 0); thread = find_inferior_id (&all_threads, ptid); if (thread == NULL) return; delete_thread_info (thread); }
static void wait_for_sigstop (struct inferior_list_entry *entry) { struct process_info *process = (struct process_info *) entry; struct thread_info *saved_inferior, *thread; int wstat; unsigned long saved_tid; if (process->stopped) return; saved_inferior = current_inferior; saved_tid = ((struct inferior_list_entry *) saved_inferior)->id; thread = (struct thread_info *) find_inferior_id (&all_threads, process->tid); wstat = linux_wait_for_event (thread); /* If we stopped with a non-SIGSTOP signal, save it for later and record the pending SIGSTOP. If the process exited, just return. */ if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) != SIGSTOP) { if (debug_threads) fprintf (stderr, "Stopped with non-sigstop signal\n"); process->status_pending_p = 1; process->status_pending = wstat; process->stop_expected = 1; } if (linux_thread_alive (saved_tid)) current_inferior = saved_inferior; else { if (debug_threads) fprintf (stderr, "Previously current thread died.\n"); /* Set a valid thread as current. */ set_desired_inferior (0); } }
/* Add a thread to the thread list. */ static thread_info * child_add_thread (DWORD tid, HANDLE h) { thread_info *th; if ((th = thread_rec (tid, FALSE))) return th; th = (thread_info *) malloc (sizeof (*th)); memset (th, 0, sizeof (*th)); th->tid = tid; th->h = h; add_thread (tid, th, (unsigned int) tid); set_inferior_regcache_data ((struct thread_info *) find_inferior_id (&all_threads, tid), new_register_cache ()); /* Set the debug registers for the new thread if they are used. */ if (debug_registers_used) { /* Only change the value of the debug registers. */ th->context.ContextFlags = CONTEXT_DEBUGGER_DR; GetThreadContext (th->h, &th->context); th->context.Dr0 = dr[0]; th->context.Dr1 = dr[1]; th->context.Dr2 = dr[2]; th->context.Dr3 = dr[3]; /* th->context.Dr6 = dr[6]; FIXME: should we set dr6 also ?? */ th->context.Dr7 = dr[7]; SetThreadContext (th->h, &th->context); th->context.ContextFlags = 0; } return th; }
/* Add a thread to the thread list. */ static win32_thread_info * child_add_thread (DWORD tid, HANDLE h) { win32_thread_info *th; if ((th = thread_rec (tid, FALSE))) return th; th = calloc (1, sizeof (*th)); th->tid = tid; th->h = h; add_thread (tid, th, (unsigned int) tid); set_inferior_regcache_data ((struct thread_info *) find_inferior_id (&all_threads, tid), new_register_cache ()); if (the_low_target.thread_added != NULL) (*the_low_target.thread_added) (th); return th; }
/* Find a thread record given a thread id. If GET_CONTEXT is set then also retrieve the context for this thread. */ static thread_info * thread_rec (DWORD id, int get_context) { struct thread_info *thread; thread_info *th; thread = (struct thread_info *) find_inferior_id (&all_threads, id); if (thread == NULL) return NULL; th = inferior_target_data (thread); if (!th->suspend_count && get_context) { if (get_context > 0 && id != current_event.dwThreadId) th->suspend_count = SuspendThread (th->h) + 1; else if (get_context < 0) th->suspend_count = -1; th->context.ContextFlags = CONTEXT_DEBUGGER_DR; GetThreadContext (th->h, &th->context); if (id == current_event.dwThreadId) { /* Copy dr values from that thread. */ dr[0] = th->context.Dr0; dr[1] = th->context.Dr1; dr[2] = th->context.Dr2; dr[3] = th->context.Dr3; dr[6] = th->context.Dr6; dr[7] = th->context.Dr7; } } return th; }
/* Add a thread to the thread list. */ static win32_thread_info * child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb) { win32_thread_info *th; ptid_t ptid = ptid_build (pid, tid, 0); if ((th = thread_rec (ptid, FALSE))) return th; th = xcalloc (1, sizeof (*th)); th->tid = tid; th->h = h; th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb; add_thread (ptid, th); set_inferior_regcache_data ((struct thread_info *) find_inferior_id (&all_threads, ptid), new_register_cache ()); if (the_low_target.thread_added != NULL) (*the_low_target.thread_added) (th); return th; }
static unsigned char fbsd_wait (char *status) { int w; struct thread_info *child = NULL; retry: /* If we were only supposed to resume one thread, only wait for that thread - if it's still alive. If it died, however - which can happen if we're coming from the thread death case below - then we need to make sure we restart the other threads. We could pick a thread at random or restart all; restarting all is less arbitrary. */ if (cont_thread > 0) { child = (struct thread_info *) find_inferior_id (&all_threads, cont_thread); /* No stepping, no signal - unless one is pending already, of course. */ if (child == NULL) { struct thread_resume resume_info; resume_info.thread = -1; resume_info.step = resume_info.sig = resume_info.leave_stopped = 0; fbsd_resume (&resume_info); } } enable_async_io (); unblock_async_io (); w = fbsd_wait_for_event (child); stop_all_processes (); disable_async_io (); /* If we are waiting for a particular child, and it exited, fbsd_wait_for_event will return its exit status. Similarly if the last child exited. If this is not the last child, however, do not report it as exited until there is a 'thread exited' response available in the remote protocol. Instead, just wait for another event. This should be safe, because if the thread crashed we will already have reported the termination signal to GDB; that should stop any in-progress stepping operations, etc. Report the exit status of the last thread to exit. This matches LinuxThreads' behavior. */ if (all_threads.head == all_threads.tail) { if (WIFEXITED (w)) { fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); *status = 'W'; clear_inferiors (); free (all_processes.head); all_processes.head = all_processes.tail = NULL; return ((unsigned char) WEXITSTATUS (w)); } else if (!WIFSTOPPED (w)) { fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); *status = 'X'; clear_inferiors (); free (all_processes.head); all_processes.head = all_processes.tail = NULL; return ((unsigned char) WTERMSIG (w)); } } else { if (!WIFSTOPPED (w)) goto retry; } *status = 'T'; return ((unsigned char) WSTOPSIG (w)); }
static int fbsd_wait_for_event (struct thread_info *child) { CORE_ADDR stop_pc; struct process_info *event_child; int wstat; /* Check for a process with a pending status. */ /* It is possible that the user changed the pending task's registers since it stopped. We correctly handle the change of PC if we hit a breakpoint (in check_removed_breakpoint); signals should be reported anyway. */ if (child == NULL) { event_child = (struct process_info *) find_inferior (&all_processes, status_pending_p, NULL); if (debug_threads && event_child) fprintf (stderr, "Got a pending child %d\n", event_child->lwpid); } else { event_child = get_thread_process (child); if (event_child->status_pending_p && check_removed_breakpoint (event_child)) event_child = NULL; } if (event_child != NULL) { if (event_child->status_pending_p) { if (debug_threads) fprintf (stderr, "Got an event from pending child %d (%04x)\n", event_child->lwpid, event_child->status_pending); wstat = event_child->status_pending; event_child->status_pending_p = 0; event_child->status_pending = 0; current_inferior = get_process_thread (event_child); return wstat; } } /* We only enter this loop if no process has a pending wait status. Thus any action taken in response to a wait status inside this loop is responding as soon as we detect the status, not after any pending events. */ while (1) { if (child == NULL) event_child = NULL; else event_child = get_thread_process (child); fbsd_wait_for_process (&event_child, &wstat); if (event_child == NULL) error ("event from unknown child"); current_inferior = (struct thread_info *) find_inferior_id (&all_threads, event_child->tid); if (using_threads) { /* Check for thread exit. */ if (! WIFSTOPPED (wstat)) { if (debug_threads) fprintf (stderr, "Thread %d (LWP %d) exiting\n", event_child->tid, event_child->head.id); /* If the last thread is exiting, just return. */ if (all_threads.head == all_threads.tail) return wstat; dead_thread_notify (event_child->tid); remove_inferior (&all_processes, &event_child->head); free (event_child); remove_thread (current_inferior); current_inferior = (struct thread_info *) all_threads.head; /* If we were waiting for this particular child to do something... well, it did something. */ if (child != NULL) return wstat; /* Wait for a more interesting event. */ continue; } if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP && event_child->stop_expected) { if (debug_threads) fprintf (stderr, "Expected stop.\n"); event_child->stop_expected = 0; fbsd_resume_one_process (&event_child->head, event_child->stepping, 0); continue; } /* FIXME drow/2002-06-09: Get signal numbers from the inferior's thread library? */ if (WIFSTOPPED (wstat)) { if (debug_threads) fprintf (stderr, "Ignored signal %d for %d (LWP %d).\n", WSTOPSIG (wstat), event_child->tid, event_child->head.id); fbsd_resume_one_process (&event_child->head, event_child->stepping, WSTOPSIG (wstat)); continue; } } /* If this event was not handled above, and is not a SIGTRAP, report it. */ if (!WIFSTOPPED (wstat) || WSTOPSIG (wstat) != SIGTRAP) return wstat; /* If this target does not support breakpoints, we simply report the SIGTRAP; it's of no concern to us. */ if (the_low_target.get_pc == NULL) return wstat; stop_pc = get_stop_pc (); /* bp_reinsert will only be set if we were single-stepping. Notice that we will resume the process after hitting a gdbserver breakpoint; single-stepping to/over one is not supported (yet). */ if (event_child->bp_reinsert != 0) { if (debug_threads) fprintf (stderr, "Reinserted breakpoint.\n"); reinsert_breakpoint (event_child->bp_reinsert); event_child->bp_reinsert = 0; /* Clear the single-stepping flag and SIGTRAP as we resume. */ fbsd_resume_one_process (&event_child->head, 0, 0); continue; } if (debug_threads) fprintf (stderr, "Hit a (non-reinsert) breakpoint.\n"); if (check_breakpoints (stop_pc) != 0) { /* We hit one of our own breakpoints. We mark it as a pending breakpoint, so that check_removed_breakpoint () will do the PC adjustment for us at the appropriate time. */ event_child->pending_is_breakpoint = 1; event_child->pending_stop_pc = stop_pc; /* Now we need to put the breakpoint back. We continue in the event loop instead of simply replacing the breakpoint right away, in order to not lose signals sent to the thread that hit the breakpoint. Unfortunately this increases the window where another thread could sneak past the removed breakpoint. For the current use of server-side breakpoints (thread creation) this is acceptable; but it needs to be considered before this breakpoint mechanism can be used in more general ways. For some breakpoints it may be necessary to stop all other threads, but that should be avoided where possible. If breakpoint_reinsert_addr is NULL, that means that we can use PT_STEP on this platform. Uninsert the breakpoint, mark it for reinsertion, and single-step. Otherwise, call the target function to figure out where we need our temporary breakpoint, create it, and continue executing this process. */ if (the_low_target.breakpoint_reinsert_addr == NULL) { event_child->bp_reinsert = stop_pc; uninsert_breakpoint (stop_pc); fbsd_resume_one_process (&event_child->head, 1, 0); } else { reinsert_breakpoint_by_bp (stop_pc, (*the_low_target.breakpoint_reinsert_addr) ()); fbsd_resume_one_process (&event_child->head, 0, 0); } continue; } /* If we were single-stepping, we definitely want to report the SIGTRAP. The single-step operation has completed, so also clear the stepping flag; in general this does not matter, because the SIGTRAP will be reported to the client, which will give us a new action for this thread, but clear it for consistency anyway. It's safe to clear the stepping flag because the only consumer of get_stop_pc () after this point is check_removed_breakpoint, and pending_is_breakpoint is not set. It might be wiser to use a step_completed flag instead. */ if (event_child->stepping) { event_child->stepping = 0; return wstat; } /* A SIGTRAP that we can't explain. It may have been a breakpoint. Check if it is a breakpoint, and if so mark the process information accordingly. This will handle both the necessary fiddling with the PC on decr_pc_after_break targets and suppressing extra threads hitting a breakpoint if two hit it at once and then GDB removes it after the first is reported. Arguably it would be better to report multiple threads hitting breakpoints simultaneously, but the current remote protocol does not allow this. */ if ((*the_low_target.breakpoint_at) (stop_pc)) { event_child->pending_is_breakpoint = 1; event_child->pending_stop_pc = stop_pc; } return wstat; } /* NOTREACHED */ return 0; }
struct thread_info * find_thread_ptid (ptid_t ptid) { return (struct thread_info *) find_inferior_id (&all_threads, ptid); }
struct process_info * find_process_pid (int pid) { return (struct process_info *) find_inferior_id (&all_processes, pid_to_ptid (pid)); }
static int get_child_debug_event (struct target_waitstatus *ourstatus) { ptid_t ptid; last_sig = TARGET_SIGNAL_0; ourstatus->kind = TARGET_WAITKIND_SPURIOUS; /* Check if GDB sent us an interrupt request. */ check_remote_input_interrupt_request (); if (soft_interrupt_requested) { soft_interrupt_requested = 0; fake_breakpoint_event (); goto gotevent; } #ifndef _WIN32_WCE attaching = 0; #else if (attaching) { /* WinCE doesn't set an initial breakpoint automatically. To stop the inferior, we flush all currently pending debug events -- the thread list and the dll list are always reported immediatelly without delay, then, we suspend all threads and pretend we saw a trap at the current PC of the main thread. Contrary to desktop Windows, Windows CE *does* report the dll names on LOAD_DLL_DEBUG_EVENTs resulting from a DebugActiveProcess call. This limits the way we can detect if all the dlls have already been reported. If we get a real debug event before leaving attaching, the worst that will happen is the user will see a spurious breakpoint. */ current_event.dwDebugEventCode = 0; if (!WaitForDebugEvent (¤t_event, 0)) { OUTMSG2(("no attach events left\n")); fake_breakpoint_event (); attaching = 0; } else OUTMSG2(("got attach event\n")); } else #endif { /* Keep the wait time low enough for confortable remote interruption, but high enough so gdbserver doesn't become a bottleneck. */ if (!WaitForDebugEvent (¤t_event, 250)) { DWORD e = GetLastError(); if (e == ERROR_PIPE_NOT_CONNECTED) { /* This will happen if the loader fails to succesfully load the application, e.g., if the main executable tries to pull in a non-existing export from a DLL. */ ourstatus->kind = TARGET_WAITKIND_EXITED; ourstatus->value.integer = 1; return 1; } return 0; } } gotevent: switch (current_event.dwDebugEventCode) { case CREATE_THREAD_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT " "for pid=%d tid=%x)\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); /* Record the existence of this thread. */ child_add_thread (current_event.dwProcessId, current_event.dwThreadId, current_event.u.CreateThread.hThread, current_event.u.CreateThread.lpThreadLocalBase); break; case EXIT_THREAD_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); child_delete_thread (current_event.dwProcessId, current_event.dwThreadId); current_inferior = (struct thread_info *) all_threads.head; return 1; case CREATE_PROCESS_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); CloseHandle (current_event.u.CreateProcessInfo.hFile); current_process_handle = current_event.u.CreateProcessInfo.hProcess; main_thread_id = current_event.dwThreadId; ourstatus->kind = TARGET_WAITKIND_EXECD; ourstatus->value.execd_pathname = "Main executable"; /* Add the main thread. */ child_add_thread (current_event.dwProcessId, main_thread_id, current_event.u.CreateProcessInfo.hThread, current_event.u.CreateProcessInfo.lpThreadLocalBase); ourstatus->value.related_pid = debug_event_ptid (¤t_event); #ifdef _WIN32_WCE if (!attaching) { /* Windows CE doesn't set the initial breakpoint automatically like the desktop versions of Windows do. We add it explicitly here. It will be removed as soon as it is hit. */ set_breakpoint_at ((CORE_ADDR) (long) current_event.u .CreateProcessInfo.lpStartAddress, auto_delete_breakpoint); } #endif break; case EXIT_PROCESS_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); ourstatus->kind = TARGET_WAITKIND_EXITED; ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode; child_continue (DBG_CONTINUE, -1); CloseHandle (current_process_handle); current_process_handle = NULL; break; case LOAD_DLL_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); CloseHandle (current_event.u.LoadDll.hFile); handle_load_dll (); ourstatus->kind = TARGET_WAITKIND_LOADED; ourstatus->value.sig = TARGET_SIGNAL_TRAP; break; case UNLOAD_DLL_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); handle_unload_dll (); ourstatus->kind = TARGET_WAITKIND_LOADED; ourstatus->value.sig = TARGET_SIGNAL_TRAP; break; case EXCEPTION_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); handle_exception (ourstatus); break; case OUTPUT_DEBUG_STRING_EVENT: /* A message from the kernel (or Cygwin). */ OUTMSG2 (("gdbserver: kernel event OUTPUT_DEBUG_STRING_EVENT " "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); handle_output_debug_string (ourstatus); break; default: OUTMSG2 (("gdbserver: kernel event unknown " "for pid=%d tid=%x code=%ld\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId, current_event.dwDebugEventCode)); break; } ptid = debug_event_ptid (¤t_event); current_inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid); return 1; }