一、背景介绍
vxWorks提供了一种错误检测及报告的机制帮助开发者调试软件,6.9版本中称为edr功能。在创建内核时,在workbench组件编辑
component configuration 中可以找到如下组件
这个edr功能的特性:在RAM中保留一块内存区域,热启动时不会擦除该块区域的内容,此区域用做edr记录的空间。vxWorks内核在处理CPU、用户以及地址等各种各样的异常时,会在EDR中记录异常发生的任务、地址、堆栈调用、异常触发时间,然后再进行保护机制例如热启动等当重启发生后,我们可以通过vxWorks提供的show routine在shell中进行查看发生的故障,进行问题回溯。
edr功能记录的种种信息对于我们评估驱动程序、应用程序、调试bug等具有非常大的帮助,但是edr毕竟是在RAM中记录,一旦冷启动,所有的记录信息都会消失。
基于想要保存edr中各种重要的记录的想法,我们可以设置一些列的辅助手段,将EDR中记录的内容写到非易失存储器当中。
二、edr 概要
edr中记录的异常种类极多,但是按照严重级别可以分为几类如图所示发生异常的事项图1,严重级别图2,可以注意到有一个envent type是user,这表示edr功能运行开发者在运行时自动触发异常记录,api如图所示
一般来说我们程序遇到的几项异常都涵盖在当中,异常记录完毕后,shell中通过edrShow就能查看发生的异常内容,edr记录的格式
三 辅助手段
前面说过我们可以通过辅助手段将异常记录导入到非易失存储器,下面来介绍我实践过的做法。
首先,edr功能存在一个vxWorks中最常见的一个功能,钩子函数。edrErrorInjectPrePostHookAdd(FUNC(int post))这是我使用的函数,功能是在内核发现异常创建记录时会调用一次钩子,异常记录完成时调用一次钩子,通过函数指针中的post确定是创建前还是创建后。
钩子通知我们异常记录完成后,接下来就是获取异常记录。这个东西是在内核里搞得,我们怎么弄出来?内核也是人写的,比如说edrShow可以把所有异常记录都记下来,显示出来,那么肯定有办法把异常记录形成字符串。
接下来我在workBench中找到了edrShow的实现,当调用钩子时,我把edrShow中流程走一遍,将形成的字符串不进行打印输出直接写文件保存在硬件设备中的/ata0:0(也可以是其他文件系统设备、裸层flash)。
最后,把具体的代码中贴出来。我是在钩子函数中通过信号量通知任务进行异常记录,记录完成后进行重启操作
while( 1 )
{
semTake( pTempDevId->semCIdEdrFile, WAIT_FOREVER );
/* clock_gettime(CLOCK_REALTIME, &tp);
nowSec = tp.tv_sec;
gmtime_r(&nowSec, &timeBuffer);
timeLen = strftime(datetime, 64, "%Y-%m-%d %H:%M:%S", &timeBuffer);*/
if ((pLog = ( EDR_ERR_LOG *) edrLogBaseGet ()) == NULL)
{
FUNC_LOG ( "edrShow: cannot get address of persistent memory region\n" );
return (ERROR);
}
if ( ( recordNum = edrErrLogNodeCount (pLog)) == ERROR )
{
FUNC_LOG ("edrShow: the ED&R log is corrupt, unable to display records.\n");
return (ERROR);
}
if (edrErrLogIterCreate (pLog, &iter, recordNum - 1, 1 ) )
{
EDR_ERR_LOG_NODE * pNode;
while ((pNode = edrErrLogIterNext (&iter)) != NULL)
{
EDR_ERROR_RECORD * pRecInLog = (EDR_ERROR_RECORD *) pNode->data;
EDR_ERROR_RECORD * pER = (EDR_ERROR_RECORD *) recordBuf;
UINT32 genCount = pNode->genCount;
memcpy (pER, pRecInLog, pRecInLog->size);
/* ensure node wasn't updated during copy */
if ( genCount == pNode->genCount)
{
/* skip empty (recovered) records */
if (pRecInLog->size == 0)
{
FUNC_LOG ("Incomplete record!\n");
}
/* only display records meeting the display criteria */
if ( pRecInLog->size != 0 )
{
unsigned int uiTempKind = pER->kind & EDR_FACILITY_MASK;
if( uiTempKind == EDR_FACILITY_KERNEL || uiTempKind == EDR_FACILITY_INTERRUPT ||
uiTempKind == EDR_FACILITY_USER )
{
edrErrorRecordDecode (pER, edrFileBuf, EDR_WIDTH );
if( fprintf (fpVm,"%s", edrFileBuf ) < 0 )
{
FUNC_LOG ("Incomplete record!\n");
}
fprintf( fpVm,"%s\n\n");
memset( edrFileBuf, 0, EDR_WIDTH );
fclose( fpVm );
reboot( 0 );
}
}
}
else
{
fprintf ( fpVm, "Record was updated while trying to be displayed!\n" );
}
}
}
}
在发生异常时,系统自动重启完成整个系统工作恢复,同时能够在文件系统中找到文件相应异常的记录,通过具体信息调试程序中出现的问题。
PS: 弱鸡程序员,如有不对之处,恳请批评指教