ARTICLE DETAIL

资讯详情

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

UDS刷写日志离线分析:从CAN原始帧到诊断回放

UDS刷写日志离线分析:从CAN原始帧到诊断回放 深夜十一点半产线那边转来一封邮件某车型ECU刷写偶发失败抓回了一大包CAN日志压缩完还有六十几兆让我帮忙看看。我打开日志目录扫了一眼里面密密麻麻的.asc文件——不用想里面是几百万行的CAN原始帧全是时间戳加十六进制数据。在Excel里翻这种文件和在一座没有索引的图书馆里找一句话没什么区别。这种场景我碰过太多次了。所以后来只要有人问我UDS刷写日志到底怎么分析最省力我基本上都会推荐借助开源的UDS/ISO-TP离线分析工具先把原始总线数据还原成哪一秒发了哪个服务、ECU回了什么NRC、卡在哪一步的诊断回放再做判断。这类工具能做的事一句话就能说明白把CAN总线上抓到的原始帧按ISO-TP传输层重组、UDS应用层解析最终输出一份带时间线、带错误定位、带耗时统计的刷写过程报告。它特别适合三类人——做Bootloader刷写开发的、搞售后诊断复现的、以及在台架和产线反复做刷写验证的工程师。这篇文章我就从底层还原链路讲起把离线分析工具的设计思路、核心解析逻辑、实际用法和我踩过的坑一次性说透。1. 一线刷写排查为什么这么依赖能回放的离线日志1.1 刷写日志到底长什么样几百兆CAN帧不等于看得懂的诊断记录先看一段最典型的CAN原始日志长什么样很多工程师应该不陌生0.123456 RX 0x7E0 8 02 10 02 00 00 00 00 00 0.123789 TX 0x7E8 8 02 50 02 00 00 00 00 00 0.124056 RX 0x7E0 8 10 14 27 01 AA BB CC DD 0.124321 TX 0x7E8 8 30 00 00 00 00 00 00 00 0.124588 RX 0x7E0 8 21 11 22 33 44 55 66 77 ...每一行代表CAN总线上的一条报文四要素分别是时间戳、收发方向、CAN ID、最多8字节数据。问题在于一次完整的UDS刷写流程尤其是带Security Access27服务和分段传输34/36/37服务的操作产生的报文动辄数万条。比如36服务传输数据一条完整的逻辑数据块可能被拆成几十上百个连续帧CF每个CF只有7字节有效载荷而ECU回的是单字节流控确认。这些报文的含义完全分散在帧头标志、扩展地址、数据场偏移和时序关系里根本没法通过肉眼把某一条36请求和它对应的下载逻辑地址、块序号对应起来。所以刷写日志离看得懂还隔着一整条协议栈。换句话说你在日志里看到的是一堆拆散的快递包裹每辆卡车上装了哪几件包裹、包裹里是什么货物得靠协议解析工具把包裹按运输批次打开、分拣、再按收件人归类。1.2 从地上跑的到看得懂的缺的就是中间这层翻译CAN日志之所以难分析是因为它跨越了三层协议应用层跑的是UDSISO 14229-1传输层跑的是ISO-TPISO 15765-2再往下才是CAN数据链路层。打个比方UDS服务是你要寄的货物ISO-TP是把大货物装箱的集装箱CAN帧则是拉集装箱的卡车。每辆卡车运什么完全取决于集装箱上的标签而你把CAN日志抓回来的时候记录到的恰恰只有卡车本身——集装箱里的货物清单需要你自己去拆箱。在线诊断工具比如很多商业总线分析软件的Diagnostics模块能实时完成这层翻译但它的瓶颈也在在线两个字上你得有硬件盒子、有授权、软件还得开着历史日志更是没法批量回放。离线分析工具的意义正在于此——它把这层翻译从实时环境里解放出来变成一行命令就能对任意历史日志反复执行的事情。这在复现偶发故障、批量分析路试验证数据、或者在CI环节做回归测试时价值是实打实的。2. 日志从CAN原始帧到刷写故事的还原链路2.1 第一关ISO-TP报文重组SF/FF/FC/CF的状态机拿到一堆原始CAN帧离线工具要做的第一件事是把ISO-TP层的数据块还原出来。ISO-TP报文一共四类识别方法全部写在CAN数据场的第一个字节里单帧SF首字节高四位是0表示整条消息压在一个帧里后面最多跟7字节数据。首帧FF首字节高四位是1表示这是一条多帧消息的开头低四位和第二个字节一起组成12位长度说明整条消息有多长。流控帧FC首字节高四位是3这是接收方发给发送方的你继续发信号同时告知每次可以发多少帧、间隔多长。连续帧CF首字节高四位是2表示这是多帧消息的第N个后续分段低四位放着顺序号。重组的多帧消息逻辑上是这样一条状态流收到FF - 记录总长度 - 发送FC - 期待CF SN1 收到CF SN1 - 期待CF SN2 收到CF SN2 - 期待CF SN3 ... 累积长度达到总长度 - 输出完整消息这里有个很容易写错的地方连续帧的序号SN只在0到15之间循环而且每收到一帧SN必须严格加1一旦出现跳号说明中间丢帧了。一次合格的传输层重组至少要维护当前期待序号、已接收长度、总长度、最后接收时间这几个状态。我见过很多半吊子解析脚本只按首帧长度截取数据、完全不管序号结果在遇到丢帧重传的日志时解析出来的UDS消息全是错位的。2.2 第二关UDS服务与子功能识别ISO-TP把我们恢复出来的完整消息交到应用层UDS解析就开始干活了。刷写场景下最常见的一组服务离线分析工具必须第一时间识别出来服务ID服务名刷写场景中的作用0x10DiagnosticSessionControl切换编程会话0x27SecurityAccess安全访问、Seed/Key解锁0x22ReadDataByIdentifier读版本号、刷写前状态0x2EWriteDataByIdentifier写配置、写标定0x31RoutineControl擦除、校验、复位等例程0x34RequestDownload请求下载声明地址和大小0x36TransferData分段传输固件数据0x37RequestTransferExit结束传输、请求校验0x11ECUReset刷写完成后复位ECU0x19ReadDTCInformation刷写后读故障码确认状态真正的功夫在负响应上。ECU每次拒绝服务都会回一条7F 本服务ID NRC比如7F 36 72表示36服务被拒绝原因是一般编程失败0x72。同样的日志不同工程师看到7F 31 22和7F 31 24的判断路径完全不同0x22是条件不满足通常是安全访问没过0x24是请求序列错误通常是上一步没做完就发了下一步。离线工具的价值就是把这串十六进制自动翻译成人话并且顺着时间线告诉你是哪条请求触发的、距离上一条成功响应隔了多久。2.3 第三关把点状事件拼成流程时间线单个服务解析出来还不够刷写日志分析的终极目标是把一堆点状事件还原成一条流程时间线。一次完整刷写通常会按这个顺序走预编程检查DTC、读写版本→ 切换编程会话10 02/10 03→ 安全访问27 01/27 02→ 擦除/下载31 01 xx 或直接34/36/37→ 传输完成校验 → ECU复位。离线工具会自动识别这些阶段并且把每个阶段的耗时、每条请求的响应时间、NRC出现次数都统计出来。这里我特别想说一下0x78响应待定ResponsePending的识别逻辑。ECU在擦Flash或者算校验和的时候经常几十上百毫秒给不出结果于是先回一个7F xx 78让主节点别超时。分析工具如果只把0x78当成普通负响应标红那满屏都是错误什么都看不出来。所以工具必须追踪从请求发出到真正正响应之间的完整等待过程并把7F xx 78归类为等待中状态而不是失败状态。这一步做得好的工具报告里才能出现某阶段P2/P2*等待次数、最长等待时间这类对排查偶发超时极有用的指标。3. 核心解析器的关键设计SF/FF/FC重组与UDS服务状态机3.1 时间戳精度、DBC映射与CAN ID过滤先想清楚线再动手解析离线工具看起来是读日志、吐报告两步但真正用的时候第一步其实是配置输入。你至少要告诉它四件事才能保证解析正确。第一个是日志格式。.asc、.blf、.trc、.csv都有不同的时间戳和帧格式哪怕同样叫.ascVector的CANoe和PCAN的规则也有差异。第二个是诊断ID对比如0x7E0是物理请求ID、0x7E8是物理响应ID0x7DF是功能请求ID。这里有个刚入门的人特别容易忽略的点有的ECU在编程会话下请求ID和响应ID不变但有的会切换到一组编程专用ID比如0x7E2/0x7EA你如果只过滤默认ID日志会凭空消失一大半。第三个是时间戳单位有的日志是秒、有的毫秒、有的微秒单位错了后面所有超时统计全是废的。第四个是DBC映射——如果你需要对报文做信号级分析比如看某个状态的位变化那就得提供DBC文件让工具能把CAN ID映射成报文名、把数据场映射成信号。我习惯在动手解析之前先在工具里把这四项配置全部填好然后挑一段三秒左右的日志做个冒烟测试确认SF帧能正常出来、多帧能正常重组、UDS服务名能正常显示再跑全量。3.2 传输层状态机里最容易漏的两个场景流控帧丢失和跨帧超时传输层重组的大多数逻辑在教科书上都有真正考验工程经验的是边界场景。第一个是流控帧丢失。ISO-TP里发送方发完首帧后会等待接收方回流控帧FC这个等待在ISO 15765-2里有明确的时间上限N_Bs和N_Br时间参数。如果日志里出现FF之后等了很久都没有FC最后发送方放弃的情况说明总线负载太高或者接收方的接收缓冲区没及时腾出来。好的离线工具必须能把这类事件显式报告为等待FC超时而不是直接把首帧之后的连续帧当作独立报文解析——很多解析脚本正是栽在这里把一堆本来属于同一条多帧消息的CF拆成了无数条单帧上层UDS全乱。第二个是跨帧超时。多帧传输的连续帧之间不是无限间隔的接收方如果超过N_Cr时间没收到下一帧会直接丢弃整个消息。在日志里这意味着重组状态机必须在收到FF但没有后续CF或者收到CF但序号跳变的时候能及时把缓冲区清掉并输出一条消息不完整的告警。如果你写解析器时没有做这个状态超时复位缓存里的脏数据会一直跟着后面的帧拼下去出来的UDS服务ID是伪造的整个分析报告都会失真。3.3 UDS服务层的会话级状态安全访问的seed/key、27服务序列校验UDS不像HTTP那样每个请求都是独立的它是有会话状态的应用层协议。解析器如果只做单帧翻译、不维护会话状态很多问题的根因就看不出来。最典型的例子是安全访问。刷写流程里34请求下载之前通常必须先过27服务客户端发27 01请求种子SeedECU回带随机数的种子客户端算好Key之后发27 02ECU验证通过才回正响应。离线工具只有记住了当前会话安全状态是locked还是unlocked才能判断后续的34请求、31擦除例程被拒是不是因为安全访问没过。我看到过一种常见问题日志整个刷写过程里34一次都没成功机械地看单条报文会以为是地址或长度参数错了但顺着会话状态查其实是27 02的Key算错了ECU一直没解锁——这两者的修复方向完全不同。另一个服务层的重要状态是P2/P2定时器。UDS规定ECU对请求默认要在P2一般50ms内响应超过P2但还没准备好时可以回0x78然后它就有最多P2一般5000ms的时间继续干活。离线工具如果在报告里标注出这条36请求等了3.2秒才拿到正响应期间收到了11次0x78工程师一眼就能判断是Flash擦除慢还是文件系统忙而不是对着时间戳手数。4. 命令行动手实操解析一份典型刷写日志并产出报告4.1 输入输出格式与命令行用法示例不同工具的具体参数有差异但离线分析工具常用交互方式基本是命令行加输出文件。假设工具叫flashlog-analyzer开源社区里这类工具很多用法大同小异一份典型命令长这样flashlog-analyzer report \ --input flash_2024_06_01.asc \ --format vector \ --request-ids 0x7E0,0x7E2 \ --response-ids 0x7E8,0x7EA \ --output report.html这些参数的含义逐条说一遍--input输入日志文件支持绝对路径和相对路径。--format和前面说的时间戳/帧格式强相关最常选vector.asc、pcan.trc、csv三种。--request-ids物理请求ID列表多个ID用逗号分隔有的ECU在编程会话里会切ID所以这里要写全。--response-ids对应的物理响应ID列表。--output报告输出路径HTML格式通常带时间线和表格比纯文本好读。我自己的使用习惯是先用--dump参数导出一份纯文本的完整服务时间线每行一条UDS消息包含时间、方向、服务名、子功能、NRC。先看时间线定位大方向再开HTML报告看统计和阶段切分。小日志用纯文本足够大日志几十万帧以上建议直接上HTML毕竟浏览器里筛选比终端翻页舒服得多。4.2 拿一份实际日志走查从服务序列到失败根因我拿一份典型的失败刷写日志做个走查。时间线还原之后关键段长这样0.0000 - 10 02 DiagnosisSessionControl(ProgrammingSession) 正响应 0x50 02 0.0234 - 27 01 SecurityAccess(SeedRequest) 正响应 0x67 01, seed0x11223344 0.0689 - 27 02 SecurityAccess(KeySend) 负响应 0x7F 27 35 0.1267 - 10 02 DiagnosisSessionControl(ProgrammingSession) 正响应 0x50 02 0.1502 - 27 01 SecurityAccess(SeedRequest) 正响应 0x67 01, seed0xAABBCCDD 0.1924 - 27 02 SecurityAccess(KeySend) 负响应 0x7F 27 35 ...看到没有ECU两次都在27 02返回0x35InvalidKey也就是Key验证失败。如果不看这个序列直接去翻34请求你会看到后面所有34请求全被NRC 0x24或者0x33打回来很容易误判成地址范围问题或者安全等级不够。但从服务序列上看根因很明确算法或者种子解析环节有问题Key压根算不对。再拿一份正常日志的下载阶段做对比1.0520 - 34 00 44 10 00 20 00 00 00 RequestDownload 正响应, maxBlockLen0x100 1.0550 - 36 01 [256 bytes] TransferData 正响应 1.0890 - 36 02 [256 bytes] TransferData 正响应 ... 23.4520 - 37 00 RequestTransferExit 正响应 23.4580 - 31 01 FF 00 RoutineControl(CheckChecksum) 正响应 23.4600 - 19 02 0F ReadDTCInformation 正响应, 无DTC 23.4700 - 11 01 ECUReset 正响应这份日志里能读出的信息量很大34声明的下载地址是0x10020、总字节数0x0000080036按0x100字节一块传、每个块都单包正响应全程只用了22秒没有任何0x78。工具如果能把每块36的传输耗时单独显示出来还能进一步定位是每一块都慢还是某一块突然慢——后者往往指向Flash写入策略或者看门狗喂狗时机的问题。4.3 报告里该重点看的体检指标一份成熟的离线分析报告除了时间线一定会给出几个统计维度。我拿到报告先看这几项基本能过滤掉七八成的问题指标正常范围参考异常信号总刷写耗时视固件大小而定通常几秒到几十秒某阶段耗时占比异常偏大下载阶段吞吐率固件字节数/下载耗时明显低于均值说明流控/等待太多单块36传输平均耗时几毫秒到几十毫秒个别块耗时是平峰值的数倍P2超时次数0次出现即说明有响应超过50ms且没有0x780x78响应待定次数视ECU Flash算法而定刷写阶段大量出现但最终失败往往是写Flash失败NRC分布无某一NRC集中出现直接指向根因安全访问尝试次数1次成功多次失败后成功Key算法/种子有问题阶段切换顺序预编程→编程→下载→校验→复位顺序错乱或重复切换说明Bootloader状态机异常这些指标不需要你自己去逐条数工具把这些统计算清楚剩下的就是人做判断了。5. 我用这类工具时踩过的坑与值得保留的工程习惯5.1 坑1时间戳单位/时区/周期计数混用导致毫秒对不上有次我拿到一批从国外测试场发回来的日志解析出来所有UDS请求的响应时间都在几百毫秒以上P2超时标红一大片。对着原始文件核对发现部分文件的时间戳是UTC时间部分抓取工具用的是设备开机后的相对计时还有一个文件的时间戳只有周期计数没有单位换算——三种混在一起工具默认按微秒解析结果全乱。从那以后我给自己定了一条规矩任何日志进分析工具之前先检查头部元数据或者文件后缀把时间戳单位统一换算好。这也是很多工具为什么要支持时间戳单位检测功能的原因不是锦上添花是救命用的。5.2 坑2多ECU同时响应时ID过滤不全导致串线还有一个高频坑车上不止一个ECU响应诊断请求尤其是有网关的时候。某个节点发了一条功能寻址的诊断请求多个ECU同时回响应如果你过滤条件里只写了主控ECU的响应ID其他ECU的响应就会被忽略时间线看起来就像主控ECU响应很慢。反过来如果请求ID过滤范围太宽把别的ECU的请求也当成了主控的服务序列会塞进大量无关的周期报文阶段切换判断就会错。我现在的做法是先不加过滤跑一遍统计出所有涉及诊断交互的CAN ID再按实际节点划分请求/响应ID对最后才生成正式报告。多花两分钟少走两小时弯路。5.3 坑3刷写成功后并不代表一切正常——校验与复位阶段容易被忽略最后一个坑是心态方面的。很多人看到36传输阶段全绿、37退出传输也正响应了就认为刷写成功。但刷写完成的定义远不止数据写完还要看校验和复位的表现。有次一台ECU反复上电后配置丢失日志里传输阶段非常干净问题出在传输退出后的31例程校验——工具报告显示校验例程返回的Checksum和Bootloader内部算的不一致但那个文件我最初只看传输阶段根本没注意到。从那以后我把报告里的收尾阶段也圈为重点37之后的校验例程是不是正响应、19服务读出来的DTC里有没有新故障、11复位之后有没有在预期时间内重新上线。这些在诊断规范里都有明文要求但在实际操作里最容易跳过。说到最后我个人在使用这类工具上还有一个习惯性的建议——不要把它定位成临时排故工具而是当成团队共享的刷写过程回放语言。把解析规则、ID配置、阶段划分逻辑沉淀成一个工程化的脚本或者基础配置文件纳入仓库管理新同事接手时不用从头啃十六进制出问题时的沟通成本会低非常多。毕竟刷写问题最磨人的从来不是技术本身而是我说的是这一帧你说的是那一帧两边的上下文各讲各的。有一份能被离线工具稳定还原、随时复查的时间线在手里很多争论在看第一眼报告的时候就结束了。
返回列表