バックグラウンド
職場では、最適化されたコードのコア ダンプを使用してポストモーテン デバッグを行うことがよくあります。
特定の種類の困難で再現性のない障害については、追加情報を利用できるようにしたいと考えています。これらの場合、追加のトレースを追加することは現実的ではありません。これは、呼び出しの大部分が成功し、1 分間に数百万の「不要な」トレースが追加され、ログ ファイルが迅速にローリングされるためです。キャッチとトレースが常に実行可能であるとは限らず、一部のエラーによって環境が破損し、トレースが失敗する可能性があります。
コア ダンプにはコールスタック メモリが含まれているため、コールスタック メモリの領域を「トレース」に使用できると考えました。
問題
最適化コンパイラのおかげで、このようなコードは機能しません
void process (int i)
{
int save_me = i;
// Do something else
}
アイデアは、ローカル変数に代入することにより、入力変数をスタックに格納することです。これは多くの場合、デバッグ モードでは正常に機能しますが、最適化されたビルドでは、コンパイラはステートメントに副作用がないと判断し、ステートメントを削除します。
alloca
サポートされていない一部のプラットフォームをターゲットalloca
にしており、C++ とどの程度うまく機能するかはわかりません。
少し実験したところ、次のコードは、最適化されたビルドでもスタックに状態を「固定」できるようです。
#include <cstdint>
#include <stdexcept>
#include <istream>
#include <sstream>
struct saved_state
{
saved_state ()
: head (0xAABBCCDD)
, tail (0xEEFF0000)
{
std::fill (state, state + 16, 0);
}
void push (std::int32_t input) volatile
{
for (auto i = 15U; i > 0U; --i)
{
state[i] = state[i - 1];
}
state[0] = input;
}
volatile std::uint32_t head ;
volatile std::int32_t state [16];
volatile std::uint32_t tail ;
};
void invoke (std::int32_t i)
{
if (i > 10)
{
throw std::runtime_error ("Busted");
}
}
void process (std::istream & input)
{
saved_state volatile ss;
while (!input.eof ())
{
std::int32_t i;
if (input >> i)
{
ss.push (i);
invoke (i);
}
}
}
int main()
{
std::istringstream input ("1\n2\n30\n");
process (input);
return 0;
}
質問
コードがやりたいことを実行することを期待できますか? 現在の一連のコンパイラ (clang & gcc) で動作するようですが、引き続き動作することを期待できますか?
私がやりたいことを達成するためのより良い方法はありますか?
より良いとは、よりシンプルで、より堅牢で、標準に準拠していることを意味します。