Chapter 15. Dyninst
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 12.1.0 . 15.1. Installing Dyninst In Red Hat Developer Toolset, the Dyninst library is provided by the devtoolset-12-dyninst package and is automatically installed with devtoolset-12-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-12-toolchain package. If you intend to write a custom instrumentation for binaries, install the relevant header files: You can also install API documentation for this library: For a complete list of documents that are included in the devtoolset-12-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: Similarly, to use the Dyninst runtime to instrument a user's process: 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: Now consider another source file named count.stp with the following contents: 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: 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: 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-12/root CXXFLAGS = -g -IUSD(DTS)/usr/include/dyninst LBITS := USD(shell getconf LONG_BIT) ifeq (USD(LBITS),64) DYNINSTLIBS = USD(DTS)/usr/lib64/dyninst else DYNINSTLIBS = USD(DTS)/usr/lib/dyninst endif .PHONY: all all: count exercise count: count.C g++ USD(CXXFLAGS) count.C -I /usr/include/dyninst -c g++ USD(CXXFLAGS) count.o -L USD(DYNINSTLIBS) -ldyninstAPI -o count exercise: exercise.C g++ USD(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: 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: 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: Now switch back to the first shell session and enter the starting number as requested by the exercise program. For example: When the exercise program terminates, the count program displays the number of times the print_iteration() function was executed: 15.3. Additional Resources For more information about Dyninst and its features, see the resources listed below. Installed Documentation The devtoolset-12-dyninst-doc package installs the following documents in the /opt/rh/devtoolset-12/root/usr/share/doc/devtoolset-12-dyninst-doc-12.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.
yum install devtoolset-12-dyninst-devel yum install devtoolset-12-dyninst-doc scl enable devtoolset-12 "stap --dyninst -c ' command ' option ... argument ..." scl enable devtoolset-12 "stap --dyninst -x process_id option ... argument ..." #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; } scl enable devtoolset-12 'g++ -g -o exercise exercise.C' #!/usr/bin/stap global count = 0 probe process.function("print_iteration") { count++ } probe end { printf("Function executed %d times.\n", count) } scl enable devtoolset-12 "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. export DYNINSTAPI_RT_LIB=/opt/rh/devtoolset-12/root/usr/lib64/dyninst/libdyninstAPI_RT.so #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; } DTS = /opt/rh/devtoolset-12/root CXXFLAGS = -g -IUSD(DTS)/usr/include/dyninst LBITS := USD(shell getconf LONG_BIT) ifeq (USD(LBITS),64) DYNINSTLIBS = USD(DTS)/usr/lib64/dyninst else DYNINSTLIBS = USD(DTS)/usr/lib/dyninst endif .PHONY: all all: count exercise count: count.C g++ USD(CXXFLAGS) count.C -I /usr/include/dyninst -c g++ USD(CXXFLAGS) count.o -L USD(DYNINSTLIBS) -ldyninstAPI -o count exercise: exercise.C g++ USD(CXXFLAGS) exercise.C -o exercise .PHONY: clean clean: rm -rf *~ *.o count exercise scl enable devtoolset-12 make g++ -g -I/opt/rh/devtoolset-12/root/usr/include/dyninst count.C -c g++ -g -I/opt/rh/devtoolset-12/root/usr/include/dyninst count.o -L /opt/rh/devtoolset-12/root/usr/lib64/dyninst -ldyninstAPI -o count g++ -g -I/opt/rh/devtoolset-12/root/usr/include/dyninst exercise.C -o exercise ./exercise Enter the starting number: export DYNINSTAPI_RT_LIB=/opt/rh/devtoolset-12/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 Enter the starting number: 5 Iteration number 5 Iteration number 4 Iteration number 3 Iteration number 2 Iteration number 1 Function executed 5 times.