有没有办法在C ++中调用new时获取堆栈跟踪?

时间:2012-06-15 19:44:11

标签: c++ memory-management gcc valgrind

我有一个长期运行的C ++程序,通常用gcc(g ++)编译。我使用valgrind来验证没有内存泄漏,所以我不是在寻找泄漏检测器。

但我关注的是临时缓冲区/对象上的内存碎片和不必要的新/删除对。

有没有办法记录所有对new的调用(即使它们发生在STL容器内),提供堆栈跟踪以便我可以在代码中搜索它们?我尝试过mtrace,但这只适用于C ++ - 它最终说当我查找负责的代码行时,所有分配都发生在全局新的分配器中。不知怎的,valgrind的memcheck几乎可以做我想要的,因为它显示了内存分配的堆栈跟踪。不幸的是,它们似乎只是在没有匹配解除分配的情况下为分配而呈现。

2 个答案:

答案 0 :(得分:6)

如果您想跟踪有关分配的其他统计信息,您可以随时覆盖全局新/删除功能:

void* operator new (size_t size)
{
    void *pPtr = alloc_memory(size); /* perform the allocation here but don't use new! */

    if(pPtr == 0)
        throw std::bad_alloc();

    /* additional code here to do whatever sort of tracking you want */
    return pPtr;
}

void operator delete (void *pPtr)
{
    if(pPtr == 0)
        return; // legal to call delete on NULL pointers - don't pass NULL to free()

    /* additional code to do whatever tracking you want here */
    free(pPtr); 
}

至于获得回溯,那就是编译器和O / S依赖,并且没有标准的方法来实现它。由于您提到GCC,以下内容可能对您有用:

http://tombarta.wordpress.com/2008/08/01/c-stack-traces-with-gcc/

答案 1 :(得分:1)

我赞成Nik B.'s answer指向我正确的方向,而这正是我实际使用libunwind做的事情,因为链接的堆栈跟踪建议只能获取链接库的函数名称。此代码可在GitHub的https://github.com/landtuna/opnew-stacktraces

上找到

newdelete.cpp:

#include <exception>
#include <new>
#include <cstdlib>
#include <iostream>

#include "stacktrace.hpp"

void* operator new (size_t size) {
  void* p = malloc(size);

  if (p == 0) {
    throw std::bad_alloc();
  }

  std::cout << "allocated " << size << std::endl;
  printTrace(std::cout);

  return p;
}

void operator delete (void* p) {
  free(p);
}

stacktrace.cpp:

#include <cxxabi.h>
#include <libunwind.h>
#include <ostream>
#include <cstdlib>
#include <cstring>
using namespace std;

#include "stacktrace.hpp"

void printTrace(ostream& out) {
  unw_cursor_t cursor;
  unw_context_t context;

  unw_getcontext(&context);
  unw_init_local(&cursor, &context);

  while (unw_step(&cursor) > 0) {
    unw_word_t offset, pc;
    char fname[200];
    size_t demangledSize = 200;
    char* demangled = (char*) malloc(demangledSize);

    unw_get_reg(&cursor, UNW_REG_IP, &pc);
    fname[0] = '\0';
    unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);

    int status;

    char *ret = abi::__cxa_demangle(fname, demangled, &demangledSize, &status);
    if (ret) {
      // return value may be a realloc() of the input
      demangled = ret;
    }
    else {
      // demangling failed, just pretend it's a C demangled with no args
      strncpy(demangled, fname, demangledSize);
      strncat(demangled, "()", demangledSize);
      demangled[demangledSize-1] = '\0';
    }

    out << hex << demangled << "+0x" << offset << " [" << pc << "]" << dec << '\n';
    free(demangled);
  }
  out << endl;
}