2012年12月22日土曜日

言語C でスコープドポインタを実現する(その3)

関数から復帰するとき

前回の使用例のように関数から復帰するときの処理について紹介する。
gccのフック機能を利用してつぎの処理を行う。
void NO_INSTRUMENT __cyg_profile_func_exit(void* functionAddress,
 void* call_site)
{
 if (!suppressed) {
  suppressed = true;
  popFrame(functionAddress);
  suppressed = false;
 }
}
関数呼び出し時同様、自身のトレースを行わないようなフラグ処理を行う。
(ただし、マルチスレッド未対応)
関数呼び出しに対応したフレームをポップする。

popFrameはこんな感じ。
private void popFrame(void* functionAddress) {
 boolean done = false;

 while (!done && ArrayList_size(frames) > 0) {
  int top = ArrayList_size(frames) - 1;
  Frame frame = (Frame)ArrayList_removeAt(frames, top);
  done = (frame->functionAddress == functionAddress);
  Frame_finalize(frame);
 }
}

framesリストの新しいものから順に現在の関数名が現れるまで、古いフレームを捨ててゆく。
こうすると、途中に -finstrument-functionsを指定せずにコンパイルされた関数があってもポップが正しくできる。
また、最低1つはポップ済みのため、関数を再帰的に呼び出しても処理できる。
再起呼び出しの中で以前紹介した例外機構や longjmpによる脱出にも対応可能である。

ただし、落とし穴もある。
-finstrument-functionsを使っていない関数に復帰しただけではすぐに解放処理が行われない。
再起呼び出しにより同じ関数を通ったとしても、条件分岐により catchしたりしなかったりした場合に正しくポップされるかは未だ考慮されていない。
関数アドレスではなく、現在のスタックフレームを比較する必要があるかも知れない。

0 件のコメント:

コメントを投稿