How can I plan to run some code after all the '_atexit () functions have completed?

I am writing a memory tracking system, and the only problem I encountered is that when the application exits, any static / global classes that are not allocated in their constructor but are freed from their deconstructor are freed after my files memory tracking reported allocated data as a leak.

As far as I can tell, the only way for me to solve this correctly would be to either force the _atexit callback memory tracking to be placed at the head of the stack (so that it is called last), or execute it after the whole _atexit stack has been unwound. Is it possible to implement any of these solutions, or is there another solution that I missed.

Edit: I am working / developing for Windows XP and compiling with VS2005.

+3
source share
6 answers

- , Windows/Visual Studio. crt ( , ), , " ", . , , , :

#include <iostream>
using std::cout;
using std::endl;

// Typedef for the function pointer
typedef void (*_PVFV)(void);

// Our various functions/classes that are going to log the application startup/exit
struct TestClass
{
    int m_instanceID;

    TestClass(int instanceID) : m_instanceID(instanceID) { cout << "  Creating TestClass: " << m_instanceID << endl; }
    ~TestClass() {cout << "  Destroying TestClass: " << m_instanceID << endl; }
};
static int InitInt(const char *ptr) { cout << "  Initializing Variable: " << ptr << endl; return 42; }
static void LastOnExitFunc() { puts("Called " __FUNCTION__ "();"); }
static void CInit() { puts("Called " __FUNCTION__ "();"); atexit(&LastOnExitFunc); }
static void CppInit() { puts("Called " __FUNCTION__ "();"); }

// our variables to be intialized
extern "C" { static int testCVar1 = InitInt("testCVar1"); }
static TestClass testClassInstance1(1);
static int testCppVar1 = InitInt("testCppVar1");

// Define where our segment names
#define SEGMENT_C_INIT      ".CRT$XIM"
#define SEGMENT_CPP_INIT    ".CRT$XCM"

// Build our various function tables and insert them into the correct segments.
#pragma data_seg(SEGMENT_C_INIT)
#pragma data_seg(SEGMENT_CPP_INIT)
#pragma data_seg() // Switch back to the default segment

// Call create our call function pointer arrays and place them in the segments created above
#define SEG_ALLOCATE(SEGMENT)   __declspec(allocate(SEGMENT))
SEG_ALLOCATE(SEGMENT_C_INIT) _PVFV c_init_funcs[] = { &CInit };
SEG_ALLOCATE(SEGMENT_CPP_INIT) _PVFV cpp_init_funcs[] = { &CppInit };


// Some more variables just to show that declaration order isn't affecting anything
extern "C" { static int testCVar2 = InitInt("testCVar2"); }
static TestClass testClassInstance2(2);
static int testCppVar2 = InitInt("testCppVar2");


// Main function which prints itself just so we can see where the app actually enters
void main()
{
    cout << "    Entered Main()!" << endl;
}

:

Called CInit();
Called CppInit();
  Initializing Variable: testCVar1
  Creating TestClass: 1
  Initializing Variable: testCppVar1
  Initializing Variable: testCVar2
  Creating TestClass: 2
  Initializing Variable: testCppVar2
    Entered Main()!
  Destroying TestClass: 2
  Destroying TestClass: 1
Called LastOnExitFunc();

, MS . , :

( , , , , IS )

extern _CRTALLOC(".CRT$XIA") _PIFV __xi_a[];
extern _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[];    /* C initializers */
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];    /* C++ initializers */
extern _CRTALLOC(".CRT$XPA") _PVFV __xp_a[];
extern _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[];    /* C pre-terminators */
extern _CRTALLOC(".CRT$XTA") _PVFV __xt_a[];
extern _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[];    /* C terminators */

'__xN_a' '__xN_z' ( N {i, c, p, t}) , . .CRT $XnA ''.CRT $XnZ '( n {I, C, P, T}), .

. , . defsects.inc ( $(VS_DIR)\VC\crt\src\), , MS ( , ) , 'U', , , "U", .

, - , , , ( , .CRT$XCT , , , , "C", , .CRT$XIT ).

, , , " " "" , DLL- . - . , , "" , atexit() "" C, , ​​ ( , / ).

() , Microsoft. / (, , , ), .

+5

atexit C/++ (CRT). , main() . , - CRT .

Windows tlibc, , : http://www.codeproject.com/KB/library/tlibc.aspx

mainCRTStartup _doexit();   ExitProcess.

, ExitProcess. ExitProcess, ( http://msdn.microsoft.com/en-us/library/ms682658%28VS.85%29.aspx):

  • , , DLL_THREAD_DETACH.
  • , 1, .
  • (DLL) DLL_PROCESS_DETACH.
  • , DLL , ExitProcess , .
  • .
  • , , .
  • STILL_ACTIVE .
  • , , .

, DLL DLL . , atexit.

, , .

+1

. , Borland ++ #pragma, . ( Borland ++ 5.0, c. 1995)

#pragma startup function-name [priority]
#pragma exit    function-name [priority]
(), ( ), ( , _exit). :
void function-name(void);
64 255, - 0; - 100. . 0 63 C .

, C ?

+1

, (cite). , , .

, , , ( ). , , , , .

, _atexit, ?

0

, . , , - . ( , , , , , - .)

main.cpp:

#include "global_init.inc"
int main() {
  // do very little work; all initialization, main-specific stuff
  // then call your application mainloop
}

# . , , . 18.3/8 ++ 03 , : " ". ( exit(), main , . 3.6.1/5.)

, ( ) . (- , , .)

0

, .

:

. , malloc/new , ( , ). , , !

class MemTracker
{
    enum State
    {
      unconstructed = 0, // must be 0 !!!
      constructed,
      destructed
    };
    State state;

    MemTracker()
    {
       if (state == unconstructed)
       {
          // construct...
          state = constructed;
       }
    }
};

static MemTracker memTracker;  // all statics are zero-initted by linker

, , !

MemTracker::malloc(...)
{
    // force call to constructor, which does nothing after first time
    new (this) MemTracker();
    ...
}

, . , :

    ~MemTracker()
    {
        OutputLeaks(file);
        state = destructed;
    }

, . , . ? ,...

   MemTracker::free(void * ptr)
   {
      do_tracking(ptr);

      if (state == destructed)
      {
          // we must getting called late
          // so re-output
          // Note that this might happen a lot...
          OutputLeaks(file); // again!
       }
   }

:

  • , malloc/free/new/delete ..: -)

EDIT:

  • and I forgot that if you put your tracker in a DLL, you probably need to load LoadLibrary () (or dlopen, etc.) to increase the reference count so that you don't prematurely get deleted from memory. Because, although your class can still be called after destruction, it cannot if the code has been unloaded.
0
source

Source: https://habr.com/ru/post/1723179/


All Articles