| 1 | /* | 
| 2 |  * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. | 
| 3 |  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
| 4 |  * | 
| 5 |  * This code is free software; you can redistribute it and/or modify it | 
| 6 |  * under the terms of the GNU General Public License version 2 only, as | 
| 7 |  * published by the Free Software Foundation. | 
| 8 |  * | 
| 9 |  * This code is distributed in the hope that it will be useful, but WITHOUT | 
| 10 |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
| 11 |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
| 12 |  * version 2 for more details (a copy is included in the LICENSE file that | 
| 13 |  * accompanied this code). | 
| 14 |  * | 
| 15 |  * You should have received a copy of the GNU General Public License version | 
| 16 |  * 2 along with this work; if not, write to the Free Software Foundation, | 
| 17 |  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
| 18 |  * | 
| 19 |  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
| 20 |  * or visit www.oracle.com if you need additional information or have any | 
| 21 |  * questions. | 
| 22 |  * | 
| 23 |  */ | 
| 24 |  | 
| 25 | #include <stdio.h> | 
| 26 | #include <stdlib.h> | 
| 27 | #include <string.h> | 
| 28 | #include <signal.h> | 
| 29 | #include <errno.h> | 
| 30 | #include <elf.h> | 
| 31 | #include <dirent.h> | 
| 32 | #include <ctype.h> | 
| 33 | #include <sys/types.h> | 
| 34 | #include <sys/wait.h> | 
| 35 | #include <sys/ptrace.h> | 
| 36 | #include <sys/uio.h> | 
| 37 | #include "libproc_impl.h" | 
| 38 |  | 
| 39 | #if defined(x86_64) && !defined(amd64) | 
| 40 | #define amd64 1 | 
| 41 | #endif | 
| 42 |  | 
| 43 | #ifndef __WALL | 
| 44 | #define __WALL          0x40000000  // Copied from /usr/include/linux/wait.h | 
| 45 | #endif | 
| 46 |  | 
| 47 | // This file has the libproc implementation specific to live process | 
| 48 | // For core files, refer to ps_core.c | 
| 49 |  | 
| 50 | typedef enum { | 
| 51 |   ATTACH_SUCCESS, | 
| 52 |   ATTACH_FAIL, | 
| 53 |   ATTACH_THREAD_DEAD | 
| 54 | } attach_state_t; | 
| 55 |  | 
| 56 | static inline uintptr_t align(uintptr_t ptr, size_t size) { | 
| 57 |   return (ptr & ~(size - 1)); | 
| 58 | } | 
| 59 |  | 
| 60 | // --------------------------------------------- | 
| 61 | // ptrace functions | 
| 62 | // --------------------------------------------- | 
| 63 |  | 
| 64 | // read "size" bytes of data from "addr" within the target process. | 
| 65 | // unlike the standard ptrace() function, process_read_data() can handle | 
| 66 | // unaligned address - alignment check, if required, should be done | 
| 67 | // before calling process_read_data. | 
| 68 |  | 
| 69 | static bool process_read_data(struct ps_prochandle* ph, uintptr_t addr, char *buf, size_t size) { | 
| 70 |   long rslt; | 
| 71 |   size_t i, words; | 
| 72 |   uintptr_t end_addr = addr + size; | 
| 73 |   uintptr_t aligned_addr = align(addr, sizeof(long)); | 
| 74 |  | 
| 75 |   if (aligned_addr != addr) { | 
| 76 |     char *ptr = (char *)&rslt; | 
| 77 |     errno = 0; | 
| 78 |     rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0); | 
| 79 |     if (errno) { | 
| 80 |       print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n" , size, addr); | 
| 81 |       return false; | 
| 82 |     } | 
| 83 |     for (; aligned_addr != addr; aligned_addr++, ptr++); | 
| 84 |     for (; ((intptr_t)aligned_addr % sizeof(long)) && aligned_addr < end_addr; | 
| 85 |         aligned_addr++) | 
| 86 |        *(buf++) = *(ptr++); | 
| 87 |   } | 
| 88 |  | 
| 89 |   words = (end_addr - aligned_addr) / sizeof(long); | 
| 90 |  | 
| 91 |   // assert((intptr_t)aligned_addr % sizeof(long) == 0); | 
| 92 |   for (i = 0; i < words; i++) { | 
| 93 |     errno = 0; | 
| 94 |     rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0); | 
| 95 |     if (errno) { | 
| 96 |       print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n" , size, addr); | 
| 97 |       return false; | 
| 98 |     } | 
| 99 |     *(long *)buf = rslt; | 
| 100 |     buf += sizeof(long); | 
| 101 |     aligned_addr += sizeof(long); | 
| 102 |   } | 
| 103 |  | 
| 104 |   if (aligned_addr != end_addr) { | 
| 105 |     char *ptr = (char *)&rslt; | 
| 106 |     errno = 0; | 
| 107 |     rslt = ptrace(PTRACE_PEEKDATA, ph->pid, aligned_addr, 0); | 
| 108 |     if (errno) { | 
| 109 |       print_debug("ptrace(PTRACE_PEEKDATA, ..) failed for %d bytes @ %lx\n" , size, addr); | 
| 110 |       return false; | 
| 111 |     } | 
| 112 |     for (; aligned_addr != end_addr; aligned_addr++) | 
| 113 |        *(buf++) = *(ptr++); | 
| 114 |   } | 
| 115 |   return true; | 
| 116 | } | 
| 117 |  | 
| 118 | // null implementation for write | 
| 119 | static bool process_write_data(struct ps_prochandle* ph, | 
| 120 |                              uintptr_t addr, const char *buf , size_t size) { | 
| 121 |   return false; | 
| 122 | } | 
| 123 |  | 
| 124 | // "user" should be a pointer to a user_regs_struct | 
| 125 | static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct user_regs_struct *user) { | 
| 126 |   // we have already attached to all thread 'pid's, just use ptrace call | 
| 127 |   // to get regset now. Note that we don't cache regset upfront for processes. | 
| 128 | // Linux on x86 and sparc are different.  On x86 ptrace(PTRACE_GETREGS, ...) | 
| 129 | // uses pointer from 4th argument and ignores 3rd argument.  On sparc it uses | 
| 130 | // pointer from 3rd argument and ignores 4th argument | 
| 131 | #if defined(sparc) || defined(sparcv9) | 
| 132 | #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, addr, data) | 
| 133 | #else | 
| 134 | #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, data, addr) | 
| 135 | #endif | 
| 136 |  | 
| 137 | #if defined(_LP64) && defined(PTRACE_GETREGS64) | 
| 138 | #define PTRACE_GETREGS_REQ PTRACE_GETREGS64 | 
| 139 | #elif defined(PTRACE_GETREGS) | 
| 140 | #define PTRACE_GETREGS_REQ PTRACE_GETREGS | 
| 141 | #elif defined(PT_GETREGS) | 
| 142 | #define PTRACE_GETREGS_REQ PT_GETREGS | 
| 143 | #endif | 
| 144 |  | 
| 145 | #ifdef PTRACE_GETREGS_REQ | 
| 146 |  if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) { | 
| 147 |    print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n" , pid); | 
| 148 |    return false; | 
| 149 |  } | 
| 150 |  return true; | 
| 151 | #elif defined(PTRACE_GETREGSET) | 
| 152 |  struct iovec iov; | 
| 153 |  iov.iov_base = user; | 
| 154 |  iov.iov_len = sizeof(*user); | 
| 155 |  if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, (void*) &iov) < 0) { | 
| 156 |    print_debug("ptrace(PTRACE_GETREGSET, ...) failed for lwp %d\n" , pid); | 
| 157 |    return false; | 
| 158 |  } | 
| 159 |  return true; | 
| 160 | #else | 
| 161 |  print_debug("ptrace(PTRACE_GETREGS, ...) not supported\n" ); | 
| 162 |  return false; | 
| 163 | #endif | 
| 164 |  | 
| 165 | } | 
| 166 |  | 
| 167 | static bool ptrace_continue(pid_t pid, int signal) { | 
| 168 |   // pass the signal to the process so we don't swallow it | 
| 169 |   if (ptrace(PTRACE_CONT, pid, NULL, signal) < 0) { | 
| 170 |     print_debug("ptrace(PTRACE_CONT, ..) failed for %d\n" , pid); | 
| 171 |     return false; | 
| 172 |   } | 
| 173 |   return true; | 
| 174 | } | 
| 175 |  | 
| 176 | // waits until the ATTACH has stopped the process | 
| 177 | // by signal SIGSTOP | 
| 178 | static attach_state_t ptrace_waitpid(pid_t pid) { | 
| 179 |   int ret; | 
| 180 |   int status; | 
| 181 |   errno = 0; | 
| 182 |   while (true) { | 
| 183 |     // Wait for debuggee to stop. | 
| 184 |     ret = waitpid(pid, &status, 0); | 
| 185 |     if (ret == -1 && errno == ECHILD) { | 
| 186 |       // try cloned process. | 
| 187 |       ret = waitpid(pid, &status, __WALL); | 
| 188 |     } | 
| 189 |     if (ret >= 0) { | 
| 190 |       if (WIFSTOPPED(status)) { | 
| 191 |         // Any signal will stop the thread, make sure it is SIGSTOP. Otherwise SIGSTOP | 
| 192 |         // will still be pending and delivered when the process is DETACHED and the process | 
| 193 |         // will go to sleep. | 
| 194 |         if (WSTOPSIG(status) == SIGSTOP) { | 
| 195 |           // Debuggee stopped by SIGSTOP. | 
| 196 |           return ATTACH_SUCCESS; | 
| 197 |         } | 
| 198 |         if (!ptrace_continue(pid, WSTOPSIG(status))) { | 
| 199 |           print_error("Failed to correctly attach to VM. VM might HANG! [PTRACE_CONT failed, stopped by %d]\n" , WSTOPSIG(status)); | 
| 200 |           return ATTACH_FAIL; | 
| 201 |         } | 
| 202 |       } else { | 
| 203 |         print_debug("waitpid(): Child process %d exited/terminated (status = 0x%x)\n" , pid, status); | 
| 204 |         return ATTACH_THREAD_DEAD; | 
| 205 |       } | 
| 206 |     } else { | 
| 207 |       switch (errno) { | 
| 208 |         case EINTR: | 
| 209 |           continue; | 
| 210 |           break; | 
| 211 |         case ECHILD: | 
| 212 |           print_debug("waitpid() failed. Child process pid (%d) does not exist \n" , pid); | 
| 213 |           return ATTACH_THREAD_DEAD; | 
| 214 |         case EINVAL: | 
| 215 |           print_error("waitpid() failed. Invalid options argument.\n" ); | 
| 216 |           return ATTACH_FAIL; | 
| 217 |         default: | 
| 218 |           print_error("waitpid() failed. Unexpected error %d\n" , errno); | 
| 219 |           return ATTACH_FAIL; | 
| 220 |       } | 
| 221 |     } // else | 
| 222 |   } // while | 
| 223 | } | 
| 224 |  | 
| 225 | // checks the state of the thread/process specified by "pid", by reading | 
| 226 | // in the 'State:' value from the /proc/<pid>/status file. From the proc | 
| 227 | // man page, "Current state of the process. One of "R (running)", | 
| 228 | // "S (sleeping)", "D (disk sleep)", "T (stopped)", "T (tracing stop)", | 
| 229 | // "Z (zombie)", or "X (dead)"." Assumes that the thread is dead if we | 
| 230 | // don't find the status file or if the status is 'X' or 'Z'. | 
| 231 | static bool process_doesnt_exist(pid_t pid) { | 
| 232 |   char fname[32]; | 
| 233 |   char buf[30]; | 
| 234 |   FILE *fp = NULL; | 
| 235 |   const char state_string[] = "State:" ; | 
| 236 |  | 
| 237 |   sprintf(fname, "/proc/%d/status" , pid); | 
| 238 |   fp = fopen(fname, "r" ); | 
| 239 |   if (fp == NULL) { | 
| 240 |     print_debug("can't open /proc/%d/status file\n" , pid); | 
| 241 |     // Assume the thread does not exist anymore. | 
| 242 |     return true; | 
| 243 |   } | 
| 244 |   bool found_state = false; | 
| 245 |   size_t state_len = strlen(state_string); | 
| 246 |   while (fgets(buf, sizeof(buf), fp) != NULL) { | 
| 247 |     char *state = NULL; | 
| 248 |     if (strncmp (buf, state_string, state_len) == 0) { | 
| 249 |       found_state = true; | 
| 250 |       state = buf + state_len; | 
| 251 |       // Skip the spaces | 
| 252 |       while (isspace(*state)) { | 
| 253 |         state++; | 
| 254 |       } | 
| 255 |       // A state value of 'X' indicates that the thread is dead. 'Z' | 
| 256 |       // indicates that the thread is a zombie. | 
| 257 |       if (*state == 'X' || *state == 'Z') { | 
| 258 |         fclose (fp); | 
| 259 |         return true; | 
| 260 |       } | 
| 261 |       break; | 
| 262 |     } | 
| 263 |   } | 
| 264 |   // If the state value is not 'X' or 'Z', the thread exists. | 
| 265 |   if (!found_state) { | 
| 266 |     // We haven't found the line beginning with 'State:'. | 
| 267 |     // Assuming the thread exists. | 
| 268 |     print_error("Could not find the 'State:' string in the /proc/%d/status file\n" , pid); | 
| 269 |   } | 
| 270 |   fclose (fp); | 
| 271 |   return false; | 
| 272 | } | 
| 273 |  | 
| 274 | // attach to a process/thread specified by "pid" | 
| 275 | static attach_state_t ptrace_attach(pid_t pid, char* err_buf, size_t err_buf_len) { | 
| 276 |   errno = 0; | 
| 277 |   if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) { | 
| 278 |     if (errno == EPERM || errno == ESRCH) { | 
| 279 |       // Check if the process/thread is exiting or is a zombie | 
| 280 |       if (process_doesnt_exist(pid)) { | 
| 281 |         print_debug("Thread with pid %d does not exist\n" , pid); | 
| 282 |         return ATTACH_THREAD_DEAD; | 
| 283 |       } | 
| 284 |     } | 
| 285 |     char buf[200]; | 
| 286 |     char* msg = strerror_r(errno, buf, sizeof(buf)); | 
| 287 |     snprintf(err_buf, err_buf_len, "ptrace(PTRACE_ATTACH, ..) failed for %d: %s" , pid, msg); | 
| 288 |     print_error("%s\n" , err_buf); | 
| 289 |     return ATTACH_FAIL; | 
| 290 |   } else { | 
| 291 |     attach_state_t wait_ret = ptrace_waitpid(pid); | 
| 292 |     if (wait_ret == ATTACH_THREAD_DEAD) { | 
| 293 |       print_debug("Thread with pid %d does not exist\n" , pid); | 
| 294 |     } | 
| 295 |     return wait_ret; | 
| 296 |   } | 
| 297 | } | 
| 298 |  | 
| 299 | // ------------------------------------------------------- | 
| 300 | // functions for obtaining library information | 
| 301 | // ------------------------------------------------------- | 
| 302 |  | 
| 303 | /* | 
| 304 |  * splits a string _str_ into substrings with delimiter _delim_ by replacing old * delimiters with _new_delim_ (ideally, '\0'). the address of each substring | 
| 305 |  * is stored in array _ptrs_ as the return value. the maximum capacity of _ptrs_ * array is specified by parameter _n_. | 
| 306 |  * RETURN VALUE: total number of substrings (always <= _n_) | 
| 307 |  * NOTE: string _str_ is modified if _delim_!=_new_delim_ | 
| 308 |  */ | 
| 309 | static int split_n_str(char * str, int n, char ** ptrs, char delim, char new_delim) | 
| 310 | { | 
| 311 |    int i; | 
| 312 |    for(i = 0; i < n; i++) ptrs[i] = NULL; | 
| 313 |    if (str == NULL || n < 1 ) return 0; | 
| 314 |  | 
| 315 |    i = 0; | 
| 316 |  | 
| 317 |    // skipping leading blanks | 
| 318 |    while(*str&&*str==delim) str++; | 
| 319 |  | 
| 320 |    while(*str&&i<n){ | 
| 321 |      ptrs[i++] = str; | 
| 322 |      while(*str&&*str!=delim) str++; | 
| 323 |      while(*str&&*str==delim) *(str++) = new_delim; | 
| 324 |    } | 
| 325 |  | 
| 326 |    return i; | 
| 327 | } | 
| 328 |  | 
| 329 | /* | 
| 330 |  * fgets without storing '\n' at the end of the string | 
| 331 |  */ | 
| 332 | static char * fgets_no_cr(char * buf, int n, FILE *fp) | 
| 333 | { | 
| 334 |    char * rslt = fgets(buf, n, fp); | 
| 335 |    if (rslt && buf && *buf){ | 
| 336 |        char *p = strchr(buf, '\0'); | 
| 337 |        if (*--p=='\n') *p='\0'; | 
| 338 |    } | 
| 339 |    return rslt; | 
| 340 | } | 
| 341 |  | 
| 342 | static bool read_lib_info(struct ps_prochandle* ph) { | 
| 343 |   char fname[32]; | 
| 344 |   char buf[PATH_MAX]; | 
| 345 |   FILE *fp = NULL; | 
| 346 |  | 
| 347 |   sprintf(fname, "/proc/%d/maps" , ph->pid); | 
| 348 |   fp = fopen(fname, "r" ); | 
| 349 |   if (fp == NULL) { | 
| 350 |     print_debug("can't open /proc/%d/maps file\n" , ph->pid); | 
| 351 |     return false; | 
| 352 |   } | 
| 353 |  | 
| 354 |   while(fgets_no_cr(buf, PATH_MAX, fp)){ | 
| 355 |     char * word[7]; | 
| 356 |     int nwords = split_n_str(buf, 7, word, ' ', '\0'); | 
| 357 |  | 
| 358 |     if (nwords < 6) { | 
| 359 |       // not a shared library entry. ignore. | 
| 360 |       continue; | 
| 361 |     } | 
| 362 |  | 
| 363 |     // SA does not handle the lines with patterns: | 
| 364 |     //   "[stack]", "[heap]", "[vdso]", "[vsyscall]", etc. | 
| 365 |     if (word[5][0] == '[') { | 
| 366 |         // not a shared library entry. ignore. | 
| 367 |         continue; | 
| 368 |     } | 
| 369 |  | 
| 370 |     if (nwords > 6) { | 
| 371 |       // prelink altered mapfile when the program is running. | 
| 372 |       // Entries like one below have to be skipped | 
| 373 |       //  /lib64/libc-2.15.so (deleted) | 
| 374 |       // SO name in entries like one below have to be stripped. | 
| 375 |       //  /lib64/libpthread-2.15.so.#prelink#.EECVts | 
| 376 |       char *s = strstr(word[5],".#prelink#" ); | 
| 377 |       if (s == NULL) { | 
| 378 |         // No prelink keyword. skip deleted library | 
| 379 |         print_debug("skip shared object %s deleted by prelink\n" , word[5]); | 
| 380 |         continue; | 
| 381 |       } | 
| 382 |  | 
| 383 |       // Fall through | 
| 384 |       print_debug("rectifying shared object name %s changed by prelink\n" , word[5]); | 
| 385 |       *s = 0; | 
| 386 |     } | 
| 387 |  | 
| 388 |     if (find_lib(ph, word[5]) == false) { | 
| 389 |        intptr_t base; | 
| 390 |        lib_info* lib; | 
| 391 | #ifdef _LP64 | 
| 392 |        sscanf(word[0], "%lx" , &base); | 
| 393 | #else | 
| 394 |        sscanf(word[0], "%x" , &base); | 
| 395 | #endif | 
| 396 |        if ((lib = add_lib_info(ph, word[5], (uintptr_t)base)) == NULL) | 
| 397 |           continue; // ignore, add_lib_info prints error | 
| 398 |  | 
| 399 |        // we don't need to keep the library open, symtab is already | 
| 400 |        // built. Only for core dump we need to keep the fd open. | 
| 401 |        close(lib->fd); | 
| 402 |        lib->fd = -1; | 
| 403 |     } | 
| 404 |   } | 
| 405 |   fclose(fp); | 
| 406 |   return true; | 
| 407 | } | 
| 408 |  | 
| 409 | // detach a given pid | 
| 410 | static bool ptrace_detach(pid_t pid) { | 
| 411 |   if (pid && ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) { | 
| 412 |     print_debug("ptrace(PTRACE_DETACH, ..) failed for %d\n" , pid); | 
| 413 |     return false; | 
| 414 |   } else { | 
| 415 |     return true; | 
| 416 |   } | 
| 417 | } | 
| 418 |  | 
| 419 | // detach all pids of a ps_prochandle | 
| 420 | static void detach_all_pids(struct ps_prochandle* ph) { | 
| 421 |   thread_info* thr = ph->threads; | 
| 422 |   while (thr) { | 
| 423 |      ptrace_detach(thr->lwp_id); | 
| 424 |      thr = thr->next; | 
| 425 |   } | 
| 426 | } | 
| 427 |  | 
| 428 | static void process_cleanup(struct ps_prochandle* ph) { | 
| 429 |   detach_all_pids(ph); | 
| 430 | } | 
| 431 |  | 
| 432 | static ps_prochandle_ops process_ops = { | 
| 433 |   .release=  process_cleanup, | 
| 434 |   .p_pread=  process_read_data, | 
| 435 |   .p_pwrite= process_write_data, | 
| 436 |   .get_lwp_regs= process_get_lwp_regs | 
| 437 | }; | 
| 438 |  | 
| 439 | // attach to the process. One and only one exposed stuff | 
| 440 | JNIEXPORT struct ps_prochandle* JNICALL | 
| 441 | Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) { | 
| 442 |   struct ps_prochandle* ph = NULL; | 
| 443 |   thread_info* thr = NULL; | 
| 444 |   attach_state_t attach_status = ATTACH_SUCCESS; | 
| 445 |  | 
| 446 |   if ( (ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle))) == NULL) { | 
| 447 |     snprintf(err_buf, err_buf_len, "can't allocate memory for ps_prochandle" ); | 
| 448 |     print_debug("%s\n" , err_buf); | 
| 449 |     return NULL; | 
| 450 |   } | 
| 451 |  | 
| 452 |   if ((attach_status = ptrace_attach(pid, err_buf, err_buf_len)) != ATTACH_SUCCESS) { | 
| 453 |     if (attach_status == ATTACH_THREAD_DEAD) { | 
| 454 |        print_error("The process with pid %d does not exist.\n" , pid); | 
| 455 |     } | 
| 456 |     free(ph); | 
| 457 |     return NULL; | 
| 458 |   } | 
| 459 |  | 
| 460 |   // initialize ps_prochandle | 
| 461 |   ph->pid = pid; | 
| 462 |   add_thread_info(ph, ph->pid); | 
| 463 |  | 
| 464 |   // initialize vtable | 
| 465 |   ph->ops = &process_ops; | 
| 466 |  | 
| 467 |   // read library info and symbol tables, must do this before attaching threads, | 
| 468 |   // as the symbols in the pthread library will be used to figure out | 
| 469 |   // the list of threads within the same process. | 
| 470 |   read_lib_info(ph); | 
| 471 |  | 
| 472 |   /* | 
| 473 |    * Read thread info. | 
| 474 |    * SA scans all tasks in /proc/<PID>/task to read all threads info. | 
| 475 |    */ | 
| 476 |   char taskpath[PATH_MAX]; | 
| 477 |   DIR *dirp; | 
| 478 |   struct dirent *entry; | 
| 479 |  | 
| 480 |   snprintf(taskpath, PATH_MAX, "/proc/%d/task" , ph->pid); | 
| 481 |   dirp = opendir(taskpath); | 
| 482 |   int lwp_id; | 
| 483 |   while ((entry = readdir(dirp)) != NULL) { | 
| 484 |     if (*entry->d_name == '.') { | 
| 485 |       continue; | 
| 486 |     } | 
| 487 |     lwp_id = atoi(entry->d_name); | 
| 488 |     if (lwp_id == ph->pid) { | 
| 489 |       continue; | 
| 490 |     } | 
| 491 |     if (!process_doesnt_exist(lwp_id)) { | 
| 492 |       add_thread_info(ph, lwp_id); | 
| 493 |     } | 
| 494 |   } | 
| 495 |   closedir(dirp); | 
| 496 |  | 
| 497 |   // attach to the threads | 
| 498 |   thr = ph->threads; | 
| 499 |  | 
| 500 |   while (thr) { | 
| 501 |     thread_info* current_thr = thr; | 
| 502 |     thr = thr->next; | 
| 503 |     // don't attach to the main thread again | 
| 504 |     if (ph->pid != current_thr->lwp_id) { | 
| 505 |       if ((attach_status = ptrace_attach(current_thr->lwp_id, err_buf, err_buf_len)) != ATTACH_SUCCESS) { | 
| 506 |         if (attach_status == ATTACH_THREAD_DEAD) { | 
| 507 |           // Remove this thread from the threads list | 
| 508 |           delete_thread_info(ph, current_thr); | 
| 509 |         } | 
| 510 |         else { | 
| 511 |           Prelease(ph); | 
| 512 |           return NULL; | 
| 513 |         } // ATTACH_THREAD_DEAD | 
| 514 |       } // !ATTACH_SUCCESS | 
| 515 |     } | 
| 516 |   } | 
| 517 |   return ph; | 
| 518 | } | 
| 519 |  |