本文翻译自Analyzing the performance of RTOS-based systems using Streamline
=============================================================
Streamline是DS-5中的性能分析工具, Streamline可以用来分析裸机系统,RTOS,Linux,Android和Tizen系统的性能。
通过Streamline可以更详细地了解软件的运行,让arm优化工作更简单,你可以将游戏运行的更流畅,榨干GPU性能,找到程序的热点让SoC运行的更有效率。
本文描述了如何在RTOS系统中使用Streamline进行性能分析,Streamline可以揭示哪个函数在系统运行中占用时间最多,以及线程的活动
文中例子基于Keil RTOS RTX5,目标系统基于arm Cortex M33,但在Cortex-A, Cortex-R, Cortex-M系统中都可以运行。DS-5中还包含一个基于Cortex-A9的例子,这里有一个视频可以用来辅助理解本文。
用Streamline分析基于RTOS的应用可以分为以下四步:
为了演示这些步骤,我们来导入一个DS-5中已经准备好的例子,这是一个用C语言编写的多线程程序,使用arm compiler 6进行编译,和CMSIS Pack中的RTX5库链接生成镜像。为了导入这个例子,我们执行如下步骤,File > Import > DS-5 > Examples & Programming Libraries,在Import对话框,展开Examples > Streamline Bare-Metal Agent Examples,选择RTX5_Cortex-M33_Blinky_Streamline例子,点击Finish。用到的CMSIS包会自动导入,这里也可以看到基于arm Cortex A9的例子。
例子中已经做好了上面要求的第一步和第二步,但是我们这里还是演示了如何操作,方便你将来在自己的项目中集成。
第一步使用Streamline中的Generate Barman sources代码向导,生成barman代码。启动Streamline,选择Streamline > Generate Barman sources:
向导对话框会指引你进行一系列配置。比如,如何保存和传输采集来的数据(保存在板载的内存上,还是通过ITM输入到Debugger)?对几个CPU进行分析?本文中只用了一个核,但是多核也是可以的。应用中最多有几个任务?
系统中使用了哪种CPU,本文是Cortex-M33
要采集来自哪些Counter数据?可以在左边的窗口按Ctrl+A进行全选,拖入右边的窗口
接下来会设置采样率,和你自己定制的Counter,最后生成barman代码,包含两个代码文件,barman.c/barman.h。这两个文件会编译链接到你的程序中,同时还有barman.xml文件,这个文件包含你前面的配置信息,可以后面用来修改,重新生成代码文件。
将barman代码集成到应用中,这样才可以采集数据。Streamline已经帮我们生成了barman代码,我们只需要后面再应用中调用它即可。
完整的barman API和参数说明可以在Streamline的文档中找到。
将barman初始化代码加入main函数。
enable_barman(); // Enable barman
barman集成代码如下
#include "barman.h" #include "rtx_lib.h" // provides osRtxThreadxxx functions and os_thread_t type /* * Perform the necessary initialization of the bare-metal agent */ static void enable_barman(void) { /* For M-class, the cycle counter provides the timestamps, so convert it to nS by multiplying by 10**9 and dividing by the clock frequency in Hz */ const struct bm_protocol_clock_info clock_info = { .timestamp_base = 0, .timestamp_multiplier = 1000000000, .timestamp_divisor = 25000000, /* 25MHz system freq of AN521 FPGA */ .unix_base_ns = 0 }; #if BM_CONFIG_MAX_TASK_INFOS > 0 const struct bm_protocol_task_info task_entries[] = { { (bm_task_id_t)tid_phaseA, "phaseA" }, { (bm_task_id_t)tid_phaseB, "phaseB" }, { (bm_task_id_t)tid_phaseC, "phaseC" }, { (bm_task_id_t)tid_phaseD, "phaseD" }, { (bm_task_id_t)tid_clock, "clock" }, { (bm_task_id_t)tid_app_main, "app_main" }, { (bm_task_id_t)(osRtxConfig.idle_thread_attr->cb_mem), "osRtxIdleThread" }, { (bm_task_id_t)(osRtxConfig.timer_thread_attr->cb_mem), "osRtxTimerThread" }, }; #endif /* Initialize barman but if there is a problem we will loop here */ while (!barman_initialize_with_itm_interface("RTX5 Cortex-M33 Streamline bare-metal example", &clock_info, #if BM_CONFIG_MAX_TASK_INFOS > 0 /* All the tasks */ 8, task_entries, #endif #if BM_CONFIG_MAX_MMAP_LAYOUTS > 0 /* We only have one image for all tasks so we don't need to provide these */ 0, BM_NULL, #endif 1)); /* Now we are ready to enable sampling */ barman_enable_sampling(); } /* Allow barman to read the current thread id from RTX */ bm_task_id_t barman_ext_get_current_task_id(void) { return (bm_task_id_t)osRtxThreadGetRunning(); } /* Allow RTX to inform barman when a task switch occurs */ extern void $Super$$osRtxThreadSwitch(os_thread_t *thread); void $Sub$$osRtxThreadSwitch(os_thread_t *thread) { $Super$$osRtxThreadSwitch(thread); // Call the original osRtxThreadSwitch barman_record_task_switch(BM_TASK_SWITCH_REASON_PREEMPTED); // Record the task switch }
注意代码中的 Arm Linker (armlink) $Sub/$Super mechanism ,这里我们使用arm链接器的特性去修改RTOS代码中的一个函数,从而避免直接修改RTOS代码。GCC链接器也可以通过参数"--wrap"添加类似功能。
然后用arm编译器6编译整个工程。如果你是使用makefile来管理整个工程,你需要将barman.c加入到编译文件中,并将生成的barman.o链接到最后应用里。如果你是使用Eclipse,它会自动完成文件添加并重新编译。
重新编译整个工程后,下载,在目标设备上运行应用进行数据采集。本例使用 Arm MPS2+ board,基于FPGA的arm Cortex-M33软核。通过DStream连接开发板,Dstream用来启动/停止控制,并通过ITM从开发板采集数据,所以我们在开始前要配置DS-5,采集ITM Trace信息。将Dstream和板子连接,启动应用,采集数据,停止,通过"trace dump"命令将数据保存到PC上。如果你的应用是将数据保存在内存中,请使用“dump memory”。
最后一步是将采集到的数据用Streamline的采集向导导入,采样的数据可以用来分析源码的符号信息,Streamline可以用来将它可视化。
我们来看看图形化效果
时序图中将计数器和线程活动相关联,本例中,我们通过代码标注不同线程运行的时间,下图中的红色长条表示phaseA,绿色长条表示phaseB,蓝色表示phaseC,黄色表示phaseD。
函数调用栏显示每个函数在该线程中占用的时间比例。
函数栏,显示每个函数在整个运行中占用的时间。
双击函数,可以得到每个函数被采样的次数,以及相应源码。
最后,Log栏显示应用代码中添加的标注。
如果你想改变时序图中显示的计数器,你可以通过Streamline > Generate Barman sources进行修改。重新生成barman.c/.h/.xml,重新编译代码,运行,分析。
综上,我们看见了如何利用Streamline分析RTOS系统的性能,找到系统或者应用的热点,你可以集中精力优化这些热点来最大效率地提高系统的整体性能。
写得好,章政辛苦了