1 /* Copyright (c) 2005-2007, Google Inc. 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * --- 31 * Author: Markus Gutschke 32 */ 33 34 #include "base/linuxthreads.h" 35 36 #ifdef THREADS 37 #ifdef __cplusplus 38 extern "C" { 39 #endif 40 41 #include <sched.h> 42 #include <signal.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <fcntl.h> 46 #include <sys/socket.h> 47 #include <sys/wait.h> 48 49 #include "base/linux_syscall_support.h" 50 #include "base/thread_lister.h" 51 52 #ifndef CLONE_UNTRACED 53 #define CLONE_UNTRACED 0x00800000 54 #endif 55 56 57 /* Synchronous signals that should not be blocked while in the lister thread. 58 */ 59 static const int sync_signals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS, 60 SIGXCPU, SIGXFSZ }; 61 62 /* itoa() is not a standard function, and we cannot safely call printf() 63 * after suspending threads. So, we just implement our own copy. A 64 * recursive approach is the easiest here. 65 */ 66 static char *local_itoa(char *buf, int i) { 67 if (i < 0) { 68 *buf++ = '-'; 69 return local_itoa(buf, -i); 70 } else { 71 if (i >= 10) 72 buf = local_itoa(buf, i/10); 73 *buf++ = (i%10) + '0'; 74 *buf = '\000'; 75 return buf; 76 } 77 } 78 79 80 /* Wrapper around clone() that runs "fn" on the same stack as the 81 * caller! Unlike fork(), the cloned thread shares the same address space. 82 * The caller must be careful to use only minimal amounts of stack until 83 * the cloned thread has returned. 84 * There is a good chance that the cloned thread and the caller will share 85 * the same copy of errno! 86 */ 87 #ifdef __GNUC__ 88 #if __GNUC__ == 3 && __GNUC_MINOR__ >= 1 || __GNUC__ > 3 89 /* Try to force this function into a separate stack frame, and make sure 90 * that arguments are passed on the stack. 91 */ 92 static int local_clone (int (*fn)(void *), void *arg, ...) 93 __attribute__ ((noinline)); 94 #endif 95 #endif 96 97 static int local_clone (int (*fn)(void *), void *arg, ...) { 98 /* Leave 4kB of gap between the callers stack and the new clone. This 99 * should be more than sufficient for the caller to call waitpid() until 100 * the cloned thread terminates. 101 * 102 * It is important that we set the CLONE_UNTRACED flag, because newer 103 * versions of "gdb" otherwise attempt to attach to our thread, and will 104 * attempt to reap its status codes. This subsequently results in the 105 * caller hanging indefinitely in waitpid(), waiting for a change in 106 * status that will never happen. By setting the CLONE_UNTRACED flag, we 107 * prevent "gdb" from stealing events, but we still expect the thread 108 * lister to fail, because it cannot PTRACE_ATTACH to the process that 109 * is being debugged. This is OK and the error code will be reported 110 * correctly. 111 */ 112 return sys_clone(fn, (char *)&arg - 4096, 113 CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_UNTRACED, arg, 0, 0, 0); 114 } 115 116 117 /* Local substitute for the atoi() function, which is not necessarily safe 118 * to call once threads are suspended (depending on whether libc looks up 119 * locale information, when executing atoi()). 120 */ 121 static int local_atoi(const char *s) { 122 int n = 0; 123 int neg = *s == '-'; 124 if (neg) 125 s++; 126 while (*s >= '0' && *s <= '9') 127 n = 10*n + (*s++ - '0'); 128 return neg ? -n : n; 129 } 130 131 132 /* Re-runs fn until it doesn't cause EINTR 133 */ 134 #define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR) 135 136 137 /* Wrap a class around system calls, in order to give us access to 138 * a private copy of errno. This only works in C++, but it has the 139 * advantage of not needing nested functions, which are a non-standard 140 * language extension. 141 */ 142 #ifdef __cplusplus 143 namespace { 144 class SysCalls { 145 public: 146 #define SYS_CPLUSPLUS 147 #define SYS_ERRNO my_errno 148 #define SYS_INLINE inline 149 #define SYS_PREFIX -1 150 #undef SYS_LINUX_SYSCALL_SUPPORT_H 151 #include "linux_syscall_support.h" 152 SysCalls() : my_errno(0) { } 153 int my_errno; 154 }; 155 } 156 #define ERRNO sys.my_errno 157 #else 158 #define ERRNO my_errno 159 #endif 160 161 162 /* Wrapper for open() which is guaranteed to never return EINTR. 163 */ 164 static int c_open(const char *fname, int flags, int mode) { 165 ssize_t rc; 166 NO_INTR(rc = sys_open(fname, flags, mode)); 167 return rc; 168 } 169 170 171 /* abort() is not safely reentrant, and changes it's behavior each time 172 * it is called. This means, if the main application ever called abort() 173 * we cannot safely call it again. This would happen if we were called 174 * from a SIGABRT signal handler in the main application. So, document 175 * that calling SIGABRT from the thread lister makes it not signal safe 176 * (and vice-versa). 177 * Also, since we share address space with the main application, we 178 * cannot call abort() from the callback and expect the main application 179 * to behave correctly afterwards. In fact, the only thing we can do, is 180 * to terminate the main application with extreme prejudice (aka 181 * PTRACE_KILL). 182 * We set up our own SIGABRT handler to do this. 183 * In order to find the main application from the signal handler, we 184 * need to store information about it in global variables. This is 185 * safe, because the main application should be suspended at this 186 * time. If the callback ever called ResumeAllProcessThreads(), then 187 * we are running a higher risk, though. So, try to avoid calling 188 * abort() after calling ResumeAllProcessThreads. 189 */ 190 static volatile int *sig_pids, sig_num_threads, sig_proc, sig_marker; 191 192 193 /* Signal handler to help us recover from dying while we are attached to 194 * other threads. 195 */ 196 static void SignalHandler(int signum, siginfo_t *si, void *data) { 197 if (sig_pids != NULL) { 198 if (signum == SIGABRT) { 199 while (sig_num_threads-- > 0) { 200 /* Not sure if sched_yield is really necessary here, but it does not */ 201 /* hurt, and it might be necessary for the same reasons that we have */ 202 /* to do so in sys_ptrace_detach(). */ 203 sys_sched_yield(); 204 sys_ptrace(PTRACE_KILL, sig_pids[sig_num_threads], 0, 0); 205 } 206 } else if (sig_num_threads > 0) { 207 ResumeAllProcessThreads(sig_num_threads, (int *)sig_pids); 208 } 209 } 210 sig_pids = NULL; 211 if (sig_marker >= 0) 212 NO_INTR(sys_close(sig_marker)); 213 sig_marker = -1; 214 if (sig_proc >= 0) 215 NO_INTR(sys_close(sig_proc)); 216 sig_proc = -1; 217 218 sys__exit(signum == SIGABRT ? 1 : 2); 219 } 220 221 222 /* Try to dirty the stack, and hope that the compiler is not smart enough 223 * to optimize this function away. Or worse, the compiler could inline the 224 * function and permanently allocate the data on the stack. 225 */ 226 static void DirtyStack(size_t amount) { 227 char buf[amount]; 228 memset(buf, 0, amount); 229 sys_read(-1, buf, amount); 230 } 231 232 233 /* Data structure for passing arguments to the lister thread. 234 */ 235 #define ALT_STACKSIZE (MINSIGSTKSZ + 4096) 236 237 struct ListerParams { 238 int result, err; 239 char *altstack_mem; 240 ListAllProcessThreadsCallBack callback; 241 void *parameter; 242 va_list ap; 243 }; 244 245 246 static void ListerThread(struct ListerParams *args) { 247 int found_parent = 0; 248 pid_t clone_pid = sys_gettid(), ppid = sys_getppid(); 249 char proc_self_task[80], marker_name[48], *marker_path; 250 const char *proc_paths[3]; 251 const char *const *proc_path = proc_paths; 252 int proc = -1, marker = -1, num_threads = 0; 253 int max_threads = 0, sig; 254 struct kernel_stat marker_sb, proc_sb; 255 stack_t altstack; 256 257 /* Create "marker" that we can use to detect threads sharing the same 258 * address space and the same file handles. By setting the FD_CLOEXEC flag 259 * we minimize the risk of misidentifying child processes as threads; 260 * and since there is still a race condition, we will filter those out 261 * later, anyway. 262 */ 263 if ((marker = sys_socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0 || 264 sys_fcntl(marker, F_SETFD, FD_CLOEXEC) < 0) { 265 failure: 266 args->result = -1; 267 args->err = errno; 268 if (marker >= 0) 269 NO_INTR(sys_close(marker)); 270 sig_marker = marker = -1; 271 if (proc >= 0) 272 NO_INTR(sys_close(proc)); 273 sig_proc = proc = -1; 274 sys__exit(1); 275 } 276 277 /* Compute search paths for finding thread directories in /proc */ 278 local_itoa(strrchr(strcpy(proc_self_task, "/proc/"), '\000'), ppid); 279 strcpy(marker_name, proc_self_task); 280 marker_path = marker_name + strlen(marker_name); 281 strcat(proc_self_task, "/task/"); 282 proc_paths[0] = proc_self_task; /* /proc/$$/task/ */ 283 proc_paths[1] = "/proc/"; /* /proc/ */ 284 proc_paths[2] = NULL; 285 286 /* Compute path for marker socket in /proc */ 287 local_itoa(strcpy(marker_path, "/fd/") + 4, marker); 288 if (sys_stat(marker_name, &marker_sb) < 0) { 289 goto failure; 290 } 291 292 /* Catch signals on an alternate pre-allocated stack. This way, we can 293 * safely execute the signal handler even if we ran out of memory. 294 */ 295 memset(&altstack, 0, sizeof(altstack)); 296 altstack.ss_sp = args->altstack_mem; 297 altstack.ss_flags = 0; 298 altstack.ss_size = ALT_STACKSIZE; 299 sys_sigaltstack(&altstack, (const stack_t *)NULL); 300 301 /* Some kernels forget to wake up traced processes, when the 302 * tracer dies. So, intercept synchronous signals and make sure 303 * that we wake up our tracees before dying. It is the caller's 304 * responsibility to ensure that asynchronous signals do not 305 * interfere with this function. 306 */ 307 sig_marker = marker; 308 sig_proc = -1; 309 for (sig = 0; sig < sizeof(sync_signals)/sizeof(*sync_signals); sig++) { 310 struct kernel_sigaction sa; 311 memset(&sa, 0, sizeof(sa)); 312 sa.sa_sigaction_ = SignalHandler; 313 sys_sigfillset(&sa.sa_mask); 314 sa.sa_flags = SA_ONSTACK|SA_SIGINFO|SA_RESETHAND; 315 sys_sigaction(sync_signals[sig], &sa, (struct kernel_sigaction *)NULL); 316 } 317 318 /* Read process directories in /proc/... */ 319 for (;;) { 320 /* Some kernels know about threads, and hide them in "/proc" 321 * (although they are still there, if you know the process 322 * id). Threads are moved into a separate "task" directory. We 323 * check there first, and then fall back on the older naming 324 * convention if necessary. 325 */ 326 if ((sig_proc = proc = c_open(*proc_path, O_RDONLY|O_DIRECTORY, 0)) < 0) { 327 if (*++proc_path != NULL) 328 continue; 329 goto failure; 330 } 331 if (sys_fstat(proc, &proc_sb) < 0) 332 goto failure; 333 334 /* Since we are suspending threads, we cannot call any libc 335 * functions that might acquire locks. Most notably, we cannot 336 * call malloc(). So, we have to allocate memory on the stack, 337 * instead. Since we do not know how much memory we need, we 338 * make a best guess. And if we guessed incorrectly we retry on 339 * a second iteration (by jumping to "detach_threads"). 340 * 341 * Unless the number of threads is increasing very rapidly, we 342 * should never need to do so, though, as our guestimate is very 343 * conservative. 344 */ 345 if (max_threads < proc_sb.st_nlink + 100) 346 max_threads = proc_sb.st_nlink + 100; 347 348 /* scope */ { 349 pid_t pids[max_threads]; 350 int added_entries = 0; 351 sig_num_threads = num_threads; 352 sig_pids = pids; 353 for (;;) { 354 struct kernel_dirent *entry; 355 char buf[4096]; 356 ssize_t nbytes = sys_getdents(proc, (struct kernel_dirent *)buf, 357 sizeof(buf)); 358 if (nbytes < 0) 359 goto failure; 360 else if (nbytes == 0) { 361 if (added_entries) { 362 /* Need to keep iterating over "/proc" in multiple 363 * passes until we no longer find any more threads. This 364 * algorithm eventually completes, when all threads have 365 * been suspended. 366 */ 367 added_entries = 0; 368 sys_lseek(proc, 0, SEEK_SET); 369 continue; 370 } 371 break; 372 } 373 for (entry = (struct kernel_dirent *)buf; 374 entry < (struct kernel_dirent *)&buf[nbytes]; 375 entry = (struct kernel_dirent *)((char *)entry+entry->d_reclen)) { 376 if (entry->d_ino != 0) { 377 const char *ptr = entry->d_name; 378 pid_t pid; 379 380 /* Some kernels hide threads by preceding the pid with a '.' */ 381 if (*ptr == '.') 382 ptr++; 383 384 /* If the directory is not numeric, it cannot be a 385 * process/thread 386 */ 387 if (*ptr < '0' || *ptr > '9') 388 continue; 389 pid = local_atoi(ptr); 390 391 /* Attach (and suspend) all threads */ 392 if (pid && pid != clone_pid) { 393 struct kernel_stat tmp_sb; 394 char fname[entry->d_reclen + 48]; 395 strcat(strcat(strcpy(fname, "/proc/"), 396 entry->d_name), marker_path); 397 398 /* Check if the marker is identical to the one we created */ 399 if (sys_stat(fname, &tmp_sb) >= 0 && 400 marker_sb.st_ino == tmp_sb.st_ino) { 401 long i, j; 402 403 /* Found one of our threads, make sure it is no duplicate */ 404 for (i = 0; i < num_threads; i++) { 405 /* Linear search is slow, but should not matter much for 406 * the typically small number of threads. 407 */ 408 if (pids[i] == pid) { 409 /* Found a duplicate; most likely on second pass */ 410 goto next_entry; 411 } 412 } 413 414 /* Check whether data structure needs growing */ 415 if (num_threads >= max_threads) { 416 /* Back to square one, this time with more memory */ 417 NO_INTR(sys_close(proc)); 418 goto detach_threads; 419 } 420 421 /* Attaching to thread suspends it */ 422 pids[num_threads++] = pid; 423 sig_num_threads = num_threads; 424 if (sys_ptrace(PTRACE_ATTACH, pid, (void *)0, 425 (void *)0) < 0) { 426 /* If operation failed, ignore thread. Maybe it 427 * just died? There might also be a race 428 * condition with a concurrent core dumper or 429 * with a debugger. In that case, we will just 430 * make a best effort, rather than failing 431 * entirely. 432 */ 433 num_threads--; 434 sig_num_threads = num_threads; 435 goto next_entry; 436 } 437 while (sys_waitpid(pid, (int *)0, __WALL) < 0) { 438 if (errno != EINTR) { 439 sys_ptrace_detach(pid); 440 num_threads--; 441 sig_num_threads = num_threads; 442 goto next_entry; 443 } 444 } 445 446 if (sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || i++ != j || 447 sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || i != j) { 448 /* Address spaces are distinct, even though both 449 * processes show the "marker". This is probably 450 * a forked child process rather than a thread. 451 */ 452 sys_ptrace_detach(pid); 453 num_threads--; 454 sig_num_threads = num_threads; 455 } else { 456 found_parent |= pid == ppid; 457 added_entries++; 458 } 459 } 460 } 461 } 462 next_entry:; 463 } 464 } 465 NO_INTR(sys_close(proc)); 466 sig_proc = proc = -1; 467 468 /* If we failed to find any threads, try looking somewhere else in 469 * /proc. Maybe, threads are reported differently on this system. 470 */ 471 if (num_threads > 1 || !*++proc_path) { 472 NO_INTR(sys_close(marker)); 473 sig_marker = marker = -1; 474 475 /* If we never found the parent process, something is very wrong. 476 * Most likely, we are running in debugger. Any attempt to operate 477 * on the threads would be very incomplete. Let's just report an 478 * error to the caller. 479 */ 480 if (!found_parent) { 481 ResumeAllProcessThreads(num_threads, pids); 482 sys__exit(3); 483 } 484 485 /* Now we are ready to call the callback, 486 * which takes care of resuming the threads for us. 487 */ 488 args->result = args->callback(args->parameter, num_threads, 489 pids, args->ap); 490 args->err = errno; 491 492 /* Callback should have resumed threads, but better safe than sorry */ 493 if (ResumeAllProcessThreads(num_threads, pids)) { 494 /* Callback forgot to resume at least one thread, report error */ 495 args->err = EINVAL; 496 args->result = -1; 497 } 498 499 sys__exit(0); 500 } 501 detach_threads: 502 /* Resume all threads prior to retrying the operation */ 503 ResumeAllProcessThreads(num_threads, pids); 504 sig_pids = NULL; 505 num_threads = 0; 506 sig_num_threads = num_threads; 507 max_threads += 100; 508 } 509 } 510 } 511 512 513 /* This function gets the list of all linux threads of the current process 514 * passes them to the 'callback' along with the 'parameter' pointer; at the 515 * call back call time all the threads are paused via 516 * PTRACE_ATTACH. 517 * The callback is executed from a separate thread which shares only the 518 * address space, the filesystem, and the filehandles with the caller. Most 519 * notably, it does not share the same pid and ppid; and if it terminates, 520 * the rest of the application is still there. 'callback' is supposed to do 521 * or arrange for ResumeAllProcessThreads. This happens automatically, if 522 * the thread raises a synchronous signal (e.g. SIGSEGV); asynchronous 523 * signals are blocked. If the 'callback' decides to unblock them, it must 524 * ensure that they cannot terminate the application, or that 525 * ResumeAllProcessThreads will get called. 526 * It is an error for the 'callback' to make any library calls that could 527 * acquire locks. Most notably, this means that most system calls have to 528 * avoid going through libc. Also, this means that it is not legal to call 529 * exit() or abort(). 530 * We return -1 on error and the return value of 'callback' on success. 531 */ 532 int ListAllProcessThreads(void *parameter, 533 ListAllProcessThreadsCallBack callback, ...) { 534 char altstack_mem[ALT_STACKSIZE]; 535 struct ListerParams args; 536 pid_t clone_pid; 537 int dumpable = 1, sig; 538 struct kernel_sigset_t sig_blocked, sig_old; 539 540 va_start(args.ap, callback); 541 542 /* If we are short on virtual memory, initializing the alternate stack 543 * might trigger a SIGSEGV. Let's do this early, before it could get us 544 * into more trouble (i.e. before signal handlers try to use the alternate 545 * stack, and before we attach to other threads). 546 */ 547 memset(altstack_mem, 0, sizeof(altstack_mem)); 548 549 /* Some of our cleanup functions could conceivable use more stack space. 550 * Try to touch the stack right now. This could be defeated by the compiler 551 * being too smart for it's own good, so try really hard. 552 */ 553 DirtyStack(32768); 554 555 /* Make this process "dumpable". This is necessary in order to ptrace() 556 * after having called setuid(). 557 */ 558 dumpable = sys_prctl(PR_GET_DUMPABLE, 0); 559 if (!dumpable) 560 sys_prctl(PR_SET_DUMPABLE, 1); 561 562 /* Fill in argument block for dumper thread */ 563 args.result = -1; 564 args.err = 0; 565 args.altstack_mem = altstack_mem; 566 args.parameter = parameter; 567 args.callback = callback; 568 569 /* Before cloning the thread lister, block all asynchronous signals, as we */ 570 /* are not prepared to handle them. */ 571 sys_sigfillset(&sig_blocked); 572 for (sig = 0; sig < sizeof(sync_signals)/sizeof(*sync_signals); sig++) { 573 sys_sigdelset(&sig_blocked, sync_signals[sig]); 574 } 575 if (sys_sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old)) { 576 args.err = errno; 577 args.result = -1; 578 goto failed; 579 } 580 581 /* scope */ { 582 /* After cloning, both the parent and the child share the same instance 583 * of errno. We must make sure that at least one of these processes 584 * (in our case, the parent) uses modified syscall macros that update 585 * a local copy of errno, instead. 586 */ 587 #ifdef __cplusplus 588 #define sys0_sigprocmask sys.sigprocmask 589 #define sys0_waitpid sys.waitpid 590 SysCalls sys; 591 #else 592 int my_errno; 593 #define SYS_ERRNO my_errno 594 #define SYS_INLINE inline 595 #define SYS_PREFIX 0 596 #undef SYS_LINUX_SYSCALL_SUPPORT_H 597 #include "linux_syscall_support.h" 598 #endif 599 600 int clone_errno; 601 clone_pid = local_clone((int (*)(void *))ListerThread, &args); 602 clone_errno = errno; 603 604 sys_sigprocmask(SIG_SETMASK, &sig_old, &sig_old); 605 606 if (clone_pid >= 0) { 607 int status, rc; 608 while ((rc = sys0_waitpid(clone_pid, &status, __WALL)) < 0 && 609 ERRNO == EINTR) { 610 /* Keep waiting */ 611 } 612 if (rc < 0) { 613 args.err = ERRNO; 614 args.result = -1; 615 } else if (WIFEXITED(status)) { 616 switch (WEXITSTATUS(status)) { 617 case 0: break; /* Normal process termination */ 618 case 2: args.err = EFAULT; /* Some fault (e.g. SIGSEGV) detected */ 619 args.result = -1; 620 break; 621 case 3: args.err = EPERM; /* Process is already being traced */ 622 args.result = -1; 623 break; 624 default:args.err = ECHILD; /* Child died unexpectedly */ 625 args.result = -1; 626 break; 627 } 628 } else if (!WIFEXITED(status)) { 629 args.err = EFAULT; /* Terminated due to an unhandled signal*/ 630 args.result = -1; 631 } 632 } else { 633 args.result = -1; 634 args.err = clone_errno; 635 } 636 } 637 638 /* Restore the "dumpable" state of the process */ 639 failed: 640 if (!dumpable) 641 sys_prctl(PR_SET_DUMPABLE, dumpable); 642 643 va_end(args.ap); 644 645 errno = args.err; 646 return args.result; 647 } 648 649 /* This function resumes the list of all linux threads that 650 * ListAllProcessThreads pauses before giving to its callback. 651 * The function returns non-zero if at least one thread was 652 * suspended and has now been resumed. 653 */ 654 int ResumeAllProcessThreads(int num_threads, pid_t *thread_pids) { 655 int detached_at_least_one = 0; 656 while (num_threads-- > 0) { 657 detached_at_least_one |= sys_ptrace_detach(thread_pids[num_threads]) >= 0; 658 } 659 return detached_at_least_one; 660 } 661 662 #ifdef __cplusplus 663 } 664 #endif 665 #endif 666