我有一个非常大的项目。我正在尝试监视分配和释放的内存。这是我试过的示例程序。但是,我看到它只打印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
答案 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
抛出会发生什么。你会怎么处理?此外,此示例未完成,因为new
(new(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