c-effective Cソース中で、実際に使用されるコードを判別する(自作編)
目的
#ifdefが複雑にネストしているCソースファイル中で、どの部分が有効かを簡単に調べたい。
背景と動機については id:taiyo:20080202#p1 などを参照。
Cプリプロセスの部分適応ツールについては昨日(id:taiyo:20080204#p1)試したので、今回はそれ以外にどんな方法があるかを検討する。
結果
3通りの方法を実施した。
名前 | 方法 | 判別の精度 | 事前の準備 | 備考 |
---|---|---|---|---|
show-run | オブジェクト内のデバッグ情報を使う | 低い | 簡単 | デバッグ情報付きでのコンパイルが必要 |
show-eff | Cプリプロセッサ出力を使う | 高い | やや面倒 | gcc-3.0.1以前相当のコンパイラにはパッチ宛が必要 |
cpp-parse | マクロ定義結果を元にプリプロセス処理を自前で行う | 中くらい | やや面倒 |
1と2については完了、3については挑戦中。
以下、詳細。
show-run - オブジェクト内のデバッグ情報を使う(自作ツール)
概要
コンパイル時に -g オプションなどでデバッグ情報を付加した場合、オブジェクトファイル内にソースコードの行番号が埋め込まれている(objdump に -l オプションを付けると表示される)。
これを使って、ソース中の実行される行を特定できないか?
デメリット
精度が低い。この方法は、要はソースコードデバッガでブレークポイントを設定できる行を特定することと同じ。デバッガを使った人ならわかるだろうが、実行される行でもインライン展開や最適化によってブレークポイントが設定できない場合がある。なので、この方法では実行されるのに実行マークが付かない場合がありうる。
表示スクリプト
オブジェクトのデバッグ情報とソースコードから、実行されるコードを表示するscriptを作成した。
- show-operate-lines.rb
- https://sssvn.jp/svn/spikelet/c/cparser/tool/show-operate-lines.rb
実行例
以下のソースを例にする:
#include <stdio.h> #define DEBUG #ifdef NONEXISTENT #define LOOP_NUM 10 #else #define LOOP_NUM 100 #endif static inline void dummy(void) { printf("inside dummy\n"); } int main(void) { int i; for (i=0; i<LOOP_NUM; i++) { printf("Hello, world.\n"); #ifdef DEBUG printf("loop counter is %d.\n", i); #endif } dummy(); return 0; }
% gcc -Wall -g -O2 -c -o hello.o hello.c
表示スクリプト実行:
1 - #include <stdio.h> 2 - #define DEBUG 3 - #ifdef NONEXISTENT 4 - #define LOOP_NUM 10 5 - #else 6 - #define LOOP_NUM 100 7 - #endif 8 - 9 - static inline void 10 - dummy(void) 11 - { 12 T printf("inside dummy\n"); 13 - } 14 - 15 - int 16 - main(void) 17 T { 18 - int i; 19 T for (i=0; i<LOOP_NUM; i++) { 20 T printf("Hello, world.\n"); 21 - #ifdef DEBUG 22 T printf("loop counter is %d.\n", i); 23 - #endif 24 - } 25 - dummy(); 26 - return 0; 27 T }
行番号のあとに 'T' とあるのが、実行される行。
見ての通り、プリプロセッサ命令行と、inline関数の呼び出し行、auto変数定義行にマークが付いていない。
まあ、このレベルでも、デバッガをいちいち立ち挙げて確認するのに比べたら、便利と言える。
B:Cプリプロセッサ出力を使う
(後で書く)
C:マクロ定義結果を元にプリプロセス処理を自前で行う
(後で書く)