11

C++ では、コンパイラーが任意の順序で静的オブジェクトを初期化することを選択できる (いくつかの制約がある) こと、および一般に、静的な初期化順序を選択または決定することはできないことを私は知っています。

ただし、プログラムがコンパイルされると、コンパイラはこれらのオブジェクトを初期化する順序を決定する必要があります。デバッグシンボルを使用してコンパイルされたプログラムから、静的コンストラクターが呼び出される順序を決定する方法はありますか?

コンテキストは次のとおりです。新しいツールチェーンでビルドすると、main() の前に突然 segfault になる大きなプログラムがあります。これは静的な初期化順序の問題であるか、ロードしているライブラリの 1 つに問題があります。ただし、gdb を使用してデバッグすると、クラッシュの場所はシンボリック情報やバックトレースなしで生のアドレスとして単純に報告されます。最初の静的に初期化されたオブジェクトのコンストラクターにブレークポイントを配置することにより、これら2つの問題のどちらであるかを判断したいと思いますが、どのオブジェクトであるかを判断する方法がわかりません。

4

6 に答える 6

11

Linux 上の G++ では、静的コンストラクターとデストラクターの順序は、.ctors セクションと .dtors セクションの関数ポインターによって決定されます。十分なデバッグが利用可能であれば、実際にバックトレースを取得できることに注意してください。

(gdb) bt
#0  0xb7fe3402 in __kernel_vsyscall ()
#1  0xb7d59680 in *__GI_raise (sig=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#2  0xb7d5cd68 in *__GI_abort () at abort.c:88
#3  0x08048477 in foo::foo() ()
#4  0x0804844e in __static_initialization_and_destruction_0(int, int) ()
#5  0x0804846a in global constructors keyed to foo_inst ()
#6  0x0804850d in __do_global_ctors_aux ()
#7  0x08048318 in _init ()
#8  0x080484a9 in __libc_csu_init ()
#9  0xb7d4470c in __libc_start_main (main=0x8048414 <main>, argc=1,
    ubp_av=0xbfffcbc4, init=0x8048490 <__libc_csu_init>,
    fini=0x8048480 <__libc_csu_fini>, rtld_fini=0xb7ff2820 <_dl_fini>,
    stack_end=0xbfffcbbc) at libc-start.c:181
#10 0x08048381 in _start () at ../sysdeps/i386/elf/start.S:119

これには、libc および libstdc++ のデバッグ シンボルがインストールされています。ご覧のとおり、ここでのクラッシュは、静的オブジェクト foo_inst の foo::foo() コンストラクターで発生しました。

初期化プロセスを中断したい場合は、__do_global_ctors_aux にブレークポイントを設定して、その分解をステップ実行できます。または、クラッシュするのを待って、上記のようなバックトレースを取得します。

于 2009-08-03T20:28:07.790 に答える
7

Matthew Wilson は、 Imperfect C++のこのセクション(Safari Books Online の購読が必要) で、この質問に答える方法を提供しています。(ちなみに、良い本です。) 要約すると、彼は作成時に(非標準のプリプロセッサ マクロを使用して) インクルード ソース ファイルのファイル名を出力するクラスの静的インスタンスを作成するヘッダーを作成し、すべてのソース ファイルにインクルードします。 .CUTrace.h__BASE_FILE__CUTrace.h

これには再コンパイルが必要ですが、 #include "CUTrace.h" はスクリプトを介して簡単に追加および削除できるため、セットアップは難しくありません。

于 2009-08-03T20:39:10.157 に答える
2

静的空間でダミー変数を初期化し、それらの関数呼び出しにブレーク ポイントを設定していただけますか?

extern "C" int breakOnMe () { return 0 };

int break1 = breakOnMe ();
float pi = 3.1415;
int break2 = breakOnMe ();
myClass x = myClass (1, 2, 3);

次に、プログラムをgdb実行するbreak breakOnMe前に実行します。これにより、静的初期化の前に gdb が一時停止するはずです。

私はそれがうまくいくはずだと思います..私はgdbbingで少し錆びています。

于 2009-08-03T20:19:27.107 に答える
1

この質問で強調表示されているように、テンプレートを使用して TU が初期化されている順序を確認できます。関心のある TU ごとにコードを少し変更する必要があります。

// order.h
//

#ifndef INCLUDED_ORDER
#define INCLUDED_ORDER

#include <iostream>

inline int showCountAndFile (const char * file)
{
  static int cnt = 0;
  std::cout << file << ": " << cnt << std::endl;
  ++cnt;
  return cnt;
}

template <int & i>
class A {
  static int j;
};

template <int & i>
int A<i>::j = showCountAndFile (SRC_FILE);

namespace
{
  int dummyGlobal;
}
template class A<dummyGlobal>;

#endif

基本的な考え方は、各 TU は、dummyGlobal に対して異なる一意のアドレスを持つため、テンプレートは各 TU で異なるインスタンス化を持つということです。静的メンバーの初期化により、「showCountAndFile」が呼び出され、SRC_FILE (TU で設定) が出力され、その現在の値がcnt順序を示します。

次のように使用します。

static const char * SRC_FILE=__FILE__;
#include "order.h"

int main ()
{
}
于 2009-08-04T08:29:37.703 に答える
0

g++ は、これを支援します。
移植性はありませんが、現時点ではそれが主な問題ではないと確信しています。

http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-属性

于 2009-08-03T22:31:41.740 に答える