本章描述了可用于检测目的的模块和其他工具。
9.1Overview of Instrumentation
SYS/BIOS应用程序可用的许多工具都是由XDCtools模块和api提供的。有关Assert、diag和Error模块的详细信息,请参阅CDOC在线参考文档
9.2Load Module
ti.sysbios.utils.Load 模块报告系统中线程的执行时间和加载信息。SYS/BIOS管理四个不同级别的执行线程:硬件中断服务例程、软件中断例程、任务和后台空闲函数。Load模块以每个任务为基础报告执行时间和负载,并且还提供硬件中断服务例程、软件中断例程和空闲函数(以空闲任务的形式)的全局信息。它还可以报告全局CPU负载的估计值,这是根据测量窗口中未用于空闲循环的时间百分比计算的。更具体地说,负载的计算方法如下。
global CPU load = 100 * (1 - ((x * t) / w) )
// x --- 在测量窗口期间执行空闲循环的次数
// t --- 在空闲环路上运行一次所需的最小时间,即在没有工作的情况下完成空闲环路所需的时间
// w --- 测量窗口的时间长度
在空闲循环中完成的任何工作都包含在CPU负载中。换句话说,任何超出空闲循环最短行程的时间都被视为非空闲时间。Load模块依赖于要调用的“update”来计算从上次调用“update”开始的负载和执行时间。这将在Load指定的每个时间段自动完成。当加载时,ti.sysbios.knl.Idle函数中的windowms(默认= 500 ms)。updateInIdle设置为true(默认值)。基准时间窗口是两次调用“更新”之间的时间长度。
执行时间报告为xdc.runtime.Timestamp模块存储的计数(参见第5.5节)。CPU负载以百分比报告。默认情况下收集所有线程的加载数据,可以通过配置参数Load.hwiEnabled、Load.swiEnabled和Load.taskkenabled选择监视哪种类型的线程。
9.2.1 Load Module Configuration
Load模块已经设置为在尽可能少的配置下提供数据。使用默认配置,大约每500毫秒收集和记录一次所有线程的负载数据。下面的代码将Load模块配置为将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;
9.2.2Obtaining Load Statistics
Load模块记录的负载统计数据可以通过以下两种方式之一获得:
Load module logger:如果您为Load模块配置了一个日志记录器并打开了diags_USER4,那么Load模块收集的统计信息将被记录到Load模块的日志记录器实例中。您可以使用RTOS Analyzer工具根据这些日志记录可视化Load。或者,您可以配置日志记录器,将日志打印到控制台。全局CPU负载日志打印一个百分比。例如:
LS_cpuLoad: 10
全局Swi和Hwi加载日志打印两个数字:线程中的时间和测量窗口的长度。例如:
LS_hwiLoad: 13845300,158370213
LS_swiLoad: 11963546,158370213
这些评估为8.7%和7.6%的负荷。任务加载日志使用相同的格式,只是添加了任务句柄地址作为第一个参数。例如:
LS_taskLoad: 0x11802830,56553702,158370213
这相当于35.7%的负载。
Runtime APIs:可以选择在任何时候调用Load_getTaskLoad()、load_getglobalswilload()、load_getglobalhwilload()或Load_getCPULoad()来获取运行时的统计信息。Load_getCPULoad() API返回一个实际的百分比负载,而Load_getTaskLoad()、load_getglobalswilload()和load_getglobalhwilload()返回一个Load_Stat结构。这个结构包含两个字段,线程中的时间长度,测量窗口的时间长度。负载百分比可以通过将这两个数字除以并乘以100%来计算。不过,Load模块还为此提供了一个方便的函数load_calculatload()。例如,下面的代码检索Hwi Load:
Load_Stat stat;
UInt32 hwiLoad;
Load_getGlobalHwiLoad(&stat);
hwiLoad = Load_calculateLoad(&stat);
9.3Error Handling
许多SYS/BIOS api(特别是那些创建对象和分配内存的api)都有一个参数,该参数期望Error_Block。该类型由XDCtools提供的xdc.runtime.Error模块定义。下面的例子展示了在创建Swi时声明和使用错误块的推荐方法:
#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");
}
大多数期望一个错误块的SYS/BIOS api也返回一个句柄到创建的对象或分配的内存。因此,检查这些api返回的值是最简单且性能最好的方法
对于传递Error_Block但不返回句柄或其他状态值的api,您可以使用Error_check()函数来测试Error_Block,如下例所示,该函数调用Timer_reconfig()来重新配置静态创建的Timer对象:
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 模块调用钩子函数(例如dc.runtime.SysStd)。您可能希望使用不同的abort()钩子函数来执行重置并重新启动应用程序
9.4Instrumentation Tools in Code Composer Studio
在调试应用程序时,Tools菜单中有几个项目对调试SYS/BIOS应用程序特别有用。Tools > Runtime Object View (ROV)是一个用于TI-RTOS应用程序的停止模式调试工具。Tools > RTOS Analyzer提供了几个工具,用于检查SYS/BIOS线程的执行顺序、CPU负载分析、消息和错误日志以及任务行为。为了使用这些命令,您的应用程序必须启用UIA模块LoggingSetup。启用UIA包后,默认情况下会检测SYS/BIOS线程的执行和加载。有关RTOS Analyzer工具的使用信息,请参见System Analyzer用户指南(SPRUH43)。
Tools > System Analyzer允许您访问分析工具,以查看表格和图形中的附加仪器。这些工具需要使用UIA C/ c++ api进行额外的编码。如果向基准代码添加额外的UIA事件并收集额外的数据,则可以使用这些分析工具。还提供了多核数据关联。关于System Analyzer的详细使用方法,请参见《System Analyzer用户指南(SPRUH43)》。
如果您安装了TI-RTOS,则您的应用程序可以使用UIA。默认情况下,UIA在SYS/BIOS示例中是禁用的,但是您可以通过在XGCONF配置编辑器中选择LoggingSetup模块并选中复选框来启用该模块来启用它。如果您将SYS/BIOS和UIA作为独立产品安装(没有安装TI-RTOS),您可以为项目启用UIA,如下所示:
(1)在CCS的“项目资源管理器”窗格中,右键单击项目并选择“属性”
(2)在属性对话框中,选择General类别,然后选择RTSC选项卡。
(3)在Products and Repositories区域中,选中System Analyzer (UIA Target)旁边的复选框,并确保选择了最新版本。这将使您的应用程序链接到UIA的必要部分,并使UIA模块在XGCONF中可用。(如果您看到选中了TI-RTOS项,则表示您的项目已经可以使用UIA。)
可以通过以太网收集具有NDK驱动程序的任何目标的UIA运行时数据。通过JTAG还为所有C64x+和C66x目标提供了运行时数据收集。为UIA发行说明中列出的其他目标提供了停止模式数据收集。
9.5Performance Optimization
介绍SYS/BIOS应用程序的性能优化建议。这可以通过两种方式实现:通过使用编译器和链接器优化,以及通过优化SYS/BIOS的配置。
9.5.1 Configuring Logging
日志记录可以显著影响系统的性能。您可以通过优化配置来减少日志记录的影响。有两种主要方法可以优化应用程序中使用的日志记录:
No logging:在SYS/BIOS中,默认不启用日志记录功能。
Optimizing logging:如果您需要在应用程序中启用日志记录,那么您可以进行一些配置选择来优化性能。这些将在以下小节中进行描述。
9.5.1.1 Diags Settings
每个诊断级别有四个诊断设置: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 Choosing Diagnostics Levels
SYS/BIOS模块只记录两个级别:USER1和USER2。它们遵循这样的惯例:USER1用于基本事件,USER2用于更详细的事件。为了提高性能,您只能为特定模块打开USER1,或者打开USER2。请参阅每个模块的文档,了解哪些事件被记录为USER1,哪些事件被记录为USER2。
9.5.1.3Choosing Modules to Log
要优化日志记录,请仅为您感兴趣的模块启用日志记录以进行调试。例如,由于硬件中断的频率,Hwi日志记录在性能方面往往是最昂贵的。当时钟计时器到期时,每个时钟滴答上记录两个Hwi事件。
9.5.2 Configuring Diagnostics
默认情况下,所有模块都启用了断言。SYS/BIOS使用断言来检查常见的用户错误,例如使用无效参数调用API或从不支持的上下文调用API。断言对于捕获可能导致令人困惑的bug的编码错误非常有用。在完成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.3Choosing a Heap Manager
SYS/BIOS提供了几种不同的堆管理器实现。在分配和释放内存时,每种方法都有不同的性能权衡。请参见第7.8节,详细讨论每个模块的优缺点。
HeapMem可以分配任何大小的块,但它是三者中最慢的。HeapBuf只能分配单个配置大小的块,但速度非常快。HeapMultiBuf管理一个HeapBuf实例池,并平衡其他两个实例的优点。HeapMultiBuf比HeapMem快,但比HeapBuf慢。HeapMin使用最小内存,但不能释放内存。
还要考虑为不同的角色使用不同的堆实现。例如,HeapBuf非常适合分配经常创建和删除的固定大小的对象。如果您正在分配和释放许多固定大小的数据缓冲区,您可以创建一个HeapBuf实例来分配这些数据缓冲区。
9.5.4Hwi Configuration
在默认情况下,硬件中断调度程序提供了许多增加中断延迟的特性。如果您的应用程序不需要这些功能,您可以禁用它们以减少中断延迟。
dispatcherAutoNestingSupport:如果您不需要在执行Hwi功能期间启用中断,则可以禁用此功能
dispatcherSwiSupport:果任何Hwi线程都不会发布Swi线程,则可以禁用此功能。
dispatcherTaskSupport:如果没有api从Hwi线程调用,将导致一个任务被调度,您可以禁用此功能。例如,Semaphore_post()将导致调度Task。
dispatcherIrpTrackingSupport:该特性支持Hwi_getIrp() API,该API返回中断最近的返回地址。如果您的应用程序不使用该API,则可以禁用此功能。
9.5.5Stack Checking
默认情况下,Task模块检查每个Task开关处的Task堆栈是否溢出。为了改善任务切换延迟,您可以在任务中禁用此特性。checkStackFlag属性为false。