ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AUTOSAR RTM集成实战:CPU负载测量与性能瓶颈定位

AUTOSAR RTM集成实战:CPU负载测量与性能瓶颈定位 干这行都会遇到一个场景项目做到一半系统资源告急。车辆跑起来卡顿、响应变慢或者某个功能偶发超时领导过来问你“CPU到底还剩多少余量”你总不能靠拍脑袋说“大概还行吧”。AUTOSAR开发里正确做法是在系统里埋一个运行监测模块让ECU自己把CPU负载、任务占用率、历史最大负载这些数据报出来然后拿着CANoe截图去跟人说话。这个模块就是RTMRuntime Measurement我在Vector工具链里完整集成过一遍顺便把CPU负载也实测拉了出来这篇就按实操顺序把配置、调用、读数、避坑全部讲清楚适合正在做AUTOSAR BSW集成、SWC开发或系统性能调优的工程师参考。先说明一下这篇文章的适用范围基于AUTOSAR CPClassic Platform和Vector的DaVinci Configurator Pro工具链MCU用的是英飞凌TC2xx系列。不同芯片平台和Vector版本在界面名称上会有些差异但配置逻辑和坑点基本通用。RTM这个模块在AUTOSAR规范里叫Runtime Measurement很多工程师听过但没实际用过原因无非是生产项目里RTM一般默认不开或者只作为调试手段临时打开文档又少网上能搜到的经验贴也不多。我这次踩完坑之后把完整路径记录下来你照着走能省好几天的排查时间。1. 先搞清楚RTM到底在测什么1.1 CPU负载是怎么“测”出来的很多第一次接触RTM的人会有个疑问AUTOSAR里那么多模块为什么CPU负载偏偏要让RTM来测其实原理并不复杂你可以把RTM理解成一个“打卡机”。操作系统每时每刻都在做任务调度每个任务从切入到切出中间这段就是它的执行时间。RTM的思路就是在任务切换的瞬间通过操作系统提供的Hook函数记录时间戳。任务A切入时记一个t1任务A切出时记一个t2t2减t1就是任务A这次占用的CPU时间。一个测量窗口内把所有任务和中断的执行时间全部累加起来除以窗口总时长就是这段时间的CPU负载。换算成公式就是[ CPU_Load \frac{\sum 窗口内所有Task/ISR执行时间}{窗口总时间} \times 100% ]不过这里有个细节AUTOSAR RTM的返回值很多实现里是千分比也就是0到1000对应0.0%到100.0%。我第一次拿到数据时直接当百分比显示差点闹出笑话。这个单位问题后面避坑章节会详细说。RTM模块本身不是一个独立运行的实体它需要借助两样东西一个是操作系统的时间戳信号另一个是操作系统的任务切换Hook。时间戳精度决定测量准不准Hook函数决定能不能采集到任务切换的边界。这两样没配合好RTM测出来的数据基本没法看。1.2 RTM在AUTOSAR架构里的位置RTM是基础软件层BSW里的一个模块它不属于MCAL也不属于服务层里的系统服务而是一个相对独立的测量模块。从AUTOSAR分层来看RTM通常挂在Os和GPT通用定时器驱动之上同时还会用到NvM来保存历史最大负载数据也可能通过CanIf把测量结果发到总线上。RTM对外提供两条数据输出路径实际项目里要根据需求选择输出方式适用场景数据通路RTM_SystemSWC直接读取数据内部使用RTE → Rtm_GetCPULoad接口RTM_CAN需要外部设备观测负载数据Rtm → CanIf → Can → CANoe我的经验是调试阶段用RTM_CAN最方便把CPU负载、任务占用率这些值周期发到CAN上CANoe里拉个曲线就能看到系统实时状态不用接调试器不用打断程序执行。等到要量产后做持续监测也可以保留一个低频率的RTM报文。另外提醒一句RTM不是一个功能安全模块它测量出来的数据不能直接用来做安全决策比如“CPU负载过高所以触发降级”这种逻辑是需要靠应用层自己判断的。RTM的定位更偏向开发调试、性能回归监测、故障复现分析。2. 集成前的准备参数不提前想清楚后面全是坑2.1 时间戳源怎么选RTM测量的核心是时间戳时间戳源的精度直接决定了测量结果的可信度。如果时间戳用的是OS的tick很多工程里是1ms或5ms那测量结果基本没法看。原因很简单很多任务的执行时间只有几十微秒到几百微秒一个1ms的tick采样下去要么记成0要么记成1ms误差极大。正确做法是使用独立的硬件定时器作为时间戳源。具体选哪个定时器要看MCU资源。以TC2xx为例可以用STMSystem Timer Module或者GPT12模块配置成自由运行模式分辨率做到1us到10us比较合适。时间戳分辨率选择有个基本法则要比被测量的最小任务执行周期高一个数量级。比如你的系统里有1ms周期任务执行时间可能只有50us到200us那时间戳分辨率至少做到10us最好能做到1us。这里有一个非常容易踩的坑硬件定时器资源冲突。有些工程里OS系统tick已经占用了一个定时器看门狗又占了一个CanIf或加密模块再占一个留给RTM的定时器通道可能不够或者被分配到与其他模块共享的通道。集成RTM前一定要先过一遍MCAL层定时器资源的分配表确认RTM的时间戳通道不会和别的模块打架。2.2 关键周期参数怎么配RTM涉及几个时间参数我看到很多工程师配置的时候随便填结果测出来的数据要么完全不更新要么周期性跳变。这里把几个最核心的参数列出来说明RtmTimerPeriod时间戳采样周期也就是硬件定时器的分辨率一般配10us或100us不建议超过100us。RtmMainFunctionPeriodRTM主函数调度周期也就是Rtm_MainFunction被周期调用的时间间隔通常配10ms。RtmCPULoadCalculationPeriodCPU负载的计算窗口一般配1000ms也就是每1秒计算一次平均值。这三个参数之间是有比例关系的。计算窗口要能整除主函数周期同时主函数周期内要有足够多的时间戳采样点。比如主函数10ms负载窗口1000ms那一个窗口内主函数执行100次每次主函数内可以汇总若干个时间戳样本这样计算出来的负载才是平滑的。如果计算窗口配得比主函数周期还短比如主函数10ms、计算窗口5ms那RTM根本攒不到足够的样本输出结果就是零散的毛刺。如果计算窗口太长比如10秒数据变化就会很迟钝无法反映瞬时负载波动。2.3 RTM依赖的邻居模块RTM不是孤岛配置它之前要确认工程里已经有以下几个模块并且配置正常Os操作系统提供Hook机制和任务调度。GPT/MCU硬件定时器驱动给RTM提供时间戳。NvM非易失存储器管理RTM的历史数据要存NvM。CanIfCAN接口层如果走RTM_CAN上报就要有。ComM通信管理RTM_CAN通常挂在某个Channel下受ComM状态控制。我实际集成时发现最容易出问题的是NvM块配置。RTM内部会定义一组NvM读写接口需要你在NvM模块里为RTM创建一个块块大小必须和RTM生成代码里的数据结构体大小完全一致。一般生成代码里会有对应的RAM数据结构你用编译器看下结构体大小然后去DaVinci里把NvM块大小填成一样别凭感觉估。3. 手把手配置从DaVinci Configurator到代码集成3.1 添加RTM模块并配置General参数第一步在DaVinci Configurator Pro里打开你的BSW模块配置界面在模块列表里找到Rtm节点右键添加或者勾选启用。如果你拿到的Vector软件包有MICROSAR RTM授权这一步很简单如果找不到Rtm节点检查一下你的BSW软件包清单里是否包含RTM模块Vector的RTM是独立的软件组件需要单独授权和安装。添加完模块后第一件事配置RtmGeneral部分RtmDevErrorDetect建议先打开开发阶段DET报错信息对定位问题很有帮助量产阶段可以关掉减小开销。RtmTimerPeriod填100us。如果你的任务时间非常短比如几个10us级别的ISR可以考虑10us但要注意定时器中断开销也会变大。RtmMainFunctionPeriod填10ms后续在RTE或者Os周期Task里调用Rtm_MainFunction。RtmCPULoadCalculationPeriod填1000ms。这里有个细节RtmMainFunctionPeriod这个值要和RTE里调度的Runnable周期对应。如果你在SWC里建了一个10ms的Runnable来读RTM结果那RTM主函数最好也在同一个10ms任务里调用或者用RTE的BSW调度表来触发。3.2 配置RTM实例、测量模式与NvM接下来配置RTM实例。RTM可以同时存在多个实例每个实例可以独立配置测量对象和测试周期。工程里常见的就是配一个实例专门测总CPU负载再配一个实例测关键任务负载。配置实例时重点看三个参数RtmMeasurementMode测量模式有CPU负载、任务时间、中断时间等几种。测CPU余量就选CPU_LOAD模式要单独看任务负载就选TASK_TIME模式。RtmCPULoadMeasurementType这个决定统计的是总负载还是任务负载注意别选错。RtmTestPeriod一次测量的周期通常和负载计算窗口保持一致1000ms。配置完实例后接着配置NvM关联。在RTM的配置里找到NvM相关项把NvM块编号填进去这个编号要和NvM模块里新建的块对应。然后去NvM模块里新建一个块块大小用RTM生成代码里的数据结构体大小。我遇到的一个问题是NvM块大小没有按照平台字长对齐导致NvM_ReadAll和NvM_WriteAll来回搬运数据时出现字节偏移RTM读出来的历史最大负载值完全是乱的。后来把所有NvM块大小都做了4字节对齐问题就消失了。3.3 配置RTM_CAN上报通道如果你打算像我一样通过CAN把负载数据发出来就要把RTM和CanIf链路打通。这一步在DaVinci里要配置三个地方第一在RTM模块里找到RtmCan相关配置项使能CAN上报功能配置CAN报文ID。我这边用的报文ID是0x6A1扩展帧DLC为8字节。第二在CanIf模块里新建一个发送PDUPDU长度设为8字节关联到RTM的发送接口。这一步很关键很多新手在这里卡住因为RTM不会自己创建CanIf PDU必须在CanIf里为RTM建PDU并建立关联。第三在CAN驱动和CanTrcv里确认对应的硬件Channel和收发器配置正确。如果你用的收发器是TJA1145这类支持部分网络功能的芯片还要额外检查收发器配置不然RTM报文可能在休眠唤醒后发不出来。配置完成后生成代码你会看到Rtm.c、Rtm.h文件被一起生成出来。打开Rtm.h看一下里面会有RTM的接口声明和数据结构定义。3.4 让RTM跑起来Hook、启动与SWC调用生成代码只是第一步RTM要真正跑起来需要三处代码配合。第一处是Os Hook。RTM依赖Os的PreTaskHook和PostTaskHook来采集任务切换时间点。在Os配置里要把这两个Hook使能并且确保RTM的Hook函数被调用。Vector生成的Os代码可能会自动把RTM的Hook函数嵌进去但如果你用了非Vector的OS或者做了二次开发就需要手动在Os的Hook里调用RTM对应的Hook函数。具体函数名以你生成的代码为准通常是Rtm_PreTaskHook和Rtm_PostTaskHook。第二处是启动调用。RTM需要在系统启动阶段完成初始化和启动测量。一般在EcuM的启动序列里等Os和GPT初始化完毕之后调用Rtm_Init再调用Rtm_StartRTM启动测量。如果漏掉了Rtm_StartRTMRTM虽然初始化了但不会开始采集数据表现就是负载值全为零。第三处是应用层读取。你要在SWC里创建一个Runnable周期读取RTM结果然后写到某个信号里。参考代码如下#include Rtm.h #include Rte_CPULoad.h void Rte_CPULoad_10ms(void) { uint16 loadTotal 0u; uint16 loadMax 0u; Std_ReturnType ret1 Rtm_GetCPULoad(RTM_CPU_LOAD_TYPE_TOTAL, loadTotal); Std_ReturnType ret2 Rtm_GetCPULoad(RTM_CPU_LOAD_TYPE_MAX, loadMax); if (ret1 E_OK ret2 E_OK) { /* 注意单位RTM返回的是千分比除以10才是百分比 */ Rte_Write_R_CPULoad_LoadTotal((uint8)(loadTotal / 10u)); Rte_Write_R_CPULoad_LoadMax((uint8)(loadMax / 10u)); } }这里有个经验读取RTM数据时要把单位换算搞清楚。Vector的RTM返回的负载值一般是千分比也就是875代表87.5%。如果直接把875发到CAN总线上位机再按百分比解析就会多出10倍数据完全失真。4. 实测CPU负载验证RTM数据是不是真的4.1 CANoe里怎么看RTM报文代码集成完毕烧录到板子上之后打开CANoe加载DBC文件或者手动配置报文。在CANoe的Graphics窗口里把CPU负载信号拖进去然后开始采集。正常情况下你会看到一条负载曲线随时间变化。系统跑在低负载工况时曲线比较平坦稳定在百分之二三十左右一旦有高负载任务跑起来曲线会跟着抬升。这里要特别说明一个技巧不要只看瞬时值要看趋势。CPU负载是统计量瞬时值受调度抖动影响比较大重点是观察平均值和最大值。建议在CANoe里把采样周期设置到100ms以上曲线会更平滑易读。下面是我在一个示例工程里的实测数据仅供参考不同平台差异会很大测试场景总CPU负载10ms任务负载最大负载系统空闲仅周期任务大约25%大约15%40%增加一路CAN通信全速收发大约45%大约20%60%人为缩短关键任务周期至1ms大约85%大约40%93%看到曲线后先别急着高兴。你要验证RTM测的数据是否合理最直接的办法就是人为改变系统负载。比如我测试时把某个任务周期从5ms改成1ms观察到总负载明显上升说明RTM是敏感的、可信的。如果改了任务周期负载却纹丝不动那就是RTM压根没在统计这个任务要检查任务是否被加入RTM的测量列表。4.2 用负载数据定位系统性能瓶颈RTM的价值不只是测一个总负载数字它还能帮你定位是哪个任务在抢CPU时间。在RTM的多任务负载模式下你可以单独获取每一个被监测任务的负载值。接口一般是Rtm_GetTaskLoad传入任务ID和负载类型返回这个任务在窗口内的CPU占用率。工程实践里我通常会在代码里把几个重点任务的负载分别做出来比如OsTask_10ms周期性控制主任务OsTask_100ms状态机处理任务OsTask_CAN_TxCAN发送任务OsTask_NvMNvM写任务这样当系统出现CPU资源紧张的时候打开CANoe一看就知道是哪个任务吃掉了大部分CPU时间不用瞎猜也不用一台台接调试器去抓。实测时还会遇到一个现象CPU总负载不高但某个任务偶发超时。这种问题RTM的总负载指标反映不出来需要看任务执行时间的分布。可以调用Rtm_GetTaskExecutionTime之类的接口把任务最长执行时间、最短执行时间拉出来。如果一个任务的执行时间偶发性地飙高即使平均负载不高也说明这个任务内部有偶发的长路径需要去查临界区、等待事件或者外部资源竞争。很多性能问题排查到这一步才有实质进展。RTM的数据能帮你把几十个任务、上百个函数的问题范围缩小到几个嫌疑对象这是最有价值的地方。5. 避坑点合集实测中值得记录的8个坑5.1 时间戳回绕导致负载值超100%这个问题我第一次遇到时很困惑CPU负载显示成300%多明显不对。后来查代码发现是时间戳回绕问题。当定时器计数溢出时两个时间戳直接相减会得到一个异常大的值RTM把这个错误值当成了任务执行时间。解决方法是确保时间戳函数正确处理了计数器回绕。AUTOSAR RTM规范里对时间戳函数有回绕处理要求但如果你自定义了时间戳获取函数或者底层驱动没有做正确的溢出补偿这个问题就会暴露。检查一下你的时间戳函数是否用了无符号数相减并且时间戳范围是否足够覆盖最长测量窗口。5.2 负载值单位搞错前面提到过RTM返回的负载值在Vector实现里常用千分比表示范围0到1000对应0到100%。很多工程师第一次用的时候拿到的最大值显示1000还以为是爆表了实际上那是100%负载。这个坑在数据对接时特别容易出事。如果上位机或者诊断仪那边按百分比解析你发出去的875会被当成87.5%没问题。但如果你直接把这个值存到NvM或者用于计算就要非常清楚单位是千分比。5.3 Os Hook被其他模块占用RTM依赖PreTaskHook和PostTaskHook但这两个Hook往往也被其他模块或者自研代码使用。比如有些团队会在PreTaskHook里做自己的时间片统计、喂狗、或者软件计数器累加。如果Hook函数里同时存在多个模块的逻辑要注意调用顺序RTM的Hook函数必须保证在操作系统每次任务切换时都被调用到不能被某些条件分支跳过。我踩过的坑是自研代码在Hook里加了一个if判断只有某个标志位满足时才调用后续的Hook函数导致RTM有时候能采集到数据有时候采集不到任务负载值忽高忽低。5.4 调试模式下负载值虚高用调试器跑的时候CPU负载明显比release模式高出一大截这个现象是正常的。调试器断点、printf重定向到串口、ITM日志输出、J-Link RTT这些都会占用CPU时间。如果你是用调试器观察RTM数据来评估系统性能测出来的结果不是真实运行负载。实测的时候建议用release编译配置去掉调试输出烧录后单独跑通过CAN总线观察数据。这样才能反映产品的真实CPU负载。5.5 RTM_CAN报文发不出去配置了RTM_CAN之后CANoe里一直看不到RTM报文排查半天发现是ComM状态的问题。RTM挂在ComM的某个Channel下面如果这个Channel的状态不是COMM_FULL_COMMUNICATIONRTM的CAN发送就不会执行。这也是低功耗唤醒后RTM不工作的常见原因。解决办法有两个方向一个是在测试阶段把RTM配置成不依赖ComM的状态直接由应用层周期触发发送另一个是确保测试时整车网络通信正常让ComM进入全通信状态。如果是诊断电或者台架测试很容易忽略上位机没有发网络管理报文导致ComM一直没完全唤醒。5.6 多核平台只测到了一个核的负载现在很多车规MCU是多核的TC3xx、RH850这些芯片都是多核架构。RTM的测量是基于Os任务调度的它只能统计它所在的核上的任务负载。如果你在Core0上跑RTM但实际负载热点在Core1上那你看到的总负载就会比真实系统负载低很多。多核平台如果要测全系统负载每个核都需要配置对应的RTM实例分别采集然后再由应用层把多个核的负载综合起来。如果产品没有硬性要求至少要清楚当前RTM数据代表的是哪个核的情况别拿着Core0的数据说整个ECU的负载。5.7 NvM块大小对齐问题RTM的历史最大负载、历史平均负载需要通过NvM保存掉电不丢失。NvM块大小配置不正确最直接的后果就是负载数据读回来是乱码或者NvM写操作一直报错。配置NvM块时先看Rtm.h里RTM数据结构体的大小然后按平台的字长做对齐。AUTOSAR平台的NvM块大小一般要求按2字节或4字节对齐具体看MCU架构。我曾经在32位平台上把块大小填成了结构体的原始字节数没有做4字节对齐结果每次读出来都错位排查了很久才发现是这个原因。5.8 RTM本身的开销会影响负载测量结果RTM不是免费的。时间戳中断、Hook函数调用、主函数里的数据汇总这些都会消耗CPU时间。如果把时间戳周期配置得太短比如1us那定时器中断本身的开销就可能占到CPU的5%到10%测出来的负载自然偏高。实测时要做权衡调试阶段可以用较高的采样频率换取精度量产阶段建议降低采样频率或者干脆关闭RTM。我在量产版本里一般把RTM的时间戳周期从10us放宽到100us只保留一个低频率的负载统计报文这样RTM自身开销可以控制在1%以内。如果你判断系统负载本身就在临界值附近那RTM带来的这部分开销也需要纳入考虑别测完才发现余量被测量模块自己吃掉了。最后再分享一点实际体会RTM这种模块放在整个AUTOSAR BSW里不算起眼但真正做系统性能调优的时候它就是最直接的信息来源。我用RTM处理过一个真实问题某ECU偶发通信超时现象随机复现困难后来就是在RTM里加了关键任务的执行时间统计抓了几天日志发现是某个诊断相关任务在特定条件下执行时间从正常的上百微秒飙到几十毫秒顺着这条线才定位到问题。没有RTM这种偶发问题靠人盯现场几乎不可能复现。所以我的建议是项目里尽早把RTM集成进去从开发初期就给每个软件版本建立一个“性能档案”记录标准工况下的CPU负载、关键任务执行时间、最大负载这些指标每次版本迭代做一次对比。这样性能回归一测就知道不用等问题爆发了再去排查。RTM自身那点开销相对它带来的可观测性是完全可以接受的。
返回列表