1231443Skib/*- 2231443Skib * Copyright (c) 2011, 2012 Konstantin Belousov <kib@FreeBSD.org> 3231443Skib * 4231443Skib * Redistribution and use in source and binary forms, with or without 5231443Skib * modification, are permitted provided that the following conditions 6231443Skib * are met: 7231443Skib * 1. Redistributions of source code must retain the above copyright 8231443Skib * notice, this list of conditions and the following disclaimer. 9231443Skib * 2. Redistributions in binary form must reproduce the above copyright 10231443Skib * notice, this list of conditions and the following disclaimer in the 11231443Skib * documentation and/or other materials provided with the distribution. 12231443Skib * 13231443Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14231443Skib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15231443Skib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16231443Skib * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17231443Skib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18231443Skib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19231443Skib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20231443Skib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21231443Skib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22231443Skib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23231443Skib * SUCH DAMAGE. 24231443Skib */ 25231443Skib 26231443Skib#include <sys/cdefs.h> 27231443Skib__FBSDID("$FreeBSD$"); 28231443Skib 29231443Skib#include <sys/types.h> 30231443Skib#include <sys/ptrace.h> 31231443Skib#include <sys/sysctl.h> 32231443Skib#include <sys/wait.h> 33231443Skib#include <assert.h> 34231443Skib#include <errno.h> 35231443Skib#include <signal.h> 36231443Skib#include <stdio.h> 37231443Skib#include <stdlib.h> 38231443Skib#include <string.h> 39231443Skib#include <unistd.h> 40231443Skib 41231443Skib#define TRACE ">>>> " 42231443Skib 43231443Skibstatic const char * 44231443Skibdecode_wait_status(int status) 45231443Skib{ 46231443Skib static char c[128]; 47231443Skib char b[32]; 48231443Skib int first; 49231443Skib 50231443Skib c[0] = '\0'; 51231443Skib first = 1; 52231443Skib if (WIFCONTINUED(status)) { 53231443Skib first = 0; 54231443Skib strlcat(c, "CONT", sizeof(c)); 55231443Skib } 56231443Skib if (WIFEXITED(status)) { 57231443Skib if (first) 58231443Skib first = 0; 59231443Skib else 60231443Skib strlcat(c, ",", sizeof(c)); 61231443Skib snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status)); 62231443Skib strlcat(c, b, sizeof(c)); 63231443Skib } 64231443Skib if (WIFSIGNALED(status)) { 65231443Skib if (first) 66231443Skib first = 0; 67231443Skib else 68231443Skib strlcat(c, ",", sizeof(c)); 69231443Skib snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status))); 70231443Skib strlcat(c, b, sizeof(c)); 71231443Skib if (WCOREDUMP(status)) 72231443Skib strlcat(c, ",CORE", sizeof(c)); 73231443Skib } 74231443Skib if (WIFSTOPPED(status)) { 75231443Skib if (first) 76231443Skib first = 0; 77231443Skib else 78231443Skib strlcat(c, ",", sizeof(c)); 79231443Skib snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status))); 80231443Skib strlcat(c, b, sizeof(c)); 81231443Skib } 82231443Skib return (c); 83231443Skib} 84231443Skib 85231443Skibstatic const char * 86231443Skibdecode_pl_flags(struct ptrace_lwpinfo *lwpinfo) 87231443Skib{ 88231443Skib static char c[128]; 89231443Skib static struct decode_tag { 90231443Skib int flag; 91231443Skib const char *desc; 92231443Skib } decode[] = { 93231443Skib { PL_FLAG_SA, "SA" }, 94231443Skib { PL_FLAG_BOUND, "BOUND" }, 95231443Skib { PL_FLAG_SCE, "SCE" }, 96231443Skib { PL_FLAG_SCX, "SCX" }, 97231443Skib { PL_FLAG_EXEC, "EXEC" }, 98231443Skib { PL_FLAG_SI, "SI" }, 99231443Skib { PL_FLAG_FORKED, "FORKED" }, 100231443Skib }; 101231443Skib char de[32]; 102231443Skib unsigned first, flags, i; 103231443Skib 104231443Skib c[0] = '\0'; 105231443Skib first = 1; 106231443Skib flags = lwpinfo->pl_flags; 107231443Skib for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) { 108231443Skib if ((flags & decode[i].flag) != 0) { 109231443Skib if (first) 110231443Skib first = 0; 111231443Skib else 112231443Skib strlcat(c, ",", sizeof(c)); 113231443Skib strlcat(c, decode[i].desc, sizeof(c)); 114231443Skib flags &= ~decode[i].flag; 115231443Skib } 116231443Skib } 117231443Skib for (i = 0; i < sizeof(flags) * NBBY; i++) { 118231443Skib if ((flags & (1 << i)) != 0) { 119231443Skib if (first) 120231443Skib first = 0; 121231443Skib else 122231443Skib strlcat(c, ",", sizeof(c)); 123231443Skib snprintf(de, sizeof(de), "<%d>", i); 124231443Skib strlcat(c, de, sizeof(c)); 125231443Skib } 126231443Skib } 127231443Skib return (c); 128231443Skib} 129231443Skib 130231443Skibstatic const char * 131231443Skibdecode_pl_event(struct ptrace_lwpinfo *lwpinfo) 132231443Skib{ 133231443Skib 134231443Skib switch (lwpinfo->pl_event) { 135231443Skib case PL_EVENT_NONE: 136231443Skib return ("NONE"); 137231443Skib 138231443Skib case PL_EVENT_SIGNAL: 139231443Skib return ("SIG"); 140231443Skib 141231443Skib default: 142231443Skib return ("UNKNOWN"); 143231443Skib } 144231443Skib} 145231443Skib 146231443Skibstatic void 147231443Skibget_pathname(pid_t pid) 148231443Skib{ 149231443Skib char pathname[PATH_MAX]; 150231443Skib int error, name[4]; 151231443Skib size_t len; 152231443Skib 153231443Skib name[0] = CTL_KERN; 154231443Skib name[1] = KERN_PROC; 155231443Skib name[2] = KERN_PROC_PATHNAME; 156231443Skib name[3] = pid; 157231443Skib 158231443Skib len = sizeof(pathname); 159231443Skib error = sysctl(name, 4, pathname, &len, NULL, 0); 160231443Skib if (error < 0) { 161231443Skib if (errno != ESRCH) { 162231443Skib fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n", 163231443Skib pid, strerror(errno)); 164231443Skib return; 165231443Skib } 166231443Skib fprintf(stderr, "pid %d exited\n", pid); 167231443Skib return; 168231443Skib } 169231443Skib if (len == 0 || strlen(pathname) == 0) { 170231443Skib fprintf(stderr, "No cached pathname for process %d\n", pid); 171231443Skib return; 172231443Skib } 173231443Skib printf(TRACE "pid %d path %s\n", pid, pathname); 174231443Skib} 175231443Skib 176231443Skibstatic void 177231443Skibwait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo) 178231443Skib{ 179231443Skib 180231443Skib printf(TRACE "pid %d wait %s", pid, 181231443Skib decode_wait_status(status)); 182231443Skib if (lwpinfo != NULL) { 183231443Skib printf(" event %s flags %s", 184231443Skib decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo)); 185231443Skib } 186231443Skib printf("\n"); 187231443Skib} 188231443Skib 189231443Skibstatic int 190231443Skibtrace_sc(int pid) 191231443Skib{ 192231443Skib struct ptrace_lwpinfo lwpinfo; 193231443Skib int status; 194231443Skib 195231443Skib if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) { 196231443Skib perror("PT_TO_SCE"); 197231443Skib ptrace(PT_KILL, pid, NULL, 0); 198231443Skib return (-1); 199231443Skib } 200231443Skib 201231443Skib if (waitpid(pid, &status, 0) == -1) { 202231443Skib perror("waitpid"); 203231443Skib return (-1); 204231443Skib } 205231443Skib if (WIFEXITED(status) || WIFSIGNALED(status)) { 206231443Skib wait_info(pid, status, NULL); 207231443Skib return (-1); 208231443Skib } 209231443Skib assert(WIFSTOPPED(status)); 210231443Skib assert(WSTOPSIG(status) == SIGTRAP); 211231443Skib 212231443Skib if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) { 213231443Skib perror("PT_LWPINFO"); 214231443Skib ptrace(PT_KILL, pid, NULL, 0); 215231443Skib return (-1); 216231443Skib } 217231443Skib wait_info(pid, status, &lwpinfo); 218231443Skib assert(lwpinfo.pl_flags & PL_FLAG_SCE); 219231443Skib 220231443Skib if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) { 221231443Skib perror("PT_TO_SCX"); 222231443Skib ptrace(PT_KILL, pid, NULL, 0); 223231443Skib return (-1); 224231443Skib } 225231443Skib 226231443Skib if (waitpid(pid, &status, 0) == -1) { 227231443Skib perror("waitpid"); 228231443Skib return (-1); 229231443Skib } 230231443Skib if (WIFEXITED(status) || WIFSIGNALED(status)) { 231231443Skib wait_info(pid, status, NULL); 232231443Skib return (-1); 233231443Skib } 234231443Skib assert(WIFSTOPPED(status)); 235231443Skib assert(WSTOPSIG(status) == SIGTRAP); 236231443Skib 237231443Skib if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) { 238231443Skib perror("PT_LWPINFO"); 239231443Skib ptrace(PT_KILL, pid, NULL, 0); 240231443Skib return (-1); 241231443Skib } 242231443Skib wait_info(pid, status, &lwpinfo); 243231443Skib assert(lwpinfo.pl_flags & PL_FLAG_SCX); 244231443Skib 245231443Skib if (lwpinfo.pl_flags & PL_FLAG_EXEC) 246231443Skib get_pathname(pid); 247231443Skib 248231443Skib if (lwpinfo.pl_flags & PL_FLAG_FORKED) { 249231443Skib printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid); 250231443Skib return (lwpinfo.pl_child_pid); 251231443Skib } 252231443Skib return (0); 253231443Skib} 254231443Skib 255231443Skibstatic int 256231443Skibtrace_cont(int pid) 257231443Skib{ 258231443Skib struct ptrace_lwpinfo lwpinfo; 259231443Skib int status; 260231443Skib 261231443Skib if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) { 262231443Skib perror("PT_CONTINUE"); 263231443Skib ptrace(PT_KILL, pid, NULL, 0); 264231443Skib return (-1); 265231443Skib } 266231443Skib 267231443Skib if (waitpid(pid, &status, 0) == -1) { 268231443Skib perror("waitpid"); 269231443Skib return (-1); 270231443Skib } 271231443Skib if (WIFEXITED(status) || WIFSIGNALED(status)) { 272231443Skib wait_info(pid, status, NULL); 273231443Skib return (-1); 274231443Skib } 275231443Skib assert(WIFSTOPPED(status)); 276231443Skib assert(WSTOPSIG(status) == SIGTRAP); 277231443Skib 278231443Skib if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) { 279231443Skib perror("PT_LWPINFO"); 280231443Skib ptrace(PT_KILL, pid, NULL, 0); 281231443Skib return (-1); 282231443Skib } 283231443Skib wait_info(pid, status, &lwpinfo); 284231443Skib 285231443Skib if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) == 286231443Skib (PL_FLAG_EXEC | PL_FLAG_SCX)) 287231443Skib get_pathname(pid); 288231443Skib 289231443Skib if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) == 290231443Skib (PL_FLAG_FORKED | PL_FLAG_SCX)) { 291231443Skib printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid); 292231443Skib return (lwpinfo.pl_child_pid); 293231443Skib } 294231443Skib 295231443Skib return (0); 296231443Skib} 297231443Skib 298231443Skibstatic int trace_syscalls = 1; 299231443Skib 300231443Skibstatic int 301231443Skibtrace(pid_t pid) 302231443Skib{ 303231443Skib 304231443Skib return (trace_syscalls ? trace_sc(pid) : trace_cont(pid)); 305231443Skib} 306231443Skib 307231443Skib 308231443Skibint 309231443Skibmain(int argc, char *argv[]) 310231443Skib{ 311231443Skib struct ptrace_lwpinfo lwpinfo; 312231443Skib int c, status, use_vfork; 313231443Skib pid_t pid, pid1; 314231443Skib 315231443Skib trace_syscalls = 1; 316231443Skib use_vfork = 0; 317231443Skib while ((c = getopt(argc, argv, "csv")) != -1) { 318231443Skib switch (c) { 319231443Skib case 'c': 320231443Skib trace_syscalls = 0; 321231443Skib break; 322231443Skib case 's': 323231443Skib trace_syscalls = 1; 324231443Skib break; 325231443Skib case 'v': 326231443Skib use_vfork = 1; 327231443Skib break; 328231443Skib default: 329231443Skib case '?': 330231443Skib fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]); 331231443Skib return (2); 332231443Skib } 333231443Skib } 334231443Skib 335231443Skib if ((pid = fork()) < 0) { 336231443Skib perror("fork"); 337231443Skib return 1; 338231443Skib } 339231443Skib else if (pid == 0) { 340231443Skib if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) { 341231443Skib perror("PT_TRACE_ME"); 342231443Skib _exit(1); 343231443Skib } 344231443Skib kill(getpid(), SIGSTOP); 345231443Skib getpid(); 346231443Skib if ((pid1 = use_vfork ? vfork() : fork()) < 0) { 347231443Skib perror("fork1"); 348231443Skib return (1); 349231443Skib } else if (pid1 == 0) { 350231443Skib printf("Hi from child %d\n", getpid()); 351231443Skib execl("/bin/ls", "ls", "/", (char *)NULL); 352231443Skib } 353231443Skib } 354231443Skib else { /* parent */ 355231443Skib if (waitpid(pid, &status, 0) == -1) { 356231443Skib perror("waitpid"); 357231443Skib return (-1); 358231443Skib } 359231443Skib assert(WIFSTOPPED(status)); 360231443Skib assert(WSTOPSIG(status) == SIGSTOP); 361231443Skib 362231443Skib if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, 363231443Skib sizeof(lwpinfo)) < 0) { 364231443Skib perror("PT_LWPINFO"); 365231443Skib ptrace(PT_KILL, pid, NULL, 0); 366231443Skib return (-1); 367231443Skib } 368231443Skib wait_info(pid, status, &lwpinfo); 369231443Skib 370231443Skib if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) { 371231443Skib perror("PT_FOLLOW_FORK"); 372231443Skib ptrace(PT_KILL, pid, NULL, 0); 373231443Skib return (2); 374231443Skib } 375231443Skib 376231443Skib while ((pid1 = trace(pid)) >= 0) { 377231443Skib if (pid1 != 0) { 378231443Skib printf(TRACE "attached to pid %d\n", pid1); 379231443Skib#if 0 380231443Skib kill(pid1, SIGCONT); 381231443Skib#endif 382231443Skib if (waitpid(pid1, &status, 0) == -1) { 383231443Skib perror("waitpid"); 384231443Skib return (-1); 385231443Skib } 386231443Skib printf(TRACE "nested loop, pid %d status %s\n", 387231443Skib pid1, decode_wait_status(status)); 388231443Skib assert(WIFSTOPPED(status)); 389231443Skib assert(WSTOPSIG(status) == SIGSTOP); 390231443Skib if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo, 391231443Skib sizeof(lwpinfo)) < 0) { 392231443Skib perror("PT_LWPINFO"); 393231443Skib ptrace(PT_KILL, pid1, NULL, 0); 394231443Skib return (-1); 395231443Skib } 396231443Skib wait_info(pid1, status, &lwpinfo); 397231443Skib 398231443Skib while (trace(pid1) >= 0) 399231443Skib ; 400231443Skib } 401231443Skib } 402231443Skib 403231443Skib ptrace(PT_CONTINUE, pid, (caddr_t)1, 0); 404231443Skib } 405231443Skib return (0); 406231443Skib} 407