-
Language:
English
-
Language:
English
Red Hat Training
A Red Hat training course is available for Red Hat Developer Toolset
14.2. Using Dyninst
14.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, type the following at a shell prompt:
scl enable devtoolset-6 "stap --dyninst
-c
'command' [option...] [argument...]"
Similarly, to use the Dyninst runtime to instrument a user's process, type:
scl enable devtoolset-6 "stap --dyninst
-x
process_id [option...] [argument...]"
See Chapter 11, 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 6 or the SystemTap Beginners Guide for Red Hat Enterprise Linux 7.
Example 14.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. To compile this program on the command line using the g++
compiler from Red Hat Developer Toolset, type the following at a shell prompt:
~]$ scl enable devtoolset-6 '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. To run this script on the exercise
binary file, type:
~]$scl enable devtoolset-6 "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.
14.2.2. Using Dyninst as a Stand-alone Application
Before using the Dyninst library as a stand-alone application, set the value of the
DYNINSTAPI_RT_LIB
environment variable to the path to the runtime library file. You can do so by typing the following at a shell prompt:
export DYNINSTAPI_RT_LIB=/opt/rh/devtoolset-6/root/usr/lib64/dyninst/libdyninstAPI_RT.so
This sets the
DYNINSTAPI_RT_LIB
environment variable in the current shell session.
Example 14.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 14.3, “Additional Resources”.
Example 14.2. Using Dyninst as a Stand-alone Application
Consider the
exercise.C
source file from Example 14.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-4/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 as follows:
~]$ scl enable devtoolset-6 make
g++ -g -I/opt/rh/devtoolset-6/root/usr/include/dyninst count.C -c
g++ -g -I/opt/rh/devtoolset-6/root/usr/include/dyninst count.o -L /opt/rh/devtoolset-6/root/usr/lib64/dyninst -ldyninstAPI -o count
g++ -g -I/opt/rh/devtoolset-6/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-6/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.