几何尺寸与公差论坛

 找回密码
 注册
查看: 5490|回复: 5

查内存覆盖的经验,大家能不能总结一下?

[复制链接]
发表于 2007-8-5 21:25:43 | 显示全部楼层 |阅读模式
我的实践:
1,查找泄露:首先查找内存泄露,内存泄露往往能提供内存覆盖的线索,比如:应该释放A,结果错误的释放B,导致A泄露、B破坏。
2,使用工具:用Numega检查看能不能报错。
3,搜索法:查找delete,memcpy,free三个函数和线程相关函数,检查是否有问题;
4,猜测遍历法:根据对出问题代码的怀疑程度,依次删除这些代码,直到不再出现覆盖位置,找到对应的问题代码。
不知道这算不算方法
 楼主| 发表于 2007-8-5 21:26:13 | 显示全部楼层

回复: 查内存覆盖的经验,大家能不能总结一下?

1 定位被覆盖的内存地址
2 开始调试,在memory窗口中观察该地址内存的变化
3 若观察到在两个断点之间内存被覆盖了,则可以逐步缩小断点范围,找到发生覆盖的代码
4 可重点留意数组操作和字符串
5 如果是在一个循环中发生覆盖(例如在循环中写数组溢出),需要利用条件断点功能(当 i = xx 次的时候断住),省得你按很多次F5
 楼主| 发表于 2007-8-5 21:26:47 | 显示全部楼层

回复: 查内存覆盖的经验,大家能不能总结一下?

1,(内存泄漏)根据 vc ide debugger 的 memory leak dump 判断以及检查内存泄漏。有的时候由于

内存覆盖而无法得知文件名,这是可以查找所有的new, 根据 new 所在的代码行 比对 dump 确定泄漏

的位置。

2,(内存覆盖和内存泄漏)使用 numega boundschecker。

3,(内存泄漏)使用对象计数器。如果怀疑存在对象内存泄漏的话。在构造函数中计数器 ++, 在析

构函数中 对象计数器 -- 。

4,(内存覆盖和内存泄漏)进行大量反复的操作,用 task list 观察是否内存一直在增加。

5,防止内存泄漏的方法。
a)分配缓冲区的时候避免使用 new,而要使用 CArray 或者 vector 等动态数组。
b)一旦使用一个 new ,一定要立即考虑它的 delete。

// 原来是说内存覆盖阿。

6,内存覆盖严重的时候,往往导致系统异常,无法调试。这个时候需要采用 log file, console log

, beep 等方法。用大量的 print 输出,确定执行流,进而定位崩溃位置。采用下面代码定义log宏,

详见附录。
#if USE_SS_LOG_TRACE
#define SSLOG g_log.Log
#else
#define SSLOG 1 ? (void)0 : g_log.Log
#endif

7,内存覆盖的时候,往往会有崩溃位置不确定的问题。这是需要观察 vc ide debugger 输出的

exception 信息,那往往是发生内存覆盖的第一手消息。

8,可以采用 try catch(...) 的方法防止崩溃的一塌糊涂,并且可以一步步缩小包围圈,精确定位代

码。

9,对于怀疑的数据,在程序执行流中频繁调用合法性检查的函数。对于整数,根据程序的意义确定一

个合理范围,比如 0 ~ 100000。对于浮点数可以采用 _isnan(), _finite() 等函数检查合法性,在浮

点数合法的情况下,要进一步检查取值范围的合法性。字符串数据则需要检查它的长度,并且输出其内

容,供观察。 一旦检查代码 发现数据的问题,就立即用ASSERT报告。这样可以一步步顺着执行流往前

推,直到定位到有问题的代码。除了对被怀疑数据进行自动代码的检查之外,还可以输出。见上面一条

(log)。

10,对于怀疑的内存越界,特别是数组越界,可以在那个数组后面定义一个缓冲数组。在被怀疑有破坏

作用的代码之前,初始化缓冲数组,在破坏代码之后,检查缓冲数组,一旦有问题,就立即报告。一步

步顺着执行流往前推,直到定位到有问题的代码。





附录:

1,log 类,可以方便的进行log
#define USE_SS_LOG_TRACE 1

class SSLog
{
public:
void Log(LPCTSTR lpszFormat, ...);
int32 Open();
SSLog();
virtual ~SSLog();

static void CALLBACK FlushBuf(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);

static FILE *m_pFile;
};

#if !USE_SS_LOG
#undef USE_SS_LOG_TRACE
#define USE_SS_LOG_TRACE 0
#endif

#if USE_SS_LOG_TRACE
#define SSLOG g_log.Log
#else
#define SSLOG 1 ? (void)0 : g_log.Log
#endif



