1 /*- 2 * Copyright (c) 2011, 2012 Konstantin Belousov <kib@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 #include <sys/types.h> 28 #include <sys/ptrace.h> 29 #include <sys/syscall.h> 30 #include <sys/sysctl.h> 31 #include <sys/wait.h> 32 #include <assert.h> 33 #include <errno.h> 34 #include <signal.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 #define TRACE ">>>> " 41 42 static const char * 43 decode_wait_status(int status) 44 { 45 static char c[128]; 46 char b[32]; 47 int first; 48 49 c[0] = '\0'; 50 first = 1; 51 if (WIFCONTINUED(status)) { 52 first = 0; 53 strlcat(c, "CONT", sizeof(c)); 54 } 55 if (WIFEXITED(status)) { 56 if (first) 57 first = 0; 58 else 59 strlcat(c, ",", sizeof(c)); 60 snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status)); 61 strlcat(c, b, sizeof(c)); 62 } 63 if (WIFSIGNALED(status)) { 64 if (first) 65 first = 0; 66 else 67 strlcat(c, ",", sizeof(c)); 68 snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status))); 69 strlcat(c, b, sizeof(c)); 70 if (WCOREDUMP(status)) 71 strlcat(c, ",CORE", sizeof(c)); 72 } 73 if (WIFSTOPPED(status)) { 74 if (first) 75 first = 0; 76 else 77 strlcat(c, ",", sizeof(c)); 78 snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status))); 79 strlcat(c, b, sizeof(c)); 80 } 81 return (c); 82 } 83 84 static const char * 85 decode_pl_flags(struct ptrace_lwpinfo *lwpinfo) 86 { 87 static char c[128]; 88 static struct decode_tag { 89 int flag; 90 const char *desc; 91 } decode[] = { 92 { PL_FLAG_SA, "SA" }, 93 { PL_FLAG_BOUND, "BOUND" }, 94 { PL_FLAG_SCE, "SCE" }, 95 { PL_FLAG_SCX, "SCX" }, 96 { PL_FLAG_EXEC, "EXEC" }, 97 { PL_FLAG_SI, "SI" }, 98 { PL_FLAG_FORKED, "FORKED" }, 99 { PL_FLAG_CHILD, "CHILD" }, 100 { PL_FLAG_BORN, "LWPBORN" }, 101 { PL_FLAG_EXITED, "LWPEXITED" }, 102 { PL_FLAG_VFORKED, "VFORKED" }, 103 { PL_FLAG_VFORK_DONE, "VFORKDONE" }, 104 }; 105 char de[32]; 106 unsigned first, flags, i; 107 108 c[0] = '\0'; 109 first = 1; 110 flags = lwpinfo->pl_flags; 111 for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) { 112 if ((flags & decode[i].flag) != 0) { 113 if (first) 114 first = 0; 115 else 116 strlcat(c, ",", sizeof(c)); 117 strlcat(c, decode[i].desc, sizeof(c)); 118 flags &= ~decode[i].flag; 119 } 120 } 121 for (i = 0; i < sizeof(flags) * NBBY; i++) { 122 if ((flags & (1 << i)) != 0) { 123 if (first) 124 first = 0; 125 else 126 strlcat(c, ",", sizeof(c)); 127 snprintf(de, sizeof(de), "<%d>", i); 128 strlcat(c, de, sizeof(c)); 129 } 130 } 131 return (c); 132 } 133 134 static const char * 135 decode_pl_event(struct ptrace_lwpinfo *lwpinfo) 136 { 137 138 switch (lwpinfo->pl_event) { 139 case PL_EVENT_NONE: 140 return ("NONE"); 141 142 case PL_EVENT_SIGNAL: 143 return ("SIG"); 144 145 default: 146 return ("UNKNOWN"); 147 } 148 } 149 150 static void 151 get_pathname(pid_t pid) 152 { 153 char pathname[PATH_MAX]; 154 int error, name[4]; 155 size_t len; 156 157 name[0] = CTL_KERN; 158 name[1] = KERN_PROC; 159 name[2] = KERN_PROC_PATHNAME; 160 name[3] = pid; 161 162 len = sizeof(pathname); 163 error = sysctl(name, 4, pathname, &len, NULL, 0); 164 if (error < 0) { 165 if (errno != ESRCH) { 166 fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n", 167 pid, strerror(errno)); 168 return; 169 } 170 fprintf(stderr, "pid %d exited\n", pid); 171 return; 172 } 173 if (len == 0 || strlen(pathname) == 0) { 174 fprintf(stderr, "No cached pathname for process %d\n", pid); 175 return; 176 } 177 printf(TRACE "pid %d path %s\n", pid, pathname); 178 } 179 180 static void 181 wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo) 182 { 183 long *args; 184 int error, i; 185 186 printf(TRACE "pid %d wait %s", pid, 187 decode_wait_status(status)); 188 if (lwpinfo != NULL) { 189 printf(" event %s flags %s", 190 decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo)); 191 if ((lwpinfo->pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) != 0) { 192 printf(" sc%d", lwpinfo->pl_syscall_code); 193 args = calloc(lwpinfo->pl_syscall_narg, sizeof(long)); 194 error = ptrace(PT_GET_SC_ARGS, lwpinfo->pl_lwpid, 195 (caddr_t)args, lwpinfo->pl_syscall_narg * 196 sizeof(long)); 197 if (error == 0) { 198 printf("("); 199 for (i = 0; i < (int)lwpinfo->pl_syscall_narg; 200 i++) { 201 printf("%s%#lx", i == 0 ? "" : ",", 202 args[i]); 203 } 204 printf(")"); 205 } else { 206 fprintf(stderr, "PT_GET_SC_ARGS failed: %s", 207 strerror(errno)); 208 } 209 free(args); 210 } 211 } 212 printf("\n"); 213 } 214 215 static int trace_syscalls = 1; 216 static int remote_getpid = 0; 217 218 static int 219 trace_sc(int pid) 220 { 221 struct ptrace_sc_remote pscr; 222 struct ptrace_lwpinfo lwpinfo; 223 int status; 224 225 if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) { 226 perror("PT_TO_SCE"); 227 ptrace(PT_KILL, pid, NULL, 0); 228 return (-1); 229 } 230 231 if (waitpid(pid, &status, 0) == -1) { 232 perror("waitpid"); 233 return (-1); 234 } 235 if (WIFEXITED(status) || WIFSIGNALED(status)) { 236 wait_info(pid, status, NULL); 237 return (-1); 238 } 239 assert(WIFSTOPPED(status)); 240 assert(WSTOPSIG(status) == SIGTRAP); 241 242 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) { 243 perror("PT_LWPINFO"); 244 ptrace(PT_KILL, pid, NULL, 0); 245 return (-1); 246 } 247 wait_info(pid, status, &lwpinfo); 248 assert(lwpinfo.pl_flags & PL_FLAG_SCE); 249 250 if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) { 251 perror("PT_TO_SCX"); 252 ptrace(PT_KILL, pid, NULL, 0); 253 return (-1); 254 } 255 256 if (waitpid(pid, &status, 0) == -1) { 257 perror("waitpid"); 258 return (-1); 259 } 260 if (WIFEXITED(status) || WIFSIGNALED(status)) { 261 wait_info(pid, status, NULL); 262 return (-1); 263 } 264 assert(WIFSTOPPED(status)); 265 assert(WSTOPSIG(status) == SIGTRAP); 266 267 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) { 268 perror("PT_LWPINFO"); 269 ptrace(PT_KILL, pid, NULL, 0); 270 return (-1); 271 } 272 wait_info(pid, status, &lwpinfo); 273 assert(lwpinfo.pl_flags & PL_FLAG_SCX); 274 275 if (remote_getpid) { 276 memset(&pscr, 0, sizeof(pscr)); 277 pscr.pscr_syscall = SYS_getpid; 278 pscr.pscr_nargs = 0; 279 if (ptrace(PT_SC_REMOTE, pid, (caddr_t)&pscr, 280 sizeof(pscr)) < 0) { 281 perror("PT_SC_REMOTE"); 282 ptrace(PT_KILL, pid, NULL, 0); 283 return (-1); 284 } else { 285 printf(TRACE "remote getpid %ld errno %d\n", 286 pscr.pscr_ret.sr_retval[0], pscr.pscr_ret.sr_error); 287 if (waitpid(pid, &status, 0) == -1) { 288 perror("waitpid"); 289 return (-1); 290 } 291 } 292 } 293 if (lwpinfo.pl_flags & PL_FLAG_EXEC) 294 get_pathname(pid); 295 296 if (lwpinfo.pl_flags & PL_FLAG_FORKED) { 297 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid); 298 return (lwpinfo.pl_child_pid); 299 } 300 return (0); 301 } 302 303 static int 304 trace_cont(int pid) 305 { 306 struct ptrace_lwpinfo lwpinfo; 307 int status; 308 309 if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) { 310 perror("PT_CONTINUE"); 311 ptrace(PT_KILL, pid, NULL, 0); 312 return (-1); 313 } 314 315 if (waitpid(pid, &status, 0) == -1) { 316 perror("waitpid"); 317 return (-1); 318 } 319 if (WIFEXITED(status) || WIFSIGNALED(status)) { 320 wait_info(pid, status, NULL); 321 return (-1); 322 } 323 assert(WIFSTOPPED(status)); 324 assert(WSTOPSIG(status) == SIGTRAP); 325 326 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) { 327 perror("PT_LWPINFO"); 328 ptrace(PT_KILL, pid, NULL, 0); 329 return (-1); 330 } 331 wait_info(pid, status, &lwpinfo); 332 333 if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) == 334 (PL_FLAG_EXEC | PL_FLAG_SCX)) 335 get_pathname(pid); 336 337 if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) == 338 (PL_FLAG_FORKED | PL_FLAG_SCX)) { 339 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid); 340 return (lwpinfo.pl_child_pid); 341 } 342 343 return (0); 344 } 345 346 static int 347 trace(pid_t pid) 348 { 349 350 return (trace_syscalls ? trace_sc(pid) : trace_cont(pid)); 351 } 352 353 354 int 355 main(int argc, char *argv[]) 356 { 357 struct ptrace_lwpinfo lwpinfo; 358 int c, status, use_vfork; 359 pid_t pid, pid1; 360 361 trace_syscalls = 1; 362 remote_getpid = 0; 363 use_vfork = 0; 364 while ((c = getopt(argc, argv, "crsv")) != -1) { 365 switch (c) { 366 case 'c': 367 trace_syscalls = 0; 368 break; 369 case 'r': 370 remote_getpid = 1; 371 break; 372 case 's': 373 trace_syscalls = 1; 374 break; 375 case 'v': 376 use_vfork = 1; 377 break; 378 default: 379 case '?': 380 fprintf(stderr, "Usage: %s [-c] [-r] [-s] [-v]\n", 381 argv[0]); 382 return (2); 383 } 384 } 385 386 if ((pid = fork()) < 0) { 387 perror("fork"); 388 return 1; 389 } 390 else if (pid == 0) { 391 if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) { 392 perror("PT_TRACE_ME"); 393 _exit(1); 394 } 395 kill(getpid(), SIGSTOP); 396 getpid(); 397 if ((pid1 = use_vfork ? vfork() : fork()) < 0) { 398 perror("fork1"); 399 return (1); 400 } else if (pid1 == 0) { 401 printf("Hi from child %d\n", getpid()); 402 execl("/bin/ls", "ls", "/", (char *)NULL); 403 } 404 } 405 else { /* parent */ 406 if (waitpid(pid, &status, 0) == -1) { 407 perror("waitpid"); 408 return (-1); 409 } 410 assert(WIFSTOPPED(status)); 411 assert(WSTOPSIG(status) == SIGSTOP); 412 413 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, 414 sizeof(lwpinfo)) < 0) { 415 perror("PT_LWPINFO"); 416 ptrace(PT_KILL, pid, NULL, 0); 417 return (-1); 418 } 419 wait_info(pid, status, &lwpinfo); 420 421 if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) { 422 perror("PT_FOLLOW_FORK"); 423 ptrace(PT_KILL, pid, NULL, 0); 424 return (2); 425 } 426 427 while ((pid1 = trace(pid)) >= 0) { 428 if (pid1 != 0) { 429 printf(TRACE "attached to pid %d\n", pid1); 430 #if 0 431 kill(pid1, SIGCONT); 432 #endif 433 if (waitpid(pid1, &status, 0) == -1) { 434 perror("waitpid"); 435 return (-1); 436 } 437 printf(TRACE "nested loop, pid %d status %s\n", 438 pid1, decode_wait_status(status)); 439 assert(WIFSTOPPED(status)); 440 assert(WSTOPSIG(status) == SIGSTOP); 441 if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo, 442 sizeof(lwpinfo)) < 0) { 443 perror("PT_LWPINFO"); 444 ptrace(PT_KILL, pid1, NULL, 0); 445 return (-1); 446 } 447 wait_info(pid1, status, &lwpinfo); 448 449 while (trace(pid1) >= 0) 450 ; 451 } 452 } 453 454 ptrace(PT_CONTINUE, pid, (caddr_t)1, 0); 455 } 456 return (0); 457 } 458