特定の関数の開始時刻と終了時刻を記録するために、関数ラッパーを追加したいと考えています。LLVM はこれを達成するための優れたツールになるようです。ただし、関数ラッパーの作成方法に関するチュートリアルを見つけるのに苦労しています。助言がありますか?
ps私のターゲット言語はCです
私はこれを行うのが好きで、状況に応じていくつかのアプローチを使用します。
Linuxプラットフォームを使用している場合、最も簡単なのは、すばらしいltraceユーティリティを使用することです。タイミングをとっているCプログラムをltraceの引数として指定します。「-T」オプションは、経過した通話時間を出力します。通話時間の概要が必要な場合は、「-c」オプションを使用してください。「-e」および「--library」オプションを使用して、出力量を制御できます。他のプラットフォームには、多少類似したツール(dtraceなど)がありますが、使いやすさはそれほど簡単ではありません。
もう1つの少しハックなアプローチは、マクロを使用して関数名を再定義することです。これには、マクロの潜在的な落とし穴がすべてありますが、小規模なプログラムの制御された環境でうまく機能する可能性があります。Cプリプロセッサはマクロを再帰的に展開しないため、呼び出し時にラッパーマクロ内から実際の関数を呼び出すことができます。これにより、関数本体の各潜在的なリターンの前に「停止タイミング」コードを配置するという困難が回避されます。
#define foo(a,b,c) ({long t0 = now(); int retval = foo(a,b,c); long elapsed = now() - t0; retval;})
式内で非標準のコードブロックが使用されていることに注意してください。これにより、タイミングとretvalに使用される一時的な名前の衝突が回避されます。また、ステートメントリストの最後の式としてretvalを配置することにより、このコードは、割り当てまたは他の式コンテキストに埋め込まれている関数呼び出しの時間を計測します(「retval」のタイプを関数に適したものに変更する必要があります)。
プロトタイプなどの前に#defineを含めないように十分に注意する必要があります。
お気に入りのタイマー関数とその適切なデータ型(double、long longなど)を使用します。私はC++11の<chrono>が好きです。
func_start
各関数に入るときと戻るときに呼び出す必要があると仮定するとfunc_return
、最も簡単な方法は次のようにすることです。
for each function F
insert a call to func_start(F) before the first instruction in the entry block
for each block B in function F
get the terminator instruction T
if T is a return instruction
insert a call to func_return(F) before T
全体として、 のボイラープレート コードを含めてFunctionPass
、約 40 行のコードを記述する必要があります。
本当にラッパーアプローチを使用したい場合は、次のことを行う必要があります。
for each function F
clone function F (call it G)
delete all instructions in F
insert a call to func_start(F) in F
insert a call to G in F (forwarding the arguments), put the return value in R
insert a call to func_return(F) in F
insert a return instruction returning R in F
この場合のコードの複雑さはわずかに高くなり、コンパイル時および実行時のオーバーヘッドが高くなる可能性があります。