15.2.2. Dyninst をスタンドアロンライブラリーとして使用

Dyninst ライブラリーをアプリケーションの一部として使用する前に、DYNINSTAPI_RT_LIB 環境変数の値をランタイムライブラリーファイルへのパスに設定します。

$ export DYNINSTAPI_RT_LIB=/opt/rh/devtoolset-10/root/usr/lib64/dyninst/libdyninstAPI_RT.so

これにより、現在のシェルセッションで DYNINSTAPI_RT_LIB 環境変数が設定されます。

例15.2「Dyninst をスタンドアロンアプリケーションとして使用する」 は、ユーザー空間プロセスの実行を監視するプログラムを作成およびビルドする方法を示しています。Dyninst の使用方法に関する詳細は、「関連資料」 に記載されているリソースを参照してください。

例15.2 Dyninst をスタンドアロンアプリケーションとして使用する

例15.1「SystemTap での Dyninst の使用」exercise.C ソースファイルを考慮します。このプログラムにより、ユーザーは開始番号を入力し、1 までカウントして各反復の print_iteration() 関数をカウントし、標準出力に番号を出力します。

ここ cout.C で、以下の内容を含む別のソースファイルを考慮します。

#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;
}

Dyninst ライブラリーのデストラクターが呼び出される前に、クライアントアプリケーションがすべての Bpatch オブジェクトを破棄することが期待されることに注意してください。それ以外の場合は、セグメンテーションフォールトでミューターが突然終了する可能性があります。この問題を回避するには、main() 関数で mutator の BPatch オブジェクトをローカル変数として設定します。または、グローバル変数として BPatch 使用する必要がある場合は、ミューテーターの終了前にすべての変更プロセスを手作業でデタッチします。

このプログラムは、プロセス ID および関数名をコマンドライン引数として受け入れ、プロセスの実行中に呼び出された関数の合計回数を出力します。これらの 2 つのファイル Makefile を構築するには、以下を使用します。

DTS      = /opt/rh/devtoolset-10/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

Red Hat Developer Toolset の g++ コンパイラーを使用してコマンドラインで 2 つのプログラムをコンパイルするには、make ユーティリティーを実行します。

$ scl enable devtoolset-10 make
g++ -g -I/opt/rh/devtoolset-10/root/usr/include/dyninst count.C -c
g++ -g -I/opt/rh/devtoolset-10/root/usr/include/dyninst count.o -L /opt/rh/devtoolset-10/root/usr/lib64/dyninst -ldyninstAPI -o count
g++ -g -I/opt/rh/devtoolset-10/root/usr/include/dyninst exercise.C -o exercise

これにより、exercisecount という名前のバイナリーファイルが、現在の作業ディレクトリーに作成されます。

あるシェルセッションで、以下のように exercise バイナリーファイルを実行し、開始番号の入力を求めるプロンプトを待ちます。

$ ./exercise
Enter the starting number:

この番号は入力しないでください。代わりに別のシェルセッションを開始し、プロンプトに以下のコマンドを入力します。 DYNINSTAPI_RT_LIB 環境変数を設定して、count バイナリーファイルを実行します。

$ export DYNINSTAPI_RT_LIB=/opt/rh/devtoolset-10/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...

最初のシェルセッションに切り替え、exercise プログラムで要求される開始番号を入力します。以下に例を示します。

Enter the starting number: 5
Iteration number 5
Iteration number 4
Iteration number 3
Iteration number 2
Iteration number 1

exercise プログラムが終了すると、count プログラムにより、print_iteration() 関数の実行回数が表示されます。

Function executed 5 times.