diff --git a/docs/index.rst b/docs/index.rst index 925a3c0a37..3ecc955bc0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -38,6 +38,9 @@ to more effectively use different aspects of binary analysis for building their :ref:`sec:proccontrolapi-intro` An API and library for controlling processes +:ref:`sec:stackwalk-intro` + Collect and analyze stack traces + .. _main-support: ------- @@ -90,6 +93,7 @@ Developed by parseAPI/overview patchAPI/overview proccontrol/overview + stackwalk/overview usertools/DynC/overview .. toctree:: @@ -116,6 +120,7 @@ Developed by parseAPI/public/API patchAPI/public/API proccontrol/public/API + stackwalk/public/API .. toctree:: :caption: developer docs @@ -128,6 +133,7 @@ Developed by parseAPI/developer/API patchAPI/developer/API proccontrol/developer/API + stackwalk/developer/API .. toctree:: :caption: advanced diff --git a/docs/stackwalk/developer/API.rst b/docs/stackwalk/developer/API.rst new file mode 100644 index 0000000000..38c9bf9fa9 --- /dev/null +++ b/docs/stackwalk/developer/API.rst @@ -0,0 +1,22 @@ +============ +StackwalkAPI +============ + +.. toctree:: + :caption: Developer API + :name: stackwalk-developer-api + :hidden: + :maxdepth: 1 + + aarch64-swk.h + analysis_stepper.h + dbgstepper-impl.h + framestepper_pimple.h + freebsd-swk.h + get_trap_instruction.h + libstate.h + linuxbsd-swk.h + linux-swk.h + sw.h + symtab-swk.h + x86-swk.h \ No newline at end of file diff --git a/docs/stackwalk/developer/aarch64-swk.h.rst b/docs/stackwalk/developer/aarch64-swk.h.rst new file mode 100644 index 0000000000..f63e7595c4 --- /dev/null +++ b/docs/stackwalk/developer/aarch64-swk.h.rst @@ -0,0 +1,5 @@ +aarch64-swk.h +============= + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/analysis_stepper.h.rst b/docs/stackwalk/developer/analysis_stepper.h.rst new file mode 100644 index 0000000000..68013cdc62 --- /dev/null +++ b/docs/stackwalk/developer/analysis_stepper.h.rst @@ -0,0 +1,5 @@ +analysis_stepper.h +================== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/dbgstepper-impl.h.rst b/docs/stackwalk/developer/dbgstepper-impl.h.rst new file mode 100644 index 0000000000..00bf0be034 --- /dev/null +++ b/docs/stackwalk/developer/dbgstepper-impl.h.rst @@ -0,0 +1,5 @@ +dbgstepper-impl.h +================= + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/framestepper_pimple.h.rst b/docs/stackwalk/developer/framestepper_pimple.h.rst new file mode 100644 index 0000000000..65d7a2cbb8 --- /dev/null +++ b/docs/stackwalk/developer/framestepper_pimple.h.rst @@ -0,0 +1,5 @@ +framestepper_pimple.h +===================== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/freebsd-swk.h.rst b/docs/stackwalk/developer/freebsd-swk.h.rst new file mode 100644 index 0000000000..335ebf9a63 --- /dev/null +++ b/docs/stackwalk/developer/freebsd-swk.h.rst @@ -0,0 +1,5 @@ +freebsd-swk.h +============= + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/get_trap_instruction.h.rst b/docs/stackwalk/developer/get_trap_instruction.h.rst new file mode 100644 index 0000000000..fe5d3c55aa --- /dev/null +++ b/docs/stackwalk/developer/get_trap_instruction.h.rst @@ -0,0 +1,5 @@ +get_trap_instruction.h +====================== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/libstate.h.rst b/docs/stackwalk/developer/libstate.h.rst new file mode 100644 index 0000000000..e1ae9361a7 --- /dev/null +++ b/docs/stackwalk/developer/libstate.h.rst @@ -0,0 +1,5 @@ +libstate.h +========== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/linux-swk.h.rst b/docs/stackwalk/developer/linux-swk.h.rst new file mode 100644 index 0000000000..d6c24710d7 --- /dev/null +++ b/docs/stackwalk/developer/linux-swk.h.rst @@ -0,0 +1,5 @@ +linux-swk.h +=========== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/linuxbsd-swk.h.rst b/docs/stackwalk/developer/linuxbsd-swk.h.rst new file mode 100644 index 0000000000..ddcc4d7267 --- /dev/null +++ b/docs/stackwalk/developer/linuxbsd-swk.h.rst @@ -0,0 +1,5 @@ +linuxbsd-swk.h +============== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/sw.h.rst b/docs/stackwalk/developer/sw.h.rst new file mode 100644 index 0000000000..e233291e80 --- /dev/null +++ b/docs/stackwalk/developer/sw.h.rst @@ -0,0 +1,5 @@ +sw.h +==== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/symtab-swk.h.rst b/docs/stackwalk/developer/symtab-swk.h.rst new file mode 100644 index 0000000000..7fa54f7ab3 --- /dev/null +++ b/docs/stackwalk/developer/symtab-swk.h.rst @@ -0,0 +1,5 @@ +symtab-swk.h +============ + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/developer/x86-swk.h.rst b/docs/stackwalk/developer/x86-swk.h.rst new file mode 100644 index 0000000000..da7e2492cb --- /dev/null +++ b/docs/stackwalk/developer/x86-swk.h.rst @@ -0,0 +1,5 @@ +x86-swk.h +========= + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/index.rst b/docs/stackwalk/overview.rst similarity index 78% rename from docs/stackwalk/index.rst rename to docs/stackwalk/overview.rst index fd2c4e5daa..687f36f997 100644 --- a/docs/stackwalk/index.rst +++ b/docs/stackwalk/overview.rst @@ -1,17 +1,19 @@ .. _`sec:stackwalk-intro`: -Stackwalk Introduction -====================== +.. cpp:namespace:: Dyninst::Stackwalker -This document describes StackwalkerAPI, an API and library for walking a -call stack. The call stack (also known as the run-time stack) is a stack -found in a process that contains the currently active stack frames. Each +=========== +Stackwalker +=========== + +Stackwalker is a library for walking a call stack. The call stack +(also known as the run-time stack) is a stack +found in a process that contains the currently-active stack frames. Each stack frame is a record of an executing function (or function-like object such as a signal handler or system call). StackwalkerAPI provides an API that allows users to collect a call stack (known as walking the call stack) and access information about its stack frames. The current -implementation supports Linux/x86, Linux/x86-64, Linux/Power, -Linux/Power-64, and Windows/x86. +implementation supports Linux/x86, Linux/x86-64, Linux/Power-64, and Windows/x86. StackwalkerAPI is designed to be both easy-to-use and easy-to-extend. Users can easily use StackwalkerAPI to walk a call stack without needing @@ -20,103 +22,122 @@ easily extend StackwalkerAPI to work with new platforms and types of stack frames by implementing a set of callbacks that can be plugged into StackwalkerAPI. -StackwalkerAPI’s ease-of-use comes from it providing a platform -independent interface that allows users to access detailed information -about the call stack. For example, the following C++ code-snippet is all -that is needed to walk and print the call stack of the currently running -thread. +.. _`sec:stackwalk-abstractions`: -.. code-block:: cpp +Abstractions +============ - std::vector stackwalk; - string s; +StackwalkerAPI contains two interfaces: the Stackwalking Interface and +the Callback Interface. The stackwalking interface is used to walk the +call stack, query information about stack frames, and collect basic +information about threads. The Callback Interface is used to provide +custom mechanisms for walking a call stack. Users who operate in one of +StackwalkerAPI’s standard configurations do not need to use the Callback +Interface. - Walker *walker = Walker::newWalker(); - walker->walkStack(stackwalk); - for (unsigned i=0; i`__ shows the -ownership hierarchy for StackwalkerAPI’s classes. Ownership is a -"contains" relationship; if one class owns another, then instances of -the owner class maintain an exclusive instance of the other. For -example, in Figure `[fig:object-ownership] <#fig:object-ownership>`__ -the each Walker instance contains exactly one instance of a ProcessState -object. No other instance of Walker uses that instance of ProcessState. +Host Process + The process in which StackwalkerAPI code is currently running. -This remainder of this section briefly describes the six classes that -make up StackwalkerAPI’s two interfaces. For more details, see the class -descriptions in Section `3 <#sec:api>`__. +First Party Stackwalk + StackwalkerAPI collects first party stackwalk when it walks a call + stack in the same address space it is running in, i.e. the target + process is the same as the host process. -= [rectangle, draw, rounded corners] +Third Party Stackwalk + StackwalkerAPI collects third party stackwalk when it walks the call + stack in a different address space from the one it is running in, + i.e. the target process is different from the host process. A third + party stackwalk is usually done through a debugger interface. Stackwalking Interface ---------------------- @@ -182,123 +203,148 @@ SymbolLookup StackwalkerAPI. A user could, for example, use this interface to allow StackwalkerAPI to use libelf to look up symbol names instead. -.. _`sec:stackwalk-api`: - -Stackwalk API Reference -======================= -This section describes the StackwalkerAPI interface. It is divided into -three sub-sections: a description of the definitions and basic types -used by this API, a description of the interface for collecting -stackwalks, and a description of the callback interface. -Definitions and Basic Types ---------------------------- - -The following definitions and basic types are referenced throughout the -rest of this manual. +Callback Interface Default Implementations +========================================== -.. _`subsec:definitions`: +StackwalkerAPI provides one or more default implementations of each of +the callback classes. These implementations are used by a default configuration of StackwalkerAPI. -Definitions -~~~~~~~~~~~ +.. _`subsec:debugger`: -Stack Frame - A stack frame is a record of a function (or function-like object) - invocation. When a function is executed, it may create a frame on the - call stack. StackwalkerAPI finds stack frames and returns a - description of them when it walks a call stack. The following three - definitions deal with stack frames. +Debugger Interface +------------------ -Bottom of the Stack - The bottom of the stack is the earliest stack frame in a call stack, - usually a thread’s initial function. The stack grows from bottom to - the top. +This section describes how to use StackwalkerAPI for collecting 3rd +party stack walks. In 3rd party mode StackwalkerAPI uses the OS’s +debugger interface to connect to another process and walk its call +stacks. As part of being a debugger StackwalkerAPI receives and needs to +handle debug events. When a debugger event occurs, StackwalkerAPI must +get control of the host process in order to receive the debugger event +and continue the target process. -Top of the Stack - The top of the stack is the most recent stack frame in a call stack. - The stack frame at the top of the stack is for the currently - executing function. +To illustrate the complexities with running in 3rd party mode, consider +the follow code snippet that uses StackwalkerAPI to collect a stack walk +every five seconds. -Frame Object - A Frame object is StackwalkerAPI’s representation of a stack frame. A - Frame object is a snapshot of a stack frame at a specific point in - time. Even if a stack frame changes as a process executes, a Frame - object will remain the same. Each Frame object is represented by an - instance of the Frame class. +.. code-block:: cpp + + Walker *walker = Walker::newWalker(pid); + std::vector swalk; + for (;;) { + walker->walkStack(swalk); + sleep(5); + } -The following three definitions deal with fields in a Frame object. +StackwalkerAPI is running in 3rd party mode, since it attached to the +target process, ``pid``. As the target process runs it may be generating +debug events such a thread creation and destruction, library loads and +unloads, signals, forking/execing, etc. When one of these debugger +events is generated the OS will pause the target process and send a +notice to the host process. The target process will remain paused until +the host process handles the debug event and resumes the target process. -SP (Stack Pointer) - A Frame object’s SP member points to the top of its stack frame (a - stack frame grows from bottom to top, similar to a call stack). The - Frame object for the top of the stack has a SP that is equal to the - value in the stack pointer register at the time the Frame object was - created. The Frame object for any other stack frame has a SP that is - equal to the top address in the stack frame. +In the above example the host process is spending almost all of its time +in the sleep call. If a debugger event happens during the sleep, then +StackwalkerAPI will not be able to get control of the host process and +handle the event for up to five seconds. This will cause long pauses in +the target process and lead to a potentially very large slowdown. -FP (Frame Pointer) - A Frame object’s FP member points to the beginning (or bottom) of its - stack frame. The Frame object for the top of the stack has a FP that - is equal to the value in the frame pointer register at the time the - Frame object was created. The Frame object for any other stack frame - has a FP that is equal to the beginning of the stack frame. +To work around this problem StackwalkerAPI provides a notification file +descriptor. This file descriptor represents a connection between the +StackwalkerAPI library and user code. StackwalkerAPI will write a single +byte to this file descriptor when a debug event occurs, thus notifying +the user code that it needs to let StackwalkerAPI receive and handle +debug events. The user code can use system calls such as select to watch +for events on the notification file descriptor. -RA (Return Address) - A Frame object’s RA member points to the location in the code space - where control will resume when the function that created the stack - frame resumes. The Frame object for the top of the stack has a RA - that is equal to the value in the program counter register at the - time the Frame object was created. The Frame object for any other - stack frame has a RA that is found when walking a call stack. +The following example illustrates how to properly use StackwalkerAPI to +collect a stack walk from another process at a five second interval. +Details on the ``ProcDebug`` class, ``getNotificationFD`` method, and +``handleDebugEvent`` method can be found in +Section `4.1.1 <#subsubsec:procdebug>`__. See the UNIX man pages for +more information on the ``select`` system call. Note that this example +does not include all of the proper error handling and includes that +should be present when using ``select``. -= [rectangle, draw, minimum width=3cm, minimum height=1.5em, font=, node -distance=1.5em] = [rectangle, minimum width=3cm, draw, minimum -height=1.5em, fill=white, draw=white] = [rectangle] = [rectangle, font=] -= [draw, -latex’] +.. code-block:: cpp -= [rectangle, draw, minimum width=4cm, minimum height=1.5em, font=, node -distance=1.5em] = [rectangle, minimum width=4cm, draw, minimum -height=1.5em, fill=white, draw=white] = [rectangle] = [rectangle, font=] -= [draw, -latex’] + Walker *walker = Walker::newWalker(pid); + ProcDebug *debugger = (ProcDebug *) walker->getProcessState(); + std::vector swalk; + for (;;) { + walker->walkStack(swalk); + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + int max = 1; + fd_set readfds, writefds, exceptfds; + FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); + FD_SET(ProcDebug::getNotificationFD(), &readfds); + for (;;) { + int result = select(max, &readfds, &writefds, &exceptfds, &timeout); + if (FD_ISSET(ProcDebug::getNotificationFD(), readfds)) { + //Debug event + ProcDebug::handleDebugEvent(); + } + if (result == 0) { + //Timeout + break; + } + } + } -Figure `[fig:layout] <#fig:layout>`__ shows the relationship between -application code, stack frames, and Frame objects. In the figure, the -source code on the left has run through the main and foo functions, and -into the bar function. It has created the call stack in the center, -which is shown as a sequence of words growing down. The current values -of the processor registers, while executing in bar, are shown below the -call stack. When StackwalkerAPI walks the call stack, it creates the -Frame objects shown on the right. Each Frame object corresponds to one -of the stack frames found in the call stack or application registers. +Extending Stackwalker +===================== -The call stack in Figure `[fig:layout] <#fig:layout>`__ is similar to -one that would be found on the x86 architecture. Details about how the -call stack is laid out may be different on other architectures, but the -meanings of the FP, SP, and RA fields in the Frame objects will remain -the same. The layout of the ARM64 stack may be found in -Figure `[fig:layout-armv8] <#fig:layout-armv8>`__ as an example of the -scope of architectural variations. +Our other design goal with StackwalkerAPI is to make it easy-to-extend. +The mechanics of how to walk through a stack frame can vary between +different platforms, and even between different types of stack frames on +the same platform. In addition, different platforms may have different +mechanisms for reading the data in a call stack or looking up symbolic +names that go with a stack frame. StackwalkerAPI provides a callback +interface for plugging in mechanisms for handling new systems and types +of stack frames. The callback interface can be used to port +StackwalkerAPI to new platforms, extend StackwalkerAPI support on +existing systems, or more easily integrate StackwalkerAPI into existing +tools. There are callbacks for the following StackwalkerAPI operations: -The following four definitions deal with processes involved in -StackwalkerAPI. +Walk through a stack frame +-------------------------- +StackwalkerAPI will find different types of stack frames on different +platforms and even within the same platform. For example, on +Linux/x86 the stack frame generated by a typical function looks +different from the stack frame generated by a signal handler. The +callback interface can be used to register a handler with +StackwalkerAPI that knows how to walk through a new type of stack +frame. For example, the DyninstAPI tool registers an object with +StackwalkerAPI that describes how to walk through the stack frames +generated by its instrumentation. -Target Process - The process from which StackwalkerAPI is collecting stackwalks. +Access process data +------------------- +To walk a call stack, StackwalkerAPI needs to be able to read a +process’ memory and registers. When doing a first party stackwalk, +this is done by directly reading them from the current address space. +When doing a third party stackwalk, this is done by reading them +using a debugger interface. The callback interface can be used to +register new objects for accessing process data. This can be used, +for example, to port StackwalkerAPI to a new operating system or make +it work with a new debugger interface. -Host Process - The process in which StackwalkerAPI code is currently running. +Look up symbolic names +---------------------- +When StackwalkerAPI finds a stack frame, it gets an address that +points into the piece of code that created that stack frame. This +address is not necessarily meaningful to a user, so StackwalkerAPI +attempts to associate the address with a symbolic name. The callback +interface can be used to register an object with StackwalkerAPI that +performs an address to name mapping, allowing StackwalkerAPI to +associate names with stack frames. -First Party Stackwalk - StackwalkerAPI collects first party stackwalk when it walks a call - stack in the same address space it is running in, i.e. the target - process is the same as the host process. +.. _`subsec:stackwalk-definitions`: -Third Party Stackwalk - StackwalkerAPI collects third party stackwalk when it walks the call - stack in a different address space from the one it is running in, - i.e. the target process is different from the host process. A third - party stackwalk is usually done through a debugger interface. Basic Types ~~~~~~~~~~~ @@ -1401,7 +1447,7 @@ Returns the name of the executable associated with the current process state. Class LibraryState -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ **Defined in:** ``procstate.h`` @@ -1522,372 +1568,26 @@ This method returns the ``Walker`` object associated with this This method returns the ``ProcessState`` object associated with this ``SymbolLookup``. -Callback Interface Default Implementations -========================================== - -StackwalkerAPI provides one or more default implementations of each of -the callback classes described in Section 3.5. These implementations are -used by a default configuration of StackwalkerAPI. - -.. _`subsec:debugger`: - -Debugger Interface ------------------- - -This section describes how to use StackwalkerAPI for collecting 3rd -party stack walks. In 3rd party mode StackwalkerAPI uses the OS’s -debugger interface to connect to another process and walk its call -stacks. As part of being a debugger StackwalkerAPI receives and needs to -handle debug events. When a debugger event occurs, StackwalkerAPI must -get control of the host process in order to receive the debugger event -and continue the target process. - -To illustrate the complexities with running in 3rd party mode, consider -the follow code snippet that uses StackwalkerAPI to collect a stack walk -every five seconds. +Usage +===== -.. code-block:: cpp - - Walker *walker = Walker::newWalker(pid); - std::vector swalk; - for (;;) { - walker->walkStack(swalk); - sleep(5); - } - -StackwalkerAPI is running in 3rd party mode, since it attached to the -target process, ``pid``. As the target process runs it may be generating -debug events such a thread creation and destruction, library loads and -unloads, signals, forking/execing, etc. When one of these debugger -events is generated the OS will pause the target process and send a -notice to the host process. The target process will remain paused until -the host process handles the debug event and resumes the target process. - -In the above example the host process is spending almost all of its time -in the sleep call. If a debugger event happens during the sleep, then -StackwalkerAPI will not be able to get control of the host process and -handle the event for up to five seconds. This will cause long pauses in -the target process and lead to a potentially very large slowdown. - -To work around this problem StackwalkerAPI provides a notification file -descriptor. This file descriptor represents a connection between the -StackwalkerAPI library and user code. StackwalkerAPI will write a single -byte to this file descriptor when a debug event occurs, thus notifying -the user code that it needs to let StackwalkerAPI receive and handle -debug events. The user code can use system calls such as select to watch -for events on the notification file descriptor. - -The following example illustrates how to properly use StackwalkerAPI to -collect a stack walk from another process at a five second interval. -Details on the ``ProcDebug`` class, ``getNotificationFD`` method, and -``handleDebugEvent`` method can be found in -Section `4.1.1 <#subsubsec:procdebug>`__. See the UNIX man pages for -more information on the ``select`` system call. Note that this example -does not include all of the proper error handling and includes that -should be present when using ``select``. - -.. code-block:: cpp - - Walker *walker = Walker::newWalker(pid); - ProcDebug *debugger = (ProcDebug *) walker->getProcessState(); - std::vector swalk; - for (;;) { - walker->walkStack(swalk); - struct timeval timeout; - timeout.tv_sec = 5; - timeout.tv_usec = 0; - int max = 1; - fd_set readfds, writefds, exceptfds; - FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); - FD_SET(ProcDebug::getNotificationFD(), &readfds); - for (;;) { - int result = select(max, &readfds, &writefds, &exceptfds, &timeout); - if (FD_ISSET(ProcDebug::getNotificationFD(), readfds)) { - //Debug event - ProcDebug::handleDebugEvent(); - } - if (result == 0) { - //Timeout - break; - } - } - } - -.. _`subsubsec:procdebug`: - -Class ProcDebug -~~~~~~~~~~~~~~~ - -**Defined in:** ``procstate.h`` - -Access to StackwalkerAPI’s debugger is through the ``ProcDebug`` class, -which inherits from the ``ProcessState`` interface. The easiest way to -get at a ``ProcDebug`` object is to cast the return value of -``Walker::getProcessState`` into a ``ProcDebug``. C++’s ``dynamic_cast`` -operation can be used to test if a ``Walker`` uses the ``ProcDebug`` -interface: - -.. code-block:: cpp - - ProcDebug *debugger; - debugger = dynamic_cast(walker->getProcessState()); - if (debugger != NULL) { - //3rd party - ... - } else { - //1st party - ... - } - -In addition to the handling of debug events, described in -Section `4.1 <#subsec:debugger>`__, the ``ProcDebug`` class provides a -process control interface; users can pause and resume process or -threads, detach from a process, and test for events such as process -death. As an implementation of the ``ProcessState`` class, ``ProcDebug`` -also provides all of the functionality described in -Section `3.6.4 <#subsec:processstate>`__. - -.. code-block:: cpp - - virtual bool pause(Dyninst::THR_ID tid = NULL_THR_ID) - -This method pauses a process or thread. The paused object will not -resume execution until ``ProcDebug::resume`` is called. If the ``tid`` -parameter is not ``NULL_THR_ID`` then StackwalkerAPI will pause the -thread specified by ``tid``. If ``tid`` is ``NULL_THR_ID`` then -StackwalkerAPI will pause every thread in the process. - -When StackwalkerAPI collects a call stack from a running thread it first -pauses the thread, collects the stack walk, and then resumes the thread. -When collecting a call stack from a paused thread StackwalkerAPI will -collect the stack walk and leave the thread paused. This method is thus -useful for pausing threads before stack walks if the user needs to keep -the returned stack walk synchronized with the current state of the +StackwalkerAPI’s ease-of-use comes from it providing a platform +independent interface that allows users to access detailed information +about the call stack. For example, the following C++ code-snippet is all +that is needed to walk and print the call stack of the currently running thread. -This method returns ``true`` if successful and ``false`` on error. - -.. code-block:: cpp - - virtual bool resume(Dyninst::THR_ID tid = NULL_THR_ID) - -This method resumes execution on a paused process or thread. This method -only resumes threads that were paused by the ``ProcDebug::pause`` call, -using it on other threads is an error. If the ``tid`` parameter is not -``NULL_THR_ID`` then StackwalkerAPI will resume the thread specified by -``tid``. If ``tid`` is ``NULL_THR_ID`` then StackwalkerAPI will resume -all paused threads in the process. - -This method returns ``true`` if successful and ``false`` on error. - -.. code-block:: cpp - - virtual bool detach(bool leave_stopped = false) - -This method detaches StackwalkerAPI from the target process. -StackwalkerAPI will no longer receive debug events on this target -process and will no longer be able to collect call stacks from it. This -method invalidates the associated ``Walker`` and ``ProcState`` objects, -they should be cleaned using C++’s ``delete`` operator after making this -call. It is an error to attempt to do operations on these objects after -a detach, and undefined behavior may result. - -If the ``leave_stopped`` parameter is ``true`` StackwalkerAPI will -detach from the process but leave it in a paused state so that it does -resume progress. This is useful for attaching another debugger back to -the process for further analysis. The ``leave_stopped`` parameter is not -supported on the Linux platform and its value will have no affect on the -detach call. - -This method returns ``true`` if successful and ``false`` on error. - -.. code-block:: cpp - - virtual bool isTerminated() +.. rli:: https://raw.githubusercontent.com/dyninst/examples/master/stackwalker/this_thread.cpp + :language: cpp + :linenos: -This method returns ``true`` if the associated target process has -terminated and ``false`` otherwise. A target process may terminate -itself by calling exit, returning from main, or receiving an unhandled -signal. Attempting to collect stack walks or perform other operations on -a terminated process is illegal an will lead to undefined behavior. - -A process termination will also be signaled through the notification FD. -Users should check processes for the isTerminated state after returning -from handleDebugEvent. - -.. code-block:: cpp - - static int getNotificationFD() - -This method returns StackwalkerAPI’s notification FD. The notification -FD is a file descriptor that StackwalkerAPI will write a byte to -whenever a debug event occurs that need. If the user code sees a byte on -this file descriptor it should call ``handleDebugEvent`` to let -StackwalkerAPI handle the debug event. Example code using -``getNotificationFD`` can be found in -Section `4.1 <#subsec:debugger>`__. - -StackwalkerAPI will only create one notification FD, even if it is -attached to multiple 3rd party target processes. +StackwalkerAPI can walk a call stack in the same address space as where +the StackwalkerAPI library lives (known as a first party stackwalk), or +it can walk a call stack in another process (known as a third party +stackwalk). To change the above example to perform a third party +stackwalk, we would only need to pass a process identifier to newWalker, +e.g: .. code-block:: cpp - static bool handleDebugEvent(bool block = false) - -When this method is called StackwalkerAPI will receive and handle all -pending debug events from each 3rd party target process to which it is -attached. After handling debug events each target process will be -continued (unless it was explicitly stopped by the ProcDebug::pause -method) and any bytes on the notification FD will be cleared. It is -generally expected that users will call this method when a event is sent -to the notification FD, although it can be legally called at any time. - -If the ``block`` parameter is ``true``, then ``handleDebugEvents`` will -block until it has handled at least one debug event. If the block -parameter is ``false``, then handleDebugEvents will handle any currently -pending debug events or immediately return if none are available. - -StackwalkerAPI may receive process exit events for target processes -while handling debug events. The user should check for any exited -processes by calling ``ProcDebug::isTerminated`` after handling debug -events. - -This method returns ``true`` if successful and ``false`` on error. - -.. _`sec:framesteppers`: - -FrameSteppers -------------- - -**Defined in:** ``framestepper.h`` - -StackwalkerAPI ships with numerous default implementations of the -``FrameStepper`` class. Each of these ``FrameStepper`` implementations -allow StackwalkerAPI to walk a type of call frames. -Section `3.6.1 <#subsec:defaults>`__ describes which ``FrameStepper`` -implementations are available on which platforms. This sections gives a -brief description of what each ``FrameStepper`` implementation does. -Each of the following classes implements the ``FrameStepper`` interface -described in Section `3.6.2 <#subsec:framestepper>`__, so we do not -repeat the API description for the classes here. - -Several of the ``FrameStepper``\ s use helper classes (see -``FrameFuncStepper`` as an example). Users can further customize the -behavior of a ``FrameStepper`` by providing their own implementation of -these helper classes. - -Class FrameFuncStepper -~~~~~~~~~~~~~~~~~~~~~~ - -This class implements stack walking through a call frame that is setup -with the architectures standard stack frame. For example, on x86 this -``FrameStepper`` will be used to walk through stack frames that are -setup with a ``push %ebp/mov %esp,%ebp`` prologue. - -Class FrameFuncHelper -^^^^^^^^^^^^^^^^^^^^^ - -``FrameFuncStepper`` uses a helper class, ``FrameFuncHelper``, to get -information on what kind of stack frame it’s walking through. The -``FrameFuncHelper`` will generally use techniques such as binary -analysis to determine what type of stack frame the ``FrameFuncStepper`` -is walking through. Users can have StackwalkerAPI use their own binary -analysis mechanisms by providing an implementation of this -``FrameFuncHelper``. - -There are two important types used by ``FrameFuncHelper`` and one -important function: typedef enum unknown_t=0, no_frame, standard_frame, -savefp_only_frame, frame_type; - -The ``frame_type`` describes what kind of stack frame a function uses. -If it does not set up a stack frame then ``frame_type`` should be -``no_frame``. If it sets up a standard frame then ``frame_type`` should -be ``standard_frame``. The ``savefp_only_frame`` value currently only -has meaning on the x86 family of systems, and means that a function -saves the old frame pointer, but does not setup a new frame pointer (it -has a ``push %ebp`` instruction, but no ``mov %esp,%ebp``). If the -``FrameFuncHelper`` cannot determine the ``frame_type``, then it should -be assigned the value ``unknown_t``. - -.. code-block:: cpp - - typedef enum unknown_s=0, unset_frame, halfset_frame, set_frame frame_state; - -The ``frame_state`` type determines the current state of function with a -stack frame at some point of execution. For example, a function may set -up a standard stack frame and have a ``frame_type`` of -``standard_frame``, but execution may be at the first instruction in the -function and the frame is not yet setup, in which case the -``frame_state`` will be ``unset_frame``. - -If the function sets up a standard stack frame and the execution point -is someplace where the frame is completely setup, then the -``frame_state`` should be ``set_frame``. If the function sets up a -standard frame and the execution point is at a point where the frame -does not yet exist or has been torn down, then ``frame_state`` should be -``unset_frame``. The ``halfset_frame`` value of ``frame_state`` is -currently only meaningful on the x86 family of architecture, and should -if the function has saved the old frame pointer, but not yet set up a -new frame pointer. - -.. code-block:: cpp - - typedef std::pair alloc_frame_t; virtual alloc_frame_t allocatesFrame(Address addr) = 0; - -The ``allocatesFrame`` function of ``FrameFuncHelper`` returns a -``alloc_frame_t`` that describes the frame_type of the function at -``addr`` and the ``frame_state`` of the function when execution reached -``addr``. - -If ``addr`` is invalid or an error occurs, allocatedFrame should return -``alloc_frame_t(unknown_t, unknown_s)``. - -Class SigHandlerStepper -~~~~~~~~~~~~~~~~~~~~~~~ - -The ``SigHandlerStepper`` is used to walk through UNIX signal handlers -as found on the call stack. On some systems a signal handler generates a -special kind of stack frame that cannot be walked through using normal -stack walking techniques. - -Class DebugStepper -~~~~~~~~~~~~~~~~~~ - -This class uses debug information found in a binary to walk through a -stack frame. It depends on SymtabAPI to read debug information from a -binary, then uses that debug information to walk through a call frame. - -Most binaries must be built with debug information (``-g`` with ``gcc``) -in order to include debug information that this ``FrameStepper`` uses. -Some languages, such as C++, automatically include stackwalking debug -information for use by exceptions. The ``DebugStepper`` class will also -make use of this kind of exception information if it is available. - -Class AnalysisStepper -~~~~~~~~~~~~~~~~~~~~~ - -This class uses dataflow analysis to determine possible stack sizes at -all locations in a function as well as the location of the frame -pointer. It is able to handle optimized code with omitted frame pointers -and overlapping code sequences. - -Class StepperWanderer -~~~~~~~~~~~~~~~~~~~~~ - -This class uses a heuristic approach to find possible return addresses -in the stack frame. If a return address is found that matches a valid -caller of the current function, we conclude it is the actual return -address and construct a matching stack frame. Since this approach is -heuristic it can make mistakes leading to incorrect stack information. -It has primarily been replaced by the ``AnalysisStepper`` described -above. - -Class BottomOfStackStepper -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``BottomOfStackStepper`` doesn’t actually walk through any type of -call frame. Instead it attempts to detect whether the bottom of the call -stack has been reached. If so, ``BottomOfStackStepper`` will report -``gcf_stackbottom`` from its ``getCallerFrame`` method. Otherwise it -will report ``gcf_not_me``. ``BottomOfStackStepper`` runs with a higher -priority than any other ``FrameStepper`` class. + auto *walker = sw::Walker::newWalker(pid); diff --git a/docs/stackwalk/public/API.rst b/docs/stackwalk/public/API.rst new file mode 100644 index 0000000000..49cecdc080 --- /dev/null +++ b/docs/stackwalk/public/API.rst @@ -0,0 +1,21 @@ +============== +StackwalkerAPI +============== + +This section gives an API reference for all classes, functions and types in StackwalkerAPI. + +.. toctree:: + :caption: Public API + :name: stackwalk-public-api + :hidden: + :maxdepth: 1 + + basetypes.h + frame.h + framestepper.h + local_var.h + procstate.h + steppergroup.h + swk_errors.h + symlookup.h + walker.h diff --git a/docs/stackwalk/public/basetypes.h.rst b/docs/stackwalk/public/basetypes.h.rst new file mode 100644 index 0000000000..9346c6aaf5 --- /dev/null +++ b/docs/stackwalk/public/basetypes.h.rst @@ -0,0 +1,5 @@ +basetypes.h +=========== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/public/fig/stackwalker-frame-layout-ARMv8.png b/docs/stackwalk/public/fig/stackwalker-frame-layout-ARMv8.png new file mode 100644 index 0000000000..951e3636f6 Binary files /dev/null and b/docs/stackwalk/public/fig/stackwalker-frame-layout-ARMv8.png differ diff --git a/docs/stackwalk/public/fig/stackwalker-frame-layout-x86.png b/docs/stackwalk/public/fig/stackwalker-frame-layout-x86.png new file mode 100644 index 0000000000..8b95213fe4 Binary files /dev/null and b/docs/stackwalk/public/fig/stackwalker-frame-layout-x86.png differ diff --git a/docs/stackwalk/public/frame.h.rst b/docs/stackwalk/public/frame.h.rst new file mode 100644 index 0000000000..cd3ee962e5 --- /dev/null +++ b/docs/stackwalk/public/frame.h.rst @@ -0,0 +1,7 @@ +.. _`sec:stackwalk-frame.h.rst`: + +frame.h +======= + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/public/framestepper.h.rst b/docs/stackwalk/public/framestepper.h.rst new file mode 100644 index 0000000000..e1edec9c3c --- /dev/null +++ b/docs/stackwalk/public/framestepper.h.rst @@ -0,0 +1,138 @@ +framestepper.h +============== + +.. cpp:namespace:: Dyninst::stackwalk + +FrameSteppers +------------- + +StackwalkerAPI ships with numerous default implementations of the +``FrameStepper`` class. Each of these ``FrameStepper`` implementations +allow StackwalkerAPI to walk a type of call frames. +Section `3.6.1 <#subsec:defaults>`__ describes which ``FrameStepper`` +implementations are available on which platforms. This sections gives a +brief description of what each ``FrameStepper`` implementation does. +Each of the following classes implements the ``FrameStepper`` interface +described in Section `3.6.2 <#subsec:framestepper>`__, so we do not +repeat the API description for the classes here. + +Several of the ``FrameStepper``\ s use helper classes (see +``FrameFuncStepper`` as an example). Users can further customize the +behavior of a ``FrameStepper`` by providing their own implementation of +these helper classes. + +Class FrameFuncStepper +~~~~~~~~~~~~~~~~~~~~~~ + +This class implements stack walking through a call frame that is setup +with the architectures standard stack frame. For example, on x86 this +``FrameStepper`` will be used to walk through stack frames that are +setup with a ``push %ebp/mov %esp,%ebp`` prologue. + +Class FrameFuncHelper +~~~~~~~~~~~~~~~~~~~~~ + +``FrameFuncStepper`` uses a helper class, ``FrameFuncHelper``, to get +information on what kind of stack frame it’s walking through. The +``FrameFuncHelper`` will generally use techniques such as binary +analysis to determine what type of stack frame the ``FrameFuncStepper`` +is walking through. Users can have StackwalkerAPI use their own binary +analysis mechanisms by providing an implementation of this +``FrameFuncHelper``. + +There are two important types used by ``FrameFuncHelper`` and one +important function: typedef enum unknown_t=0, no_frame, standard_frame, +savefp_only_frame, frame_type; + +The ``frame_type`` describes what kind of stack frame a function uses. +If it does not set up a stack frame then ``frame_type`` should be +``no_frame``. If it sets up a standard frame then ``frame_type`` should +be ``standard_frame``. The ``savefp_only_frame`` value currently only +has meaning on the x86 family of systems, and means that a function +saves the old frame pointer, but does not setup a new frame pointer (it +has a ``push %ebp`` instruction, but no ``mov %esp,%ebp``). If the +``FrameFuncHelper`` cannot determine the ``frame_type``, then it should +be assigned the value ``unknown_t``. + +.. code-block:: cpp + + typedef enum unknown_s=0, unset_frame, halfset_frame, set_frame frame_state; + +The ``frame_state`` type determines the current state of function with a +stack frame at some point of execution. For example, a function may set +up a standard stack frame and have a ``frame_type`` of +``standard_frame``, but execution may be at the first instruction in the +function and the frame is not yet setup, in which case the +``frame_state`` will be ``unset_frame``. + +If the function sets up a standard stack frame and the execution point +is someplace where the frame is completely setup, then the +``frame_state`` should be ``set_frame``. If the function sets up a +standard frame and the execution point is at a point where the frame +does not yet exist or has been torn down, then ``frame_state`` should be +``unset_frame``. The ``halfset_frame`` value of ``frame_state`` is +currently only meaningful on the x86 family of architecture, and should +if the function has saved the old frame pointer, but not yet set up a +new frame pointer. + +.. code-block:: cpp + + typedef std::pair alloc_frame_t; virtual alloc_frame_t allocatesFrame(Address addr) = 0; + +The ``allocatesFrame`` function of ``FrameFuncHelper`` returns a +``alloc_frame_t`` that describes the frame_type of the function at +``addr`` and the ``frame_state`` of the function when execution reached +``addr``. + +If ``addr`` is invalid or an error occurs, allocatedFrame should return +``alloc_frame_t(unknown_t, unknown_s)``. + +Class SigHandlerStepper +~~~~~~~~~~~~~~~~~~~~~~~ + +The ``SigHandlerStepper`` is used to walk through UNIX signal handlers +as found on the call stack. On some systems a signal handler generates a +special kind of stack frame that cannot be walked through using normal +stack walking techniques. + +Class DebugStepper +~~~~~~~~~~~~~~~~~~ + +This class uses debug information found in a binary to walk through a +stack frame. It depends on SymtabAPI to read debug information from a +binary, then uses that debug information to walk through a call frame. + +Most binaries must be built with debug information (``-g`` with ``gcc``) +in order to include debug information that this ``FrameStepper`` uses. +Some languages, such as C++, automatically include stackwalking debug +information for use by exceptions. The ``DebugStepper`` class will also +make use of this kind of exception information if it is available. + +Class AnalysisStepper +~~~~~~~~~~~~~~~~~~~~~ + +This class uses dataflow analysis to determine possible stack sizes at +all locations in a function as well as the location of the frame +pointer. It is able to handle optimized code with omitted frame pointers +and overlapping code sequences. + +Class StepperWanderer +~~~~~~~~~~~~~~~~~~~~~ + +This class uses a heuristic approach to find possible return addresses +in the stack frame. If a return address is found that matches a valid +caller of the current function, we conclude it is the actual return +address and construct a matching stack frame. Since this approach is +heuristic it can make mistakes leading to incorrect stack information. +It has primarily been replaced by the ``AnalysisStepper`` described +above. + +Class BottomOfStackStepper +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``BottomOfStackStepper`` doesn’t actually walk through any type of +call frame. Instead it attempts to detect whether the bottom of the call +stack has been reached. If so, ``BottomOfStackStepper`` will report +``gcf_stackbottom`` from its ``getCallerFrame`` method. Otherwise it +will report ``gcf_not_me``. ``BottomOfStackStepper`` runs with a higher +priority than any other ``FrameStepper`` class. \ No newline at end of file diff --git a/docs/stackwalk/public/local_var.h.rst b/docs/stackwalk/public/local_var.h.rst new file mode 100644 index 0000000000..00d040fdef --- /dev/null +++ b/docs/stackwalk/public/local_var.h.rst @@ -0,0 +1,5 @@ +local_var.h +=========== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/public/procstate.h.rst b/docs/stackwalk/public/procstate.h.rst new file mode 100644 index 0000000000..64b694d24f --- /dev/null +++ b/docs/stackwalk/public/procstate.h.rst @@ -0,0 +1,141 @@ +procstate.h +=========== + +.. cpp:namespace:: Dyninst::stackwalk + +Class ProcDebug +~~~~~~~~~~~~~~~ + +Access to StackwalkerAPI’s debugger is through the ``ProcDebug`` class, +which inherits from the ``ProcessState`` interface. The easiest way to +get at a ``ProcDebug`` object is to cast the return value of +``Walker::getProcessState`` into a ``ProcDebug``. C++’s ``dynamic_cast`` +operation can be used to test if a ``Walker`` uses the ``ProcDebug`` +interface: + +.. code-block:: cpp + + ProcDebug *debugger; + debugger = dynamic_cast(walker->getProcessState()); + if (debugger != NULL) { + //3rd party + ... + } else { + //1st party + ... + } + +In addition to the handling of debug events, described in +Section `4.1 <#subsec:debugger>`__, the ``ProcDebug`` class provides a +process control interface; users can pause and resume process or +threads, detach from a process, and test for events such as process +death. As an implementation of the ``ProcessState`` class, ``ProcDebug`` +also provides all of the functionality described in +Section `3.6.4 <#subsec:processstate>`__. + +.. code-block:: cpp + + virtual bool pause(Dyninst::THR_ID tid = NULL_THR_ID) + +This method pauses a process or thread. The paused object will not +resume execution until ``ProcDebug::resume`` is called. If the ``tid`` +parameter is not ``NULL_THR_ID`` then StackwalkerAPI will pause the +thread specified by ``tid``. If ``tid`` is ``NULL_THR_ID`` then +StackwalkerAPI will pause every thread in the process. + +When StackwalkerAPI collects a call stack from a running thread it first +pauses the thread, collects the stack walk, and then resumes the thread. +When collecting a call stack from a paused thread StackwalkerAPI will +collect the stack walk and leave the thread paused. This method is thus +useful for pausing threads before stack walks if the user needs to keep +the returned stack walk synchronized with the current state of the +thread. + +This method returns ``true`` if successful and ``false`` on error. + +.. code-block:: cpp + + virtual bool resume(Dyninst::THR_ID tid = NULL_THR_ID) + +This method resumes execution on a paused process or thread. This method +only resumes threads that were paused by the ``ProcDebug::pause`` call, +using it on other threads is an error. If the ``tid`` parameter is not +``NULL_THR_ID`` then StackwalkerAPI will resume the thread specified by +``tid``. If ``tid`` is ``NULL_THR_ID`` then StackwalkerAPI will resume +all paused threads in the process. + +This method returns ``true`` if successful and ``false`` on error. + +.. code-block:: cpp + + virtual bool detach(bool leave_stopped = false) + +This method detaches StackwalkerAPI from the target process. +StackwalkerAPI will no longer receive debug events on this target +process and will no longer be able to collect call stacks from it. This +method invalidates the associated ``Walker`` and ``ProcState`` objects, +they should be cleaned using C++’s ``delete`` operator after making this +call. It is an error to attempt to do operations on these objects after +a detach, and undefined behavior may result. + +If the ``leave_stopped`` parameter is ``true`` StackwalkerAPI will +detach from the process but leave it in a paused state so that it does +resume progress. This is useful for attaching another debugger back to +the process for further analysis. The ``leave_stopped`` parameter is not +supported on the Linux platform and its value will have no affect on the +detach call. + +This method returns ``true`` if successful and ``false`` on error. + +.. code-block:: cpp + + virtual bool isTerminated() + +This method returns ``true`` if the associated target process has +terminated and ``false`` otherwise. A target process may terminate +itself by calling exit, returning from main, or receiving an unhandled +signal. Attempting to collect stack walks or perform other operations on +a terminated process is illegal an will lead to undefined behavior. + +A process termination will also be signaled through the notification FD. +Users should check processes for the isTerminated state after returning +from handleDebugEvent. + +.. code-block:: cpp + + static int getNotificationFD() + +This method returns StackwalkerAPI’s notification FD. The notification +FD is a file descriptor that StackwalkerAPI will write a byte to +whenever a debug event occurs that need. If the user code sees a byte on +this file descriptor it should call ``handleDebugEvent`` to let +StackwalkerAPI handle the debug event. Example code using +``getNotificationFD`` can be found in +Section `4.1 <#subsec:debugger>`__. + +StackwalkerAPI will only create one notification FD, even if it is +attached to multiple 3rd party target processes. + +.. code-block:: cpp + + static bool handleDebugEvent(bool block = false) + +When this method is called StackwalkerAPI will receive and handle all +pending debug events from each 3rd party target process to which it is +attached. After handling debug events each target process will be +continued (unless it was explicitly stopped by the ProcDebug::pause +method) and any bytes on the notification FD will be cleared. It is +generally expected that users will call this method when a event is sent +to the notification FD, although it can be legally called at any time. + +If the ``block`` parameter is ``true``, then ``handleDebugEvents`` will +block until it has handled at least one debug event. If the block +parameter is ``false``, then handleDebugEvents will handle any currently +pending debug events or immediately return if none are available. + +StackwalkerAPI may receive process exit events for target processes +while handling debug events. The user should check for any exited +processes by calling ``ProcDebug::isTerminated`` after handling debug +events. + +This method returns ``true`` if successful and ``false`` on error. \ No newline at end of file diff --git a/docs/stackwalk/public/steppergroup.h.rst b/docs/stackwalk/public/steppergroup.h.rst new file mode 100644 index 0000000000..c05f9cae02 --- /dev/null +++ b/docs/stackwalk/public/steppergroup.h.rst @@ -0,0 +1,5 @@ +steppergroup.h +============== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/public/swk_errors.h.rst b/docs/stackwalk/public/swk_errors.h.rst new file mode 100644 index 0000000000..d39f7e2bed --- /dev/null +++ b/docs/stackwalk/public/swk_errors.h.rst @@ -0,0 +1,5 @@ +swk_errors.h +============ + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/public/symlookup.h.rst b/docs/stackwalk/public/symlookup.h.rst new file mode 100644 index 0000000000..2fecdcf75e --- /dev/null +++ b/docs/stackwalk/public/symlookup.h.rst @@ -0,0 +1,5 @@ +symlookup.h +=========== + +.. cpp:namespace:: Dyninst::stackwalk + diff --git a/docs/stackwalk/public/walker.h.rst b/docs/stackwalk/public/walker.h.rst new file mode 100644 index 0000000000..260cc65429 --- /dev/null +++ b/docs/stackwalk/public/walker.h.rst @@ -0,0 +1,5 @@ +walker.h +======== + +.. cpp:namespace:: Dyninst::stackwalk +