You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

129 lines
6.0 KiB

  1. ptrace and NTPL, the missing manpage
  2. == Signals ==
  3. A signal sent to a ptrace'd process or thread causes only the thread
  4. that receives it to stop and report to the attached process.
  5. Use tgkill to target a signal (for example, SIGSTOP) at a particular
  6. thread. If you use kill, the signal could be delivered to another
  7. thread in the same process.
  8. Note that SIGSTOP differs from its usual behavior when a process is
  9. being traced. Usually, a SIGSTOP sent to any thread in a thread group
  10. will stop all threads in the thread group. When a thread is traced,
  11. however, a SIGSTOP affects only the receiving thread (and any other
  12. threads in the thread group that are not traced).
  13. SIGKILL behaves like it does for non-traced processes. It affects all
  14. threads in the process and terminates them without the WSTOPSIG event
  15. generated by other signals. However, if PTRACE_O_TRACEEXIT is set,
  16. the attached process will still receive PTRACE_EVENT_EXIT events
  17. before receiving WIFSIGNALED events.
  18. See "Following thread death" for a caveat regarding signal delivery to
  19. zombie threads.
  20. == Waiting on threads ==
  21. Cloned threads in ptrace'd processes are treated similarly to cloned
  22. threads in your own process. Thus, you must use the __WALL option in
  23. order to receive notifications from threads created by the child
  24. process. Similarly, the __WCLONE option will wait only on
  25. notifications from threads created by the child process and *not* on
  26. notifications from the initial child thread.
  27. Even when waiting on a specific thread's PID using waitpid or similar,
  28. __WALL or __WCLONE is necessary or waitpid will return ECHILD.
  29. == Attaching to existing threads ==
  30. libthread_db (which gdb uses), attaches to existing threads by pulling
  31. the pthread data structures out of the traced process. The much
  32. easier way is to traverse the /proc/PID/task directory, though it's
  33. unclear how the semantics of these two approaches differ.
  34. Unfortunately, if the main thread has exited (but the overall process
  35. has not), it sticks around as a zombie process. This zombie will
  36. appear in the /proc/PID/task directory, but trying to attach to it
  37. will yield EPERM. In this case, the third field of the
  38. /proc/PID/task/PID/stat file will be "Z". Attempting to open the stat
  39. file is also a convenient way to detect races between listing the task
  40. directory and the thread exiting. Coincidentally, gdb will simply
  41. fail to attach to a process whose main thread is a zombie.
  42. Because new threads may be created while the debugger is in the
  43. process of attaching to existing threads, the debugger must repeatedly
  44. re-list the task directory until it has attached to (and thus stopped)
  45. every thread listed.
  46. In order to follow new threads created by existing threads,
  47. PTRACE_O_TRACECLONE must be set on each thread attached to.
  48. == Following new threads ==
  49. With the child process stopped, use PTRACE_SETOPTIONS to set the
  50. PTRACE_O_TRACECLONE option. This option is per-thread, and thus must
  51. be set on each existing thread individually. When an existing thread
  52. with PTRACE_O_TRACECLONE set spawns a new thread, the existing thread
  53. will stop with (SIGTRAP | PTRACE_EVENT_CLONE << 8) and the PID of the
  54. new thread can be retrieved with PTRACE_GETEVENTMSG on the creating
  55. thread. At this time, the new thread will exist, but will initially
  56. be stopped with a SIGSTOP. The new thread will automatically be
  57. traced and will inherit the PTRACE_O_TRACECLONE option from its
  58. parent. The attached process should wait on the new thread to receive
  59. the SIGSTOP notification.
  60. When using waitpid(-1, ...), don't rely on the parent thread reporting
  61. a SIGTRAP before receiving the SIGSTOP from the new child thread.
  62. Without PTRACE_O_TRACECLONE, newly cloned threads will not be
  63. ptrace'd. As a result, signals received by new threads will be
  64. handled in the usual way, which may affect the parent and in turn
  65. appear to the attached process, but attributed to the parent (possibly
  66. in unexpected ways).
  67. == Following thread death ==
  68. If any thread with the PTRACE_O_TRACEEXIT option set exits (either by
  69. returning or pthread_exit'ing), the tracing process will receive an
  70. immediate PTRACE_EVENT_EXIT. At this point, the thread will still
  71. exist. The exit status, encoded as for wait, can be queried using
  72. PTRACE_GETEVENTMSG on the exiting thread's PID. The thread should be
  73. continued so it can actually exit, after which its wait behavior is
  74. the same as for a thread without the PTRACE_O_TRACEEXIT option.
  75. If a non-main thread exits (either by returning or pthread_exit'ing),
  76. its corresponding process will also exit, producing a WIFEXITED event
  77. (after the process is continued from a possible PTRACE_EVENT_EXIT
  78. event). It is *not* necessary for another thread to ptrace_join for
  79. this to happen.
  80. If the main thread exits by returning, then all threads will exit,
  81. first generating a PTRACE_EVENT_EXIT event for each thread if
  82. appropriate, then producing a WIFEXITED event for each thread.
  83. If the main thread exits using pthread_exit, then it enters a
  84. non-waitable zombie state. It will still produce an immediate
  85. PTRACE_O_TRACEEXIT event, but the WIFEXITED event will be delayed
  86. until the entire process exits. This state exists so that shells
  87. don't think the process is done until all of the threads have exited.
  88. Unfortunately, signals cannot be delivered to non-waitable zombies.
  89. Most notably, SIGSTOP cannot be delivered; as a result, when you
  90. broadcast SIGSTOP to all of the threads, you must not wait for
  91. non-waitable zombies to stop. Furthermore, any ptrace command on a
  92. non-waitable zombie, including PTRACE_DETACH, will return ESRCH.
  93. == Multi-threaded debuggers ==
  94. If the debugger itself is multi-threaded, ptrace calls must come from
  95. the same thread that originally attached to the remote thread. The
  96. kernel simply compares the PID of the caller of ptrace against the
  97. tracer PID of the process passed to ptrace. Because each debugger
  98. thread has a different PID, calling ptrace from a different thread
  99. might as well be calling it from a different process and the kernel
  100. will return ESRCH.
  101. wait, on the other hand, does not have this restriction. Any debugger
  102. thread can wait on any thread in the attached process.