Nalezení C ++ statické problémy inicializace pořadí

hlasů
55

My jsme narazit na některé problémy se statickou inicializaci objednávek fiasko a já se hledají způsoby, jak prolézt spoustu kódu najít případné výskyty. Nějaké návrhy na to, jak to udělat efektivně?

Edit: Dostávám nějaké dobré odpovědi na to, jak vyřešit problém inicializace objednávky statické, ale ve skutečnosti to není moje otázka. Chtěl bych vědět, jak najít objekty, které jsou předmětem tohoto problému. Evan odpověď se zdá být nejlepší dosud v tomto ohledu; Nemyslím si, že můžeme použít valgrind, ale budeme mít nástroje pro analýzu paměti, které by mohly vykonávat podobnou funkci. To by podchytit problémy pouze tehdy, pokud pořadí inicializace je špatné pro danou verzi a pořadí může měnit s každou verzi. Možná, že tam je statická analýza nástroj, který by se chytit to. Naše platforma IBM XLC / C ++ kompilátor běží na operačním systému AIX.

Položena 02/12/2008 v 21:40
zdroj uživatelem
V jiných jazycích...                            


11 odpovědí

hlasů
62

Řešení pořadí inicializace:

Za prvé, je to jen dočasná práce kolem, protože máte globální proměnné, které se snaží zbavit, ale jen zatím neměl čas (se chystáte zbavit se jich nakonec ne? :-)

class A
{
    public:
        // Get the global instance abc
        static A& getInstance_abc()  // return a reference
        {
            static A instance_abc;
            return instance_abc;
        }
};

To zaručí, že se inicializuje při prvním použití a zničeno, když je aplikace ukončena.

Multi-threaded Problém:

C ++ 11 nemá záruku, že toto je thread-safe:

§6.7 [stmt.dcl] p4
Pokud kontrola vstupuje prohlášení současně zatímco proměnná je inicializována, souběžné provedení musí počkat na dokončení inicializace.

Nicméně, C ++ 03 však není oficiálně zaručit, že výstavba statických objektů funkce je vlákno v bezpečí. Takže technicky getInstance_XXX()metoda musí být obezřetný s kritická sekce. Na druhou stranu, gcc má explicitní náplast jako součást překladače, která zaručuje, že každý statická funkce objekt se inicializuje pouze jednou a to iv přítomnosti závitů.

Upozornění: Nepoužívejte používat dvakrát zkontrolováno blokování vzor , aby se pokusila vyhnout se náklady na zajištění. To nebude fungovat v C ++ 03.

Problémy creation:

Na vytváření, nejsou žádné problémy, protože zaručují, že je vytvořen před tím, než mohou být použity.

Problémy Destruction:

Existuje potenciální problém přístupu k objektu poté, co byl zničen. Tato situace nastane pouze tehdy, pokud máte přístup k objektu z destruktor jiné globální proměnné (o globální, mám na mysli jakékoliv jiné místní statické proměnné).

Řešením je, aby se ujistil, že jste vynutit pořadí destrukce.
Pamatovat pořadí destrukce je přesný inverzní pořadí výstavby. Takže pokud máte přístup k objektu ve svém destructor, je třeba zaručit, že objekt nebyl zničen. K tomu je třeba jen zaručit, že objekt je stavebně zcela dokončena dříve, než volající objekt je postaven.

class B
{
    public:
        static B& getInstance_Bglob;
        {
            static B instance_Bglob;
            return instance_Bglob;;
        }

        ~B()
        {
             A::getInstance_abc().doSomthing();
             // The object abc is accessed from the destructor.
             // Potential problem.
             // You must guarantee that abc is destroyed after this object.
             // To guarantee this you must make sure it is constructed first.
             // To do this just access the object from the constructor.
        }

        B()
        {
            A::getInstance_abc();
            // abc is now fully constructed.
            // This means it was constructed before this object.
            // This means it will be destroyed after this object.
            // This means it is safe to use from the destructor.
        }
};
Odpovězeno 02/12/2008 v 23:51
zdroj uživatelem

hlasů
28

Jen jsem napsal kus kódu vystopovat tohoto problému. Máme dobrou velikost kódové základny (1000+ soubory), který byl v pořádku na Windows / VC ++ 2005, ale zřítilo při startu na Solaris / gcc. Napsal jsem následující h souboru:

#ifndef FIASCO_H
#define FIASCO_H

