使用Streamline分析RTOS系统性能

本文翻译自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的应用可以分为以下四步:

  1. 生成采集数据的代码,barman。
  2. 把barman代码集成到待分析的应用中。
  3. 运行程序,收集数据
  4. 将采集来的数据导入Streamline

为了演示这些步骤,我们来导入一个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代码集成到RTOS应用中

将barman代码集成到应用中,这样才可以采集数据。Streamline已经帮我们生成了barman代码,我们只需要后面再应用中调用它即可。

  • 初始化barman
  • 允许barman从RTOS中读取当前threadID
  • 让RTOS在任务切换的时候通知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”。

  1. 启动DS-5。
  2. 选择Run > Debug Configurations。
  3. Debug Configurations对话框中创建一个调试连接,本例中展开左边对话框的DS-5配置选项,选择RTX5_Cortex-M33_Blinky_Streamline_MPS2。
  4. Connections面板,在调试器地址中输入DStream的IP地址或者USB ID,或者从列表中选择。
  5. ITM配置可以点击 DTSL Options Edit... 按钮,在Trace Capture栏选择DSTREAM 4GB Trace Buffer,在ITM栏勾选Enable CSITM Trace,点击OK,保存DTSL配置。
  6. 点击Debug开始调试,本例将下载编译好的镜像到开发板,并将PC设到程序第一条指令。
  7. 调试需要DS-5调试视图,如果这个时候出现视图切换对话框,点击确认。
  8. 通过电子调试视图中的绿色Continue按钮 ,或F8执行程序。
  9. 程序运行时DStream通过ITM采集数据,你会看到Trace视图中的Buffer Used数字一直在增长。
  10. 几秒钟后,点击调试视图中的Interrup,或者按F9停止应用。
  11. 在命令行,输入 trace dump [path\to\a\local\folder] CSITM

第四步,将数据导入Streamline

最后一步是将采集到的数据用Streamline的采集向导导入,采样的数据可以用来分析源码的符号信息,Streamline可以用来将它可视化。

  1. 启动Streamline
  2. 在Streamline Data视图,点击Import Capture File(s)...,选择刚刚保存的CSITM_0.bin。
  3. Select what to Import中,选择Barman Agent Capture (via ITM),点击Next。
  4. Provide Required Files,选择刚才执行的镜像文件(本例中是RTX5_Cortex-M33_Blinky_Streamline.axf),和barman.xml文件。
  5. 在左侧的Streamline Data视图,右键单击刚才创建的工程,选择Analyse...,勾选执行镜像(RTX5_Cortex-M33_Blinky_Streamline.axf),点击Analyse
  6. 经过一会处理,Streamline会显示barman.xml中选择的那些计数器的时序图

我们来看看图形化效果

时序图中将计数器和线程活动相关联,本例中,我们通过代码标注不同线程运行的时间,下图中的红色长条表示phaseA,绿色长条表示phaseB,蓝色表示phaseC,黄色表示phaseD。 

函数调用栏显示每个函数在该线程中占用的时间比例。

函数栏,显示每个函数在整个运行中占用的时间。

双击函数,可以得到每个函数被采样的次数,以及相应源码。

最后,Log栏显示应用代码中添加的标注。

如果你想改变时序图中显示的计数器,你可以通过Streamline > Generate Barman sources进行修改。重新生成barman.c/.h/.xml,重新编译代码,运行,分析。

综上,我们看见了如何利用Streamline分析RTOS系统的性能,找到系统或者应用的热点,你可以集中精力优化这些热点来最大效率地提高系统的整体性能。

Anonymous
  • 使用Streamline分析RTOS系统性能

    本文翻译自Analyzing the performance of RTOS-based systems using Streamline ============================================================= Streamline是DS-5中的性能分析工具, Streamline可以用来分析裸机系统,RTOS,Linux,Android和Tizen系统的性能…
  • 免费的原生aarch64 CI/CD

            开源软件社区的朋友们有福了!如果你有个开源软件项目想支持Arm架构平台,却苦于没有Arm服务器做CI/CD (持续集成/持续交付),那这个好消息你可不能错过。7月10日,Shippable宣布和Arm,Packet.net达成合作伙伴关系,为Arm平台上的软件开发提供原生的CI/CD服务,重点是(敲黑板了)对于开源软件项目,Shippble提供免费的CI/CD服务!这些服务运行在给所有开源软件共享的64位的Armv8…
  • 当增强现实(AR)遇到机器学习(ML),一个结合 Google ARCore 与 Arm NN 的 Unity 案例

    背景介绍 在 Unity 中打开 Google ARCore 功能 编译 Arm NN 共享库 作为 Unity 的 Native 插件的一个 C++ 物体检测器 整合 Arm NN 到 Unity 中 背景介绍 增强现实和机器学习是当前两项最前沿的技术。其中增强现实可以把虚拟的物体惟妙惟肖地渲染到真实世界当中。 而机器学习,则可以帮助程序更好的认识真实世界中的物体。同时运用这两项技术,我们将可以创造出一系列新颖的应用…