FILE *SSLog::m_pFile;

SSLog::SSLog()
{
m_pFile = NULL;
}

SSLog::~SSLog()
{
if(m_pFile)
{
Log("#1 ss dll terminated.\n");
fclose(m_pFile);
}
}

/**
#1 ÆÕͨlog
#2 ¸¡µã¼ÆËã·½³Ì×éµü´úlog
#3 graph solver
#4 SSketchSolver::Solve entry
#5 ÑÏÖØ´íÎó
#6 explode numerical vars
#7 time out control
#8 cluster scale log
#9 whole time consumed in one solving
#10 ÑſɱȾØÕóÄÚ´æ·ÖÅä
#11 ASSERT(jude<2);
*/

int32 SSLog::Open()
{
#if USE_SS_LOG
if(!m_pFile)
{
/*
Controls stream buffering and buffer size.
int setvbuf( FILE *stream, char *buffer, int mode, size_t size );
*/


CString strFile = "\\\\Urtsoftserver\\development\\&frac14;&macr;&sup3;&Eacute;\\&Euml;&iuml;&Ntilde;&copy;&Ccedil;à\\sslog\\";
TCHAR pszName[MAX_PATH];
DWORD dwSize=MAX_PATH;
GetComputerName(pszName, &dwSize);
pszName[dwSize] = 0;
strFile = strFile + pszName + ".txt";
if(!m_pFile)
m_pFile = fopen(strFile, "a");

if(m_pFile)
{
setvbuf(m_pFile, NULL, _IOFBF, 2048);
}

CTime time = CTime::GetCurrentTime();
CString strTime = time.Format("%Y-%m-%d %H:%M:%S");
Log("#1 ss dll started (%s) s3k Build %d (%s) ----------------------\n±&auml;&Aacute;

&iquest;&Ecirc;&yacute;\t·&frac12;&sup3;&Igrave;&Ecirc;&yacute;\t&micro;ü&acute;ú&acute;&Icirc;&Ecirc;&yacute;\t&micro;ü&acute;ú&Ecirc;±&frac14;&auml;\n",
(LPCTSTR)strTime,
YA_BUILD_NO,
__TIMESTAMP__);

SetTimer(NULL, 12, 2000, FlushBuf);
}
#endif
return TRUE;
}

void SSLog:og(LPCTSTR lpszFormat, ...)
{
// static int32 cc;
// CONOUT("log cc %d\n", cc++);
if(m_pFile)
{
va_list args;
va_start(args, lpszFormat);

int nBuf;
TCHAR szBuffer[512];

nBuf = _vsntprintf(szBuffer, 512, lpszFormat, args);
if(nBuf<0)
{
fputs(lpszFormat, m_pFile);
}
else
{
// was there an error? was the expanded string too long?
ASSERT(nBuf >= 0);
fputs(szBuffer, m_pFile);
}

va_end(args);
}
else
{
CONOUT("error, no log file did not open.");
}

// CONOUT("log end.\n");
}

void CALLBACK SSLog::FlushBuf(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
if(m_pFile)
{
fflush(m_pFile);
}
}
 楼主| 发表于 2007-8-5 21:28:30 | 显示全部楼层

回复: 查内存覆盖的经验,大家能不能总结一下?

1.查系统回调函数的调用规则是否正确,__cdecl和__stdcall的清栈角色不同有可能造成访问违规
2.用折半法确认出错位置,出错位置前对该内存的操作都可疑,在分配该内存处打断点,记下分配的内存地址,新建断点,新断点条件是该内存发生变化即断下
3.检查多线程访问变量的互斥和同步
4.若使用了别的dll的导出数据(导出全局变量是没有模块引用计数的),dll是否被提前释放?
5.是否多次delete同一变量,带[]的和不带的配套
6.crt是否是正确的线程版本



养好写代码习惯,分配内存后马上写释放内存的代码,以保证代码永远是健康的可测的.若分配和释放之间有多个返回点(return,exception),可新建ResourceHelper,利用ctor和dtor进行资源管理
发现有错误时,马上停止手头工作,把bug解决了再说
发表于 2008-12-27 22:10:23 | 显示全部楼层

回复: 查内存覆盖的经验,大家能不能总结一下?

经验总结:
设条件断点在heap allocation的位置即可。
发表于 2012-8-24 07:14:36 | 显示全部楼层

回复: 查内存覆盖的经验,大家能不能总结一下?

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|Archiver|小黑屋|几何尺寸与公差论坛

GMT+8, 2024-4-27 06:15 , Processed in 0.039323 second(s), 19 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表