/////////////////////////////////////////////////////////////////////////////////////////////////////
// [WS 2010-07-30] Detect the infamous "Static initialization order fiasco"
// email warrenstevens --> [initials]@[firstnamelastname].com 
// read --> http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 if you haven't suffered
// To enable this feature --> define E-N-A-B-L-E-_-F-I-A-S-C-O-_-F-I-N-D-E-R, rebuild, and run
#define ENABLE_FIASCO_FINDER
/////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef ENABLE_FIASCO_FINDER

#include <iostream>
#include <fstream>

inline bool WriteFiasco(const std::string& fileName)
{
    static int counter = 0;
    ++counter;

    std::ofstream file;
    file.open("FiascoFinder.txt", std::ios::out | std::ios::app);
    file << "Starting to initialize file - number: [" << counter << "] filename: [" << fileName.c_str() << "]" << std::endl;
    file.flush();
    file.close();
    return true;
}

// [WS 2010-07-30] If you get a name collision on the following line, your usage is likely incorrect
#define FIASCO_FINDER static const bool g_psuedoUniqueName = WriteFiasco(__FILE__);

#else // ENABLE_FIASCO_FINDER
// do nothing
#define FIASCO_FINDER

#endif // ENABLE_FIASCO_FINDER

#endif //FIASCO_H

a v rámci každého souboru cpp v roztoku, jsem přidal toto:

#include "PreCompiledHeader.h" // (which #include's the above file)
FIASCO_FINDER
#include "RegularIncludeOne.h"
#include "RegularIncludeTwo.h"

Při spuštění aplikace, získáte výstupní soubor jako tak:

Starting to initialize file - number: [1] filename: [p:\\OneFile.cpp]
Starting to initialize file - number: [2] filename: [p:\\SecondFile.cpp]
Starting to initialize file - number: [3] filename: [p:\\ThirdFile.cpp]

Dojde-li k havárii, viník musí být v posledním souboru cpp uvedeny. A přinejmenším, to dá vám dobré místo pro nastavení zarážky, protože tento kód by měl být absolutní první svého kódu vykonat (po kterém můžete procházet kódu a vidět všechny globals které jsou inicializovat) ,

Poznámky:

  • Je důležité, aby si dal „FIASCO_FINDER“ makro co nejblíže k horní části souboru, jak je to možné. Pokud je kazeta vložena pod některými dalšími #includes riskujete na to shazovat před identifikaci soubor, který se právě nacházíte.

  • Pokud používáte Visual Studio, a předkompilované hlavičky, přidání tohoto makra další řádek pro všechny vaše .CPP souborů lze provést rychle pomocí dialogového okna Najít a nahradit nahradit stávající #include „precompiledheader.h“ s stejný text plus FIASCO_FINDER linka (pokud odškrtávat „regulární výrazy, můžete použít‚\ n‘vložit multi-linka náhradní text)

Odpovězeno 04/08/2010 v 03:42
zdroj uživatelem

hlasů
14

V závislosti na vašem kompilátoru, můžete umístit zarážku na konstruktor kódu inicializace. V aplikaci Visual C ++, je to _inittermfunkce, která je dána počáteční a koncový ukazatel seznamu funkcí pro volání.

Pak vstoupil do jednotlivých funkcí dostat soubor a název funkce (za předpokladu, že jste sestaven s ladění Informace o). Jakmile budete mít jména, vystoupit z funkce (zpět do _initterm) a pokračovat až do _inittermvýchody.

Která vám dává všechny statické inicializátory, a to nejen těch v kódu - je to nejjednodušší způsob, jak získat vyčerpávající seznam. Můžete odfiltrovat ty, které nemají kontrolu nad (jako jsou v knihovnách třetích stran).

Tato teorie platí i pro ostatní kompilátory, ale název funkce a schopnost debuggeru může změnit.

Odpovězeno 02/12/2008 v 22:57
zdroj uživatelem

hlasů
5

K dispozici je kód, který v podstatě „inicializuje“ C ++, který je generován kompilátorem. Snadný způsob, jak najít tento kód / zásobníku volání v době, kdy je vytvořit statický objekt s něčím, který získává NULL v konstruktoru - přerušení v debuggeru a prozkoumat trochu. MSVC kompilátor nastaví tabulku ukazatelů funkcí, které se opakuje po dobu statické inicializaci. Ty by měly mít přístup k této tabulce a určit všechny statické inicializaci koná ve vašem programu.

Odpovězeno 11/01/2011 v 16:01
zdroj uživatelem

hlasů
5

Možná použít Valgrind najít využití neinicializované paměti. Nejhezčí řešení „statická inicializace objednávky fiasko“ je použít statickou funkci, která vrací instanci objektu, jako je tento:

class A {
public:
    static X &getStatic() { static X my_static; return my_static; }
};

