#endif
#include <sys/ptrace.h>
#include "linux-nat.h"
+#include "linux-ptrace.h"
+#include "linux-procfs.h"
#include "linux-fork.h"
#include "gdbthread.h"
#include "gdbcmd.h"
#include "terminal.h"
#include <sys/vfs.h>
#include "solib.h"
+#include "linux-osdata.h"
#ifndef SPUFS_MAGIC
#define SPUFS_MAGIC 0x23c9b64e
#define O_LARGEFILE 0
#endif
-/* If the system headers did not provide the constants, hard-code the normal
- values. */
-#ifndef PTRACE_EVENT_FORK
-
-#define PTRACE_SETOPTIONS 0x4200
-#define PTRACE_GETEVENTMSG 0x4201
-
-/* Options set using PTRACE_SETOPTIONS. */
-#define PTRACE_O_TRACESYSGOOD 0x00000001
-#define PTRACE_O_TRACEFORK 0x00000002
-#define PTRACE_O_TRACEVFORK 0x00000004
-#define PTRACE_O_TRACECLONE 0x00000008
-#define PTRACE_O_TRACEEXEC 0x00000010
-#define PTRACE_O_TRACEVFORKDONE 0x00000020
-#define PTRACE_O_TRACEEXIT 0x00000040
-
-/* Wait extended result codes for the above trace options. */
-#define PTRACE_EVENT_FORK 1
-#define PTRACE_EVENT_VFORK 2
-#define PTRACE_EVENT_CLONE 3
-#define PTRACE_EVENT_EXEC 4
-#define PTRACE_EVENT_VFORK_DONE 5
-#define PTRACE_EVENT_EXIT 6
-
-#endif /* PTRACE_EVENT_FORK */
-
/* Unlike other extended result codes, WSTOPSIG (status) on
PTRACE_O_TRACESYSGOOD syscall events doesn't return SIGTRAP, but
instead SIGTRAP with bit 7 set. */
#define SYSCALL_SIGTRAP (SIGTRAP | 0x80)
-/* We can't always assume that this flag is available, but all systems
- with the ptrace event handlers also have __WALL, so it's safe to use
- here. */
-#ifndef __WALL
-#define __WALL 0x40000000 /* Wait for any child. */
-#endif
-
-#ifndef PTRACE_GETSIGINFO
-# define PTRACE_GETSIGINFO 0x4202
-# define PTRACE_SETSIGINFO 0x4203
-#endif
-
/* The single-threaded native GNU/Linux target_ops. We save a pointer for
the use of the multi-threaded target. */
static struct target_ops *linux_ops;
value);
}
-static int debug_linux_nat_async = 0;
-static void
-show_debug_linux_nat_async (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
-{
- fprintf_filtered (file,
- _("Debugging of GNU/Linux async lwp module is %s.\n"),
- value);
-}
-
-static int disable_randomization = 1;
-
-static void
-show_disable_randomization (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
-{
-#ifdef HAVE_PERSONALITY
- fprintf_filtered (file,
- _("Disabling randomization of debuggee's "
- "virtual address space is %s.\n"),
- value);
-#else /* !HAVE_PERSONALITY */
- fputs_filtered (_("Disabling randomization of debuggee's "
- "virtual address space is unsupported on\n"
- "this platform.\n"), file);
-#endif /* !HAVE_PERSONALITY */
-}
-
-static void
-set_disable_randomization (char *args, int from_tty,
- struct cmd_list_element *c)
-{
-#ifndef HAVE_PERSONALITY
- error (_("Disabling randomization of debuggee's "
- "virtual address space is unsupported on\n"
- "this platform."));
-#endif /* !HAVE_PERSONALITY */
-}
-
struct simple_pid_list
{
int pid;
static int linux_supports_tracevforkdone_flag = -1;
-/* Async mode support. */
-
-/* Zero if the async mode, although enabled, is masked, which means
- linux_nat_wait should behave as if async mode was off. */
-static int linux_nat_async_mask_value = 1;
-
/* Stores the current used ptrace() options. */
static int current_ptrace_options = 0;
+/* Async mode support. */
+
/* The read/write ends of the pipe registered as waitable file in the
event loop. */
static int linux_nat_event_pipe[2] = { -1, -1 };
(enum inferior_event_type event_type,
void *context),
void *context);
-static int linux_nat_async_mask (int mask);
static int kill_lwp (int lwpid, int signo);
static int stop_callback (struct lwp_info *lp, void *data);
*listp = new_pid;
}
+static int
+in_pid_list_p (struct simple_pid_list *list, int pid)
+{
+ struct simple_pid_list *p;
+
+ for (p = list; p != NULL; p = p->next)
+ if (p->pid == pid)
+ return 1;
+ return 0;
+}
+
static int
pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
{
return 0;
}
-static void
-linux_record_stopped_pid (int pid, int status)
-{
- add_to_pid_list (&stopped_pids, pid, status);
-}
-
\f
/* A helper function for linux_test_for_tracefork, called after fork (). */
add_thread (inferior_ptid);
child_lp = add_lwp (inferior_ptid);
child_lp->stopped = 1;
- child_lp->resumed = 1;
+ child_lp->last_resume_kind = resume_stop;
/* If this is a vfork child, then the address-space is
shared with the parent. */
if (has_vforked)
{
- struct lwp_info *lp;
+ struct lwp_info *parent_lp;
struct inferior *parent_inf;
parent_inf = current_inferior ();
parent_inf->waiting_for_vfork_done = detach_fork;
parent_inf->pspace->breakpoints_not_allowed = detach_fork;
- lp = find_lwp_pid (pid_to_ptid (parent_pid));
+ parent_lp = find_lwp_pid (pid_to_ptid (parent_pid));
gdb_assert (linux_supports_tracefork_flag >= 0);
+
if (linux_supports_tracevforkdone (0))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LCFF: waiting for VFORK_DONE on %d\n",
parent_pid);
-
- lp->stopped = 1;
- lp->resumed = 1;
+ parent_lp->stopped = 1;
/* We'll handle the VFORK_DONE event like any other
event, in target_wait. */
and leave it pending. The next linux_nat_resume call
will notice a pending event, and bypasses actually
resuming the inferior. */
- lp->status = 0;
- lp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
- lp->stopped = 0;
- lp->resumed = 1;
+ parent_lp->status = 0;
+ parent_lp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+ parent_lp->stopped = 1;
/* If we're in async mode, need to tell the event loop
there's something here to process. */
else
{
struct inferior *parent_inf, *child_inf;
- struct lwp_info *lp;
+ struct lwp_info *child_lp;
struct program_space *parent_pspace;
if (info_verbose || debug_linux_nat)
inferior_ptid = ptid_build (child_pid, child_pid, 0);
add_thread (inferior_ptid);
- lp = add_lwp (inferior_ptid);
- lp->stopped = 1;
- lp->resumed = 1;
+ child_lp = add_lwp (inferior_ptid);
+ child_lp->stopped = 1;
+ child_lp->last_resume_kind = resume_stop;
/* If this is a vfork child, then the address-space is shared
with the parent. If we detached from the parent, then we can
return !linux_supports_tracefork (pid);
}
+static int
+linux_child_remove_fork_catchpoint (int pid)
+{
+ return 0;
+}
+
static int
linux_child_insert_vfork_catchpoint (int pid)
{
return !linux_supports_tracefork (pid);
}
+static int
+linux_child_remove_vfork_catchpoint (int pid)
+{
+ return 0;
+}
+
static int
linux_child_insert_exec_catchpoint (int pid)
{
return !linux_supports_tracefork (pid);
}
+static int
+linux_child_remove_exec_catchpoint (int pid)
+{
+ return 0;
+}
+
static int
linux_child_set_syscall_catchpoint (int pid, int needed, int any_count,
int table_size, int *table)
{
sigprocmask (SIG_SETMASK, prev_mask, NULL);
}
+
+/* Mask of signals to pass directly to the inferior. */
+static sigset_t pass_mask;
+
+/* Update signals to pass to the inferior. */
+static void
+linux_nat_pass_signals (int numsigs, unsigned char *pass_signals)
+{
+ int signo;
+
+ sigemptyset (&pass_mask);
+
+ for (signo = 1; signo < NSIG; signo++)
+ {
+ int target_signo = target_signal_from_host (signo);
+ if (target_signo < numsigs && pass_signals[target_signo])
+ sigaddset (&pass_mask, signo);
+ }
+}
+
\f
/* Prototypes for local functions. */
memset (lp, 0, sizeof (struct lwp_info));
+ lp->last_resume_kind = resume_continue;
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
lp->ptid = ptid;
delete_lwp (lp->ptid);
}
-/* Return an lwp's tgid, found in `/proc/PID/status'. */
-
-int
-linux_proc_get_tgid (int lwpid)
-{
- FILE *status_file;
- char buf[100];
- int tgid = -1;
-
- snprintf (buf, sizeof (buf), "/proc/%d/status", (int) lwpid);
- status_file = fopen (buf, "r");
- if (status_file != NULL)
- {
- while (fgets (buf, sizeof (buf), status_file))
- {
- if (strncmp (buf, "Tgid:", 5) == 0)
- {
- tgid = strtoul (buf + strlen ("Tgid:"), NULL, 10);
- break;
- }
- }
-
- fclose (status_file);
- }
-
- return tgid;
-}
-
/* Detect `T (stopped)' in `/proc/PID/status'.
Other states including `T (tracing stop)' are reported as false. */
return status;
}
-/* Attach to the LWP specified by PID. Return 0 if successful or -1
- if the new LWP could not be attached. */
+/* Attach to the LWP specified by PID. Return 0 if successful, -1 if
+ the new LWP could not be attached, or 1 if we're already auto
+ attached to this thread, but haven't processed the
+ PTRACE_EVENT_CLONE event of its parent thread, so we just ignore
+ its existance, without considering it an error. */
int
lin_lwp_attach_lwp (ptid_t ptid)
{
struct lwp_info *lp;
sigset_t prev_mask;
+ int lwpid;
gdb_assert (is_lwp (ptid));
block_child_signals (&prev_mask);
lp = find_lwp_pid (ptid);
+ lwpid = GET_LWP (ptid);
/* We assume that we're already attached to any LWP that has an id
equal to the overall process id, and to any LWP that is already
and we've had PID wraparound since we last tried to stop all threads,
this assumption might be wrong; fortunately, this is very unlikely
to happen. */
- if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
+ if (lwpid != GET_PID (ptid) && lp == NULL)
{
int status, cloned = 0, signalled = 0;
- if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
+ if (ptrace (PTRACE_ATTACH, lwpid, 0, 0) < 0)
{
+ if (linux_supports_tracefork_flag)
+ {
+ /* If we haven't stopped all threads when we get here,
+ we may have seen a thread listed in thread_db's list,
+ but not processed the PTRACE_EVENT_CLONE yet. If
+ that's the case, ignore this new thread, and let
+ normal event handling discover it later. */
+ if (in_pid_list_p (stopped_pids, lwpid))
+ {
+ /* We've already seen this thread stop, but we
+ haven't seen the PTRACE_EVENT_CLONE extended
+ event yet. */
+ restore_child_signals_mask (&prev_mask);
+ return 0;
+ }
+ else
+ {
+ int new_pid;
+ int status;
+
+ /* See if we've got a stop for this new child
+ pending. If so, we're already attached. */
+ new_pid = my_waitpid (lwpid, &status, WNOHANG);
+ if (new_pid == -1 && errno == ECHILD)
+ new_pid = my_waitpid (lwpid, &status, __WCLONE | WNOHANG);
+ if (new_pid != -1)
+ {
+ if (WIFSTOPPED (status))
+ add_to_pid_list (&stopped_pids, lwpid, status);
+
+ restore_child_signals_mask (&prev_mask);
+ return 1;
+ }
+ }
+ }
+
/* If we fail to attach to the thread, issue a warning,
but continue. One way this can happen is if thread
creation is interrupted; as of Linux kernel 2.6.19, a
status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
if (!WIFSTOPPED (status))
- return -1;
+ {
+ restore_child_signals_mask (&prev_mask);
+ return 1;
+ }
lp = add_lwp (ptid);
lp->stopped = 1;
lp->stopped = 1;
}
+ lp->last_resume_kind = resume_stop;
restore_child_signals_mask (&prev_mask);
return 0;
}
}
#endif /* HAVE_PERSONALITY */
+ /* Make sure we report all signals during startup. */
+ linux_nat_pass_signals (0, NULL);
+
linux_ops->to_create_inferior (ops, exec_file, allargs, env, from_tty);
#ifdef HAVE_PERSONALITY
int status;
ptid_t ptid;
+ /* Make sure we report all signals during attach. */
+ linux_nat_pass_signals (0, NULL);
+
linux_ops->to_attach (ops, args, from_tty);
/* The ptrace base target adds the main thread with (pid,0,0)
/* Resume LP. */
-static int
-resume_callback (struct lwp_info *lp, void *data)
+static void
+resume_lwp (struct lwp_info *lp, int step)
{
- struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
-
- if (lp->stopped && inf->vfork_child != NULL)
+ if (lp->stopped)
{
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "RC: Not resuming %s (vfork parent)\n",
- target_pid_to_str (lp->ptid));
+ struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
+
+ if (inf->vfork_child != NULL)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RC: Not resuming %s (vfork parent)\n",
+ target_pid_to_str (lp->ptid));
+ }
+ else if (lp->status == 0
+ && lp->waitstatus.kind == TARGET_WAITKIND_IGNORE)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RC: PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
+ target_pid_to_str (lp->ptid));
+
+ linux_ops->to_resume (linux_ops,
+ pid_to_ptid (GET_LWP (lp->ptid)),
+ step, TARGET_SIGNAL_0);
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RC: PTRACE_CONT %s, 0, 0 (resume sibling)\n",
+ target_pid_to_str (lp->ptid));
+ lp->stopped = 0;
+ lp->step = step;
+ memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+ lp->stopped_by_watchpoint = 0;
+ }
+ else
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RC: Not resuming sibling %s (has pending)\n",
+ target_pid_to_str (lp->ptid));
+ }
}
- else if (lp->stopped && lp->status == 0)
+ else
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
- "RC: PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
+ "RC: Not resuming sibling %s (not stopped)\n",
target_pid_to_str (lp->ptid));
-
- linux_ops->to_resume (linux_ops,
- pid_to_ptid (GET_LWP (lp->ptid)),
- 0, TARGET_SIGNAL_0);
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "RC: PTRACE_CONT %s, 0, 0 (resume sibling)\n",
- target_pid_to_str (lp->ptid));
- lp->stopped = 0;
- lp->step = 0;
- memset (&lp->siginfo, 0, sizeof (lp->siginfo));
- lp->stopped_by_watchpoint = 0;
}
- else if (lp->stopped && debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "RC: Not resuming sibling %s (has pending)\n",
- target_pid_to_str (lp->ptid));
- else if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "RC: Not resuming sibling %s (not stopped)\n",
- target_pid_to_str (lp->ptid));
+}
+static int
+resume_callback (struct lwp_info *lp, void *data)
+{
+ resume_lwp (lp, 0);
return 0;
}
resume_clear_callback (struct lwp_info *lp, void *data)
{
lp->resumed = 0;
+ lp->last_resume_kind = resume_stop;
return 0;
}
resume_set_callback (struct lwp_info *lp, void *data)
{
lp->resumed = 1;
+ lp->last_resume_kind = resume_continue;
return 0;
}
/* Remember if we're stepping. */
lp->step = step;
+ lp->last_resume_kind = step ? resume_step : resume_continue;
/* If we have a pending wait status for this thread, there is no
point in resuming the process. But first make sure that
if (lp->status && WIFSTOPPED (lp->status))
{
- enum target_signal saved_signo;
- struct inferior *inf;
-
- inf = find_inferior_pid (ptid_get_pid (lp->ptid));
- gdb_assert (inf);
- saved_signo = target_signal_from_host (WSTOPSIG (lp->status));
-
- /* Defer to common code if we're gaining control of the
- inferior. */
- if (inf->control.stop_soon == NO_STOP_QUIETLY
- && signal_stop_state (saved_signo) == 0
- && signal_print_state (saved_signo) == 0
- && signal_pass_state (saved_signo) == 1)
+ if (!lp->step
+ && WSTOPSIG (lp->status)
+ && sigismember (&pass_mask, WSTOPSIG (lp->status)))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
/* FIXME: What should we do if we are supposed to continue
this thread with a signal? */
gdb_assert (signo == TARGET_SIGNAL_0);
- signo = saved_signo;
+ signo = target_signal_from_host (WSTOPSIG (lp->status));
lp->status = 0;
}
}
if (event == PTRACE_EVENT_FORK
&& linux_fork_checkpointing_p (GET_PID (lp->ptid)))
{
- struct fork_info *fp;
-
/* Handle checkpointing by linux-fork.c here as a special
case. We don't want the follow-fork-mode or 'catch fork'
to interfere with this. */
detach_breakpoints (new_pid);
/* Retain child fork in ptrace (stopped) state. */
- fp = find_fork_pid (new_pid);
- if (!fp)
- fp = add_fork (new_pid);
+ if (!find_fork_pid (new_pid))
+ add_fork (new_pid);
/* Report as spurious, so that infrun doesn't want to follow
this fork. We're actually doing an infcall in
new_lp->stopped = 0;
new_lp->resumed = 1;
+ new_lp->last_resume_kind = resume_continue;
signo = (status
? target_signal_from_host (WSTOPSIG (status))
_("unknown ptrace event %d"), event);
}
+/* Return non-zero if LWP is a zombie. */
+
+static int
+linux_lwp_is_zombie (long lwp)
+{
+ char buffer[MAXPATHLEN];
+ FILE *procfile;
+ int retval = 0;
+
+ xsnprintf (buffer, sizeof (buffer), "/proc/%ld/status", lwp);
+ procfile = fopen (buffer, "r");
+ if (procfile == NULL)
+ {
+ warning (_("unable to open /proc file '%s'"), buffer);
+ return 0;
+ }
+ while (fgets (buffer, sizeof (buffer), procfile) != NULL)
+ if (strcmp (buffer, "State:\tZ (zombie)\n") == 0)
+ {
+ retval = 1;
+ break;
+ }
+ fclose (procfile);
+
+ return retval;
+}
+
/* Wait for LP to stop. Returns the wait status, or 0 if the LWP has
exited. */
wait_lwp (struct lwp_info *lp)
{
pid_t pid;
- int status;
+ int status = 0;
int thread_dead = 0;
+ sigset_t prev_mask;
gdb_assert (!lp->stopped);
gdb_assert (lp->status == 0);
- pid = my_waitpid (GET_LWP (lp->ptid), &status, 0);
- if (pid == -1 && errno == ECHILD)
+ /* Make sure SIGCHLD is blocked for sigsuspend avoiding a race below. */
+ block_child_signals (&prev_mask);
+
+ for (;;)
{
- pid = my_waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
+ /* If my_waitpid returns 0 it means the __WCLONE vs. non-__WCLONE kind
+ was right and we should just call sigsuspend. */
+
+ pid = my_waitpid (GET_LWP (lp->ptid), &status, WNOHANG);
+ if (pid == -1 && errno == ECHILD)
+ pid = my_waitpid (GET_LWP (lp->ptid), &status, __WCLONE | WNOHANG);
if (pid == -1 && errno == ECHILD)
{
/* The thread has previously exited. We need to delete it
fprintf_unfiltered (gdb_stdlog, "WL: %s vanished.\n",
target_pid_to_str (lp->ptid));
}
+ if (pid != 0)
+ break;
+
+ /* Bugs 10970, 12702.
+ Thread group leader may have exited in which case we'll lock up in
+ waitpid if there are other threads, even if they are all zombies too.
+ Basically, we're not supposed to use waitpid this way.
+ __WCLONE is not applicable for the leader so we can't use that.
+ LINUX_NAT_THREAD_ALIVE cannot be used here as it requires a STOPPED
+ process; it gets ESRCH both for the zombie and for running processes.
+
+ As a workaround, check if we're waiting for the thread group leader and
+ if it's a zombie, and avoid calling waitpid if it is.
+
+ This is racy, what if the tgl becomes a zombie right after we check?
+ Therefore always use WNOHANG with sigsuspend - it is equivalent to
+ waiting waitpid but the linux_lwp_is_zombie is safe this way. */
+
+ if (GET_PID (lp->ptid) == GET_LWP (lp->ptid)
+ && linux_lwp_is_zombie (GET_LWP (lp->ptid)))
+ {
+ thread_dead = 1;
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "WL: Thread group leader %s vanished.\n",
+ target_pid_to_str (lp->ptid));
+ break;
+ }
+
+ /* Wait for next SIGCHLD and try again. This may let SIGCHLD handlers
+ get invoked despite our caller had them intentionally blocked by
+ block_child_signals. This is sensitive only to the loop of
+ linux_nat_wait_1 and there if we get called my_waitpid gets called
+ again before it gets to sigsuspend so we can safely let the handlers
+ get executed here. */
+
+ sigsuspend (&suspend_mask);
}
+ restore_child_signals_mask (&prev_mask);
+
if (!thread_dead)
{
gdb_assert (pid == GET_LWP (lp->ptid));
target_pid_to_str (lp->ptid),
status_to_str (status));
}
- }
- /* Check if the thread has exited. */
- if (WIFEXITED (status) || WIFSIGNALED (status))
- {
- thread_dead = 1;
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog, "WL: %s exited.\n",
- target_pid_to_str (lp->ptid));
+ /* Check if the thread has exited. */
+ if (WIFEXITED (status) || WIFSIGNALED (status))
+ {
+ thread_dead = 1;
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog, "WL: %s exited.\n",
+ target_pid_to_str (lp->ptid));
+ }
}
if (thread_dead)
if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
{
/* A ptrace event, like PTRACE_FORK|VFORK|EXEC, syscall event,
- or a a pending process exit. Note that `W_EXITCODE(0,0) ==
+ or a pending process exit. Note that `W_EXITCODE(0,0) ==
0', so a clean process exit can not be stored pending in
lp->status, it is indistinguishable from
no-pending-status. */
static int
running_callback (struct lwp_info *lp, void *data)
{
- return (lp->stopped == 0 || (lp->status != 0 && lp->resumed));
+ return (!lp->stopped
+ || ((lp->status != 0
+ || lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+ && lp->resumed));
}
/* Count the LWP's that have had events. */
static int
select_singlestep_lwp_callback (struct lwp_info *lp, void *data)
{
- if (lp->step && lp->status != 0)
+ if (lp->last_resume_kind == resume_step
+ && lp->status != 0)
return 1;
else
return 0;
static int
stop_and_resume_callback (struct lwp_info *lp, void *data)
{
- struct lwp_info *ptr;
-
- if (!lp->stopped && !lp->signalled)
+ if (!lp->stopped)
{
+ enum resume_kind last_resume_kind = lp->last_resume_kind;
+ ptid_t ptid = lp->ptid;
+
stop_callback (lp, NULL);
stop_wait_callback (lp, NULL);
- /* Resume if the lwp still exists. */
- for (ptr = lwp_list; ptr; ptr = ptr->next)
- if (lp == ptr)
- {
- resume_callback (lp, NULL);
- resume_set_callback (lp, NULL);
- }
+
+ /* Resume if the lwp still exists, and the core wanted it
+ running. */
+ if (last_resume_kind != resume_stop)
+ {
+ lp = find_lwp_pid (ptid);
+ if (lp)
+ resume_lwp (lp, lp->step);
+ }
}
return 0;
}
from waitpid before or after the event is. */
if (WIFSTOPPED (status) && !lp)
{
- linux_record_stopped_pid (lwpid, status);
+ add_to_pid_list (&stopped_pids, lwpid, status);
return NULL;
}
"LLW: Delayed SIGSTOP caught for %s.\n",
target_pid_to_str (lp->ptid));
- /* This is a delayed SIGSTOP. */
lp->signalled = 0;
- registers_changed ();
+ if (lp->last_resume_kind != resume_stop)
+ {
+ /* This is a delayed SIGSTOP. */
- linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
+ registers_changed ();
+
+ linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
- lp->step ?
- "PTRACE_SINGLESTEP" : "PTRACE_CONT",
- target_pid_to_str (lp->ptid));
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
+ lp->step ?
+ "PTRACE_SINGLESTEP" : "PTRACE_CONT",
+ target_pid_to_str (lp->ptid));
- lp->stopped = 0;
- gdb_assert (lp->resumed);
+ lp->stopped = 0;
+ gdb_assert (lp->resumed);
- /* Discard the event. */
- return NULL;
+ /* Discard the event. */
+ return NULL;
+ }
}
/* Make sure we don't report a SIGINT that we have already displayed
int status = 0;
pid_t pid;
- if (debug_linux_nat_async)
+ if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "LLW: enter\n");
/* The first time we get here after starting a new inferior, we may
{
ourstatus->kind = TARGET_WAITKIND_IGNORE;
- if (debug_linux_nat_async)
+ if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "LLW: exit (no resumed LWP)\n");
restore_child_signals_mask (&prev_mask);
lp = NULL;
}
- if (lp && lp->signalled)
+ if (lp && lp->signalled && lp->last_resume_kind != resume_stop)
{
/* A pending SIGSTOP may interfere with the normal stream of
events. In a typical case where interference is a problem,
/* No interesting event. */
ourstatus->kind = TARGET_WAITKIND_IGNORE;
- if (debug_linux_nat_async)
+ if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "LLW: exit (ignore)\n");
restore_child_signals_mask (&prev_mask);
/* No interesting event for PID yet. */
ourstatus->kind = TARGET_WAITKIND_IGNORE;
- if (debug_linux_nat_async)
+ if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "LLW: exit (ignore)\n");
restore_child_signals_mask (&prev_mask);
if (WIFSTOPPED (status))
{
enum target_signal signo = target_signal_from_host (WSTOPSIG (status));
- struct inferior *inf;
-
- inf = find_inferior_pid (ptid_get_pid (lp->ptid));
- gdb_assert (inf);
- /* Defer to common code if we get a signal while
- single-stepping, since that may need special care, e.g. to
- skip the signal handler, or, if we're gaining control of the
- inferior. */
+ /* When using hardware single-step, we need to report every signal.
+ Otherwise, signals in pass_mask may be short-circuited. */
if (!lp->step
- && inf->control.stop_soon == NO_STOP_QUIETLY
- && signal_stop_state (signo) == 0
- && signal_print_state (signo) == 0
- && signal_pass_state (signo) == 1)
+ && WSTOPSIG (status) && sigismember (&pass_mask, WSTOPSIG (status)))
{
/* FIMXE: kettenis/2001-06-06: Should we resume all threads
here? It is not clear we should. GDB may not expect
iterate_over_lwps (minus_one_ptid, resume_clear_callback, NULL);
}
else
- lp->resumed = 0;
+ {
+ lp->resumed = 0;
+ lp->last_resume_kind = resume_stop;
+ }
if (linux_nat_status_is_event (status))
{
else
store_waitstatus (ourstatus, status);
- if (debug_linux_nat_async)
+ if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
restore_child_signals_mask (&prev_mask);
+ if (lp->last_resume_kind == resume_stop
+ && ourstatus->kind == TARGET_WAITKIND_STOPPED
+ && WSTOPSIG (status) == SIGSTOP)
+ {
+ /* A thread that has been requested to stop by GDB with
+ target_stop, and it stopped cleanly, so report as SIG0. The
+ use of SIGSTOP is an implementation detail. */
+ ourstatus->value.sig = TARGET_SIGNAL_0;
+ }
+
if (ourstatus->kind == TARGET_WAITKIND_EXITED
|| ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
lp->core = -1;
static int
kill_callback (struct lwp_info *lp, void *data)
{
+ /* PTRACE_KILL may resume the inferior. Send SIGKILL first. */
+
+ errno = 0;
+ kill (GET_LWP (lp->ptid), SIGKILL);
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "KC: kill (SIGKILL) %s, 0, 0 (%s)\n",
+ target_pid_to_str (lp->ptid),
+ errno ? safe_strerror (errno) : "OK");
+
+ /* Some kernels ignore even SIGKILL for processes under ptrace. */
+
errno = 0;
ptrace (PTRACE_KILL, GET_LWP (lp->ptid), 0, 0);
if (debug_linux_nat)
static int
linux_thread_alive (ptid_t ptid)
{
- int err;
+ int err, tmp_errno;
gdb_assert (is_lwp (ptid));
running thread errors out claiming that the thread doesn't
exist. */
err = kill_lwp (GET_LWP (ptid), 0);
-
+ tmp_errno = errno;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLTA: KILL(SIG0) %s (%s)\n",
target_pid_to_str (ptid),
- err ? safe_strerror (err) : "OK");
+ err ? safe_strerror (tmp_errno) : "OK");
if (err != 0)
return 0;
if (get_exec_file (0))
{
- strncpy (fname, strrchr (get_exec_file (0), '/') + 1, sizeof (fname));
+ strncpy (fname, lbasename (get_exec_file (0)), sizeof (fname));
strncpy (psargs, get_exec_file (0), sizeof (psargs));
if (get_inferior_args ())
{
const char *annex, gdb_byte *readbuf,
const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
{
- /* We make the process list snapshot when the object starts to be
- read. */
- static const char *buf;
- static LONGEST len_avail = -1;
- static struct obstack obstack;
-
- DIR *dirp;
-
gdb_assert (object == TARGET_OBJECT_OSDATA);
- if (!annex)
- {
- if (offset == 0)
- {
- if (len_avail != -1 && len_avail != 0)
- obstack_free (&obstack, NULL);
- len_avail = 0;
- buf = NULL;
- obstack_init (&obstack);
- obstack_grow_str (&obstack, "<osdata type=\"types\">\n");
-
- obstack_xml_printf (&obstack,
- "<item>"
- "<column name=\"Type\">processes</column>"
- "<column name=\"Description\">"
- "Listing of all processes</column>"
- "</item>");
-
- obstack_grow_str0 (&obstack, "</osdata>\n");
- buf = obstack_finish (&obstack);
- len_avail = strlen (buf);
- }
-
- if (offset >= len_avail)
- {
- /* Done. Get rid of the obstack. */
- obstack_free (&obstack, NULL);
- buf = NULL;
- len_avail = 0;
- return 0;
- }
-
- if (len > len_avail - offset)
- len = len_avail - offset;
- memcpy (readbuf, buf + offset, len);
-
- return len;
- }
-
- if (strcmp (annex, "processes") != 0)
- return 0;
-
- gdb_assert (readbuf && !writebuf);
-
- if (offset == 0)
- {
- if (len_avail != -1 && len_avail != 0)
- obstack_free (&obstack, NULL);
- len_avail = 0;
- buf = NULL;
- obstack_init (&obstack);
- obstack_grow_str (&obstack, "<osdata type=\"processes\">\n");
-
- dirp = opendir ("/proc");
- if (dirp)
- {
- struct dirent *dp;
-
- while ((dp = readdir (dirp)) != NULL)
- {
- struct stat statbuf;
- char procentry[sizeof ("/proc/4294967295")];
-
- if (!isdigit (dp->d_name[0])
- || NAMELEN (dp) > sizeof ("4294967295") - 1)
- continue;
-
- sprintf (procentry, "/proc/%s", dp->d_name);
- if (stat (procentry, &statbuf) == 0
- && S_ISDIR (statbuf.st_mode))
- {
- char *pathname;
- FILE *f;
- char cmd[MAXPATHLEN + 1];
- struct passwd *entry;
-
- pathname = xstrprintf ("/proc/%s/cmdline", dp->d_name);
- entry = getpwuid (statbuf.st_uid);
-
- if ((f = fopen (pathname, "r")) != NULL)
- {
- size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
-
- if (len > 0)
- {
- int i;
-
- for (i = 0; i < len; i++)
- if (cmd[i] == '\0')
- cmd[i] = ' ';
- cmd[len] = '\0';
-
- obstack_xml_printf (
- &obstack,
- "<item>"
- "<column name=\"pid\">%s</column>"
- "<column name=\"user\">%s</column>"
- "<column name=\"command\">%s</column>"
- "</item>",
- dp->d_name,
- entry ? entry->pw_name : "?",
- cmd);
- }
- fclose (f);
- }
-
- xfree (pathname);
- }
- }
-
- closedir (dirp);
- }
-
- obstack_grow_str0 (&obstack, "</osdata>\n");
- buf = obstack_finish (&obstack);
- len_avail = strlen (buf);
- }
-
- if (offset >= len_avail)
- {
- /* Done. Get rid of the obstack. */
- obstack_free (&obstack, NULL);
- buf = NULL;
- len_avail = 0;
- return 0;
- }
-
- if (len > len_avail - offset)
- len = len_avail - offset;
- memcpy (readbuf, buf + offset, len);
-
- return len;
+ return linux_common_xfer_osdata (annex, readbuf, offset, len);
}
static LONGEST
linux_target_install_ops (struct target_ops *t)
{
t->to_insert_fork_catchpoint = linux_child_insert_fork_catchpoint;
+ t->to_remove_fork_catchpoint = linux_child_remove_fork_catchpoint;
t->to_insert_vfork_catchpoint = linux_child_insert_vfork_catchpoint;
+ t->to_remove_vfork_catchpoint = linux_child_remove_vfork_catchpoint;
t->to_insert_exec_catchpoint = linux_child_insert_exec_catchpoint;
+ t->to_remove_exec_catchpoint = linux_child_remove_exec_catchpoint;
t->to_set_syscall_catchpoint = linux_child_set_syscall_catchpoint;
t->to_pid_to_exec_file = linux_child_pid_to_exec_file;
t->to_post_startup_inferior = linux_child_post_startup_inferior;
/* NOTE: palves 2008-03-21: We're only async when the user requests
it explicitly with the "set target-async" command.
Someday, linux will always be async. */
- if (!target_async_permitted)
- return 0;
-
- /* See target.h/target_async_mask. */
- return linux_nat_async_mask_value;
+ return target_async_permitted;
}
/* target_can_async_p implementation. */
/* NOTE: palves 2008-03-21: We're only async when the user requests
it explicitly with the "set target-async" command.
Someday, linux will always be async. */
- if (!target_async_permitted)
- return 0;
-
- /* See target.h/target_async_mask. */
- return linux_nat_async_mask_value;
+ return target_async_permitted;
}
static int
return linux_multi_process;
}
-/* target_async_mask implementation. */
-
static int
-linux_nat_async_mask (int new_mask)
+linux_nat_supports_disable_randomization (void)
{
- int curr_mask = linux_nat_async_mask_value;
-
- if (curr_mask != new_mask)
- {
- if (new_mask == 0)
- {
- linux_nat_async (NULL, 0);
- linux_nat_async_mask_value = new_mask;
- }
- else
- {
- linux_nat_async_mask_value = new_mask;
-
- /* If we're going out of async-mask in all-stop, then the
- inferior is stopped. The next resume will call
- target_async. In non-stop, the target event source
- should be always registered in the event loop. Do so
- now. */
- if (non_stop)
- linux_nat_async (inferior_event_handler, 0);
- }
- }
-
- return curr_mask;
+#ifdef HAVE_PERSONALITY
+ return 1;
+#else
+ return 0;
+#endif
}
static int async_terminal_is_ours = 1;
{
int old_errno = errno;
- if (debug_linux_nat_async)
- fprintf_unfiltered (gdb_stdlog, "sigchld\n");
+ if (debug_linux_nat)
+ ui_file_write_async_safe (gdb_stdlog,
+ "sigchld\n", sizeof ("sigchld\n") - 1);
if (signo == SIGCHLD
&& linux_nat_event_pipe[0] != -1)
linux_nat_async (void (*callback) (enum inferior_event_type event_type,
void *context), void *context)
{
- if (linux_nat_async_mask_value == 0 || !target_async_permitted)
- internal_error (__FILE__, __LINE__,
- "Calling target_async when async is masked");
-
if (callback != NULL)
{
async_client_callback = callback;
target_pid_to_str (lwp->ptid));
- stop_callback (lwp, NULL);
- stop_wait_callback (lwp, NULL);
-
- /* If the lwp exits while we try to stop it, there's nothing
- else to do. */
- lwp = find_lwp_pid (ptid);
- if (lwp == NULL)
- return 0;
+ if (lwp->last_resume_kind == resume_stop)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "linux-nat: already stopping LWP %ld at "
+ "GDB's request\n",
+ ptid_get_lwp (lwp->ptid));
+ return 0;
+ }
- /* If we didn't collect any signal other than SIGSTOP while
- stopping the LWP, push a SIGNAL_0 event. In either case, the
- event-loop will end up calling target_wait which will collect
- these. */
- if (lwp->status == 0)
- lwp->status = W_STOPCODE (0);
- async_file_mark ();
+ stop_callback (lwp, NULL);
+ lwp->last_resume_kind = resume_stop;
}
else
{
if (target_is_async_p ())
target_async (NULL, 0);
- /* Reset the async_masking. */
- linux_nat_async_mask_value = 1;
-
if (linux_ops->to_close)
linux_ops->to_close (quitting);
}
t->to_detach = linux_nat_detach;
t->to_resume = linux_nat_resume;
t->to_wait = linux_nat_wait;
+ t->to_pass_signals = linux_nat_pass_signals;
t->to_xfer_partial = linux_nat_xfer_partial;
t->to_kill = linux_nat_kill;
t->to_mourn_inferior = linux_nat_mourn_inferior;
t->to_is_async_p = linux_nat_is_async_p;
t->to_supports_non_stop = linux_nat_supports_non_stop;
t->to_async = linux_nat_async;
- t->to_async_mask = linux_nat_async_mask;
t->to_terminal_inferior = linux_nat_terminal_inferior;
t->to_terminal_ours = linux_nat_terminal_ours;
t->to_close = linux_nat_close;
t->to_supports_multi_process = linux_nat_supports_multi_process;
+ t->to_supports_disable_randomization
+ = linux_nat_supports_disable_randomization;
+
t->to_core_of_thread = linux_nat_core_of_thread;
/* We don't change the stratum; this target will sit at
show_debug_linux_nat,
&setdebuglist, &showdebuglist);
- add_setshow_zinteger_cmd ("lin-lwp-async", class_maintenance,
- &debug_linux_nat_async, _("\
-Set debugging of GNU/Linux async lwp module."), _("\
-Show debugging of GNU/Linux async lwp module."), _("\
-Enables printf debugging output."),
- NULL,
- show_debug_linux_nat_async,
- &setdebuglist, &showdebuglist);
-
/* Save this mask as the default. */
sigprocmask (SIG_SETMASK, NULL, &normal_mask);
sigdelset (&suspend_mask, SIGCHLD);
sigemptyset (&blocked_mask);
-
- add_setshow_boolean_cmd ("disable-randomization", class_support,
- &disable_randomization, _("\
-Set disabling of debuggee's virtual address space randomization."), _("\
-Show disabling of debuggee's virtual address space randomization."), _("\
-When this mode is on (which is the default), randomization of the virtual\n\
-address space is disabled. Standalone programs run with the randomization\n\
-enabled by default on some platforms."),
- &set_disable_randomization,
- &show_disable_randomization,
- &setlist, &showlist);
}
\f