@@ -557,6 +557,52 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
/* Report the event. */
return 0;
}
+ else if (event == PTRACE_EVENT_EXEC && report_exec_events)
+ {
+ struct regcache *regcache;
+
+ if (debug_threads)
+ {
+ debug_printf ("HEW: Got exec event from LWP %ld\n",
+ lwpid_of (event_thr));
+ }
+
+ /* If the exec was not called by the thread group leader, then
+ the lwp_info and thread_info structures are out-of-date,
+ containing information about the original leader thread and
+ not the new exec'ing leader thread. Invalidate the register
+ cache without flushing it to the target, and reset the stop
+ pc value in the lwp. */
+ regcache = (struct regcache *) inferior_regcache_data (event_thr);
+ free_register_cache (regcache);
+ set_inferior_regcache_data (event_thr, NULL);
+ event_lwp->stop_pc = get_pc (event_lwp);
+
+ event_lwp->waitstatus.kind = TARGET_WAITKIND_EXECD;
+ event_lwp->waitstatus.value.execd_pathname
+ = xstrdup (linux_proc_pid_to_exec_file (lwpid_of (event_thr)));
+
+ /* Mark the exec status as pending. */
+ event_lwp->stopped = 1;
+ event_lwp->status_pending_p = 1;
+ event_lwp->status_pending = wstat;
+ event_thr->last_resume_kind = resume_stop;
+ event_thr->last_status.kind = TARGET_WAITKIND_IGNORE;
+
+ if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
+ {
+ /* In non-stop mode, make sure we delete the lwp entry for a
+ non-leader exec'ing thread, which will have vanished. We
+ do this by sending a signal to all the other threads in the
+ lwp list, deleting any that are not found. Note that in
+ all-stop mode this will happen when we stop all the threads. */
+ stop_all_lwps (0, event_lwp);
+ unstop_all_lwps (0, event_lwp);
+ }
+
+ /* Report the event. */
+ return 0;
+ }
internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
@@ -1609,7 +1655,7 @@ check_zombie_leaders (void)
leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
linux_proc_pid_is_zombie (leader_pid));
- if (leader_lp != NULL
+ if (leader_lp != NULL && !leader_lp->stopped
/* Check if there are other threads in the group, as we may
have raced with the inferior simply exiting. */
&& !last_thread_of_process_p (leader_pid)
@@ -2035,6 +2081,9 @@ linux_low_ptrace_options (int attached)
if (report_vfork_events)
options |= (PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE);
+ if (report_exec_events)
+ options |= PTRACE_O_TRACEEXEC;
+
return options;
}
@@ -2051,6 +2100,38 @@ linux_low_filter_event (int lwpid, int wstat)
child = find_lwp_pid (pid_to_ptid (lwpid));
+ /* Check for stop events reported by a process we didn't already
+ know about - anything not already in our LWP list.
+
+ If we're expecting to receive stopped processes after
+ fork, vfork, and clone events, then we'll just add the
+ new one to our list and go back to waiting for the event
+ to be reported - the stopped process might be returned
+ from waitpid before or after the event is.
+
+ But note the case of a non-leader thread exec'ing after the
+ leader having exited, and gone from our lists (because
+ check_zombie_leaders deleted it). The non-leader thread
+ changes its tid to the tgid. */
+
+ if (WIFSTOPPED (wstat) && (child == NULL) && (WSTOPSIG (wstat) == SIGTRAP)
+ && (linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC))
+ {
+ ptid_t child_ptid;
+
+ /* A multi-thread exec after we had seen the leader exiting. */
+ if (debug_threads)
+ {
+ debug_printf ("LLW: Re-adding thread group leader LWP %d"
+ "after exec.\n", lwpid);
+ }
+
+ child_ptid = ptid_build (lwpid, lwpid, 0);
+ child = add_lwp (child_ptid);
+ child->stopped = 1;
+ current_thread = child->thread;
+ }
+
/* If we didn't find a process, one of two things presumably happened:
- A process we started and then detached from has exited. Ignore it.
- A process we are controlling has forked and the new child's stop
@@ -2342,8 +2423,7 @@ linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
- When a non-leader thread execs, that thread just vanishes
without reporting an exit (so we'd hang if we waited for it
explicitly in that case). The exec event is reported to
- the TGID pid (although we don't currently enable exec
- events). */
+ the TGID pid. */
errno = 0;
ret = my_waitpid (-1, wstatp, options | WNOHANG);
@@ -2741,7 +2821,8 @@ extended_event_reported (const struct target_waitstatus *waitstatus)
return (waitstatus->kind == TARGET_WAITKIND_FORKED
|| waitstatus->kind == TARGET_WAITKIND_VFORKED
- || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
+ || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE
+ || waitstatus->kind == TARGET_WAITKIND_EXECD);
}
/* Wait for process, returns status. */
@@ -3286,7 +3367,8 @@ linux_wait_1 (ptid_t ptid,
ourstatus->value.sig = GDB_SIGNAL_0;
}
else if (current_thread->last_resume_kind == resume_stop
- && WSTOPSIG (w) != SIGSTOP)
+ && WSTOPSIG (w) != SIGSTOP
+ && !extended_event_reported (ourstatus))
{
/* A thread that has been requested to stop by GDB with vCont;t,
but, it stopped for other reasons. */
@@ -3404,6 +3486,7 @@ static void
send_sigstop (struct lwp_info *lwp)
{
int pid;
+ int ret;
pid = lwpid_of (get_lwp_thread (lwp));
@@ -3421,7 +3504,20 @@ send_sigstop (struct lwp_info *lwp)
debug_printf ("Sending sigstop to lwp %d\n", pid);
lwp->stop_expected = 1;
- kill_lwp (pid, SIGSTOP);
+ errno = 0;
+ ret = kill_lwp (pid, SIGSTOP);
+ if (ret == -1 && errno == ESRCH)
+ {
+ /* If the kill fails with "No such process", on GNU/Linux we know
+ that the LWP has vanished - it is not a zombie, it is gone.
+ This is because a thread that was not the thread group leader
+ called exec and took over the leader's lwp. */
+ delete_lwp (lwp);
+ set_desired_thread (0);
+
+ if (debug_threads)
+ debug_printf ("send_sigstop: lwp %d has vanished\n", pid);
+ }
}
static int
@@ -5594,6 +5690,14 @@ linux_supports_vfork_events (void)
return linux_supports_tracefork ();
}
+/* Check if exec events are supported. */
+
+static int
+linux_supports_exec_events (void)
+{
+ return linux_supports_traceexec ();
+}
+
/* Callback for 'find_inferior'. Set the (possibly changed) ptrace
options for the specified lwp. */
@@ -6681,6 +6785,7 @@ static struct target_ops linux_target_ops = {
linux_supports_multi_process,
linux_supports_fork_events,
linux_supports_vfork_events,
+ linux_supports_exec_events,
linux_handle_new_gdb_connection,
#ifdef USE_THREAD_DB
thread_db_handle_monitor_command,
@@ -764,6 +764,7 @@ static struct target_ops lynx_target_ops = {
NULL, /* supports_multi_process */
NULL, /* supports_fork_events */
NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
NULL, /* handle_new_gdb_connection */
NULL, /* handle_monitor_command */
};
@@ -1116,6 +1116,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
case TARGET_WAITKIND_STOPPED:
case TARGET_WAITKIND_FORKED:
case TARGET_WAITKIND_VFORKED:
+ case TARGET_WAITKIND_EXECD:
{
struct thread_info *saved_thread;
const char **regp;
@@ -1133,6 +1134,25 @@ prepare_resume_reply (char *buf, ptid_t ptid,
buf = write_ptid (buf, status->value.related_pid);
strcat (buf, ";");
}
+ else if ((status->kind == TARGET_WAITKIND_EXECD) && multi_process)
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+ const char *event = "exec";
+ char hexified_pathname[PATH_MAX];
+
+ sprintf (buf, "T%02x%s:", signal, event);
+ buf += strlen (buf);
+
+ /* Encode pathname to hexified format. */
+ bin2hex ((const gdb_byte *) status->value.execd_pathname,
+ hexified_pathname,
+ strlen (status->value.execd_pathname));
+
+ sprintf (buf, "%s;", hexified_pathname);
+ xfree (status->value.execd_pathname);
+ status->value.execd_pathname = NULL;
+ buf += strlen (buf);
+ }
else
sprintf (buf, "T%02x", status->value.sig);
@@ -59,6 +59,7 @@ int run_once;
int multi_process;
int report_fork_events;
int report_vfork_events;
+int report_exec_events;
int non_stop;
int swbreak_feature;
int hwbreak_feature;
@@ -2107,6 +2108,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_vfork_events ())
report_vfork_events = 1;
}
+ if (strcmp (p, "exec-events+") == 0)
+ {
+ /* GDB supports and wants exec events if possible. */
+ if (target_supports_exec_events ())
+ report_exec_events = 1;
+ }
else
target_process_qsupported (p);
@@ -2163,6 +2170,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_vfork_events ())
strcat (own_buf, ";vfork-events+");
+ if (target_supports_exec_events ())
+ strcat (own_buf, ";exec-events+");
+
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
@@ -3545,6 +3555,7 @@ captured_main (int argc, char *argv[])
multi_process = 0;
report_fork_events = 0;
report_vfork_events = 0;
+ report_exec_events = 0;
/* Be sure we're out of tfind mode. */
current_traceframe = -1;
cont_thread = null_ptid;
@@ -86,6 +86,7 @@ extern int run_once;
extern int multi_process;
extern int report_fork_events;
extern int report_vfork_events;
+extern int report_exec_events;
extern int non_stop;
/* True if the "swbreak+" feature is active. In that case, GDB wants
@@ -287,6 +287,9 @@ struct target_ops
/* Returns true if vfork events are supported. */
int (*supports_vfork_events) (void);
+ /* Returns true if exec events are supported. */
+ int (*supports_exec_events) (void);
+
/* Allows target to re-initialize connection-specific settings. */
void (*handle_new_gdb_connection) (void);
@@ -458,6 +461,10 @@ int kill_inferior (int);
(the_target->supports_vfork_events ? \
(*the_target->supports_vfork_events) () : 0)
+#define target_supports_exec_events() \
+ (the_target->supports_exec_events ? \
+ (*the_target->supports_exec_events) () : 0)
+
#define target_handle_new_gdb_connection() \
do \
{ \
@@ -1831,6 +1831,7 @@ static struct target_ops win32_target_ops = {
NULL, /* supports_multi_process */
NULL, /* supports_fork_events */
NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
NULL, /* handle_new_gdb_connection */
NULL, /* handle_monitor_command */
NULL, /* core_of_thread */
@@ -527,9 +527,7 @@ ptrace_supports_feature (int ptrace_options)
}
/* Returns non-zero if PTRACE_EVENT_FORK is supported by ptrace,
- 0 otherwise. Note that if PTRACE_EVENT_FORK is supported so is
- PTRACE_EVENT_CLONE, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK,
- since they were all added to the kernel at the same time. */
+ 0 otherwise. */
int
linux_supports_tracefork (void)
@@ -537,6 +535,15 @@ linux_supports_tracefork (void)
return ptrace_supports_feature (PTRACE_O_TRACEFORK);
}
+/* Returns non-zero if PTRACE_EVENT_EXEC is supported by ptrace,
+ 0 otherwise. */
+
+int
+linux_supports_traceexec (void)
+{
+ return ptrace_supports_feature (PTRACE_O_TRACEEXEC);
+}
+
/* Returns non-zero if PTRACE_EVENT_CLONE is supported by ptrace,
0 otherwise. Note that if PTRACE_EVENT_CLONE is supported so is
PTRACE_EVENT_FORK, PTRACE_EVENT_EXEC and PTRACE_EVENT_VFORK,
@@ -161,6 +161,7 @@ extern void linux_check_ptrace_features (void);
extern void linux_enable_event_reporting (pid_t pid, int attached);
extern void linux_disable_event_reporting (pid_t pid);
extern int linux_supports_tracefork (void);
+extern int linux_supports_traceexec (void);
extern int linux_supports_traceclone (void);
extern int linux_supports_tracevforkdone (void);
extern int linux_supports_tracesysgood (void);
@@ -1366,6 +1366,9 @@ enum {
/* Support for the Qbtrace-conf:pt:size packet. */
PACKET_Qbtrace_conf_pt_size,
+ /* Support for exec events. */
+ PACKET_exec_event_feature,
+
PACKET_MAX
};
@@ -4162,6 +4165,8 @@ static const struct protocol_feature remote_protocol_features[] = {
PACKET_fork_event_feature },
{ "vfork-events", PACKET_DISABLE, remote_supported_packet,
PACKET_vfork_event_feature },
+ { "exec-events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_exec_event_feature },
{ "Qbtrace-conf:pt:size", PACKET_DISABLE, remote_supported_packet,
PACKET_Qbtrace_conf_pt_size }
};
@@ -4250,6 +4255,9 @@ remote_query_supported (void)
if (packet_set_cmd_state (PACKET_vfork_event_feature)
!= AUTO_BOOLEAN_FALSE)
q = remote_query_supported_append (q, "vfork-events+");
+ if (packet_set_cmd_state (PACKET_exec_event_feature)
+ != AUTO_BOOLEAN_FALSE)
+ q = remote_query_supported_append (q, "exec-events+");
}
q = reconcat (q, "qSupported:", q, (char *) NULL);
@@ -5909,6 +5917,25 @@ Packet: '%s'\n"),
event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
p = skip_to_semicolon (p1 + 1);
}
+ else if (strncmp (p, "exec", p1 - p) == 0)
+ {
+ ULONGEST pid;
+ char pathname[PATH_MAX];
+
+ p = unpack_varlen_hex (++p1, &pid);
+
+ /* Save the pathname for event reporting and for
+ the next run command. */
+ hex2bin (p1, (gdb_byte *) pathname, (p - p1)/2);
+ /* Add the null terminator. */
+ pathname[(p - p1)/2] = '\0';
+ /* This is freed during event handling. */
+ event->ws.value.execd_pathname = xstrdup (pathname);
+ event->ws.kind = TARGET_WAITKIND_EXECD;
+ /* Save the pathname for the next run command. */
+ xfree (remote_exec_file);
+ remote_exec_file = xstrdup (pathname);
+ }
else
{
ULONGEST pnum;
@@ -12960,6 +12987,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_pt_size],
"Qbtrace-conf:pt:size", "btrace-conf-pt-size", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_exec_event_feature],
+ "exec-event-feature", "exec-event-feature", 0);
+
/* Assert that we've registered "set remote foo-packet" commands
for all packet configs. */
{