Domanda

In C ++, so che il compilatore può scegliere di inizializzare gli oggetti statici nell'ordine che preferisce (soggetto ad alcuni vincoli) e che in generale non è possibile scegliere o determinare l'ordine di inizializzazione statica.

Tuttavia, una volta che un programma è stato compilato, il compilatore deve aver deciso in quale ordine inizializzare questi oggetti. Esiste un modo per determinare, da un programma compilato con simboli di debug, in quale ordine saranno chiamati costruttori statici?

Il contesto è questo: ho un programma considerevole che sta improvvisamente segfaulting prima di main () quando è costruito sotto una nuova toolchain. Questo è un problema di ordine di inizializzazione statico o è qualcosa di sbagliato in una delle librerie che sta caricando. Tuttavia, quando eseguo il debug con gdb, la posizione dell'arresto anomalo viene semplicemente riportata come indirizzo non elaborato senza alcuna informazione simbolica o backtrace. Vorrei decidere quale di questi due problemi è ponendo un breakpoint nel costruttore del primissimo oggetto inizializzato staticamente, ma non so come dire quale oggetto sia.

È stato utile?

Soluzione

Matthew Wilson fornisce un modo per rispondere a questa domanda in questa sezione (Safari Books Online è richiesto un abbonamento) di C ++ imperfetto . (Buon libro, a proposito.) Per riassumere, crea un'intestazione CUTrace.h che crea un'istanza statica di una classe che stampa il nome del file sorgente incluso (usando la macro non standard del preprocessore < codice> __ BASE_FILE__ ) quando creato, quindi include CUTrace.h in ogni file sorgente.

Ciò richiede una ricompilazione, ma #include " CUTrace.h " può essere facilmente aggiunto e rimosso tramite uno script, quindi non dovrebbe essere troppo difficile da configurare.

Altri suggerimenti

In G ++ su Linux, l'ordinamento di costruttori e distruttori statici è determinato dai puntatori di funzione nelle sezioni .ctors e .dtors. Tieni presente che con un debug sufficiente disponibile, puoi effettivamente ottenere un backtrace:

(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

Questo è con i simboli di debug per libc e libstdc ++ installati. Come puoi vedere, il crash qui si è verificato nel costruttore foo :: foo () per l'oggetto statico foo_inst.

Se si desidera interrompere il processo di inizializzazione, è quindi possibile impostare un breakpoint su __do_global_ctors_aux e procedere attraverso il suo smontaggio, suppongo. O semplicemente aspettare che si blocchi per ottenere il backtrace come sopra.

Potresti inizializzare variabili fittizie nello spazio statico e mettere dei punti di interruzione su quelle chiamate di funzione?

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

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

Quindi in gdb esegui break breakOnMe prima di eseguire il programma. Ciò dovrebbe mettere in pausa gdb prima di ciascuna delle inizializzazioni statiche.

Penso che dovrebbe funzionare .. Sono un po 'arrugginito su gdbbing.

Puoi trovare l'ordine di inizializzazione delle TU utilizzando i modelli come evidenziato da questo domanda . Richiede una piccola modifica del codice a ciascuna delle TU che ti interessano:

// 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

L'idea di base è che ogni TU avrà un indirizzo univoco diverso per dummyGlobal e quindi il modello avrà un'istanza diversa in ogni TU. L'inizializzazione del membro statico comporta la chiamata a " showCountAndFile " che quindi stampa SRC_FILE (impostato nella TU) e il valore corrente di cnt che mostrerà quindi l'ordine.

Lo useresti come segue:

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

int main ()
{
}

g ++ fornisce un aiuto in questo.
Non è portatile ma sono sicuro a questo punto che non è il tuo problema principale.

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

In realtà, attraverso l'uso di Singleton puoi controllare l'ordine di inizializzazione di oggetti globali / statici in modo abbastanza efficace in C ++.

Ad esempio, supponiamo di avere:

class Abc
{
public:
    void foo();
};

e un oggetto corrispondente definito nell'ambito globale:

Abc abc;

Quindi hai una classe:

class Def
{
public:
    Def()
    {
        abc.foo();
    }
};

che ha anche un oggetto definito nell'ambito globale:

Def def;

In questa situazione, non hai il controllo dell'ordine di inizializzazione e se def è inizializzato per primo, allora è probabile che il tuo programma andrà in crash perché sta chiamando il metodo foo () su un Abc che non è stato ancora inizializzato.

La soluzione è avere una funzione di portata globale che faccia qualcosa del genere:

Abc& abc()
{
    static Abc a;
    return a;
}

e quindi Def sarebbe simile a:

class Def
{
public:
    Def()
    {
        abc().foo();
    }
};

In questo modo, viene sempre garantito l'inizializzazione di abc prima che venga utilizzato perché ciò accadrà durante la prima chiamata della funzione abc (). Allo stesso modo, dovresti fare lo stesso con l'oggetto globale Def per fare in modo che non ci siano dipendenze di inizializzazione impreviste.

Def& def()
{
    static Def d;
    return d;
}

Se devi controllare rigorosamente l'ordine di inizializzazione oltre a assicurarti che tutto sia inizializzato prima di essere usato, metti tutti gli oggetti globali in un singleton globale come segue.

struct Global
{
    Abc abc;
    Def def;
};

Global& global()
{
    static Global g;
    return g;
}

E fai riferimento a questi elementi come segue:

//..some code
global().abc.foo();
//..more code here
global().def.bar();

Indipendentemente da quale si ricevano prima le chiamate, le regole di inizializzazione dei membri C ++ garantiranno che gli oggetti abc e def siano inizializzati nell'ordine in cui sono definiti nella classe Global.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top