0

MFC アプリケーションがあります。そのアプリのメモリ リークの原因を突き止めるために、すべての動的メモリ割り当て (ヒープ上) をトレースしたいと考えています。IDE は Visual Studio 2010 です。

私は次のことをしました:

  • 「MEMORY_LEAK_FINDER」と呼ばれるプリプロセッサ ディレクティブが導入されました。
  • 「CMemLeakHunter」というクラスが追加されました。これらのファイルの正確な内容は以下にあります。
  • すべての新しい演算子 (3 つすべて: new、new[]、および CObject::new) をオーバーロードし、それらを使用してメモリが割り当てられた場所 (ファイル、行) をトレースするというアイデアがありました。実行の最後に、'CMemoryState' クラスを使用してメモリ リークの場所を出力したかったので、最終的に割り当てのトレースを CMemoryState の比較 (差分) トレースと比較できました。

問題は、アプリケーションが (VS 2010 デバッグ モードで) コンパイルされるが、次のリンカ エラーが発生することです。

エラー 4 エラー LNK2005: "void * __cdecl operator new[](unsigned int,char const *,int)" (??_U@YAPAXIPBDH@Z) は CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem. obj) エラー 3 エラー LNK2005: "void * __cdecl operator new(unsigned int,char const *,int)" (??2@YAPAXIPBDH@Z) CMemLeakHunter.obj E:\Software\Nafxcwd.lib(afxmem. obj) エラー 5 エラー LNK2005: "public: static void * __stdcall CObject::operator new(unsigned int,char const *,int)" (??2CObject@@SGPAXIPBDH@Z) CMemLeakHunter.obj E:\Software で既に定義されています\Nafxcwd.lib(afxmem.obj) エラー 6 エラー LNK1169: 1 つ以上の多重定義シンボルが見つかりました E:\Software\Module1.exe 1

ライブラリNafxcwd.libを無視すると問題が解決する可能性があることがわかりました。私のアプリケーションではそうではありませんでしたが、そのライブラリを無視して、別の 17000 リンカ エラー (未解決の外部) を試しました。

追加の依存関係は次のとおりです。Nafxcwd.lib;Ws2_32.lib;Version.lib

特定のデフォルト ライブラリを無視する:msvcprtd.lib;libcimtd.lib;libcmt.lib

ソフトウェアを簡単に分割できないので、助けを求めます: MFC を使用していて、上記の .lib ファイルを使用する必要がある場合、自分のアプリによって行われたメモリ割り当てを追跡するにはどうすればよいですか? 解決策は何ですか?この問題を解決して、メモリ割り当てを追跡し、リークの原因を突き止められるようにしてください。また、別の MFC 組み込みルーチンが使用できる場合は、それを使用することにも前向きです。しかし、私は自分で役に立つものを見つけませんでした。

ヘッダー ファイルCMemLeakHunter.hppは次のように記述されます。

#ifndef _MEM_LEAK_HUNTER_
#define _MEM_LEAK_HUNTER_

#ifdef MEMORY_LEAK_FINDER
#pragma message("Macro MEMORY_LEAK_FINDER is active, overloading new...")

#include "stdafx.h"
#include <map>
using std::map;

#undef new
void* operator new(size_t size, LPCSTR file, int line);
void* operator new[](size_t size, LPCSTR file, int line);
#define new new(__FILE__, __LINE__)

namespace
{
    static const size_t LOG_BUFFER_SIZE = 2;
    static const size_t DEFAULT_BUFFER_LINE_SIZE = 512;
}

class CMemLeakHunter
{
public:
    static CMemLeakHunter& singleton();

    void startMemoryTrace(const char* leakPath, const char* allocPath);
    void endMemoryTrace();

    void addMemory(void* area, size_t size, LPCSTR file, int line);
    void deleteMemory(void* area, LPCSTR file, int line);

private:
    void flushAllocLog();
    void filterTrace();

    map<DWORD, size_t> mMemChunks;
    CString sLogBuffer[LOG_BUFFER_SIZE];
    size_t nLogBufferLines;

    CMemoryState oldMemState;
    CMemoryState newMemState;
    CMemoryState diffMemState;

    CString sMemLeakTracePath;
    CString sMemAllocTracePath;

    static CMutex s_oObjMutex;
};

#endif // MEMORY_LEAK_FINDER

#endif // _MEM_LEAK_HUNTER_

ソース ファイルCMemLeakHunter.cppは次のように記述されます。

#include "CMemLeakHunter.hpp"

#ifdef MEMORY_LEAK_FINDER
#pragma message("Memory-Leak finder is activated, building functions for the class...")

#undef new
void* operator new(size_t size, LPCSTR file, int line)
{
    void* pArea = ::operator new(size);
    CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
    return pArea;
}

void* operator new[](size_t size, LPCSTR file, int line)
{
    void* pArea = ::operator new[](size);
    CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
    return pArea;
}

void* PASCAL CObject::operator new(size_t size, LPCSTR file, int line)
{
    void* pArea = CObject::operator new(size);
    CMemLeakHunter::singleton().addMemory(pArea, size, file, line);
    return pArea;
}

CMutex CMemLeakHunter::s_oObjMutex;

