作者:Surest

一、背景介绍

VxWorks提供了一种错误检测及报告的机制帮助开发者调试软件,6.9版本中称为edr功能。在创建内核时,在workbench组件编辑component configuration中可以找到如下组件

VxWorks 6.9 EDR Component

这个edr功能的特性:在RAM中保留一块内存区域,热启动时不会擦除该块区域的内容,此区域用做edr记录的空间。VxWorks内核在处理CPU、用户以及地址等各种各样的异常时,会在EDR中记录异常发生的任务、地址、堆栈调用、异常触发时间,然后再进行保护机制例如热启动等,当重启发生后,我们可以通过VxWorks提供的show routine在shell中进行查看发生的故障,进行问题回溯。

edr功能记录的种种信息对于我们评估驱动程序、应用程序、调试bug等具有非常大的帮助,但是edr毕竟是在RAM中记录,一旦冷启动,所有的记录信息都会消失。

基于想要保存edr中各种重要的记录的想法,我们可以设置一些列的辅助手段,将EDR中记录的内容写到非易失存储器当中。

二、edr 概要

edr中记录的异常种类极多,但是按照严重级别可以分为几类,如图所示发生异常的事项(Table 14-1),严重级别(Table 14-2),可以注意到有一个envent type是user,这表示edr功能运行开发者在运行时自动触发异常记录,api如下所示。

VxWorks 6.9 Exception Event type

VxWorks 6.9 Exception Event API

一般来说我们程序遇到的几项异常都涵盖在当中,异常记录完毕后,shell中通过edrShow就能查看发生的异常内容,edr记录的格式。

VxWorks 6.9 EDR Log

三 辅助手段

前面说过我们可以通过辅助手段将异常记录导入到非易失存储器,下面来介绍我实践过的做法。

首先,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" );
				}
			}
 
			
	    }
      }

在发生异常时,系统自动重启完成整个系统工作恢复,同时能够在文件系统中找到文件相应异常的记录,通过具体信息调试程序中出现的问题。