Question

En C ++, je sais que le compilateur peut choisir d'initialiser des objets statiques dans l'ordre de son choix (sous réserve de quelques contraintes) et qu'en général, vous ne pouvez pas choisir ou déterminer l'ordre d'initialisation statique.

Cependant, une fois qu'un programme a été compilé, le compilateur doit avoir pris une décision sur l'ordre d'initialisation de ces objets. Y a-t-il un moyen de déterminer, à partir d'un programme compilé avec des symboles de débogage, dans quel ordre les constructeurs statiques seront appelés?

Le contexte est le suivant: j’ai un programme important qui se brise soudainement avant main () lorsqu’il est construit sous une nouvelle chaîne d’outils. Soit il s’agit d’un problème d’ordre d’initialisation statique, soit il ya un problème avec l’une des bibliothèques en cours de chargement. Cependant, lorsque je débogue avec gdb, l'emplacement de l'incident est simplement signalé comme une adresse brute sans aucune information symbolique ni trace. Je voudrais déterminer lequel de ces deux problèmes il s'agit en plaçant un point d'arrêt au constructeur du tout premier objet initialisé de manière statique, mais je ne sais pas comment dire quel objet il s'agit.

Était-ce utile?

La solution

Matthew Wilson propose un moyen de répondre à cette question dans cette section (Safari Books Online abonnement requis) de Imparfait C ++ . (Bon livre, en passant.) Pour résumer, il crée un en-tête CUTrace.h qui crée une instance statique d'une classe qui affiche le nom du fichier source inclus (à l'aide de la macro de préprocesseur non standard < code> __ BASE_FILE __ ) lors de sa création, il inclut CUTrace.h dans chaque fichier source.

Ceci nécessite une recompilation, mais le #include "CUTrace.h" peut facilement être ajouté et supprimé via un script, il ne devrait donc pas être trop difficile à configurer.

Autres conseils

Dans G ++ sous Linux, l’ordre des constructeurs et des destructeurs statiques est déterminé par les pointeurs de fonction des sections .ctors et .dtors. Notez qu’avec suffisamment de débogage disponible, vous pouvez réellement obtenir une trace:

(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

Ceci est avec les symboles de débogage pour libc et libstdc ++ installés. Comme vous pouvez le constater, le crash s'est produit ici dans le constructeur foo :: foo () pour l'objet statique foo_inst.

Si vous souhaitez entrer dans le processus d'initialisation, vous pouvez ensuite définir un point d'arrêt sur __do_global_ctors_aux et procéder à son désassemblage, je suppose. Ou attendez simplement qu’il tombe en panne pour obtenir la trace comme ci-dessus.

Pourriez-vous initialiser des variables factices dans l'espace statique et mettre des points d'arrêt sur ces appels de fonction?

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

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

Ensuite, dans gdb , exécutez break breakOnMe avant d'exécuter le programme. Cela devrait faire péter gdb avant chaque initialisation statique.

Je pense que cela devrait fonctionner .. Je suis un peu rouillé sur gdbbing.

Vous pouvez trouver la commande d'initialisation des UT à l'aide de modèles, comme indiqué par ce question . Un petit peu de code doit être modifié pour chacune des UT qui vous intéressent:

// 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'idée de base est que chaque TU aura une adresse unique différente pour dummyGlobal et que le modèle aura une instanciation différente dans chaque TU. L'initialisation du membre statique entraîne l'appel à " showCountAndFile " qui affiche ensuite SRC_FILE (défini dans la TU) et la valeur actuelle de cnt , qui affichera donc la commande.

Vous l'utiliseriez comme suit:

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

int main ()
{
}

g ++ fournit de l'aide à ce sujet.
Ce n’est pas portable, mais je suis sûr à ce stade que ce n’est pas votre principal problème.

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

En fait, grâce à l'utilisation de singletons, vous pouvez contrôler l'ordre d'initialisation des objets globaux / statiques assez efficacement en C ++.

Par exemple, supposons que vous ayez:

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

et un objet correspondant défini au niveau global:

Abc abc;

Ensuite, vous avez une classe:

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

qui a également un objet défini au niveau global:

Def def;

Dans cette situation, vous n’avez pas le contrôle de l’ordre d’initialisation et si def est initialisé en premier, il est probable que votre programme plante, car il appelle la méthode foo () sur un Abc qui n’a pas encore été initialisé.

La solution est d’avoir une fonction de portée globale comme suit:

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

puis Def ressemblerait à quelque chose comme:

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

De cette façon, il est toujours garanti que abc sera initialisé avant son utilisation car cela se produira lors du premier appel de la fonction abc (). De même, vous devriez faire la même chose avec l'objet global Def pour éviter qu'il ait des dépendances d'initialisation inattendues.

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

Si vous devez contrôler strictement l'ordre d'initialisation en plus de vous assurer que tout est initialisé avant son utilisation, placez tous les objets globaux dans un singleton global comme suit.

struct Global
{
    Abc abc;
    Def def;
};

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

Et faites référence à ces éléments comme suit:

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

Quels que soient les appels reçus en premier, les règles d'initialisation des membres C ++ garantissent que les objets abc et def sont initialisés dans l'ordre dans lequel ils ont été définis dans la classe Global.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top