例えば、printfとかは内部で大量の関数を呼び出しており、関数の呼出関係のトレース図を作ると大変なことになる。 下の例では、Coremarkにおいて、cmp_complexから先を全て階層トレースとして出力した場合の結果だ。
<FunctionCall 51052: cmp_complex(0x80000180)> <FunctionCall 51061: calc_func(0x80000058)> <FunctionCall 51085: crcu16(0x800015c0)> <Return: crcu16> <Return: calc_func> <FunctionCall 51266: calc_func(0x80000058)> <FunctionCall 51290: crcu16(0x800015c0)> <Return: crcu16> <Return: calc_func> <Return: cmp_complex>
そこで、任意の関数の呼出から先のトレースは、階層トレースのログから省略する機能を実装した。 Luaのスクリプトで以下のように記述しておくと、ee_printfから先の階層呼び出しのトレース表示を省略する。
skip_hier (mips, "cmp_complex", "debug_skip")
階層トレースの結果は以下のようになる。cmp_complexから先の階層は表示が省略される。
<FunctionCall 51052: cmp_complex(0x80000180) ...> <Return: cmp_complex>
また、オプションとして"debug_skip"を指定すると、デバッグ表示モードにおいて、命令トレースの表示も省略させることができる。 詳細を解析したいが、不必要な部分の命令トレースを省略したいときに利用できる。
トレース表示省略機能の実装
実際には、上記のhier_skipが実行されると、ISSの実行環境にリストが存在しており、そこに関数名が登録される。 階層トレースは、命令の実行中に、自分のPCが関数の先頭かどうかを探索するが、その際に、当該関数名がそのリストに 登録されていると、そこから先は関数トレースを表示しないようにフラグを上げる。 また、関数から戻ってきたときにフラグを解除することで、当該関数から戻ってきた瞬間からトレース表示を再開するようにしている。
■ trace.cpp の実装
- 関数に入ってきたときに、当該関数が省略対象かを探索し、対象であればスイッチ m_hier_func_in_skipを有効にする。
void TraceInfo::HierFunctionCall (Addr_t fetch_pc) ... // find skip list std::vector<PairSkipFunc *>::iterator it = m_hier_skip_func.begin (); while (it != m_hier_skip_func.end ()) { if ((*it)->first == func_symbol) { m_hier_func_in_skip = true; if ((*it)->second == InstSkip) { m_hier_debug_in_skip = true; } fprintf (GetTraceHierFp(), " ..."); break; } it++; } fprintf (GetTraceHierFp(), ">\n"); ...
- 関数から脱出する場合には、フラグを落として、そこから先は関数トレースを表示させるようにする。
void TraceInfo::HierReturn (Addr_t fetch_pc) ... if (is_found_pc == true) { while (pop_count-- >= 0) { m_hier_stack.pop_back(); SetHierDepth (GetHierDepth()-1); } for (int i = 0; i < GetHierDepth (); i++) { fprintf (GetTraceHierFp(), " "); } fprintf (GetTraceHierFp(), "<Return: %s>\n", (*trace_it)->second.c_str()); m_hier_func_in_skip = false; m_hier_debug_in_skip = false; } ...