📄 195.txt
字号:
【处理过程】:处理过程如下:
#define MOD128 127 //队列长128,当队头到128时,上其返回。
#define W_MOD 127 //发送窗口队列,意义同上。
在函数L2_TO_L1()中,有如下语句:
linkstate_ptr->SendWin.head = (head + 1) % W_MOD ;
这里当head=126时,SendWin.head = 0,这将造成发送窗口指针和队列窗口指针错位,造
成链路复位;
另外,在重发函数void INVOKE_RETRANSMISSION(_US logic_link,_US n_r)中,有如下语
句:
retran_num = (LinkState[logic_link].Vs + MOD128 - (_UC)n_r) % MOD128
;
w_head = (LinkState[logic_link].SendWin.head + W_MOD - retran_num) %
W_MOD ;
第一个语句求欲重发的消息包个数,第二个语句求重发的起始位置,当Vs小于n_r时,将造
成实际重发数小于欲重发数,同时造成实际起始重发位置和欲重发起始位置错开,从而引
起链路复位。上面三个语句应该做如下改动:
linkstate_ptr->SendWin.head = (head + 1) & W_MOD ;
retran_num = (LinkState[logic_link].Vs + MOD128 + 1 - (_UC)n_r) & MO
D128 ;
w_head = (LinkState[logic_link].SendWin.head + W_MOD + 1 - retran_num)
& W_MOD ;
【结 论】:由于链路通信对系统效率要求很高,算法采用效率最高的,但位与(&)和求
模(%)这小小的区别,造成的竟是链路复位这种严重的错误。
【思考与启示】:对这类问题,大家在阅读代码或代码审查时一定要注意,仔细一点往往
能发现问题,但在测试中来定位这种问题,花费的时间往往更长。
6、注意数据类型的匹配
【案例1.6.1】
【案例描述】
下面通过测试中的一个例子来说明这个问题:命令DSP N7C是用来显示NO7电路状态的,其
参数设备类型DID支持TUP和ISUP,参数信道号BSN支持多值输入(最多支持32路查询),正
常情况下该命令没有问题。但试了非正常情况下,问题就出来了。
1、首先试BSN参数越界情况,即参数BSN超过32路查询,选了几个数据段,问题就出来了
。对于0&&300和0&&256,该命令返回结果不一致,对前者认为参数越界,对后者返回执行
成功。
2、对于参数DID,选定一种设备类型(TUP或ISUP),让参数BSN所包含的32路电路跨越T
UP和ISUP,两次结果是不一致的。
【处理过程】
反馈到开发人员那里,第一个问题是BAM的问题,第二个问题是SM的问题。
【结 论】
1、为数据超出范围溢出造成,int值赋值给BYTE,造成数据丢失。
2、问题的产生是因为查询的第一个信道是TUP电路,但是却按ISUP电路查询。ISUP的维护
处理函数判断第一个信道不是ISUP信道,认为整个的PCM不是ISUP类型的PCM,返回全部的
电路状态为未安装。消息处理不合理。TUP也会产生如此错误。
【思考与启示】
我们的MML命令并不是无懈可击的,许多表面上的小问题,往往隐藏着代码的缺陷和错误
。
【案例1.6.2】
【正 文】
当我们使用PC-LINT检查代码时,会发现大量的数据类型不匹配的告警,大部分情况下,这
种代码上存在的问题并不会引起程序功能实现上的错误,但有些情况下,也许会产生严重
的问题:
一、不同数据类型变量之间赋值引起的问题,实际上,该类问题也可以分为几种情
况:
1、直接赋值,比如,把一个WORD型变量赋给一个INT型变量,如果WORD型变量大于32767,
INT型变量得到的就是一个负值了。
【例一】一次测试过程中发现,SDH送的告警在BAM调试窗口打印出红色提示:File(XXX),
Line(XXX):Invalid alarm id ,from: 7, AlarmId: 65463
经过检查数据发现,并没有ID为65463的告警,分析上报的数据帧,发现上报的告警ID为B
7,原来代码中有一处强制类型转换:
sdhAlmStru.AlarmId = (WORD)RecvBuffer[iTmpLen + 5];
char型强制转换成WORD型。B7就变成了FFB7,十进制就是65463。由于char是有符号型,B
7的第8位为1,所以转换后为FFB7,而不是代码作者希望的00B7,如果第8位是0,或该变量
是BYTE型,转换就不会有问题了。
2、函数形参和实参不一致,实际上和第一种情况本质上是一样的,只是表现的形式不太一
样,这种情况也是代码中经常出现的问题,下面例子是测试中曾经发现的一个小问题:
【例二】在file01中的INT DebugMsgProc(char byMsg0, char byMsg1)函数,两个形参都
是char型,而实际传入的参数都是BYTE型,结果函数中的如下语句:
PrintfE(PID_RED," %d ticks time out!",byMsg1);
在byMsg1大于127时,输出错误的结果。
二、不同数据类型之间的比较操作
在循环终止条件的判断中,不同类型变量的比较操作是容易造成死循环错误的地方,同时
也是开发人员容易忽视的地方,值得测试人员多加留意。下面两个例子是该类错误的两种
典型情况:
【例三】file02文件中某函数中如下代码,可能造成死循环:
......
int i;
WORD *pCheck =(WORD*)p;
WORD wCheckSum=*pCheck;
pCheck++;
for(i=1;i<dwLen/2;i++)
{
wCheckSum^=(*pCheck);
pCheck++;
}
//binlen had already word alignment
return (wCheckSum);
......
该段代码是在DOS环境下用BC编译的,由于循环变量i是int型(2个字节),而dwLen是
DWORD型(4个字节),如果dwLen大于65536,那么该函数就是死循环了。
上面的例子是不同类型变量之间直接比较操作,还有一种情况是函数的返回值与另一不同
类型的变量比较,见下面例子:
【例四】file03.c文件中某函数中如下代码,
while( ftell(fp)< Part[3])
{.....
}
ftell返回long型,而Part是DWORD型,有符号变量和无符号变量的比较,可能造成死循
环。
类似的例子还有很多,类型不匹配的问题还有许多种情况,都是代码中的隐患,有时会造
成严重的后果,需要引起足够的重视。对于该类问题,我们可以利用PC-LINT工具对代码进
行细致的检查。
7、用于控制条件转移的表达式及取值范围是否书写正确
【案例1.7.1】
【案例描述】:
在测试主机MPU板倒换功能时,如果MPU备份充分,倒换前后对处于激活状态的电路应无影
响,即不影响通话。但近期测试发现,如果两局通过DT板进行一号对接,MPU备份倒换却发
生断话。具体现象为:如果DT板的第1个PCM系统电路为故障,则MPU倒换时复位该DT板,如
果DT板的第2个PCM系统电路为故障,则MPU倒换时复位下一块DT。
【处理过程】:
据查,MPU倒换时会自动复位处于“故障”态的电路,但由于计算错误(多加了32),错
复位了下一个PCM系统32路电路。
【结 论】:
如此严重问题为什么到今天才发现?因为我们在实验室中一般采用同一单板的2个PCM系统
自环进行测试,则不会在某单板上有故障和空闲电路共存,自环屏蔽了错误。
【思考与启示】:
自环是在测试环境下常用的一种提高效率的手段,但一旦条件允许,我们的测试工作应尽
量模拟网上的实际环境进行。
【案例1.7.2】
平时对计费功能进行测试的时候,浏览详细话单都是比较注意话单本身的正确性,并没有
注意该命令对系统的影响。所以当浏览少量话单的时候,并没有发现该命令的异常。但是
当时间的跨度较大时,详细话单数量较多,问题就出现了。执行如下命令:
LST AMA: TP=NRM, SD=1999&7&1, SA=YES;
当浏览了大约10万张详细话单后,终端与BAM的连接关闭。重建连接后,发现话单台的命
令不能执行。观察BAM的性能,发现话单台仍占有CPU50%以上的利用率,说明原来的任务仍
在执行。需要关一下话单台才能恢复正常。
重复上述步骤,当终端与BAM的连接尚未关闭时主动断开此次连接,结果同上。
反馈到开发人员那里,发现该现象与设计的初衷是相违背的。本来话单台控制最多输出2
00张话单,这是为了防止过多话单的输出显示会增加BAM的开销,从而降低BAM的性能。查
看一下源代码,问题就发现了。
话单台控制最多输出200张话单
程序如下
while(timeCur <= timeEnd)
{
timeCur += tsOneDay;//加一天
while(fileBill.Read(&rpt, sizeof(CBillReport)) ==
sizeof(CBillReport))
{
.....................
//只输出满足条件的前200张话单
if (++wBillCount == 200)
{
break;
}
}//一个文件查询结束
}//所有文件查询结束
在话单输出200张之后,程序只退出一层循环,仍然会从下一天话单继续输出,导致向MM
L发帧过多,造成MML和话单台都被堵死。
修改ProcessQueryBill()函数
//只输出满足条件的前200张话单
if (++wBillCount == 200)
{
timeCur = timeEnd + tsOneDay;//退出第二层循环,
while(timeCur <= timeEnd)
break;
}
作上述修改后问题就不再出现了。
一些MML命令从完成的功能来讲可能是没什么问题的,但其执行对系统性能的影响我们在
测试时时往往给忽视了。在我们目前的BAM方案中,存在着多个终端协同工作,如果某个终
端发出的命令在BAM中长时间独占着大部分系统资源,造成的后果是严重的。这是在设计时
要避免的,在测试中要注意的问题。
【案例1.7.3】
【正 文】
在判断模拟用户端口是否反极性时有这样一段程序:
if ( ( bsn >= g_wASL32StartPSN ) &&
( ( ( bsn - g_wASL32StartPSN ) % 32 ) == 15 || ( ( bsn - g_wASL32
StartPSN ) % 32 == 16 ) ) )
return TRUE;
if ( ( bsn % 16 ) == 7 || ( bsn % 16 ) == 8 )
return TRUE;
return FALSE;
作者的本意是如果是32路用户板(蓝色字体判断),就看端口号是否是第15和16路,如果
是,就是反极性端口,返回TRUE,否则就不是,应该返回FALSE。但代码表达的意思是:如
果是32路用户板并且端口号是15或16就返回真值,否则还要执行下边语句。
当端口在32路用户板上,但端口号不是15或16时,不同的32路端口的起始地址g_wASL32S
tartPSN,会导致不同的非15、16端口被误认为是反极性端口。举个例子,当g_wASL32Sta
rtPSN的值为3000时,端口号为3000(第一块板上的第0个端口)就被认为是反极性端口,
这与作者的意图完全相悖。
可以将代码修改如下:
if ( ( bsn >= g_wASL32StartPSN )
{
if ( ( ( bsn - g_wASL32StartPSN ) % 32 ) == 15 || ( ( bsn - g_wASL32Sta
rtPSN ) % 32 == 16 ) ) )
return TRUE;
}
else
if ( ( bsn % 16 ) == 7 || ( bsn % 16 ) == 8 )
return TRUE;
return FALSE;
通过这个例子,我觉得在代码审查时应该留意在判断条件较多的情况下,每个输入是否都
能正确输出,在单元测试、集成测试、系统测试时要针对边界值设计相应的测试用例。
判断条件较多时开发人员也应该适当分开写,既使代码更易读,又不容易出错。
8、条件分支处理是否有遗漏
【案例1.8.1】
【现 象】
在接入网主机程序的代码审查中,发现dbquery.c的DBQ_Init_ANType函数中如下
代码段缺少应有的条件分支,在数据异常的情况下,会产生较严重的问题。
【处理过程】
该错误比较隐蔽,现在说明如下:
Max2B1QStatTime 最大统计时间
Max2B1QStatPortNum最大统计端口数
MAX_2B1Q_STAT_PSN 最大统计内存分配数量
其中:Max2B1QStatTime(最大统计时间)和Max2B1QStatPortNum
(最大统计 端口数)的乘积不能大于MAX_2B1Q_STAT_PSN
程序如下:
//查询数据库,获得Max2B1QStatTime的值
directQueryCond.tupleNo = 10;
error_code = DB_Query( RID_OTHERS_PARA_INFO, 1,
(LPDBCondition)&directQueryCond,
(BYTE FAR *)&tempstruct0 );
//查询数据库成功
if( error_code == DB_SUCCESS )
{
//tempstruct0.data是数据库中为Max2B1QStatTime配置的值
if ( tempstruct0.data > MAX_2B1Q_STAT_PSN )
Max2B1QStatTime = MAX_2B1Q_STAT_PSN;
else if ( tempstruct0.data != 0 )
Max2B1QStatTime = tempstruct0.data;
}
//查询数据库,获得Max2B1QStatPortNum的值
directQueryCond.tupleNo = 11;
error_code = DB_Query( RID_OTHERS_PARA_INFO, 1,
(LPDBCondition)&directQueryCond,
(BYTE FAR *)&tempstruct0 );
//查询数据库成功
if( error_code == DB_SUCCESS )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -