如何在重载的new运算符中打印调用函数名?

时间:2015-02-24 11:51:01

标签: c++ new-operator

我有一个非常大的项目。我正在尝试监视分配和释放的内存。这是我试过的示例程序。但是,我看到它只打印new的函数名,我理解。问题是如何打印函数名称,调用者的行号。

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

3 个答案:

答案 0 :(得分:1)

如果您唯一关心的是跟踪new / delete次操作,那么重载全局new操作不仅必要且过度杀伤,而且会引入太多额外的麻烦,人们甚至无法开始理解。

为了正确重载(全局或非全局)新/删除,以下是一些资源:

另请注意重载新/删除运算符的正当理由:

最干净的解决方案是让您编写自己的new / delete - 包装器宏/函数,并替换源中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 Seymour 指出这一点)还有一个显而易见的额外麻烦,#define new必须非常仔细地确定只影响你的源代码,以及任何声明之后定义。看到他的评论是为了额外的恐怖。

即使采用这种方法,您仍然需要包装解除分配/删除,因为delete运算符无法在表达式语法中接收额外的参数。

总而言之,这是一个非常肮脏的黑客,我不会推荐它。

最后,如果你决定实际重载全局新/删除,请确保你读得很好。然后,您可以通过以下有关“来电者信息”/“来电者姓名”的建议来跟踪其中的来电者功能,例如:

答案 1 :(得分:0)

对于使用Microsoft c ++编译器的Windows,您可以使用CaptureStackBackTrace和Microsoft的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?

答案 2 :(得分:0)

在我的计算机上,一次修订后的代码产生了26503条记录(new次调用),其中QtAplication条记录为2042条记录,而未使用Qt

目视检查这些操作是没用的。使用valgrind,您可以确定泄漏了多少内存,导致泄漏的原因,导致内存损坏的原因,从未分配的内存中读取垃圾等等。

此外,您可以重载运算符new / delete您选择的几个类,以便在创建/删除对象时知道。此信息可以存储在文件中,然后进行检查。

修订代码:

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