4

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

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

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

4

2 回答 2

6

因此,如果您想跟踪有关分配的其他统计信息,您始终可以覆盖全局 new/delete 函数:

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/

于 2012-06-16T08:58:19.450 回答
1

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

新删除.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);
}

堆栈跟踪.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;
}
于 2012-06-18T18:31:58.087 回答