调试(debug)是软件开发的一个重要环节,对于嵌入式开发而言这个环节其实比较依赖一些硬件资源(硬件debugger)的支持。传统的嵌入式系统的调试比较依赖断点(breakpoint)和单步调试(single step through)。而 ARM cortex-M 系列的芯片其实有很强的CoreSight片上调试支持,实际上就是一个小的调试硬件,作为ARM的标准,内嵌在ARM的芯片里。在ARM自家的调试器ULINK-pro等的帮助下,可以实现代码覆盖率,代码剖析,代码性能分析等非常强大的调试功能。不同架构的Cortex-M系列的芯片支持不同的CoreSight部件,详见官方网站的说明。
作为ARM自家的软件开发工具,Keil自然也在调试方面有很多相应的支持。我们这里简单介绍一下Keil对RTX的支持,操作系统感知调试(OS-aware debugging)。主要有两个功能,一个是系统和进程观察窗口(System and Thread Viewer),另外一个是事件窗口(Event Viewer)。
这里以STM32F4Discovery板作为例子。
首先这个功能和其他Cortex-M芯片,查看内存值(Memory Window 或者 Watch window)的运作原理类似,RTOS会把相关的进程信息储存在内存里面,然后这个系统和进程观察窗口就会从这个内存区域里面提取信息,其他的RTOS开发者也可以利用这个功能。
用法具体说来,非常简单,首先进入调试模式。
然后在View菜单中,确定Periodic Window Update被点选了:
最后,在Debug菜单中的OS Support栏中,点选System and Thread Viewer:
然后就可以看到这个窗口:
这就是系统和进程窗口,里面提供了RTX系统的基本设置,例如堆栈大小,时间片设置等和进程的基本信息,例如进程ID,进程优先度,当前进程状态,进程的delay时间,进程等待的事件值和当前的事件值和堆栈的适用程度。这个窗口还会实时更新各个进程的状态,非常好用。
举一个简单的例子,如果所有的进程都处于WAIT_SEM(等待信号量)的状态,而只有os_idle_demon处于Running状态,那么很可能就是进入了死锁的状态。调试时可以根据这里了解更多关于进程的实时情况。
可能在第一步配置系统和进程窗口时很多人都留意到了OS support中的,Event Viewer选项,没错,勾选它,就能启动这个调试功能。但这个调试功能需要SWV(Serial Wire Viewer)的支持。
首先要退出调试模式,然后点选Target Option,选择其中的Debug 栏目,点选在Debugger选择栏右侧的Settings按钮:
然后点选里面的Trace(跟踪)栏目,开启Trace,失能Periodic和EXCTRC选项,其余配置参考下图:
然后再进入调试模式,运行,就能看到这个事件窗口了:
清晰记录了具体某一时刻,哪个进程在运行,和进程运行之间的切换。
如果你勾选开启右上角的3个小功能:Task Info, Cursor和 Show Cycles,然后再把鼠标移至任一进程,就会出现进程信息框:
这些信息包括当前时间片里,进程开始的事件结束的事件,和该进程最大和最小的一段时间片。最为重要的是这个Called,这个数据是当前进程运行的次数,也就相当于这个是个profiler的数据,我们可以找出运行次数最高的进程,针对性地去优化程序。
RTX本身提供了一个功能,能够返回当前时间(以RTX设置的时间单位为单位,初始时间是RTX开始Initialize的时间,也就是os_sys_init()的那一刻):
current_time=os_time_get();
通过读取当前的操作系统时间,也能够实现一些性能方面的调试功能,但这需要代码里面额外写入这部分相关的调试代码。