diff options
author | kib <kib@FreeBSD.org> | 2012-02-10 21:33:12 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2012-02-10 21:33:12 +0000 |
commit | 290f0204ae95d6dc92ba9c15fafe090e9d4338af (patch) | |
tree | 1275629c32863f943008205f6df4e91f7acf2d6b /tools/test | |
parent | d34d9917c8290035954a157525008893a51b495e (diff) | |
download | FreeBSD-src-290f0204ae95d6dc92ba9c15fafe090e9d4338af.zip FreeBSD-src-290f0204ae95d6dc92ba9c15fafe090e9d4338af.tar.gz |
Add a test program for recently added ptrace(2) interfaces.
MFC after: 1 week
Diffstat (limited to 'tools/test')
-rw-r--r-- | tools/test/ptrace/Makefile | 7 | ||||
-rw-r--r-- | tools/test/ptrace/scescx.c | 406 |
2 files changed, 413 insertions, 0 deletions
diff --git a/tools/test/ptrace/Makefile b/tools/test/ptrace/Makefile new file mode 100644 index 0000000..681aee5 --- /dev/null +++ b/tools/test/ptrace/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PROG= scescx +NO_MAN= +WARNS?= 6 + +.include <bsd.prog.mk> diff --git a/tools/test/ptrace/scescx.c b/tools/test/ptrace/scescx.c new file mode 100644 index 0000000..cdd8b60 --- /dev/null +++ b/tools/test/ptrace/scescx.c @@ -0,0 +1,406 @@ +/*- + * Copyright (c) 2011, 2012 Konstantin Belousov <kib@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/sysctl.h> +#include <sys/wait.h> +#include <assert.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define TRACE ">>>> " + +static const char * +decode_wait_status(int status) +{ + static char c[128]; + char b[32]; + int first; + + c[0] = '\0'; + first = 1; + if (WIFCONTINUED(status)) { + first = 0; + strlcat(c, "CONT", sizeof(c)); + } + if (WIFEXITED(status)) { + if (first) + first = 0; + else + strlcat(c, ",", sizeof(c)); + snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status)); + strlcat(c, b, sizeof(c)); + } + if (WIFSIGNALED(status)) { + if (first) + first = 0; + else + strlcat(c, ",", sizeof(c)); + snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status))); + strlcat(c, b, sizeof(c)); + if (WCOREDUMP(status)) + strlcat(c, ",CORE", sizeof(c)); + } + if (WIFSTOPPED(status)) { + if (first) + first = 0; + else + strlcat(c, ",", sizeof(c)); + snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status))); + strlcat(c, b, sizeof(c)); + } + return (c); +} + +static const char * +decode_pl_flags(struct ptrace_lwpinfo *lwpinfo) +{ + static char c[128]; + static struct decode_tag { + int flag; + const char *desc; + } decode[] = { + { PL_FLAG_SA, "SA" }, + { PL_FLAG_BOUND, "BOUND" }, + { PL_FLAG_SCE, "SCE" }, + { PL_FLAG_SCX, "SCX" }, + { PL_FLAG_EXEC, "EXEC" }, + { PL_FLAG_SI, "SI" }, + { PL_FLAG_FORKED, "FORKED" }, + }; + char de[32]; + unsigned first, flags, i; + + c[0] = '\0'; + first = 1; + flags = lwpinfo->pl_flags; + for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) { + if ((flags & decode[i].flag) != 0) { + if (first) + first = 0; + else + strlcat(c, ",", sizeof(c)); + strlcat(c, decode[i].desc, sizeof(c)); + flags &= ~decode[i].flag; + } + } + for (i = 0; i < sizeof(flags) * NBBY; i++) { + if ((flags & (1 << i)) != 0) { + if (first) + first = 0; + else + strlcat(c, ",", sizeof(c)); + snprintf(de, sizeof(de), "<%d>", i); + strlcat(c, de, sizeof(c)); + } + } + return (c); +} + +static const char * +decode_pl_event(struct ptrace_lwpinfo *lwpinfo) +{ + + switch (lwpinfo->pl_event) { + case PL_EVENT_NONE: + return ("NONE"); + + case PL_EVENT_SIGNAL: + return ("SIG"); + + default: + return ("UNKNOWN"); + } +} + +static void +get_pathname(pid_t pid) +{ + char pathname[PATH_MAX]; + int error, name[4]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_PATHNAME; + name[3] = pid; + + len = sizeof(pathname); + error = sysctl(name, 4, pathname, &len, NULL, 0); + if (error < 0) { + if (errno != ESRCH) { + fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n", + pid, strerror(errno)); + return; + } + fprintf(stderr, "pid %d exited\n", pid); + return; + } + if (len == 0 || strlen(pathname) == 0) { + fprintf(stderr, "No cached pathname for process %d\n", pid); + return; + } + printf(TRACE "pid %d path %s\n", pid, pathname); +} + +static void +wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo) +{ + + printf(TRACE "pid %d wait %s", pid, + decode_wait_status(status)); + if (lwpinfo != NULL) { + printf(" event %s flags %s", + decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo)); + } + printf("\n"); +} + +static int +trace_sc(int pid) +{ + struct ptrace_lwpinfo lwpinfo; + int status; + + if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) { + perror("PT_TO_SCE"); + ptrace(PT_KILL, pid, NULL, 0); + return (-1); + } + + if (waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + return (-1); + } + if (WIFEXITED(status) || WIFSIGNALED(status)) { + wait_info(pid, status, NULL); + return (-1); + } + assert(WIFSTOPPED(status)); + assert(WSTOPSIG(status) == SIGTRAP); + + if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) { + perror("PT_LWPINFO"); + ptrace(PT_KILL, pid, NULL, 0); + return (-1); + } + wait_info(pid, status, &lwpinfo); + assert(lwpinfo.pl_flags & PL_FLAG_SCE); + + if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) { + perror("PT_TO_SCX"); + ptrace(PT_KILL, pid, NULL, 0); + return (-1); + } + + if (waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + return (-1); + } + if (WIFEXITED(status) || WIFSIGNALED(status)) { + wait_info(pid, status, NULL); + return (-1); + } + assert(WIFSTOPPED(status)); + assert(WSTOPSIG(status) == SIGTRAP); + + if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) { + perror("PT_LWPINFO"); + ptrace(PT_KILL, pid, NULL, 0); + return (-1); + } + wait_info(pid, status, &lwpinfo); + assert(lwpinfo.pl_flags & PL_FLAG_SCX); + + if (lwpinfo.pl_flags & PL_FLAG_EXEC) + get_pathname(pid); + + if (lwpinfo.pl_flags & PL_FLAG_FORKED) { + printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid); + return (lwpinfo.pl_child_pid); + } + return (0); +} + +static int +trace_cont(int pid) +{ + struct ptrace_lwpinfo lwpinfo; + int status; + + if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) { + perror("PT_CONTINUE"); + ptrace(PT_KILL, pid, NULL, 0); + return (-1); + } + + if (waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + return (-1); + } + if (WIFEXITED(status) || WIFSIGNALED(status)) { + wait_info(pid, status, NULL); + return (-1); + } + assert(WIFSTOPPED(status)); + assert(WSTOPSIG(status) == SIGTRAP); + + if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) { + perror("PT_LWPINFO"); + ptrace(PT_KILL, pid, NULL, 0); + return (-1); + } + wait_info(pid, status, &lwpinfo); + + if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) == + (PL_FLAG_EXEC | PL_FLAG_SCX)) + get_pathname(pid); + + if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) == + (PL_FLAG_FORKED | PL_FLAG_SCX)) { + printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid); + return (lwpinfo.pl_child_pid); + } + + return (0); +} + +static int trace_syscalls = 1; + +static int +trace(pid_t pid) +{ + + return (trace_syscalls ? trace_sc(pid) : trace_cont(pid)); +} + + +int +main(int argc, char *argv[]) +{ + struct ptrace_lwpinfo lwpinfo; + int c, status, use_vfork; + pid_t pid, pid1; + + trace_syscalls = 1; + use_vfork = 0; + while ((c = getopt(argc, argv, "csv")) != -1) { + switch (c) { + case 'c': + trace_syscalls = 0; + break; + case 's': + trace_syscalls = 1; + break; + case 'v': + use_vfork = 1; + break; + default: + case '?': + fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]); + return (2); + } + } + + if ((pid = fork()) < 0) { + perror("fork"); + return 1; + } + else if (pid == 0) { + if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) { + perror("PT_TRACE_ME"); + _exit(1); + } + kill(getpid(), SIGSTOP); + getpid(); + if ((pid1 = use_vfork ? vfork() : fork()) < 0) { + perror("fork1"); + return (1); + } else if (pid1 == 0) { + printf("Hi from child %d\n", getpid()); + execl("/bin/ls", "ls", "/", (char *)NULL); + } + } + else { /* parent */ + if (waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + return (-1); + } + assert(WIFSTOPPED(status)); + assert(WSTOPSIG(status) == SIGSTOP); + + if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, + sizeof(lwpinfo)) < 0) { + perror("PT_LWPINFO"); + ptrace(PT_KILL, pid, NULL, 0); + return (-1); + } + wait_info(pid, status, &lwpinfo); + + if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) { + perror("PT_FOLLOW_FORK"); + ptrace(PT_KILL, pid, NULL, 0); + return (2); + } + + while ((pid1 = trace(pid)) >= 0) { + if (pid1 != 0) { + printf(TRACE "attached to pid %d\n", pid1); +#if 0 + kill(pid1, SIGCONT); +#endif + if (waitpid(pid1, &status, 0) == -1) { + perror("waitpid"); + return (-1); + } + printf(TRACE "nested loop, pid %d status %s\n", + pid1, decode_wait_status(status)); + assert(WIFSTOPPED(status)); + assert(WSTOPSIG(status) == SIGSTOP); + if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo, + sizeof(lwpinfo)) < 0) { + perror("PT_LWPINFO"); + ptrace(PT_KILL, pid1, NULL, 0); + return (-1); + } + wait_info(pid1, status, &lwpinfo); + + while (trace(pid1) >= 0) + ; + } + } + + ptrace(PT_CONTINUE, pid, (caddr_t)1, 0); + } + return (0); +} |