最近课题组里面要用 QCAT 分析一些从手机里面采集出来的高通基带的数据. 这个 “重任” (其实当时我们并不觉得这是什么重任, 毕竟就是个用 QCAT 导出数据的活) 落到了我头上. 于是我用一个小数据样本开始折腾 QCAT 导出. 在小样本上, 导出还算顺利; 但最终当我们需要在所有 (~300GB) 的 log 中导出数据的时候, 我们遇到了显著的问题, 无法一次性打开这些文件 (QCAT 会慢的要死, 打开所有的文件可能需要半个小时? 但是谁知道 QCAT 到底有没有动, 是不是直接就死掉了), 需要一个一个完成. 既然如此, 写脚本就是必要的了. 于是我和 QCAT 的脚本斗争了两三天, 气得要死; 在和学长讲应该如何使用的时候, 学长表示,

你这应该记录下来, 不然过个几个月就又没有人知道该咋整了.

故作此.

什么是 tmd QCAT

(from ChatGLM)

高通QCAT是一款专为高通芯片设备设计的诊断日志分析工具,其核心功能在于深度解析设备运行过程中生成的底层诊断数据。该工具能够将原始的二进制日志文件(如.dmc或.qmdl格式)解码为人类可读的结构化信息,全面覆盖无线通信协议栈的各个层面,包括但不限于信令交互、数据传输、射频参数及系统状态。通过强大的过滤与搜索机制,用户可精准定位特定事件或异常片段,例如追踪VoLTE呼叫建立失败的具体信令节点,或分析LTE/NR网络切换过程中的参数冲突。同时,QCAT提供多维度的可视化分析能力,将复杂的协议流程、信号强度变化、吞吐量波动等数据转化为直观的图表与时序图,辅助工程师快速识别性能瓶颈。此外,工具支持生成结构化分析报告,并可通过脚本接口实现自动化处理,显著提升大规模日志的排查效率。其功能设计聚焦于通信问题的根因定位,从协议一致性验证到射频异常捕获,为设备调试与网络优化提供关键的技术支撑。

通过命令行导出数据

QCAT 的文档中介绍了两种方式, 一种是通过命令行导出数据, 一种是通过 Python wrapper 导出数据. 对于命令行方式, 其给出了一些参数; 对于 Python, 其给出了一些示例.

首先我尝试了通过命令行导出数据. 按照文档, 如果要使用命令行导出大致如下:

The format of analysis output command is as follows:

1
2
3
-export [options] Logfile
or
-export [options] Directory

If the name of a log file is given, then that file will be processed. If a directory is given, it will look for all .qmdl files within that directory, merge them, then run analysis on the merged file.

我们的 log 都是 hdf 文件, 所以这样就没法给他扔一整个文件夹了, 只能一个一个喂给他.

然后我就写了一行:

1
2
& "C:\Program Files\Qualcomm\QCAT7\Bin\QCAT.exe" `
-export "07-27.16-34-28-451.hdf"

看看会导出什么. 于是, QCAT 导出了一坨大的, 一个文件夹底下有一万个文件. 所以… QCAT 到底导出了什么?

经过了我的一番尝试, 我发现这个 export 功能会导出 “当你打开 QCAT 的时候 dispalys 栏里面所有勾选的内容”… 也就是说, 你需要 先确定你要导出什么, 再用脚本导出.

本来我以为这已经够逆天了, 结果我看到:

The analyzer option lets the user specify which analyzer they want to run. The names of the analyzers are in the format Name of Workspace; subfolder; Name of Analyzer. For example, if a user wants to run LTE PDCP DL Stats Summary, as shown in Figure 5-1, the command for this would be:

1
>-export -analyzer=”QCAT Sample;LTE;Summary;LTE PDCP Summary;LTE PDCP DL Stats Summary” mylogfile.hdf

我满心欢喜, 原来可以 Filter 啊! 于是赶忙添加了 Filter, 结果还是吐出来一大坨东西…

所以, 我请问了?

于是我老老实实开始在 QCAT 里面选择我要导出什么. 我清除了 Sample 中的所有东西, 选择了 User Workspace 的部分 Grid. 关闭 QCAT 后, 我又跑了一遍上述命令, 结果, 还是给我输出了一万个默认的 Grid. 怎么回事呢, 我请问了?

然后我又打开了 QCAT, 发现默认 Workspace 的那些 Log 又被勾选了, 重复好几次都没能取消选中. 我想, 得了, 别管了, 就这么用吧.

于是我写了一个 Python 脚本, 遍历某个指定目录里面的所有子目录的所有 hdf 文件, 然后使用 QCAT 导出. 万万没想到, 这个脚本越跑越慢, 后来, QCAT 干脆就打不开了. 为什么呢?

然后我实在受不了, 打开了任务管理器, 发现 QCAT 在打开的时候疯狂地读某一个文件. 我又打开了资源监视器, 它告诉我文件是 C:\Users\Admin\AppData\Local\QCAT.config. 不就是个配置文件嘛… 怎么要读这么久? 我打开文件管理器:

WTF QCAT Config

What the FUCK??? 这个配置文件有 8GB 大???

由于这个文件有 8GB 大, 我在 Windows 上面找不到一个可以很好打开并浏览其内容的工具. 所以我索性直接删掉了这个文件. 结果, 我之前的选项被清空了, 剩下的又回到了全选的状态.

后来我又看到了有

The workspace (-workspace=) option allows you to specify a workspace file to use instead of the default workspace loaded.

1
-export -analyzer=”/user/MyWorkspace.aws” mylogfile.hdf

你别问我为什么上面说的 -workspace, 底下写的 -analyzer我他妈不知道这个文档是怎么写的, 你们写文档的他妈的没有校验过吗???

但是不管怎样, 我知道了 QCAT 可以自己造一个工作区. 于是我 Save As 了一份当前的 User Workspace, 准备魔改. 我 Save 到了我自己的文件夹里面, 结果, 我找了 5min 愣是没找到如何 Open Workspace. 准确的说, Open Workspace 是个特殊的对话框, 里面会给你几个识别的 Workspace 让你选. 但是它 并没有 提供一个按钮让你在操作系统里面找文件. 换句话说, 只有 C:\Program Files\Qualcomm\QCAT7\Workspaces 里面的 .aws 文件才会在 QCAT 中被显示出来. 紧接着问题又来了, 我的额外的工作区在 QCAT 里面是 只读 的, 没有 “Edit” 选项, 没法往里面添加 Grid… 于是只能在 User 工作区里面添加需要的 Grid, 然后 Export Item, 然后把导出的 .awsi 文件用 VSCode 打开, 然后复制里面的 Grid XML 到自己的工作区里面, 然后重启 QCAT.

最后我总算是把自定义的工作区弄好了, 然后选中自定义的工作区, 选择需要导出的东西之后关闭, 再次打开总算是记住了需要导出的内容, 不会瞎搞了.

QCAT Grids

但是尽管如此, 有时候莫名其妙出现的 1GB 大的 config 文件的问题依然没有解决 (也没有找到具体的原因). 于是我使用了大抽象方式: 我删掉现有的 config, 打开一个 QCAT, 选中我需要的工作区和我需要的 log, 关掉 QCAT. 此时, QCAT 会生成一个正常的 ~100kb 的 config. 我直接使用 Windows 的喵喵文件权限管理功能, 勾选 “只读”, 在 “安全” 窗口里面给 everyone 增加了一个 “拒绝写入” 的条目 — 这下没人能更改这个 config 文件了 (当然, 你可以重命名这个文件以实现 更改 的功能, 但是 QCAT 不会这样).

终于! 我的脚本可以正常运行了. 组里面有两台装了 QCAT 的机器, 一台台式机, 一台笔记本. 本来我是在笔记本上面开发的, 当我开始运行的时候, 我发现它异常缓慢… 我打开任务管理器, 就看到 CPU 只有 1.4GHz. 我一脸懵逼地下载了 HWInfo, 然后发现它的 CPU 只有 7W, 而且, Limiting Factor 是 Power… 也就是说, 这台电脑的 PL1 是 7W…

我 不 理 解 !

于是后来我开始使用台式机提取 Log, 代码如下 (当然, 大部分是 Claude 写的; 本来里面有一些中文和符号 (Claude 的最爱), 但是在跨过 RDP 粘贴的时候, 这些玩意全都变成 ?? 了):

通过 Python 代码导出数据

本来我是觉得用 Python 代码会更好导出数据的, 后来证明我错了. QCAT 提供了一些 Sample Script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CloseFile.py
ConfigTest.py
DebugMsgFilter.py
EventFilter.py
ExportTextAndExcel.py
ExtractPcmFiles.py
filtermask.py
GetLogCodes.py
GetVersion.py
LogProcessingStatus.py
PacketFilter.py
PacketWindowSample.py
Perl/
TimeStampSample.py
TimeWindowSample.py

但是, QCAT 提供了这些 Sample Script…

或者说, QCAT 的 Python Binding 就 只有这些功能

发现了吗? 它似乎没有导出 Grid 的功能…

所以我一开始尝试了一点之后就放弃了.

后来, 我们要统计总共有多少包, 这次我使用了 Python Binding 来实现.

中间又有一万行委屈的故事, 直接跳到结论吧.

首先, 要用管理员模式打开 Python 脚本, 不然 QCAT 会慢的要死, 原因未知.

然后, QCAT 的依赖库是在脚本里面动态添加的,

1
2
3
4
5
6
7
from sys import platform
if platform == "linux" or platform == "linux2":
sys.path.append('/opt/qcom/QCAT7/Support/Python')
# 这里也有一行
elif platform == "win32":
sys.path.append('C:\\Program Files\\Qualcomm\\QUTS\\Support\\python')
sys.path.append('C:\\Program Files\\Qualcomm\\QCAT7\\Support\\Python')

所以要让 VSCode 能补全这个, 需要

1
2
3
4
5
6
7
8
9
10
{
"python.autoComplete.extraPaths": [
"C:\\Program Files\\Qualcomm\\QUTS\\Support\\python",
"C:\\Program Files\\Qualcomm\\QCAT7\\Support\\python"
],
"python.analysis.extraPaths": [
"C:\\Program Files\\Qualcomm\\QUTS\\Support\\python",
"C:\\Program Files\\Qualcomm\\QCAT7\\Support\\python"
]
}

(虽然最后也没什么可以补的, 所有东西统统都是 Any, 不看 Sample 谁也不会写)

1
2
3
4
5
6
7
8
9
10
def Set(self, filterType, logCode, bEnable):
"""
Parameters:
- filterType
- logCode
- bEnable

"""
self.send_Set(filterType, logCode, bEnable)
return self.recv_Set()

那么传入的参数是什么类型呢? 自己看 Sample 去吧!

不仅如此, 它的 Sample 里面甚至有 语法错误 (比如括号不匹配), 行尾有分号, 等等莫名其妙的问题, 很让人不理解写这个文档和 Sample 的人到底有没有用过 Sample…

还没完! 在命令行参数里面, 你可以传入一个 Filter 文件来配置 QCAT 的 Filter; 在 GUI 里面, 你设置好 Filter 之后可以关闭当前文件, 打开下一个 / 下一组文件, Filter 会自动生效; 但是在 Python Binding 里面 都不会 ! 你必须手动 Parse 这个 Filter File, 一个个设置 Filter, 然后打开一个文件, 然后弄完之后先关闭它, 然后再设置一遍 Filter, 然后才能打开下一个文件…

算了, 能用就行.

总结

QCAT 就是一坨大的