📄 195.txt
字号:
//tempstruct0.data为数据库中为Max2B1QStatPortNum配置的值,如果其缺省值和Ma
x2B1QStatTime乘积值大于MAX_2B1Q_STAT_PSN的话:
if ( (tempstruct0.data * Max2B1QStatTime) > MAX_2B1Q_STAT_PSN )
Max2B1QStatPortNum = MAX_2B1Q_STAT_PSN / Max2B1QStatTim
e;
//如果在合理范围内且不为0的话:
else if ( tempstruct0.data != 0 )
Max2B1QStatPortNum = tempstruct0.data;
}
此处if-else if 分支没有判断 值为0的情况,即数据库为Max2B1QStatPortNum配
置的值为0: tempstruct0.data == 0,则Max2B1QStatPortNum就为缺省值32。
【结 论】
由于内存限制,Max2B1QStatTime(最大统计时间)和Max2B1QStatPortNum(最大
统计端口数)的乘积不能大于MAX_2B1Q_STAT_PSN,
如果从数据库中得到Max2B1QStatTime为MAX_2B1Q_STAT_PSN,而数据库中最大统
计端口数恰好为0,由于上述代码没有对tempstruct0.data == 0的情况进行判断,Max2B1
QStatPortNum为缺省值32,这样Max2B1QStatTime和Max2B1QStatPortNum乘积已经是32倍M
AX_2B1Q_STAT_PSN了,远远超过了设计内存的限制。
造成这种错误的原因是判断语句对条件判断不完整。
【思考与启示】
在代码审查时,应该十分注意条件判断的的完备性。好多问题就是因为条件判断
不完全造成的。
9、引用已释放的资源
【案例1.9.1】
【正 文】
在计费测试的过程中,用呼叫器进行大话务量呼叫测试。30路话路通过TUP自环呼叫另外
30路话路,计费数据的设定是这样的:通过计费情况索引对主叫计费,得到详细话单。首
先保证计费数据设定的正确性,打了几次自环电话后,查看话单正常,则开始呼叫。
呼叫几万次后停止呼叫,取话单进行观察。发现这30路每次呼叫总会出现一张告警话单,
其余话单正常,该告警话单相对于话路来说是随机出现的。
通知开发人员后,首先我们再次对计费数据进行了确认。某个用户在某次呼叫产生了告警
话单,其上一次和下一次呼叫的计费情况都正常,两次呼叫之间的时间间隔只有几秒钟,
排除了人为修改数据的可能。开发人员认为是CCB的问题,后来一查果然如此。
当中继选线发生了同抢需要重新选线时,CCB的reset_CCB_for_reseatch_called_locati
on()就会把有关的呼叫信息清掉,造成计费情况分析失败,产生计费费用为0的告警话单。
更正reset_CCB_for_reseatch_called_location()中清除被叫信息的代码,重选中继时不
清除被叫用户这部分属性。
思考与启示:
1、在计费测试过程中,对话单的观察很重要,不应该放过任何一个细小的疑点;
2、计费测试仅仅打几次电话往往达不到效果,越接近用户实际使用的情况越可能发现问
题。
【案例1.9.2】
【案例描述】
在进行128模块V5用户CENTREX新业务测试时,偶然遇到一个怪现象:对群内一个
V5ST用户只开放MCT权限,在进行恶意呼叫追查时,有一次报恶意呼叫追查成功音只报了一
半,当正要报出恶意呼叫的号码时,业务中断重新回到通话态,随即重新追查一次,报“
已申请其它新业务,本次申请不成功”。恶意呼叫追查与任何新业务都不会冲突,而且此
用户也只有恶意呼叫追查有权,可以肯定此时程序出问题了。为了重现,再次挂机,重新
呼叫,应用此新业务,但这个现象一直没有出现。大约反复操作20遍,又出现了一次这样
的情况,显然程序中可能存在某种问题。
【处理过程】
出现这个问题后,及时与开发人员A取得了联系,并一起试图重现这个问题,通过
许多次的反复操作,又出现了一次这种情况。确认问题后,A表现出高度的责任心,马上驾
调试环境,反复调测,终于在当天就逮住了狐狸尾巴:
1、当用户接听恶意呼叫者的电话, 并启动恶意呼叫追查业务后, 在V5_CR_VOICE
TONE状态下, 只要听MCT音的用户用脉冲方式拨任意一个数字, 则立即停止送MCT音, 而将
用户切换回与恶意呼叫者的通话. 但是程序中没有对拨号类型作判断, 导致用户若用音频
拨号也会作同样的处理。
2、除了取消此次MCT服务, 将用户切换回与恶意呼叫者的通话外, 如果不释放M
CT_HANDLE, 由于每个模块只有一个这样的资源, 则下一次使用MCT业务的用户不能成功,
因为会在申请MCT_HANDLE时失败, V5模块和ST模块在这个地方处理都有问题, 没有将MCT
_HANDLE释放掉, 对于V5用户会听新业务失败音, 对于ST用户会听音乐。
当不停的拨测V5用户的MCT业务时, 有时在听音时, 可能由于网板有杂音等原因(
或用户碰了话机的按键), 导致DTR收到一位号, 则会立即停止此次MCT服务, 用户会听到
MCT送音突然中断, 然后恢复了与恶意呼叫者的通话. 而下次再用MCT时, 由于上面所述的
原因, 会听到新业务失败音, 此次失败后, 无论MCT_HANDLE分配成功与否, 该用户的MCT标
志都被置为1, 所以在用户挂机时, 会将该模块唯一的MCT_HANDLE资源释放掉. 则以后该功
能又可以正常实现。
在追查这个问题时,开发人员A又发现了一个可能导致死机的严重问题:在用户启
动MCT服务, 正在听报追查号码的MCT音时, 若恶意用户此时挂机, CCB的处理中, 只针对
ST用户送DISCONNECT, 而对V5ST用户送的是RELEASE消息, 这导致V5X收到此消息后, 将该
V5ST用户的cr2清除掉, V5_USER_TALBE[ ]. cr2变为0xFFFF, 这样在V5_CR_VOICETONE超
时后, 程序中会检查cr2的状态是否为HOLD, 当取cr2的内容时, 由于cr2已被清除, 会发
生指针越界的GP错误。
【结 论】
通过调测发现、定位并解决问题。
【思考与启示】
我们平常一些熟视无睹的业务或按正常流程操作没有问题的业务,不能保证它就
一定没有问题,要善于抓住一丝一毫的异常现象。对于很难重现的问题千万不要轻易放过
,我们网上设备所出的问题很多都是一些在实验室难以出现或很难重现的一些问题,一些
显而易见的问题一般都可消灭在实验室,难就难在消灭一些隐藏很深的问题。说老实话,
我们的产品还有许多问题 ,需要我们扎扎实实锲而不舍的工作。
10、分配资源是否已正确释放
【案例1.10.1】
【正 文】
在对接入网A产品的网管软件测试中,发现了一个WINDSOWS资源损耗的的问题:
当网管软件运行几天后,WINDOWS总会出现“资源不够”的告警提示。如果网管软件不关掉
再重新启动的话,就会出现WINDOWS资源完全耗尽的现象,最终网管系统反应很慢,无法正
常工作。
从现象上可以判断出,网管软件存在隐蔽的内存泄露或资源不释放的问题,并且这种资源
耗尽是一个缓慢的过程。如何定位这个问题呢?
定位这种问题可以利用WINDOWS中的一个系统资源监视工具。打开Windows的“附件/系统
工具/资源状况”,这是一个系统资源、用户资源、和GDI资源的实时监视工具。
工具有了,那么如何发现导致不断消耗资源的特定操作呢?
首先和开发人员共同探讨,列出几个最可能消耗资源的操作和一些操作组合,这样就缩小
了监视范围,避免没有范围的碰运气,否则如大海捞针。
监视前,首先重新启动WINDOWS,最好不运行其他的程序,打开“系统状况”这个监视工
具,然后运行网管软件,记下此时的资源状况数据。
然后针对一个可疑的操作,快速大量地重复进行。这种重复性的操作可以利用QArun测试
工具执行,QArun可以记录操作者的一次操作步骤,然后按照设定的次数重复操作。操作后
,观察此时的资源状况,并记下此时的数据,与操作前的数据比较,如果操作前后的数据
数据没有变化或变化很小,可排除此项操作,否则就可断定此项操作会引起资源耗尽。
对其它可疑的操作和操作组合重复以上过程。
通过以上的步骤,终于找出引起资源耗尽的罪魁祸首。分析相应部分的代码,发现引起资
源耗尽原因有:内存泄露,画笔和画刷资源用完后未释放等。
【案例1.10.2】
【正 文】
某产品后台软件版本,是用C++写的,程序员在写代码时,经常在构造函数中申请一
块内存,而不释放,在程序其他代码中也经常只管申请,不管释放。
例如:
void WarnSvr::SaveWarnData()
{
......
for(int m=0;m<RecordsInBuffer[EVENT_ALARM];m++)
{
HISTORY_FILTER_INDEX* item=
new HISTORY_FILTER_INDEX;
item->Csn=Buffer[EVENT_ALARM][m].Csn;
item->Position=m
+(RecordsInHistoryFile-RecordsInBuffer[EVENT_ALARM]);
//If a warn with a certain Csn is not in EventFilterIndex
//it is not necessary to be added to HistoryFilterIndex
int item_total=EventFilterIndex.GetItemsInContainer();
BOOL find_flag=false;
for(int k=0;k<item_total;k++)
if(EventFilterIndex[k]->Csn==item->Csn)
{
find_flag=true;
break;
}
if(find_flag)
{
HistoryFilterIndex.Add(item);
if(HistoryFilterIndex.IsFull())
ClearIndexEntry();
}
//建议在此处加上:
// else
// delete item;
}。
有的程序员认为,后台运行的环境有大量内存,几个字节的浪费不会造成死机等
重大事故。然而,长时间累计起来,必然会造成资源紧张而出现故障。
实际上,这种思想是造成我们产品不稳定的原因之一。我们的主机在网上能运行
几个月几年,大家对内存的分配释放较敏感,而我们的后台产品往往只能正常运行几天。
这个地方不注意也是原因之一吧。
【案例1.10.3】
【正 文】
在进行代码审查过程中,造成内存泄漏的代码比较多。下面举几种常见的内存泄漏错误,
供测试人员在代码审查中参考:
1. 函数有多个出口时,没有在每个出口处对动态申请的内存进行释放。一般在异常处理
时容易出现这种错误。下面的代码段就是这样的例子:
.....
pRecord = new char[pTable->GetRecordLength()];
assert(pRecord != NULL);
if (pTable->GoTop(FALSE) != DBIERR_NONE)
return; // 如果从这里返回,pRecord将得不到释放
.....
pTable->Close();
delete[] pRecord;
}
2. 给指针赋值时,没有检查指针是否为空,如果指针不为空,那么指针原来指向的内存
将丢失。请看如下代码段:
....
struct FileInfo * pdbffile = new struct FileInfo;
pdbffile->pfileinfo = new struct ffblk;
pdbffile->srcname = srcRootPath;
pdbffile->desname = desRootPath;
pdbffile->prev = NULL;
pfile = pdbffile;
//赋值之前没有检查一下pfile是否为空,如果不为空,会造成pfile指向的内存丢失。
dbf_start_needed = FALSE;
dbf_Finish = FALSE;
flag_begined = TRUE;
if(FALSE == Copy(TRUE))
{
dbf_start_needed = TRUE;
WarnMsgOut("Error occurs while copying files in directory <dbf>,trying again
.");
}
}
3. 连续二次内存动态分配,在第二次分配失败时,忘记释放第一次已经申请到的内存。
....
pMsgDB_DEV = (PDBDevMsg)GetBuff( sizeof( DBDevMsg ), __LINE__);
if( pMsgDB_DEV == NULL )
return;
pMsgDBApp_To_Logic = (LPDBSelfMsg)GetBuff( sizeof(DBSelfMsg), __LINE__ );
if( pMsgDBApp_To_Logic == NULL )
return;//此处返回造成pMsgDB_DEV指向的内存丢失
....
4.代码中缺少应有的条件分支处理,导致程序未执行任何操作而退出时,也可能没有释放
应释放的内存,这种情况一般是缺少应有的else分支,或switch语句的default分支没有应
有的处理。
static void OncePowerCmdHandle( struct HT_Appmsg * msg )
{
... ...
pPower_test_answer =(struct _oncepower_test_answer *)GetBuff(sizeof(struc
t _oncepower_test_answer),__LINE__);
if( pPower_test_answer == NULL_PTR )
return;
... ...
if (TSS_State[testpsn].state == TEST_DEV_BUSY ||
TSS_State[testpsn].state == TEST_DEV_ERROR )
{...
}
else if (TSS_State[testpsn].state == TEST_DEV_IDLE )
{...
}
// 缺少 else 分支,可能造成 pPower_test_answer 得不到释放
}
造成内存泄漏的情况很多,以上是几种典型的情况。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -