现有技术中,嵌入式系统的最常用的调试方法主要有以下三种:
(1)在进程中要调试的地方添加printf打印函数
这种方式是嵌入式系统中使用最多的,也是效率最低下的调试方式。这是因为,该方式会在进程中加很多的printf打印函数。当程序调通后发布版本的时候,需要再把printf打印函数全部去除。当现场版本测出有问题时,又要换版本重新添加printf打印函数调试。由于printf打印函数的反复添加和去除,导致进程调试的效率比较低下。如若现场版本出问题,还需要重新升级换版本调试,从而破坏现场测试环境,如果这个漏洞(bug)是不易复现的话,还要增加复现bug的时间,进一步影响调试效率。
(2)在进程中加入了一个调试宏
具体地,在编译的时候定义一个调试宏,用于将进程中的调试信息都打印出来。相较于方法(1),printf打印函数的调试信息不用被去掉。当进程程序调通过进行发布时,只要在编译的时候不定义该调试宏即可。但是,如若现场版本出现问题,还是需要重新定义调试宏,重新编译版本。
(3)用gdb和gdb_server调试
采用gdb和gdb_server调试的话,调试效率相对来说比较高。但是,该方法具有以下不足:
a)在编译的时候需要加入-g参数,会导致编译出来的程序占用空间比较大;
b)需要将gdb_server放在嵌入式系统里面,这样也比较占空间;
c)调试的时候需要交叉编译的gdb环境,导致对调试的宿主机的要求也比较高;
d)调试的时候需要占用网口,并且网络通信没有问题。
e),这种调试方式比较适用于早期软件的开发,而不适用于后期现场版本软件的维护和诊断。
因此,需要克服现有技术的上述不足,提供一种提高嵌入式进程调试效率的新方案。
发明内容
鉴于以上所述现有技术的缺点,本发明的目的在于提供一种在嵌入式系统进程中调试日志的方法,通过提供一套调用调试日志(debug log)的库函数,统一了系统的调试函数,避免了各个模块和各个程序员的各种风格的调试打印,也有利于后期整个系统的维护,从而提高了研发和测试的工作效率和后期系统维护的效率。
为实现上述目的及其他相关目的,本发明提供一种在嵌入式系统进程中调试日志的方法,包括以下内容:1)将日志调用函数集成为库函数,以供各个进程来调用;2)初始化进程的日志等级、日志目的地和进程标示;3)定义日志等级调试函数,以添加不同日志等级的调试信息;4)添加接收进程日志等级和日志目的地的设置消息的接口;5)添加设置进程的调试等级和调试信息输出目的地的命令接口;6)根据调试等级和进程的日志等级,输出调试日志打印记录。
于本发明一实施例中,所述日志等级用于表示日志的等级,包括错误日志、注意日志和调试日志。
于本发明一实施例中,所述日志目的地包括标准输出设备、telnet终端设备和文件。
于本发明一实施例中,初始化时,所述日志等级默认为错误日志,所述日志目的地默认为标准输出设备。
于本发明一实施例中,在添加调试信息时,只需调用日志等级调用函数中的错误日志函数或注意日志函数或调试日志函数即可,无需添加其他附件的调试信息。
于本发明一实施例中,通过cli命令来设置进程的调试等级和调试信息输出目的地。
于本发明一实施例中,打印调试日志时,须判断调试等级是否小于进程的日志等级;若是,则继续打印调试日志;若否,则直接退出。
于本发明一实施例中,输出调试日志包括进程名、日志等级、日志生成时间、调用函数、日志行号、调试日志打印条件。
于本发明一实施例中,所述日志生成时间精确到毫秒输出。
如上所述,本发明的在嵌入式系统进程中调试日志的方法,具有以下有益效果:
(1)调试日志可以保存在程序中,默认发布版本的时候将调试全部关掉,这样就不会看到很多打印;
(2)不用重新编译版本就可以打开调试开关;
(3)能够设置不同的调试等级,避免过多的调试信息输出;
(4)能够将调试信息输出到不同的输出设备上;
(5)能够输出详细的调试日志打印记录,包括打印时间,调试进程、调试函数,以及调试位置;
(6)极大地方便了程序员的调试,程序员在设计软件的时候只需要加上调试的断点,而无需关注其他;若发现程序有漏洞,只需要打开相应的调试开关,抓取调试信息即可,无需切换升级版本。
具体实施方式
以下通过特定的具体实例说明本发明的实施方式,本领域技术人员可由本说明书所揭露的内容轻易地了解本发明的其他优点与功效。本发明还可以通过另外不同的具体实施方式加以实施或应用,本说明书中的各项细节也可以基于不同观点与应用,在没有背离本发明的精神下进行各种修饰或改变。
需要说明的是,本实施例中所提供的图示仅以示意方式说明本发明的基本构想,遂图式中仅显示与本发明中有关的组件而非按照实际实施时的组件数目、形状及尺寸绘制,其实际实施时各组件的型态、数量及比例可为一种随意的改变,且其组件布局型态也可能更为复杂。
本发明的在嵌入式系统进程中调试日志的方法提供了一种在嵌入式系统中优化调试的方案,将调试日志封装成一套库函数,通过cli命令可以设置各个进程的日志等级(logLevel)和日志目的地(logDestination),提供简单的接口给程序员,以方便程序员的调用;同时统一了各个程序员的调试打印,能够提供具体详细的调试日志输出。
参照图1,本发明的在嵌入式系统进程中调试日志的方法包括以下内容:
(1)将日志调用函数集成为库函数,以供各个进程来调用。
具体地,将日志调用函数做成一个动态lib库,方便不同的进程来调用。log_log(LOG_ERR,__FUNCTION__,__LINE__,args)函数是一个动态库函数。其中,__FUNCTION__参数是调用函数名,__LINE__是调用的行号,args是打印参数,log_log函数为自己定义的一个库函数,里面的逻辑是自己定义的,而非系统自带的一个库函数。所有的进程只要链接到动态库,都可以调用这个函数。日志等级设置函数Log_setLevel(log_level)、日志目的地设置函数Log_setDestination(log_dest)都是动态库函数。
(2)初始化进程的日志等级、日志目的地和进程标示。
其中,每个进程包含日志等级(logLevel)、日志目的地(logDestination)和进程标示(gEid)等全局变量。进程初始化的时候,首先需要初始化这几个全局变量。具体地,通过调用初始化库函数Log_init(gEid)来初始化上述全局变量,其中gEid表示进程标示。
日志等级用于表示日志的等级,包括错误日志(LOG_LEVEL_ERR)、注意日志(LOG_LEVEL_NOTICE)和调试日志(LOG_LEVEL_DEBUG)。
日志目的地(logDestination)用于表示日志存放的目的地,即日志存放在什么设备上。日志目的地包括标准输出设备(LOG_DEST_STDERR)、telnet终端设备(LOG_DEST_TELNET)和文件(LOG_DEST_FILE)。在本发明中,由于日志目的的多样性,在现场应用环境下,往往是不能接串口的,这时可以把调试信息输出到telnet终端设备上;如果调试信息较多,不方便查看,还可以把调试信息输出到文件中,直接在文件中查看调试信息比较方便。
通常,默认初始化日志等级为错误日志,日志目的地为标准输出设备。Log_init为一个库函数,每个进程初始化的时候,将自身进程标示号输入该库函数,使得不同的进程的gEid指向不同的进程标示。
(3)定义日志等级调试函数,以添加不同日志等级的调试信息。
具体地,通过以下代码来定义日志等级:
#define Log_error(args...)log_log(LOG_ERR,__FUNCTION__,__LINE__,args)
#define Log_notice(args...)log_log(LOG_NOTICE,__FUNCTION__,__LINE__,args)
#define Log_debug(args...)log_log(LOG_DEBUG,__FUNCTION__,__LINE__,args)
在添加调试信息时,只要把传统的printf打印函数替换成日志等级调试函数中的错误日志(Log_error)或注意日志(Log_notice)或调试日志(Log_debug)函数即可,不需要加其他附件的调试信息,系统会自动加上去。其中,错误日志(Log_error)表示在系统出现严重错误的时候添加的调试信息。调试日志(Log_debug)表示系统中需要添加的调试信息。注意日志(Log_notice)表示系统中添加的一些注意的信息。通过设置日志等级,来显示不同等级的日志。
其中,程序员调用函数会用Log_error或Log_debug,这两个函数都会调用log_log函数。log_Log函数是自己写的日志函数,做成了一个动态库,log_log函数会去判断进程的Log_level等级,程序员调试一般都调用Log_debug,其传进去的日志等级是LOG_DEBUG,log_log函数判断目前进程设的调试等级为LOG_LEVEL_ERR,则DEBUG是大于ERR等级的,则Log_debug的打印不会被输出,通过cli命令可以改变进程的log_level等级;把进程的log_level等级设为LOG_LEVEL_DEBUG,log_log函数判断进程的debug等级LOG_LEVEL_DEBUG,因为Log_debug传进去的等级是LOG_DEBUG,它小于等于进程设定的log_level等级,所以会打印输出,主要通过cli命令去设定各个进程的log_level等级来体现各个进程调试开关的灵活打开和关闭。
当log_log函数实现传进去的log小于等于进程设定的log_level等级,则打印输出。这里定义ERR等级是3,DEBUG等级是5,NOTICE等级是7。
(4)添加接收进程日志等级和日志目的地的设置消息的接口。
其中,本发明主要定义如下几个接收消息接口:
a)MSG_SET_LOG_LEVEL
接收到消息MSG_SET_LOG_LEVEL表示要设置该进程的日志等级。通过调用库函数Log_setLevel(log_level),修改进程的全局变量中的日志等级。
b)MSG_SET_LOG_DESTINATION
接收到消息MSG_SET_LOG_DESTINATION表示要设置该进程的调试日志的目的地。。通过调用库函数Log_setDestination(log_dest),修改进程的全局变量中的日志目的地。
(5)添加设置进程的调试等级和调试信息输出目的地的命令接口。
具体地,通过在系统cli命令行加入如下命令接口来设置进程的调试等级和调试信息输出目的地:
a)Loglevel set appname Debuglevel
其中,appname为设置参数,表示进程的标示或名字。Debuglevel表示进程的调试等级,用于控制调试输出的等级,以避免输出过多的调试信息。若设置的调试等级为最低级,则这个调试等级以上的所有调试信息都会输出。
b)Logdest set appname logdest
其中,Appname为设置参数,表示进程的标示或名字。logdest表示进程的调试信息的输出目的地。
cli进程通过解析命令,解析出进程的名字,向指定的进程发送MSG_SET_LOG_LEVEL、MSG_SET_LOG_DESTINATION,来修改各个进程的日志等级和日志目的地。
(6)根据调试等级和进程的日志等级,输出调试日志打印记录
具体地,通过库函数log_log(LOG_DEBUG_LEVEL,__FUNCTION__,__LINE__,args)输出调试日志打印记录。
打印调试日志时,判断调试等级是否小于进程的日志等级;若是,则继续打印调试日志;若否,则直接退出。通过进程的全局变量进程标示(gEid)来解析出进程的名称。通过参数LOG_DEBUG_LEVEL来标示不同的调试等级,如:error,notice,debug。
输出当时设备的时间,精确到毫秒输出,同时将__FUNCTION__和__LINE__字段也输出,表示是哪一个函数的调试信息以及具体哪一行的调试信息。这些调试信息都拼接到一个字符串缓存中。
根据进程的日志目的地,将调试信息写到不同的输出设备上。如输出到标准输出设备,则直接写到标准输出文件句柄stderr;如输出到telnet终端设备,则打开telnet终端设备句柄,logTelnetFd=open("/dev/ttyp0",O_RDWR),写入到telnet文件句柄中;如输出到文件中,则打开一个日志文件,将日志缓存写入到这个日志文件的文件描述符。
tr069:debug:419.372:runRPC:2944:=====>EXIT,rpcStatus=1
tr069:notice:464.405:main_cleanup:444:exiting with code 0
如上是输出调试日志的打印格式,依次包括进程名、日志等级、日志生成时间、调用函数、日志行号、调试日志打印条件。
综上所述,本发明的在嵌入式系统进程中调试日志的方法调试日志可以保存在程序中,默认发布版本的时候将调试全部关掉,这样就不会看到很多打印;不用重新编译版本就可以打开调试开关;能够设置不同的调试等级,避免过多的调试信息输出;能够将调试信息输出到不同的输出设备上;能够输出详细的调试打印记录,包括打印时间,调试进程、调试函数,以及调试位置;极大地方便了程序员的调试,程序员在设计软件的时候只需要加上调试的断点,而无需关注其他;若发现程序有漏洞,只需要打开相应的调试开关,抓取调试信息即可,无需切换升级版本。所以,本发明有效克服了现有技术中的种种缺点而具高度产业利用价值。
上述实施例仅例示性说明本发明的原理及其功效,而非用于限制本发明。任何熟悉此技术的人士皆可在不违背本发明的精神及范畴下,对上述实施例进行修饰或改变。因此,举凡所属技术领域中具有通常知识者在未脱离本发明所揭示的精神与技术思想下所完成的一切等效修饰或改变,仍应由本发明的权利要求所涵盖。