[PATCH] make strace handle SIGTRAP properly
Dmitry V. Levin
ldv at altlinux.org
Tue May 24 22:23:22 UTC 2011
Hi,
On Fri, May 20, 2011 at 06:02:14PM +0200, Denys Vlasenko wrote:
> int debug = 0, followfork = 0;
> -unsigned int ptrace_setoptions = 0;
> +unsigned int ptrace_setoptions_followfork = 0;
> +static unsigned int ptrace_setoptions_for_all = 0;
> +/* Which WSTOPSIG(status) value marks syscall traps? */
> +static unsigned int SYSCALLTRAP = SIGTRAP;
I think giving macros-like names to variables is confusing,
even when these variables are going to be used in place of macros.
> +static void error_msg_and_die(const char *fmt, ...) /* TODO: __attribute__ ((noreturn, format (printf, 1, 2))) */ ;
> +static void error_msg_and_die(const char *fmt, ...)
I really don't like this interface, it would be less flexible than error(3).
I think an error(3)-like interface would be useful in other parts of
strace, too.
> +{
> + char *msg;
> + va_list p;
> +
> + va_start(p, fmt);
> + msg = NULL;
> + vasprintf(&msg, fmt, p);
> + if (msg) {
> + fprintf(stderr, "%s: %s\n", progname, msg);
> + free(msg);
> + }
> + va_end(p);
> +
> + cflag = 0; /* or else cleanup may print summary */
> + cleanup();
> + fflush(NULL);
> + _exit(1);
When such function is called in parent process, exit() is more appropriate
than _exit(). Otherwise, cleanup() must not be called.
> +/*
> + * Test whether the kernel support PTRACE_O_TRACESYSGOOD.
> + * First fork a new child, call ptrace(PTRACE_SETOPTIONS) on it,
> + * and then see whether it will stop with (SIGTRAP | 0x80).
> + */
> +static void
> +test_ptrace_setoptions_for_all(void)
> +{
> + const unsigned int test_options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC;
> + int pid;
> + int it_worked = 0;
> +
> + pid = fork();
> + if (pid < 0)
> + error_msg_and_die("fork failed");
> +
> + if (pid == 0) {
> + pid = getpid();
> + if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0)
> + /* "parent, something is deeply wrong!" */
> + kill(pid, SIGKILL);
The approach used in test_ptrace_setoptions() was just to _exit(1) in this
case. Why this test does this part differently?
> + kill(pid, SIGSTOP);
> + _exit(0); /* parent should see entry into this syscall */
> + }
> +
> + while (1) {
> + int status, tracee_pid;
> +
> + errno = 0;
> + tracee_pid = wait(&status);
> + if (tracee_pid <= 0) {
> + if (errno == EINTR)
> + continue;
In test_ptrace_setoptions() there is a check for errno == ECHILD.
I suppose it won't harm in this case, too.
> + kill(pid, SIGKILL);
> + error_msg_and_die("%s: unexpected wait result %d", __func__, tracee_pid);
Well, I'm not quite happy with test_* functions aborting the whole strace
unconditionally.
> + }
> + if (WIFEXITED(status))
> + break;
I'd also check for exit status here.
> + if (!WIFSTOPPED(status)) {
> + kill(pid, SIGKILL);
> + error_msg_and_die("%s: unexpected wait status %x", __func__, status);
> + }
Same unhappiness about error_msg_and_die.
> + if (WSTOPSIG(status) == SIGSTOP) {
> + /*
> + * We don't check "options aren't accepted" error.
> + * If it happens, we'll never get (SIGTRAP | 0x80),
> + * and thus will decide to not use the option.
> + * IOW: the outcome of the test will be the same.
> + */
> + ptrace(PTRACE_SETOPTIONS, pid, 0L, test_options);
I'm not sure that this lengthy comment is more obvious than the check
itself.
> + }
> + if (WSTOPSIG(status) == (SIGTRAP | 0x80)) {
> + it_worked = 1;
> + }
> + if (ptrace(PTRACE_SYSCALL, pid, 0L, 0L) < 0) {
> + kill(pid, SIGKILL);
> + error_msg_and_die("%s: PTRACE_SYSCALL doesn't work");
What about ESRCH?
> + }
> + }
> +
> + if (it_worked) {
> + SYSCALLTRAP = (SIGTRAP | 0x80);
> + ptrace_setoptions_for_all = test_options;
> + if (debug)
> + fprintf(stderr, "ptrace_setoptions_for_all = %#x\n",
> + ptrace_setoptions_for_all);
> + return;
> + }
> +
> + fprintf(stderr,
> + "Test for PTRACE_O_TRACESYSGOOD failed, giving up using this feature.\n");
> +}
> #endif
>
> int
> @@ -977,16 +1075,17 @@ main(int argc, char *argv[])
>
> #ifdef LINUX
> if (followfork) {
> - if (test_ptrace_setoptions() < 0) {
> + if (test_ptrace_setoptions_followfork() < 0) {
> fprintf(stderr,
> "Test for options supported by PTRACE_SETOPTIONS "
> "failed, giving up using this feature.\n");
> - ptrace_setoptions = 0;
> + ptrace_setoptions_followfork = 0;
> }
> if (debug)
> - fprintf(stderr, "ptrace_setoptions = %#x\n",
> - ptrace_setoptions);
> + fprintf(stderr, "ptrace_setoptions_followfork = %#x\n",
> + ptrace_setoptions_followfork);
> }
> + test_ptrace_setoptions_for_all();
> #endif
>
> /* Check if they want to redirect the output. */
> @@ -1751,7 +1850,7 @@ int sig;
> break;
> }
> error = ptrace_restart(PTRACE_CONT, tcp,
> - WSTOPSIG(status) == SIGTRAP ? 0
> + WSTOPSIG(status) == SYSCALLTRAP ? 0
> : WSTOPSIG(status));
> if (error < 0)
> break;
> @@ -2408,6 +2507,11 @@ handle_ptrace_event(int status, struct t
> }
> return handle_new_child(tcp, childpid, 0);
> }
> + if (status >> 16 == PTRACE_EVENT_EXEC) {
> + if (debug)
> + fprintf(stderr, "PTRACE_EVENT_EXEC on pid %d (ignored)\n", tcp->pid);
> + return 0;
> + }
> return 1;
> }
> #endif
> @@ -2597,7 +2701,7 @@ Process %d attached (waiting for parent)
> fprintf(stderr, "pid %u stopped, [%s]\n",
> pid, signame(WSTOPSIG(status)));
>
> - if (ptrace_setoptions && (status >> 16)) {
> + if (status >> 16) {
Is this change accurate enough?
How this (status >> 16) should be handled if strace haven't requested it?
--
ldv
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.strace.io/pipermail/strace-devel/attachments/20110525/7b486dc7/attachment.bin>
More information about the Strace-devel
mailing list