Tímto způsobem lze získat přístup k statický objekt je voláním getStatic, bude to zaručit, že se inicializuje při prvním použití.

Potřebujete-li se starat o pořadí de-inicializaci vrátí new'd objekt namísto staticky přidělené objektu.

EDIT: odstraněny redundantní statický objekt, já nevím proč, ale i smíšené a uzavřeno dva způsoby, které mají statickou jak ve svém původním příkladu.

Odpovězeno 02/12/2008 v 21:51
zdroj uživatelem

hlasů
4

My jsme narazit na některé problémy se statickou inicializaci objednávek fiasko, a já se hledají způsoby, jak se rozčesávají přes spoustu kódu najít případné výskyty. Nějaké návrhy na to, jak to udělat efektivně?

To není triviální problém, ale alespoň to lze uskutečnit na základě těchto poměrně jednoduchých kroků, pokud máte snadno analyzovat zastoupení středního formátu kódu.

1) Najděte všechny globals, které mají non-triviální konstruktérů a dát je do seznamu.

2) Pro každý z těchto non-triviálně budovaných objektů, vytvářet celý potenciál-funkce strom názvem jejich výrobci.

3) Procházka funkce stromu non-triviálně-konstruktoru a v případě, že kód odkazuje na jiné non-triviálně postavené globals (které jsou docela obratně v seznamu, který generovaných v prvním kroku), budete muset potenciální včasného statická inicializace objednávku problém.

4) Opakujte kroky 2 a 3, dokud se naplnily seznamu vytvořené v kroku 1.

Poznámka: můžete být schopni optimalizovat tím, že jen na návštěvě potenciální-Function strom jednou za třídu objektu, spíše než jednou za globální Například pokud máte více globals z jedné třídy.

Odpovězeno 05/05/2009 v 23:23
zdroj uživatelem

hlasů
2

Nahradit všechny globální objekty s globálními funkcemi, které vracejí odkaz na objekt prohlášen statické funkce. To není thread-safe, takže pokud vaše aplikace je multi-threaded byste mohli potřebovat nějaké triky, jako pthread_once nebo globálního zámku. Tím bude zajištěno, že vše je inicializována před použitím.

Nyní, ať už váš program funguje (hurá!), Nebo jinde to sedí v nekonečné smyčce, protože máte kruhovou závislost (redesign nutná), jinak budete pohybovat na další chybu.

Odpovězeno 02/12/2008 v 21:52
zdroj uživatelem

hlasů
1

Gimpel Software (www.gimpel.com), tvrdí, že jejich PC-Lint / FlexeLint statika nástroje budou zjišťovat takové problémy.

Měl jsem dobré zkušenosti s jejich nástrojů, nikoli však s tímto konkrétním problémem, takže nemohu ručit za kolik by pomohlo.

Odpovězeno 05/05/2009 v 23:34
zdroj uživatelem

hlasů
0

Jiné odpovědi jsou správné, jen jsem chtěl dodat, že kariérista objektu by měla být provedena v souboru cpp a nemělo by být statická. Pokud ji implementovat do záhlaví souboru, objekt bude vytvořen v každé knihovně / rámce, ze kterého jej ....

Odpovězeno 28/06/2011 v 10:24
zdroj uživatelem

hlasů
0

Pokud váš projekt je ve Visual Studio (Zkoušel jsem to s VC ++ Express 2005 a Visual Studio 2008 Pro):

  1. Open Class View (Main menu-> Zobrazení-> Class View)
  2. Rozbalte každý projekt v vaše řešení a klikněte na „globální funkce a proměnné“

To by vám mělo poskytnout slušný seznam všech globals, které jsou předmětem fiasko .

Na konci, lepší přístup je pokusit se odstranit tyto objekty z projektu (snadněji řekne, než udělá, někdy).

Odpovězeno 22/07/2010 v 03:32
zdroj uživatelem

hlasů
0

První věc, kterou musíte udělat, je vytvořit seznam všech statických objektů, které mají non-triviální konstruktérů.

Vzhledem k tomu, že buď je třeba k počítači připojit přes ně jeden po druhém, nebo jednoduše nahradit všechny objekty singleton-vzor.

Singleton přichází v pro spoustu kritiky, ale líný „as-vyžadováno“ Stavba je poměrně snadný způsob, jak opravit většinu problémů, nyní i v budoucnu.

starý...

MyObject myObject

Nový...

MyObject &myObject()
{
  static MyObject myActualObject;
  return myActualObject;
}

Samozřejmě, pokud vaše aplikace je multi-threaded, to může způsobit vám více problémů, než jste měli v první řadě ...

Odpovězeno 02/12/2008 v 21:53
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more