static int sigtrap_is_event (int status);
static int (*linux_nat_status_is_event) (int status) = sigtrap_is_event;
+\f
+/* LWP accessors. */
+
+/* See nat/linux-nat.h. */
+
+ptid_t
+ptid_of_lwp (struct lwp_info *lwp)
+{
+ return lwp->ptid;
+}
+
+/* See nat/linux-nat.h. */
+
+void
+lwp_set_arch_private_info (struct lwp_info *lwp,
+ struct arch_lwp_info *info)
+{
+ lwp->arch_private = info;
+}
+
+/* See nat/linux-nat.h. */
+
+struct arch_lwp_info *
+lwp_arch_private_info (struct lwp_info *lwp)
+{
+ return lwp->arch_private;
+}
+
+/* See nat/linux-nat.h. */
+
+int
+lwp_is_stopped (struct lwp_info *lwp)
+{
+ return lwp->stopped;
+}
+
+/* See nat/linux-nat.h. */
+
+enum target_stop_reason
+lwp_stop_reason (struct lwp_info *lwp)
+{
+ return lwp->stop_reason;
+}
+
\f
/* Trivial list manipulation functions to keep track of a list of
new stopped processes. */
return 0;
}
+/* Return the ptrace options that we want to try to enable. */
+
+static int
+linux_nat_ptrace_options (int attached)
+{
+ int options = 0;
+
+ if (!attached)
+ options |= PTRACE_O_EXITKILL;
+
+ options |= (PTRACE_O_TRACESYSGOOD
+ | PTRACE_O_TRACEVFORKDONE
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEEXEC);
+
+ return options;
+}
+
/* Initialize ptrace warnings and check for supported ptrace
features given PID.
static void
linux_init_ptrace (pid_t pid, int attached)
{
- linux_enable_event_reporting (pid, attached);
+ int options = linux_nat_ptrace_options (attached);
+
+ linux_enable_event_reporting (pid, options);
linux_ptrace_init_warnings ();
}
return NULL;
}
-/* Call CALLBACK with its second argument set to DATA for every LWP in
- the list. If CALLBACK returns 1 for a particular LWP, return a
- pointer to the structure describing that LWP immediately.
- Otherwise return NULL. */
+/* See nat/linux-nat.h. */
struct lwp_info *
iterate_over_lwps (ptid_t filter,
- int (*callback) (struct lwp_info *, void *),
+ iterate_over_lwps_ftype callback,
void *data)
{
struct lwp_info *lp, *lpnext;
if (ptid_match (lp->ptid, filter))
{
- if ((*callback) (lp, data))
+ if ((*callback) (lp, data) != 0)
return lp;
}
}
attach_proc_task_lwp_callback);
if (target_can_async_p ())
- target_async (inferior_event_handler, 0);
+ target_async (1);
}
/* Get pending status of LP. */
single-step it. If SIGNAL is nonzero, give it that signal. */
static void
-linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
+linux_resume_one_lwp_throw (struct lwp_info *lp, int step,
+ enum gdb_signal signo)
{
lp->step = step;
if (linux_nat_prepare_to_resume != NULL)
linux_nat_prepare_to_resume (lp);
linux_ops->to_resume (linux_ops, lp->ptid, step, signo);
- lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+
+ /* Successfully resumed. Clear state that no longer makes sense,
+ and mark the LWP as running. Must not do this before resuming
+ otherwise if that fails other code will be confused. E.g., we'd
+ later try to stop the LWP and hang forever waiting for a stop
+ status. Note that we must not throw after this is cleared,
+ otherwise handle_zombie_lwp_error would get confused. */
lp->stopped = 0;
+ lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
registers_changed_ptid (lp->ptid);
}
+/* Called when we try to resume a stopped LWP and that errors out. If
+ the LWP is no longer in ptrace-stopped state (meaning it's zombie,
+ or about to become), discard the error, clear any pending status
+ the LWP may have, and return true (we'll collect the exit status
+ soon enough). Otherwise, return false. */
+
+static int
+check_ptrace_stopped_lwp_gone (struct lwp_info *lp)
+{
+ /* If we get an error after resuming the LWP successfully, we'd
+ confuse !T state for the LWP being gone. */
+ gdb_assert (lp->stopped);
+
+ /* We can't just check whether the LWP is in 'Z (Zombie)' state,
+ because even if ptrace failed with ESRCH, the tracee may be "not
+ yet fully dead", but already refusing ptrace requests. In that
+ case the tracee has 'R (Running)' state for a little bit
+ (observed in Linux 3.18). See also the note on ESRCH in the
+ ptrace(2) man page. Instead, check whether the LWP has any state
+ other than ptrace-stopped. */
+
+ /* Don't assume anything if /proc/PID/status can't be read. */
+ if (linux_proc_pid_is_trace_stopped_nowarn (ptid_get_lwp (lp->ptid)) == 0)
+ {
+ lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+ lp->status = 0;
+ lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+ return 1;
+ }
+ return 0;
+}
+
+/* Like linux_resume_one_lwp_throw, but no error is thrown if the LWP
+ disappears while we try to resume it. */
+
+static void
+linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
+{
+ TRY
+ {
+ linux_resume_one_lwp_throw (lp, step, signo);
+ }
+ CATCH (ex, RETURN_MASK_ERROR)
+ {
+ if (!check_ptrace_stopped_lwp_gone (lp))
+ throw_exception (ex);
+ }
+ END_CATCH
+}
+
/* Resume LP. */
static void
if (target_can_async_p ())
{
- target_async (inferior_event_handler, 0);
+ target_async (1);
/* Tell the event loop we have something to process. */
async_file_mark ();
}
if (resume_many)
iterate_over_lwps (ptid, linux_nat_resume_callback, lp);
- linux_resume_one_lwp (lp, step, signo);
-
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLR: %s %s, %s (resume event thread)\n",
step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
- target_pid_to_str (ptid),
+ target_pid_to_str (lp->ptid),
(signo != GDB_SIGNAL_0
? strsignal (gdb_signal_to_host (signo)) : "0"));
+ linux_resume_one_lwp (lp, step, signo);
+
if (target_can_async_p ())
- target_async (inferior_event_handler, 0);
+ target_async (1);
}
/* Send a signal to an LWP. */
add_thread (new_lp->ptid);
}
+ /* Even if we're stopping the thread for some reason
+ internal to this module, from the user/frontend's
+ perspective, this new thread is running. */
+ set_running (new_lp->ptid, 1);
if (!stopping)
{
- set_running (new_lp->ptid, 1);
set_executing (new_lp->ptid, 1);
/* thread_db_attach_lwp -> lin_lwp_attach_lwp forced
resume_stop. */
if (lp->must_set_ptrace_flags)
{
struct inferior *inf = find_inferior_pid (ptid_get_pid (lp->ptid));
+ int options = linux_nat_ptrace_options (inf->attach_flag);
- linux_enable_event_reporting (ptid_get_lwp (lp->ptid), inf->attach_flag);
+ linux_enable_event_reporting (ptid_get_lwp (lp->ptid), options);
lp->must_set_ptrace_flags = 0;
}
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
- "SWC: Delayed SIGSTOP caught for %s.\n",
+ "SWC: Expected SIGSTOP caught for %s.\n",
target_pid_to_str (lp->ptid));
/* Reset SIGNALLED only after the stop_wait_callback call
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
- "CSBB: Push back software "
- "breakpoint for %s\n",
+ "CSBB: %s stopped by software "
+ "breakpoint\n",
target_pid_to_str (lp->ptid));
/* Back up the PC if necessary. */
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
- "CSBB: Push back hardware "
- "breakpoint/watchpoint for %s\n",
+ "CSBB: %s stopped by hardware "
+ "breakpoint/watchpoint\n",
target_pid_to_str (lp->ptid));
lp->stop_pc = pc;
lp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
return 1;
}
+ else if (siginfo.si_code == TRAP_TRACE)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "CSBB: %s stopped by trace\n",
+ target_pid_to_str (lp->ptid));
+ }
}
}
#else
breakpoint instruction. */
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
- "CB: Push back software breakpoint for %s\n",
+ "CSBB: %s stopped by software breakpoint\n",
target_pid_to_str (lp->ptid));
/* Back up the PC if necessary. */
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
- "CB: Push back hardware breakpoint for %s\n",
+ "CSBB: stopped by hardware breakpoint %s\n",
target_pid_to_str (lp->ptid));
lp->stop_pc = pc;
/* First see how many events we have. */
iterate_over_lwps (filter, count_events_callback, &num_events);
+ gdb_assert (num_events > 0);
/* Now randomly pick a LWP out of those that have had
events. */
if (WIFSTOPPED (status) && lp->must_set_ptrace_flags)
{
struct inferior *inf = find_inferior_pid (ptid_get_pid (lp->ptid));
+ int options = linux_nat_ptrace_options (inf->attach_flag);
- linux_enable_event_reporting (ptid_get_lwp (lp->ptid), inf->attach_flag);
+ linux_enable_event_reporting (ptid_get_lwp (lp->ptid), options);
lp->must_set_ptrace_flags = 0;
}
if (lp->signalled
&& WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP)
{
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "LLW: Delayed SIGSTOP caught for %s.\n",
- target_pid_to_str (lp->ptid));
-
lp->signalled = 0;
- if (lp->last_resume_kind != resume_stop)
+ if (lp->last_resume_kind == resume_stop)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLW: resume_stop SIGSTOP caught for %s.\n",
+ target_pid_to_str (lp->ptid));
+ }
+ else
{
- /* This is a delayed SIGSTOP. */
+ /* This is a delayed SIGSTOP. Filter out the event. */
- linux_resume_one_lwp (lp, lp->step, GDB_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
- "LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
+ "LLW: %s %s, 0, 0 (discard delayed SIGSTOP)\n",
lp->step ?
"PTRACE_SINGLESTEP" : "PTRACE_CONT",
target_pid_to_str (lp->ptid));
+ linux_resume_one_lwp (lp, lp->step, GDB_SIGNAL_0);
gdb_assert (lp->resumed);
-
- /* Discard the event. */
return NULL;
}
}
{
struct regcache *regcache = get_thread_regcache (lp->ptid);
struct gdbarch *gdbarch = get_regcache_arch (regcache);
- CORE_ADDR pc = regcache_read_pc (regcache);
- /* Don't bother if there's a breakpoint at PC that we'd hit
- immediately, and we're not waiting for this LWP. */
- if (!ptid_match (lp->ptid, *wait_ptid_p))
+ TRY
{
- if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
- return 0;
- }
+ CORE_ADDR pc = regcache_read_pc (regcache);
+ int leave_stopped = 0;
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "RSRL: resuming stopped-resumed LWP %s at %s: step=%d\n",
- target_pid_to_str (lp->ptid),
- paddress (gdbarch, pc),
- lp->step);
+ /* Don't bother if there's a breakpoint at PC that we'd hit
+ immediately, and we're not waiting for this LWP. */
+ if (!ptid_match (lp->ptid, *wait_ptid_p))
+ {
+ if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
+ leave_stopped = 1;
+ }
- linux_resume_one_lwp (lp, lp->step, GDB_SIGNAL_0);
+ if (!leave_stopped)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RSRL: resuming stopped-resumed LWP %s at "
+ "%s: step=%d\n",
+ target_pid_to_str (lp->ptid),
+ paddress (gdbarch, pc),
+ lp->step);
+
+ linux_resume_one_lwp_throw (lp, lp->step, GDB_SIGNAL_0);
+ }
+ }
+ CATCH (ex, RETURN_MASK_ERROR)
+ {
+ if (!check_ptrace_stopped_lwp_gone (lp))
+ throw_exception (ex);
+ }
+ END_CATCH
}
return 0;
return linux_thread_alive (ptid);
}
+/* Implement the to_update_thread_list target method for this
+ target. */
+
+static void
+linux_nat_update_thread_list (struct target_ops *ops)
+{
+ if (linux_supports_traceclone ())
+ {
+ /* With support for clone events, we add/delete threads from the
+ list as clone/exit events are processed, so just try deleting
+ exited threads still in the thread list. */
+ delete_exited_threads ();
+ }
+ else
+ prune_threads ();
+}
+
static char *
linux_nat_pid_to_str (struct target_ops *ops, ptid_t ptid)
{
static char *
linux_child_pid_to_exec_file (struct target_ops *self, int pid)
{
- static char buf[PATH_MAX];
- char name[PATH_MAX];
-
- xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid);
- memset (buf, 0, PATH_MAX);
- if (readlink (name, buf, PATH_MAX - 1) <= 0)
- strcpy (buf, name);
-
- return buf;
+ return linux_proc_pid_to_exec_file (pid);
}
/* Implement the to_xfer_partial interface for memory reads using the /proc
async_terminal_is_ours = 1;
}
-static void (*async_client_callback) (enum inferior_event_type event_type,
- void *context);
-static void *async_client_context;
-
/* SIGCHLD handler that serves two purposes: In non-stop/async mode,
so we notice when any child changes state, and notify the
event-loop; it allows us to use sigsuspend in linux_nat_wait_1
static void
handle_target_event (int error, gdb_client_data client_data)
{
- (*async_client_callback) (INF_REG_EVENT, async_client_context);
+ inferior_event_handler (INF_REG_EVENT, NULL);
}
/* Create/destroy the target events pipe. Returns previous state. */
/* target_async implementation. */
static void
-linux_nat_async (struct target_ops *ops,
- void (*callback) (enum inferior_event_type event_type,
- void *context),
- void *context)
+linux_nat_async (struct target_ops *ops, int enable)
{
- if (callback != NULL)
+ if (enable)
{
- async_client_callback = callback;
- async_client_context = context;
if (!linux_async_pipe (1))
{
add_file_handler (linux_nat_event_pipe[0],
}
else
{
- async_client_callback = callback;
- async_client_context = context;
delete_file_handler (linux_nat_event_pipe[0]);
linux_async_pipe (0);
}
{
/* Unregister from the event loop. */
if (linux_nat_is_async_p (self))
- linux_nat_async (self, NULL, NULL);
+ linux_nat_async (self, 0);
if (linux_ops->to_close)
linux_ops->to_close (linux_ops);
t->to_kill = linux_nat_kill;
t->to_mourn_inferior = linux_nat_mourn_inferior;
t->to_thread_alive = linux_nat_thread_alive;
+ t->to_update_thread_list = linux_nat_update_thread_list;
t->to_pid_to_str = linux_nat_pid_to_str;
t->to_thread_name = linux_nat_thread_name;
t->to_has_thread_control = tc_schedlock;
return 1;
}
+/* See nat/linux-nat.h. */
+
+ptid_t
+current_lwp_ptid (void)
+{
+ gdb_assert (ptid_lwp_p (inferior_ptid));
+ return inferior_ptid;
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
extern initialize_file_ftype _initialize_linux_nat;
sigdelset (&suspend_mask, SIGCHLD);
sigemptyset (&blocked_mask);
-
- /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
- support read-only process state. */
- linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
- | PTRACE_O_TRACEVFORKDONE
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEEXEC);
}
\f