Pergunta

Em C ++, eu sei que o compilador pode escolher para inicializar objetos estáticos em qualquer ordem que escolher (sujeito a algumas restrições), e que, em geral, você não pode escolher ou determinar a ordem de inicialização estática.

No entanto, uma vez que um programa foi compilado, o compilador tem de ter tomado uma decisão sobre o que pedir para inicializar esses objetos em. Existe alguma maneira de determinar, a partir de um programa compilado com símbolos de depuração, em que ordem construtores estáticos será chamado?

O contexto é esta: Eu tenho um programa considerável que é subitamente segfaulting antes de main () quando é construído sob um novo conjunto de ferramentas. Ou este é um problema de inicialização static, ou é algo de errado com uma das bibliotecas que está sendo carregado. No entanto, quando eu depurar com gdb, o local do acidente é simplesmente relatado como um endereço de matéria sem qualquer informação simbólica ou registo de chamadas. Gostaria de decidir qual destes dois problemas, é colocando um ponto de interrupção no construtor do objeto primeiro estaticamente inicializado, mas eu não sei como dizer qual objeto que é.

Foi útil?

Solução

Matthew Wilson fornece uma maneira de responder a essa pergunta em nesta seção (Safari Books Online subscrição exigida) de Imperfect C ++ . (Bom livro, por sinal.) Para resumir, ele cria um cabeçalho CUTrace.h que cria uma instância estática de uma classe que imprime o nome do arquivo do arquivo de origem, incluindo (usando o __BASE_FILE__ macro pré-processador fora do padrão), quando criado, em seguida, ele inclui CUTrace.h em cada arquivo-fonte.

Isto requer uma recompilação, mas o #include "CUTrace.h" podem ser facilmente adicionados e removidos através de um script, por isso não deve ser muito difícil de configurar.

Outras dicas

Em G ++ no Linux, construtor estático e ordenação destructor é determinada por ponteiros de função nas seções .ctors e .dtors. Note-se que com suficiente depuração disponível, você pode realmente obter um registo de chamadas:

(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

Esta é a depuração símbolos para libc e libstdc ++ instalado. Como você pode ver, o acidente ocorreu aqui no construtor foo :: foo () para a foo_inst objeto estático.

Se você quiser entrar no processo de inicialização, você poderia, então, definir um ponto de interrupção na __do_global_ctors_aux e passo através de sua desmontagem, suponho. Ou apenas esperar por ele para bater para obter o backtrace como o acima.

Você poderia inicializar variáveis ??binárias no espaço estático e pontos de quebra colocar essas chamadas de função?

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

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

Então, em gdb break breakOnMe prazo antes de executar o programa. Isso deve fazer pausa gdb antes de cada nas inicializações estáticas.

Eu acho que deve funcionar .. Eu estou um pouco enferrujado em gdbbing.

Você pode encontrar a ordem do TUs estão sendo inicializado usando modelos como destacado por este questão . Ele requer um pouco de mudança do código para cada uma das TUs que você está interessado em:

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

A idéia básica é que cada TU terá um único endereço diferente para dummyGlobal e assim o modelo terá uma instanciação diferente em cada TU. A inicialização dos resultados de membro estático na chamada para "showCountAndFile", que, em seguida, imprime SRC_FILE (definido no TU) e o valor atual de cnt que irá, portanto, mostrar a ordem.

Você poderia usá-lo da seguinte forma:

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

int main ()
{
}

g ++ fornece alguma ajuda com isso.
A sua não é portátil, mas estou certo neste momento que não é o seu principal problema.

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

Na verdade, através do uso de Singletons você pode controlar a ordem de inicialização de / static global de objetos de forma bastante eficaz em C ++.

Por exemplo, digamos que você tem:

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

e um objeto correspondente definida no escopo global:

Abc abc;

Em seguida, você tem uma classe:

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

que também tem um objeto definido no escopo global:

Def def;

Nesta situação, você não tem controle sobre a ordem de inicialização e se def é inicializado primeiro, então é provável que o programa irá falhar porque ele está chamando o método foo () em um Abc que ainda não foi inicializado.

A solução é ter uma função no escopo global fazer algo como isto:

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

e depois Def seria algo parecido com:

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

Desta forma, abc é sempre garantida a ser inicializado antes de ser usado, porque isso irá acontecer durante a primeira chamada da função abc (). Da mesma forma, você deve fazer o mesmo com Def objeto global para torná-lo não terá quaisquer dependências de inicialização inesperados também.

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

Se você precisa controlar rigorosamente a ordem de inicialização, além de simplesmente ter certeza que tudo é inicializado antes de ser usado, coloque todos os objetos globais em um singleton global da seguinte forma.

struct Global
{
    Abc abc;
    Def def;
};

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

e fazer referências a esses itens como segue:

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

Independentemente de qual deles recebe chamadas em primeiro lugar, as regras membro de inicialização C ++ garante que os objetos do ABC e DEF são inicializados na ordem em que são definidos na classe global.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top