From owner-svn-src-all@FreeBSD.ORG Fri Feb 10 21:33:13 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 1E46C1065679; Fri, 10 Feb 2012 21:33:13 +0000 (UTC) (envelope-from kib@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 07C528FC26; Fri, 10 Feb 2012 21:33:13 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q1ALXCLc095965; Fri, 10 Feb 2012 21:33:12 GMT (envelope-from kib@svn.freebsd.org) Received: (from kib@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q1ALXCu7095963; Fri, 10 Feb 2012 21:33:12 GMT (envelope-from kib@svn.freebsd.org) Message-Id: <201202102133.q1ALXCu7095963@svn.freebsd.org> From: Konstantin Belousov Date: Fri, 10 Feb 2012 21:33:12 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r231443 - head/tools/test/ptrace X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 10 Feb 2012 21:33:13 -0000 Author: kib Date: Fri Feb 10 21:33:12 2012 New Revision: 231443 URL: http://svn.freebsd.org/changeset/base/231443 Log: Add a test program for recently added ptrace(2) interfaces. MFC after: 1 week Added: head/tools/test/ptrace/ head/tools/test/ptrace/Makefile (contents, props changed) head/tools/test/ptrace/scescx.c (contents, props changed) Added: head/tools/test/ptrace/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tools/test/ptrace/Makefile Fri Feb 10 21:33:12 2012 (r231443) @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PROG= scescx +NO_MAN= +WARNS?= 6 + +.include Added: head/tools/test/ptrace/scescx.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tools/test/ptrace/scescx.c Fri Feb 10 21:33:12 2012 (r231443) @@ -0,0 +1,406 @@ +/*- + * Copyright (c) 2011, 2012 Konstantin Belousov + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +}