2015-02-24 31 views
1

我有一個非常大的項目。我試圖監視分配和釋放的內存。這是我試過的示例程序。但是,我看到它只是打印新的函數名稱,我理解。問題是如何打印函數名稱,調用者的行號。如何在重載的新操作符中打印調用者函數名稱?

main.cpp

#include <QtCore/QCoreApplication> 
#include <cstdlib> 
#include <stdio.h> 
#include <fstream> 

#include <memOperation.h> 
#include <DumpMemory.h> 

#define BUFFER (4) 

class MemPlay; 

#define LOG_STRING()\ 
{\ 
    std::ofstream dumpfile; \ 
    dumpfile.open("/export/home/joshis1/DBG_REC.log"); \ 
    dumpfile<<"FUNC = "<<__FUNCTION__<<"LINE = "<<__LINE__<<std::endl; \ 
    dumpfile.close(); \ 
} 

void* operator new(std::size_t sz) 
{ 
    void *mem = std::malloc(sz + BUFFER); 
    memset(mem+sz,'PACE',4); 
    LOG_STRING(); 
    return mem; 
} 

void operator delete(void* ptr) 
{ 
    std::free(ptr); 
} 


int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    MemPlay *pMemPlay1 = new MemPlay(); 
    pMemPlay1->MyMemPlay(); 


    return a.exec(); 
} 

memOperation.h

#include "QDebug" 

class MemPlay 
{ 
public: 

    void MyMemPlay() 
    { 
     qDebug()<<"My Mem Play"; 

     char *t = new char[10] ; 

     strcpy(t,"SHREYASJOSHI_SAYS_HELLO_WORLD_AND_CORRUPTS_MEMORY"); 

    } 

    void FreeMemPlay(void *t) 
    { 
     delete t; 
    } 

}; 

這是錯誤的結果 -

FUNC = operator newLINE = 25 
+0

可移植的,你不能。如果您使用的是GNU,請使用[backtrace](http://linux.die.net/man/3/backtrace_symbols)。 – 2015-02-24 11:55:07

+2

您可以使用內存分析器(例如[valgrind](http://valgrind.org/docs/manual/manual.html))來查看內存分配/重新分配 – 2015-02-24 11:58:18

+0

重載類特定的'operator new',而不是全局的: https://www.relisoft.com/book/tech/9new.html – 2015-02-24 12:05:04

回答

0

對於Windows的Microsoft C++編譯器,你可以用函數名,文件名,行號和使用CaptureStackBackTrace呼叫功能的地址和回溯微軟debug help library

#include <cstdio> 
#include <Windows.h> 
#include "dbghelp.h" 

using namespace std; 

#define TRACE_MAX_STACK_FRAMES 1024 
#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024 

int printStackTrace() { 
    void *stack[TRACE_MAX_STACK_FRAMES]; 
    WORD numberOfFrames = CaptureStackBackTrace(0, TRACE_MAX_STACK_FRAMES, stack, NULL); 
    SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR)); 
    symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH; 
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO); 
    DWORD displacement; 
    IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64)); 
    line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); 
    for (int i = 0; i < numberOfFrames; i++) { 
    DWORD64 address = (DWORD64)(stack[i]); 
    SymFromAddr(process, address, NULL, symbol); 
    if (SymGetLineFromAddr64(process, address, &displacement, line)) { 
     printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address); 
    } else { 
     printf("\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address); 
    } 
    } 
    free(symbol); 
    return 0; 
} 

void function2() { 
    int a = 0; 
    int b = 0; 
    throw new exception; 
} 

void function1() { 
    int a = 0; 
    function2(); 
} 

void function0() { 
    function1(); 
} 

int main(int argc, char* argv[]) { 
    HANDLE process = GetCurrentProcess(); 
    SymInitialize(process, NULL, TRUE); 
    function0(); 
    return 0; 
} 

此示例代碼基於問題How can you use CaptureStackBackTrace to capture the exception stack, not the calling stack?

0

在我的機器上,一次修改後的代碼導致26503條記錄(new調用)與QtAplication,2042條記錄 - 不使用Qt

這些操作的目視檢查是無用的。使用valgrind你可以確定有多少內存泄漏,什麼導致了泄漏,什麼導致了內存損壞,從未分配的內存中讀取垃圾等。

另外,你可以重載操作符new/delete你選擇的幾個類es)知道什麼時候創建/移除了這些對象。這些信息可以存儲在文件中,然後進行檢查。

修改後的代碼:

main.cpp

//#include <QtCore/QCoreApplication> 
#include <cstdlib> 
#include <stdio.h> 
#include <fstream> 
#include <iostream> 

#include <memOperation.h> 

void* operator new(std::size_t sz) 
{ 
    void *mem = std::malloc(sz + BUFFER); 
    memset(mem+sz,'0',BUFFER); 
    LOG_STRING(); 
    return mem; 
} 

void operator delete(void* ptr) 
{ 
    std::free(ptr); 
} 

int main(int argc, char *argv[]) 
{ 
    //QCoreApplication a(argc, argv); 

    MemPlay *pMemPlay1 = new MemPlay(); 
    pMemPlay1->MyMemPlay(); 
    std::cout<<"Memplay at "<<pMemPlay1<<" size "<<sizeof(*pMemPlay1)<<" trailing "; 
    puts((char*)(pMemPlay1)+sizeof(*pMemPlay1)); 

    return 0; 
    //return a.exec(); 
} 

memOperation.h

#ifndef MEMOPERATION_H 
#define MEMOPERATION_H 

#include "QDebug" 

#define BUFFER (4) 

#define LOG_STRING()\ 
{\ 
    std::ofstream dumpfile; \ 
    dumpfile.open("DBG_REC.log", std::ofstream::out | std::ofstream::app); \ 
    dumpfile<<"FUNC = "<<__FUNCTION__<<"LINE = "<<__LINE__<<std::endl; \ 
    dumpfile.close(); \ 
} 

class MemPlay 
{ 
public: 

    void MyMemPlay() 
    { 
     std::cout<<"My Mem Play char * "; 

     char *t = new char[10]; 

     //strcpy(t,"SHREYASJOSHI_SAYS_HELLO_WORLD_AND_CORRUPTS_MEMORY"); 
     std::cout<<" trailing "; 
     //puts(t); 
     puts(t+10); 

     FreeMemPlay(t); 
    } 

    void FreeMemPlay(char *t) 
    { 
     delete t; 
    } 

    void * operator new (std::size_t sz) 
    { 
     void *mem = std::malloc(sz + BUFFER); 
     memset(mem+sz,'1',BUFFER); 
     LOG_STRING(); 
     return mem; 
    } 
    void operator delete (void * mem) 
    { 
     if (mem) 
      std::free(mem); 
    } 

}; 

#endif // MEMOPERATION_H 
1

如果你只關注跟蹤new/delete操作,重載全局new操作不僅是不是必要和矯枉過正,但是引入了這麼多額外的麻煩,一個甚至不能開始理解。

對於(全球與否)新/刪除的恰當超載,這裏有一些資源:

還注意到正當理由超載新的/刪除運營商:

最乾淨的解決辦法是爲你寫你自己的new/delete -wrapper宏/功能,並替換的new/delete所有出現在源代碼中。例如:

#define NEW(T, ...) traced_new<T>(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) 

template <typename T, typename... Args> 
T* traced_new<T>(std::string file, unsigned long line, std::string func, Args&&... args) 
{ 
    // log ... 
    return new T { std::forward<Args>(args)... }; 
} 

如果你想避免更換新/刪除的來源,你仍然可以注入了宏跟蹤代碼new

#include <iostream> 
#include <string> 

struct traced_new_tag_t {}; 
constexpr traced_new_tag_t traced_new_tag; 

void* operator new (std::size_t n, traced_new_tag_t, std::string file, unsigned long line, std::string func) 
{ 
    void* const p = operator new(n); 
    std::cerr << file << ':' << line << ": " << func << " allocates " << n << " bytes at " << p << "\n"; 
    return p; 
} 

void* operator new[] (std::size_t n, traced_new_tag_t, std::string file, unsigned long line, std::string func) 
{ 
    void* const p = operator new[](n); 
    std::cerr << file << ':' << line << ": " << func << " allocates [] " << n << " bytes at " << p << "\n"; 
    return p; 
} 

#define new new(traced_new_tag, __FILE__, __LINE__, __FUNCTION__) 

int main (int, char**) 
{ 
    long long *p0, *p1; 
    std::cout << (p0 = new long long) << '\n'; 
    std::cout << (p1 = new long long [3]) << '\n'; 
    return 0; 
} 

打印:

t.cpp:26: main allocates 8 bytes at 0xbf9070 
0xbf9070 
t.cpp:27: main allocates [] 24 bytes at 0xbf9090 
0xbf9090 

這已經引入了額外的麻煩,特別是如果operator new拋出會發生什麼情況。你將如何處理?另外,這個例子還沒有完成,因爲沒有對於非拋出使用的超載/宏使用newnew(std::nothrow))。

感謝Mike西摩指出這一點)也有明顯的額外麻煩#define new必須非常仔細作用域隻影響你的源代碼,以及之後的任何聲明的定義。看到他對額外恐怖的評論。

即使採用這種方法,仍然需要包裝釋放/刪除操作,因爲delete操作符在表達式語法中無法接收額外的參數。

總而言之,這是一個非常骯髒的黑客,我不會推薦它。

最後,如果您決定實際上重載全局新建/刪除,請確保您閱讀完好。然後,您可以通過對「主叫信息」 /「呼叫者姓名」,像這樣的以下建議跟蹤調用函數中有:

+0

'#define new'會導致未定義的行爲;具體來說,它將破壞任何使用或聲明operator new的代碼,或者使用placement-new表達式,包括多個標準庫頭。請不要這樣做。 – 2015-02-24 18:02:09

相關問題