第9章 检测
本章描述用于检测目的的模块和其它工具。
9.1 仪器概览
很多可用于SYS/BIOS应用程序的检测工具都是由XDCtools模块和APIs提供的。参阅CDOC在线参考文档获取Assert、Diags和Error模块的详细信息。
9.2 Load模块
ti.sysbios.utils.Load模块报告系统线程的执行时间和负载信息。
SYS/BIOS管理了四个不同层级的执行线程:硬件中断服务程序、软件中断服务程序、tasks和后台idle函数。Load模块在每个task基础上报告执行时间和负载信息,还为硬件中断服务程序、软件中断服务程序和idle函数(以idle task的形式)提供全局信息。它还可以报告全局CPU负载评估值,也就是用于测量窗而非idle loop的时间百分比的计算。更具体地说,load使用如下公式计算:
global CPU load =
100 * (
1 - ((x * t) / w) )
|
其中:
- 'x'是在测量窗期间,idle loop被执行的时间。
- 't'是执行idle loop一遍的最小时间,也就是在没有工作要做的情况下,完成idle loop所需的时间。
- 'w'是测量窗的时间长度。
任何在idle loop中所完成的工作都包含在CPU负载内。换句话说,idle loop最短行程之外的任何工作都计算在non-idle时间。Load模块使用“update”函数来计算自上次“update”被调用以来的负载和执行时间。当Loadd.updateInIdle被设置为true(默认值)时,“update”在ti.sysbios.knl.Idle函数中按Load.windowInMs(默认为500ms)所指定的周期进行自动调用。基准时间窗口是两个“update”调用之间的时间长度。
执行时间使用xdc.runtime.Timestamp数量为单位进行报告,load以百分数进行报告。
默认情况下,load收集所有线程的数据。你可以使用配置参数Load.hwiEnabled、Load.swiEnabled和Load.taskEnabled来选择监控哪个类型的线程。
Load被设置为提供尽可能少的配置数据。使用默认配置,load每隔大约500ms收集并记录所有线程的数据。
以下代码将Load模块配置为将负载统计写入到一个LoggerBuf实例:
var LoggerBuf = xdc.useModule(
'xdc.runtime.LoggerBuf');
var Load = xdc.useModule( 'ti.sysbios.utils.Load'); var Diags = xdc.useModule( 'xdc.runtime.Diags'); var loggerBuf = LoggerBuf.create(); Load.common$.logger = loggerBuf; Load.common$.diags_USER4 = Diags.ALWAYS_ON; |
有关Load模块的高级配置和附加说明信息,请查阅在线参考文档。
9.2.2 获取负载统计
可通过以下两个途径获取Load模块的负载统计报告:
- Load module logger:如果你将Load模块配置为logger并打开diags_USER4,Load模块的统计数据将收集到load模块的logger实例。你可以使用RTOS分析工具将这些基于日志记录的负载可视化。参阅System Analyzer User’s Guide(SPRUH43)和System Analyzer wiki page获取更多信息。
或者,你也可以将logger配置为在控制台上打印日志。全局CPU负载日志打印一个百分数,如:
LS_cpuLoad: 10
全局Swi和Hwi负载日志打印两个数字:线程使用时间和测量窗长度,如:
LS_hwiLoad: 13845300, 158370213
LS_swiLoad: 11963546, 158370213此负载评估值为8.7%和7.6%。
Task负载日志使用相同格式,它将Task句柄地址作为第一个参数,如:
LS_taskLoad: 0x11802830, 56553702, 158370213
此负载评估值为35.7%。
- Runtime APIs:你也可以选择调用 Load_getTaskLoad()、Load_getGlobalSwiLoad()、Load_getGlobalHwiLoad()或Load_getCPULoad()在运行时的任意时间获取统计数据。
Load_getCPULoad() API返回负载的实际百分比,而Load_getTaskLoad()、Load_getGlobalSwiLoad()和Load_getGlobalHwiLoad()则返回一个Load_Stat结构体。此结构体包含两个字段:线程时间找度和测量窗时间长度。负载百分比可以通过这两个数字相除并乘上100%而获得。尽管如此,Load模块还是提供了一个便利函数Load_calculateLoad()来实现此目的。例如,以下代码取回Hwi负载
Load_Stat stat;
UInt32 hwiLoad;
Load_getGlobalHwiLoad(&stat);
hwiLoad = Load_calculateLoad(&stat);
9.3 错误(Error)处理
很多SYS/BIOS APIs — 特别是那些创建对象和分配内存的函数 — 都有一个Error_Block类型参数。此类型由XDCtools所提供的xdc.runtime.Error模块定义。下例以推荐的方式在创建Swi时声明和使用error块:
#include <xdc/std.h>
#include <xdc/runtime/Error.h> #include <xdc/runtime/System.h> #include <ti/sysbios/BIOS.h> #include <ti/sysbios/knl/Swi.h> Swi_Handle swi0; Swi_Params swiParams; Error_Block eb; Error_init(&eb); Swi_Params_init(&swiParams); swi0 = Swi_create(swiFunc, &swiParams, &eb); if (swi0 == NULL) { System_abort( "Swi create failed"); } |
if (swi0 ==
NULL)
{ System_abort( "Swi create failed"); } |
大多数SYS/BIOS APIs的错误块还返回一个创建对象的句柄或分配的内存。为此,它是最简单的,并提供最好的性能来检查这些API返回的值。
对于那些传递了Error_Block但没有返回句柄或其它状态值的APIs,你可以使用Error_check()函数来检测Error_Block。下例通过调用Timer_reconfig()来重新配置静态创建的定时器对象:
Timer_reconfig(myTimer, tickFxn, &timerParams, &eb);
if (Error_check(&eb)) { System_abort( "Timer reconfigure failed"); } |
你可能会在其它文档中看到使用NULL来代替Error_Block参数。在创建对象或分配内存时使用NULL代替Error_Block作为参数传递,如果此时发生了错误,应用程序将中止,错误原因会通过System_printf()打印输出。在任何错误都是致命的系统中,这是最好的行为方式,你不再需要任何错误检查。
传递和检测Error_Block的优势是你可以控制程序何时中止。例如,当内存无法分配时你不希望中止,而是希望释放内存并再次尝试或切换到一个拥有更多内存的模式。
注意System_abort()函数从它的System.SupportProxy模块调用一个hook函数(如xdc.runtime.SysStd)。你可能希望使用不同的abort() hook函数来执行一个重启并重新开始应用程序。
9.4 Code Composer Studio检测工具
当你在调试应用程序是地,在
Tools菜单下有几个特别有用的项目可以调试SYS/BIOS应用程序。
Tools > RTOS Object View (ROV)对TI-RTOS是一个可停止模式调试工具。获取更多信息,请参考7.5.3节的
ROV for System Stacks and Task Stacks,或者ROV的wiki页:
http://rtsc.eclipse.org/docs-tip/RTSC_Object_Viewer
Tools > RTOS Analyzer提供了几个工具用于检测SYS/BIOS线程的执行顺序、CPU负载分析、信息和错误日志以及task行为。为了使用这些命令,你的应用程序必须将UIA模块 LoggingSetup设为可用。一旦UIA包可用,SYS/BIOS线程的执行和负载在默认情况下将被检测。参考
System Analyzer
User's Guide(SPRUH43)获取有关RTOS分析工具使用的信息。
![基于TI-RTOS的CC2650DK开发(21)---检测 - 阿巴睇 - 阿巴睇的博客](http://img1.ph.126.net/ilwoq5sER5IQ6Q4J3lzGag==/6632133192466899265.jpg)
Tools > System Analyzer让你可以访问带附加图表检测的分析工具。这些工具需要使用UIA C/C++ APIs额外代码。如果你需添加额外UIA事件的基准代码和收集额外数据,可以使用这些分析工具。它还提供了多核数据相关。关于系统分析的详细使用方法,参见
System Analyzer User’s Guide (SPRUH43)
![基于TI-RTOS的CC2650DK开发(21)---检测 - 阿巴睇 - 阿巴睇的博客](http://img2.ph.126.net/QLYW9S6dVHijzGQFnaKSkQ==/6632080415908764884.jpg)
如果你已经安装了TI-RTOS,则在应用程序中,UIA是可用的。默认情况下,SYS/BIOS示例中未使用UIA,但你可通过在XGCONF配置编辑器中加入LoggingSetup模块,并选中多选框来激活UIA。
如果你安装了SYS/BIOS和独立的UIA产品(没有安装TI-RTOS),可以通过以下方式激活UIA:
- 在CCS的Project Explorer面板右键单击项目,并选择Properties。
- 在Properties对话框选择General类别,然后选中RTSC标签。
- 在Products and Repositories区域,选中System Analyzer (UIA Target)项,并确保所选的是最新版本。这让你的应用程序可链接至UIA的必要部分,并让UIA在XGCONF模块中可用。(如果你看见TI-RTOS项被选中,则UIA已经在你的项目中可用了)
- 单击OK。
![基于TI-RTOS的CC2650DK开发(21)---检测 - 阿巴睇 - 阿巴睇的博客](http://img0.ph.126.net/W3K1pHiI5U65-PCXt-TFDw==/6632006748629703540.jpg)
任何拥有NDK驱动的目标可通过以太网采集UIA运行时数据。所有C64x+和C66x目标也可通过JTAG采集运行时数据。针对UIA发布说明里罗列的额外目标也提供了停止模式数据采集。
9.5 性能优化
本节为SYS/BIOS应用程序的性能优先提供建议。它是以两种方式完成的:使用编译器和链接优化,优化SYS/BIOS配置。
9.5.1 配置日志
日志可以显著地影响系统性能。通过优化配置可以减少日志的影响。有两个主要途径优化日志在应用程序中的使用:
- 无日志:在SYS/BIOS中,默认情况下日志不可用。
- 优化日志:如果需要在应用程序中使用日志,选择某些配置可优化性能。以下子节分别进行讨论。
9.5.1.1 Diags设置
针对每个诊断等级有四个诊断设置:RUNTIME_OFF、RUNTIME_ON、ALWAYS_OFF和ALWAYS_ON。
两个运行时设置(RUNTIME_OFF和RUNTIME_ON)让你在运行时使用和禁用特定诊断等级。但是必须执行检查,以确定每次记录事件是否启用或禁用日志记录。
如果使用的是ALWAYS_OFF或ALWAYS_ON,则无法在运行时改变设置。要不直接记录入日志(ALWAYS_ON),要不被优先出代码(ALWAYS_OFF)。
var Defaults = xdc.useModule(
'xdc.runtime.Defaults');
var Diags = xdc.useModule( 'xdc.runtime.Diags'); /* 'RUNTIME' settings allow you to turn it off or on at runtime, * but require a check at runtime. */ Defaults.common$.diags_USER1 = Diags.RUNTIME_ON; Defaults.common$.diags_USER2 = Diags.RUNTIME_OFF; /* These settings cannot be changed at runtime, but optimize out * the check for better performance. */ Defaults.common$.diags_USER3 = Diags.ALWAYS_OFF; Defaults.common$.diags_USER4 = Diags.ALWAYS_ON; |
9.5.1.2 选择诊断等级
SYS/BIOS模块只记录两个等级:USER1和USER2。根据惯例,USER1是基本事件,USER2更为详细。
为提供性能,你可以仅打开USER1,或仅为特定模块打开USER2。
参阅每个模块的文档以查看哪些事件记录为USER1,哪些事件记录为USER2。
9.5.1.3 选择使用日志的模块
为了优化日志,只允许对有兴趣调试的模块进行日志记录。
例如,由于硬件中断频率方面的原因,Hwi日志往往在性能方面最为昂贵。当时钟计时器到期,每个时钟tick往往记录两个Hwi事件。
9.5.2 配置诊断
默认情况下,断言对所有模块启用。SYS/BIOS使用断言检查普通用户错误,如使用无效的参数或从不支持的上下文调用API。断言对于捕获代码错误非常有用的,否则可能会导致混乱的bugs。
在你做完API调用的基础调试后,为优化性能,可在配置文件中禁用断言:
var Defaults = xdc.useModule(
'xdc.runtime.Defaults');
var Diags = xdc.useModule( 'xdc.runtime.Diags'); /* Disable asserts in all modules. */ Defaults.common$.diags_ASSERT = Diags.ALWAYS_OFF; |
9.5.3 选择一个Heap管理器
SYS/BIOS提供了几个不同的heap管理器实现。每个在分配和释放内存方面都有不同的性能权衡。参考7.8节获取有关每个模块权衡的讨论。
HeapMem可以分配任意尺寸的块,但却是三个中最慢的。HeapBuf仅可以分配固定尺寸的块,但却非常快。HeapMultiBuf管理一个HeapBuf实例池,并平衡了其它两个的优势。HeapMultiBuf比HeapMem快,但比HeapBuf慢。HeapMin使用最小内存,但不能释放内存。
但还是要考虑使用不同的heap实现不同的作用。例如,HeapBuf完美适合频繁创建和删除的固定尺寸对象。如果您正在分配和释放许多固定尺寸的数据缓冲区,则可以创建一个HeapBuf实例来分配这些数据缓冲。
9.5.4 Hwi配置
硬件中断调度器默认提供了一些中断延迟的功能。如果你的应用程序不需要这些功能,可以禁用它们以减少中断延迟。
- dispatcherAutoNestingSupport:如果不需要在执行Hwi函数期间禁用中断,则可以禁用此项功能。
- dispatcherSwiSupport:如果不需要从任意Hwi线程提交Swi线程,则可以禁用此功能。
- dispatcherTaskSupport:如果在Hwi线程不会调用任何会导致Task被预订的APIs,则可以禁用此功能。例如,Semaphore_post()将导致Task被预订。
- dispatcherIrpTrackingSupport:此功能支持Hwi_getlrp() API,它返回一个中断的最新地址。如果你的程序不使用此API,则可以禁用此功能。
9.5.5 栈检测
默认情况下,Task模块在切换Task时会检查Task栈是否溢出。为改良Task切换延迟,可以将Task.checkStackFlag属性设置为false以禁用此功能。