CMemLeakHunter& CMemLeakHunter::singleton()
{
    static CMemLeakHunter theSingleObject;
    return theSingleObject;
}

void CMemLeakHunter::startMemoryTrace(const char* leakPath, const char* allocPath)
{
    sMemLeakTracePath = leakPath;
    sMemAllocTracePath = allocPath;

    oldMemState.Checkpoint();

    for(size_t i=0; i<LOG_BUFFER_SIZE; i++)
    {
        sLogBuffer[i] = CString('\0', DEFAULT_BUFFER_LINE_SIZE);
    }
}

void CMemLeakHunter::endMemoryTrace()
{
    newMemState.Checkpoint();
    if(FALSE != diffMemState.Difference(oldMemState, newMemState))
    {
        CFile oDumpFile;
        if(TRUE == oDumpFile.Open(sMemLeakTracePath, CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyWrite))
        {
            CFile* oldContext = afxDump.m_pFile;
            afxDump.m_pFile = &oDumpFile;

            diffMemState.DumpStatistics();
            oDumpFile.Write("\n", 1);
            diffMemState.DumpAllObjectsSince();

            oDumpFile.Close();

            afxDump.m_pFile = oldContext;
        }
        else
        {
            // TODO: log that file cannot be created!!!
        }
    }

    flushAllocLog();
    filterTrace();
}

void CMemLeakHunter::addMemory(void* area, size_t size, LPCSTR file, int line)
{
    CSingleLock oMemHunterTraceLock(&s_oObjMutex, TRUE);

    DWORD nAreaAddr = reinterpret_cast<DWORD>(area);
    mMemChunks[nAreaAddr] = size;

    if(nLogBufferLines >= LOG_BUFFER_SIZE)
    {
        flushAllocLog();
    }

    sLogBuffer[nLogBufferLines++].Format("### Memory allocation: Address 0x%08X, Size: %u, File: '%s', Line: %d\n", nAreaAddr, size, file, line);
}

void CMemLeakHunter::deleteMemory(void* area, LPCSTR file, int line)
{
    CSingleLock oMemHunterTraceLock(&s_oObjMutex, TRUE);

    DWORD nAreaAddr = reinterpret_cast<DWORD>(area);
    mMemChunks.erase(nAreaAddr);

    if(nLogBufferLines >= LOG_BUFFER_SIZE)
    {
        flushAllocLog();
    }

    sLogBuffer[nLogBufferLines++].Format("!!! Memory release: Address 0x%08X, File: '%s', Line: %d\n", nAreaAddr, file, line);
}

void CMemLeakHunter::flushAllocLog()
{
    CStdioFile oAllocFile;
    if(FALSE == PathFileExists(sMemAllocTracePath))
    {
        oAllocFile.Open(sMemAllocTracePath, CFile::modeCreate);
        oAllocFile.Close();
    }

    if(TRUE == oAllocFile.Open(sMemAllocTracePath, CFile::modeReadWrite | CFile::shareDenyWrite))
    {
        oAllocFile.SeekToEnd();
        for(size_t i=0; i<min(nLogBufferLines, LOG_BUFFER_SIZE); i++)
        {
            oAllocFile.WriteString(sLogBuffer[i]);
        }

        oAllocFile.Close();
    }
    else
    {
        // TODO: log that file cannot be accessed!!!
    }

    nLogBufferLines = 0;
}

void CMemLeakHunter::filterTrace()
{
    CStdioFile oAllocFile;
    if(TRUE == oAllocFile.Open(sMemAllocTracePath, CFile::modeRead | CFile::shareDenyWrite))
    {
        CString sFilterTraceFile;
        sFilterTraceFile.Format("filter_%s", sMemAllocTracePath);

        CStdioFile oFilterAllocFile;
        if(TRUE == oFilterAllocFile.Open(sFilterTraceFile, CFile::modeCreate | CFile::modeReadWrite | CFile::shareDenyWrite))
        {
            for (std::map<DWORD, size_t>::iterator it=mMemChunks.begin(); it!=mMemChunks.end(); it++)
            {
                CString addrHex;
                addrHex.Format("0x%08X", it->first);

                CString sLine;
                while(FALSE != oAllocFile.ReadString(sLine))
                {
                    if(sLine.Find(addrHex) > -1)
                    {
                        CString sLineWithNewline;
                        sLineWithNewline.Format("%s\n", sLine);
                        oFilterAllocFile.WriteString(sLineWithNewline);
                    }
                }
            }

            oFilterAllocFile.Close();
        }
        else
        {
            // TODO: log that file cannot be created!!!
        }

        oAllocFile.Close();
    }
    else
    {
        // TODO: log that file cannot be accessed!!!
    }
}

#endif // MEMORY_LEAK_FINDER
4

1 に答える 1

1

これを自分で行う必要はありません。

main.cpp に以下を含めます。

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

そしてあなたのメイン関数呼び出しの一番上に:

#ifdef _DEBUG
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

リークを検出するすべてのファイルについて、これを一番上に配置します。

#ifdef _DEBUG
   #ifndef DBG_NEW
      #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
      #define new DBG_NEW
   #endif
#endif  // _DEBUG

その後、検出されたリークは、アプリケーションの終了時にコンソールに出力されます。

ここを参照してください。

于 2014-12-04T16:16:40.327 に答える