Chapter 15. Dyninst
The Dyninst library provides an application programming interface (API) for instrumenting and working with user-space executables during their execution. It can be used to insert code into a running program, change certain subroutine calls, or even remove them from the program. It serves as a valuable debugging and performance-monitoring tool. The Dyninst API is also commonly used along with SystemTap to allow non-root
users to instrument user-space executables.
Red Hat Developer Toolset is distributed with Dyninst 10.1.0.
15.1. Installing Dyninst
In Red Hat Developer Toolset, the Dyninst library is provided by the devtoolset-9-dyninst package and is automatically installed with devtoolset-9-perftools as described in Section 1.5, “Installing Red Hat Developer Toolset”. In addition, it is recommended that you also install the GNU Compiler Collection provided by the devtoolset-9-toolchain package.
If you intend to write a custom instrumentation for binaries, install the relevant header files:
# yum install devtoolset-9-dyninst-devel
You can also install API documentation for this library:
# yum install devtoolset-9-dyninst-doc
For a complete list of documents that are included in the devtoolset-9-dyninst-doc package, see Section 15.3, “Additional Resources”. For detailed instructions on how to install optional packages to your system, see Section 1.5, “Installing Red Hat Developer Toolset”.
15.2. Using Dyninst
15.2.1. Using Dyninst with SystemTap
To use Dyninst along with SystemTap to allow non-root
users to instrument user-space executables, run the stap
command with the --dyninst
(or --runtime=dyninst
) command line option. This tells stap
to translate a SystemTap script into C code that uses the Dyninst library, compile this C code into a shared library, and then load the shared library and run the script. Note that when executed like this, the stap
command also requires the -c
or -x
command line option to be specified.
To use the Dyninst runtime to instrument an executable file:
$ scl enable devtoolset-9 "stap --dyninst -c 'command' option... argument..."
Similarly, to use the Dyninst runtime to instrument a user’s process:
$ scl enable devtoolset-9 "stap --dyninst -x process_id option... argument..."
See Chapter 12, SystemTap for more information about the Red Hat Developer Toolset version of SystemTap. For a general introduction to SystemTap and its usage, see the SystemTap Beginners Guide for Red Hat Enterprise Linux 7.
Example 15.1. Using Dyninst with SystemTap
Consider a source file named exercise.C
that has the following contents:
#include <stdio.h> void print_iteration(int value) { printf("Iteration number %d\n", value); } int main(int argc, char **argv) { int i; printf("Enter the starting number: "); scanf("%d", &i); for(; i>0; --i) print_iteration(i); return 0; }
This program prompts the user to enter a starting number and then counts down to 1, calling the print_iteration()
function for each iteration in order to print the number to the standard output. Compile this program on the command line using the g++
compiler from Red Hat Developer Toolset:
$ scl enable devtoolset-9 'g++ -g -o exercise exercise.C'
Now consider another source file named count.stp
with the following contents:
#!/usr/bin/stap global count = 0 probe process.function("print_iteration") { count++ } probe end { printf("Function executed %d times.\n", count) }
This SystemTap script prints the total number of times the print_iteration()
function was called during the execution of a process. Run this script on the exercise
binary file:
$scl enable devtoolset-9 "stap --dyninst -c './exercise' count.stp"
Enter the starting number:5
Iteration number 5 Iteration number 4 Iteration number 3 Iteration number 2 Iteration number 1 Function executed 5 times.
15.2.2. Using Dyninst as a Stand-alone Library
Before using the Dyninst library as a part of your application, set the value of the DYNINSTAPI_RT_LIB
environment variable to the path to the runtime library file:
$ export DYNINSTAPI_RT_LIB=/opt/rh/devtoolset-9/root/usr/lib64/dyninst/libdyninstAPI_RT.so
This sets the DYNINSTAPI_RT_LIB
environment variable in the current shell session.
Example 15.2, “Using Dyninst as a Stand-alone Application” illustrates how to write and build a program to monitor the execution of a user-space process. For a detailed explanation of how to use Dyninst, see the resources listed in Section 15.3, “Additional Resources”.
Example 15.2. Using Dyninst as a Stand-alone Application
Consider the exercise.C
source file from Example 15.1, “Using Dyninst with SystemTap”: this program prompts the user to enter a starting number and then counts down to 1, calling the print_iteration()
function for each iteration in order to print the number to standard output.
Now consider another source file named count.C
with the following contents:
#include <stdio.h> #include <fcntl.h> #include "BPatch.h" #include "BPatch_process.h" #include "BPatch_function.h" #include "BPatch_Vector.h" #include "BPatch_thread.h" #include "BPatch_point.h" void usage() { fprintf(stderr, "Usage: count <process_id> <function>\n"); } // Global information for counter BPatch_variableExpr *counter = NULL; void createCounter(BPatch_process *app, BPatch_image *appImage) { int zero = 0; counter = app->malloc(*appImage->findType("int")); counter->writeValue(&zero); } bool interceptfunc(BPatch_process *app, BPatch_image *appImage, char *funcName) { BPatch_Vector<BPatch_function *> func; appImage->findFunction(funcName, func); if(func.size() == 0) { fprintf(stderr, "Unable to find function to instrument()\n"); exit (-1); } BPatch_Vector<BPatch_snippet *> incCount; BPatch_Vector<BPatch_point *> *points; points = func[0]->findPoint(BPatch_entry); if ((*points).size() == 0) { exit (-1); } BPatch_arithExpr counterPlusOne(BPatch_plus, *counter, BPatch_constExpr(1)); BPatch_arithExpr addCounter(BPatch_assign, *counter, counterPlusOne); return app->insertSnippet(addCounter, *points); } void printCount(BPatch_thread *thread, BPatch_exitType) { int val = 0; counter->readValue(&val, sizeof(int)); fprintf(stderr, "Function executed %d times.\n", val); } int main(int argc, char *argv[]) { int pid; BPatch bpatch; if (argc != 3) { usage(); exit(1); } pid = atoi(argv[1]); BPatch_process *app = bpatch.processAttach(NULL, pid); if (!app) exit (-1); BPatch_image *appImage = app->getImage(); createCounter(app, appImage); fprintf(stderr, "Finding function %s(): ", argv[2]); BPatch_Vector<BPatch_function*> countFuncs; fprintf(stderr, "OK\nInstrumenting function %s(): ", argv[2]); interceptfunc(app, appImage, argv[2]); bpatch.registerExitCallback(printCount); fprintf(stderr, "OK\nWaiting for process %d to exit...\n", pid); app->continueExecution(); while (!app->isTerminated()) bpatch.waitForStatusChange(); return 0; }
Note that a client application is expected to destroy all Bpatch
objects before any of the Dyninst library destructors are called. Otherwise the mutator might terminate unexpectedly with a segmentation fault. To work around this problem, set the BPatch
object of the mutator as a local variable in the main()
function. Or, if you need to use BPatch
as a global variable, manually detach all the mutatee processes before the mutator exits.
This program accepts a process ID and a function name as command line arguments and then prints the total number of times the function was called during the execution of the process. You can use the following Makefile
to build these two files:
DTS = /opt/rh/devtoolset-9/root CXXFLAGS = -g -I$(DTS)/usr/include/dyninst LBITS := $(shell getconf LONG_BIT) ifeq ($(LBITS),64) DYNINSTLIBS = $(DTS)/usr/lib64/dyninst else DYNINSTLIBS = $(DTS)/usr/lib/dyninst endif .PHONY: all all: count exercise count: count.C g++ $(CXXFLAGS) count.C -I /usr/include/dyninst -c g++ $(CXXFLAGS) count.o -L $(DYNINSTLIBS) -ldyninstAPI -o count exercise: exercise.C g++ $(CXXFLAGS) exercise.C -o exercise .PHONY: clean clean: rm -rf *~ *.o count exercise
To compile the two programs on the command line using the g++
compiler from Red Hat Developer Toolset, run the make
utility:
$ scl enable devtoolset-9 make
g++ -g -I/opt/rh/devtoolset-9/root/usr/include/dyninst count.C -c
g++ -g -I/opt/rh/devtoolset-9/root/usr/include/dyninst count.o -L /opt/rh/devtoolset-9/root/usr/lib64/dyninst -ldyninstAPI -o count
g++ -g -I/opt/rh/devtoolset-9/root/usr/include/dyninst exercise.C -o exercise
This creates new binary files called exercise
and count
in the current working directory.
In one shell session, execute the exercise
binary file as follows and wait for it to prompt you to enter the starting number:
$ ./exercise
Enter the starting number:
Do not enter this number. Instead, start another shell session and type the following at its prompt to set the DYNINSTAPI_RT_LIB
environment variable and execute the count
binary file:
$export DYNINSTAPI_RT_LIB=/opt/rh/devtoolset-9/root/usr/lib64/dyninst/libdyninstAPI_RT.so
$./count `pidof exercise` print_iteration
Finding function print_iteration(): OK Instrumenting function print_iteration(): OK Waiting for process 8607 to exit...
Now switch back to the first shell session and enter the starting number as requested by the exercise
program. For example:
Enter the starting number: 5
Iteration number 5
Iteration number 4
Iteration number 3
Iteration number 2
Iteration number 1
When the exercise
program terminates, the count
program displays the number of times the print_iteration()
function was executed:
Function executed 5 times.
15.3. Additional Resources
For more information about Dyninst and its features, see the resources listed below.
Installed Documentation
The devtoolset-9-dyninst-doc package installs the following documents in the /opt/rh/devtoolset-9/root/usr/share/doc/devtoolset-9-dyninst-doc-10.1.0/
directory:
-
Dyninst Programmer’s Guide — A detailed description of the Dyninst API is stored in the
DyninstAPI.pdf
file. -
DynC API Programmer’s Guide — An introduction to DynC API is stored in the
dynC_API.pdf
file. -
ParseAPI Programmer’s Guide — An introduction to the ParseAPI is stored in the
ParseAPI.pdf
file. -
PatchAPI Programmer’s Guide — An introduction to PatchAPI is stored in the
PatchAPI.pdf
file. -
ProcControlAPI Programmer’s Guide — A detailed description of ProcControlAPI is stored in the
ProcControlAPI.pdf
file. -
StackwalkerAPI Programmer’s Guide — A detailed description of StackwalkerAPI is stored in the
stackwalker.pdf
file. -
SymtabAPI Programmer’s Guide — An introduction to SymtabAPI is stored in the
SymtabAPI.pdf
file. -
InstructionAPI Reference Manual — A detailed description of the InstructionAPI is stored in the
InstructionAPI.pdf
file.
For information on how to install this package on your system, see Section 15.1, “Installing Dyninst”.
Online Documentation
- Dyninst Home Page — The project home page provides links to additional documentation and related publications.
- Red Hat Enterprise Linux 7 SystemTap Beginners Guide — The SystemTap Beginners Guide for Red Hat Enterprise Linux 7 provides an introduction to SystemTap and its usage.
- Red Hat Enterprise Linux 7 SystemTap Tapset Reference — The SystemTap Tapset Reference for Red Hat Enterprise Linux 7 provides further details about SystemTap.
See Also
- Chapter 1, Red Hat Developer Toolset — An overview of Red Hat Developer Toolset and more information on how to install it on your system.
- Chapter 12, SystemTap — An introduction to SystemTap and instructions on how to use it to monitor the activities of a running system.
- Chapter 13, Valgrind — Instructions on using the Valgrind tool to profile applications and detect memory errors and memory management problems, such as the use of uninitialized memory, improper allocation and freeing of memory, and the use of improper arguments in system calls.
- Chapter 14, OProfile — Instructions on using the OProfile tool to determine which sections of code consume the greatest amount of CPU time and why.