npm.io
0.11.4 • Published 2 weeks ago

hmtrace-parser

Licence
MIT
Version
0.11.4
Deps
1
Size
215 kB
Vulns
0
Weekly
0

hmtrace-parser

内核 trace 相关辅助:为 hiperf_txt_parser 提供 RLS 版本内核事件的 perf trace 文本格式块format: / print fmt:),以及对 checkpoint_revoked 事件中 revoke_reason 的解析与展开;另含 hitrace ftrace 文本 的解析、按 tgid 过滤、导出回 txt、fieldDict 解码、VFS 操作码可读化op_flag_readable / reason_readable)、FFRT gid 聚合tracing_mark_write / I13),以及供 hiperf 侧车合并使用的 hitrace 线程索引快照 构建。

依赖分层:本库直接依赖 hm-pt-core≥ 2.0.0)获取 TraceParserRegistryRecordSampleHitraceThreadIndexSnapshot 等公共类型与 trace format 解码原语;不再依赖 hiperf。解析 perf 文本、批量 raw 解码与 hitrace 侧车合并仍由 hiperf(≥ 2.1.0)提供,由调用方(如 snapshot_checker)自行组合。本库仅导出 trace 机制层、profile 策略层、hitrace 预处理与领域工具,不 re-export hm-pt-core 或 hiperf 已有符号。

源码按职责分层:

路径 职责
trace(机制) src/lib/trace/ 接收 TraceEventFormat[],构建 TraceParserRegistry
profile(策略) src/lib/profile/ RLS format 块、字段变换、getTraceEventFormatsop_flag 可读化
profile(续) cp_revoked.tsbacktrack.ts revoke_reason 解析、backtrack 配对策略
hitrace src/lib/hitrace/ ftrace 文本行解析、按 tgid 过滤、导出 txt、按 event name 解码 function 为 fieldDict、由 PerfData 合成 hitrace(buildHitraceDataFromPerf)、构建线程索引快照
  • Node.js ≥ 18
  • ESM"type": "module"

安装

npm install hmtrace-parser

与 hiperf 组合解码 perf 文本时,另装 hiperf_txt_parser@^2.1.0(及 hiperf 自带的 hm-pt-core 传递依赖)。

用法概览

主入口 hmtrace-parser

当前包仅发布主入口(exports["."]),聚合 trace、profile、hitrace、logger 各层导出:

import {
  parsePerfData,
  decodePerfRawData,
  type HitraceSidecarOptions,
} from "hiperf_txt_parser";
import {
  setHmtraceLogger,
  type TraceEventFormat,
  type TraceRegistryOptions,
  buildTraceRegistry,
  getTraceEventFormats,
  getTraceEventFormatProfiles,
  buildTraceRegistryFromProfile,
  expandCheckpointRevokedFieldDict,
  passthroughFieldDict,
  applyOpFlagReadableFields,
  parseOpFlagMask,
  opFlagMaskToReadable,
  VFS_OP_FLAG_BITS,
  backtrackFS,
  backtrackStrategyRls,
  resolveBacktrackStrategy,
  buildHitraceIndexSnapshot,
  buildHitraceDataFromPerf,
  decodeHitraceFunctions,
  hiperfEventNameFromSampleType,
  mapTraceFormatNameToHitraceFunctionName,
  buildHitracePidTaskMap,
  resolvePerfSampleTaskName,
  parseHitraceText,
  parseHitraceFile,
  formatHitraceRecordLine,
  saveHitraceDataToTxt,
  filterHitraceByTgid,
  filterHitraceRecordsByTgid,
  type HitraceData,
  type HitraceRecord,
} from "hmtrace-parser";
由 decoded PerfData 合成 HitraceData

buildHitraceDataFromPerf 仅依赖 hm-pt-corePerfData依赖 hiperf_txt_parser)。映射约定与 timeline.ts 一致:sample.pidtgidsample.tidpid;无任务名时 task 默认用 String(tid)。可选传入 pidTaskMap(或自定义 resolveTask):由 buildHitracePidTaskMap 自采集 hitrace 文本构建 hitrace.pid → task,用于丰富合成行任务名(monitor 阶段通常 pid 不重复;重复时保留首次)。

严格转换规则(任一条样本不满足即抛错):

  1. 每条样本必须有非空 traceFieldDict
  2. function.nametraceEventName(format name);print 映射为 tracing_mark_write
  3. function.contenttracePrintContent(print 模式渲染文本)
  4. function.fieldDicttraceFieldDict 拷贝

合成后可再走 decodeHitraceFunctions(预填充 fieldDict 时跳过 content 反解析,仍应用 transform)。

perf raw 解码(调用方组合)

本库提供 format 块与 registry 构建;解析 perf 文本与批量 raw 解码由 hiperf 完成。省略 profile 名时等价于 default / rls

import { parsePerfData, decodePerfRawData } from "hiperf_txt_parser";
import {
  getTraceEventFormats,
  buildTraceRegistryFromProfile,
} from "hmtrace-parser";

const formats = getTraceEventFormats();
const registry = buildTraceRegistryFromProfile(undefined, {
  readable: true, // 默认 true:追加 op_flag_readable / reason_readable
});
const perfData = parsePerfData(text);

const decoded = await decodePerfRawData(perfData, registry, {
  tracePrintMode: "fieldDict",
});
hitrace 与 perf 合并(hiperf ≥ 2.1.0)

hiperf 1.4.2 起,decodePerfRawData 支持 options.hitrace 侧车:传入预处理后的 HitraceThreadIndexSnapshot 与合并策略(fallback / overlay / off)。快照由本库 buildHitraceIndexSnapshot 构建:

import {
  parsePerfData,
  decodePerfRawData,
  type HitraceSidecarOptions,
} from "hiperf_txt_parser";
import {
  getTraceEventFormats,
  buildTraceRegistryFromProfile,
  buildHitraceIndexSnapshot,
} from "hmtrace-parser";

const formats = getTraceEventFormats();

const hitraceIndex = await buildHitraceIndexSnapshot(
  { filePath: "samples/bug.hitrace" },
  formats,
  {
    tracePrintMode: "fieldDict",
    readable: true, // 默认 true
  },
);

const hitrace: HitraceSidecarOptions = {
  index: hitraceIndex,
  strategy: "fallback",
};

const perfData = parsePerfData(perfText);
const decoded = await decodePerfRawData(perfData, registry, {
  tracePrintMode: "fieldDict",
  hitrace,
});

buildHitraceIndexSnapshot 入参支持 { filePath, encoding?, highWaterMark? } 或已解析的 { data: HitraceData }filePath 在内部通过流式 parseHitraceFile 读取)。快照仅含 timestampNsfunctionNamefieldDict(由 ftrace content 反解析,不合成 perf raw 字节);合并侧车时由消费方(如 snapshot_checker)写 traceFieldDict 并将 sample raw.size 置 0。options 默认剔除 function name 不在 formats 中的行;includeUnknownEvents: true 可保留未登记事件(空 fieldDict)。快照键为 ${tgid}:${pid}(hitrace 语义),与 hiperf RecordSamplepid(进程)/ tid(线程)对齐。

Logger
导出 说明
setHmtraceLogger 注入 trace / debug / info / error 日志实现;未调用时相关模块日志为 no-op
Profile 策略层(RLS)

内置 RLS trace 事件格式(TraceEventFormattext + 可选 transformFieldDict)。defaultrls 为同一套定义的别名;未知 profile 名回退至该定义。

导出 说明
getTraceEventFormats(profile?) 取 RLS 格式列表(浅拷贝)
getTraceEventFormatProfiles() 返回 ["default", "rls"]
buildTraceRegistryFromProfile(profile?, options?) 构建 TraceParserRegistry
expandCheckpointRevokedFieldDict / passthroughFieldDict 字段字典变换
applyOpFlagReadableFields / parseOpFlagMask / parseOpFlagMaskHex / opFlagMaskToReadable VFS 操作码映射与可读化
VFS_OP_FLAG_BITS 硬编码 bit → 名称表(与 samples/op_flag.md 一致)

当前事件(RLS):

事件 ID 说明
vfs_snapshot_dump_monitor 33220 namerevoke_idop_flag;hitrace content 为空格分隔 key=value
checkpoint_revoked 32945 checkpoint_monitor_idtyperevoke_reason;FS(type=0)时展开 revoke_reason kv
tracing_mark_write 298 buffer(ftrace content 整段字符串);解码时展开 mark_typetrace_tagis_ffrt 等;I13 为 FFRT 生命周期事件,进一步解析 ffrt_eventgidlabel
FFRT gid 聚合

采集时需打开 ffrt tag(tracing_mark_writeI13)。FFRT worker 线程名通常以 OS_FFRT 为前缀。

导出 说明
parseTracingMarkWriteContent(buffer) 解析 tracing_mark_write buffer 字符串为结构化字段
aggregateFfrtByGid(data, formats, options?) 解码后按 tgid + gid 聚合生命周期事件与 exec 片段内伴生事件
aggregateFfrtByGidFromRecords(records, options?) 对已解码记录聚合(不再解码)
FfrtGidAggregate lifecycleRecordsexecSlicescompanionRecordsanomalies

companionRecords 仅包含各 exec 片段 [exec_begin, exec_end] 时间窗内、运行于 OS_FFRT* 线程上的非生命周期记录;可通过 includeEventNames / excludeEventNamesfunction.name 过滤。

import {
  parseHitraceText,
  getTraceEventFormats,
  aggregateFfrtByGid,
} from "hmtrace-parser";

const data = parseHitraceText(ffrtTraceText);
const aggregates = aggregateFfrtByGid(data, getTraceEventFormats(), {
  tgid: 1000,
  gid: 17,
  includeEventNames: ["sched_switch", "cpu_frequency"],
});

for (const agg of aggregates) {
  console.log(agg.gid, agg.execSlices, agg.companionRecords.length);
}
VFS 操作码可读化(readable

映射表见 samples/op_flag.mdWRITEMMAP、…、SPLICE_WRITE,bit 0–13)。解码时在保留原字段前提下追加:

事件 原字段 可读字段 条件
vfs_snapshot_dump_monitor op_flag op_flag_readable 掩码可解析
checkpoint_revokedtype=0 reason reason_readable revoke_reason 已展开且 kv 中含 reason:<op_flag>%x 十六进制

规则摘要:

  • 多 bit 以 | 连接(如 WRITE|ACCESS
  • 含未知 bit 时打 error 日志,写入 readable 字段
  • readable: false 时不追加可读字段(适用于 buildTraceRegistrybuildTraceRegistryFromProfiledecodeHitraceFunctionsbuildHitraceIndexSnapshot

说明:checkpoint_revoked 展开后的 reasonvfs_snapshot_dump_monitorop_flag 均按内核 %x / %lx 十六进制解析(如 200x20ACCESS400x40FACCESS)。content/raw 反解析若已在 rawFieldMap 中给出数值,则优先用该数值计算 readable,避免十进制 stringify(如 32)被误当作 hex。

示例(type=0):

revoke_reason = "devid:9, fs:ab, index:1, reason:20"
  → 展开后 reason="20", reason_readable="ACCESS"   (0x20 = ACCESS)
Trace 机制层
导出 说明
buildTraceRegistry(formats, options?) TraceEventFormat[] 构建 TraceParserRegistry;默认包装可读化 transform

hitrace 与 perf raw 共用同一 registry:transform 先执行 profile 逻辑(如展开 revoke_reason),再追加 *_readable 字段。

Hitrace 层
解析
导出 说明
parseHitraceText(text) 解析整段 ftrace 文本为 HitraceData
parseHitraceFile(filePath, options?) 流式解析 hitrace 文件为 HitraceDataoptions: encoding?highWaterMark?
ParseHitraceFileOptions parseHitraceFile 选项类型
HitraceData 聚合类型,{ records: HitraceRecord[] }
HitraceRecord 单条 ftrace 数据行:taskpid(线程 tid)、tgid(进程 pid)、cpuflagstimestamp(秒)、function

ftrace 数据行形态:TASK-PID (TGID) [CPU] FLAGS TIMESTAMP: function: content。解析时跳过空行、# 注释与 tracer: 行;不保留文件头元数据。

parseHitraceText 逐行调用 parseHitraceLine,产出 HitraceRecord 列表(此阶段 function.fieldDict 尚未填充)。大文件请用 parseHitraceFile 流式读取,避免整文件载入单个 string。

过滤

面向解析结果中的 HitraceRecord,按 tgid(进程组 / 进程 pid)或 function.name 筛选:

导出 说明
filterHitraceRecordsByTgid(records, tgid) 过滤 HitraceRecord[],保留 record.tgid === tgid
filterHitraceByTgid(data, tgid) 同上,入参/出参为 HitraceData
filterHitraceRecordsByFunctionName(records, filter) 按事件名过滤 record 列表(见下 HitraceFunctionNameFilter
filterHitraceByFunctionName(data, filter) 同上,包装为 HitraceData
filterHitraceRecordsExcludingFunctionName(records, name) 按事件名黑名单排除(namestringstring[]
filterHitraceExcludingFunctionName(data, name) 同上,包装为 HitraceData
HitraceFunctionNameFilter 过滤条件:string(单事件白名单)、string[](多事件白名单;空数组表示不匹配任何行)、{ include?, exclude? }(白名单与黑名单可组合,include / exclude 均为 stringstring[]
导出

HitraceRecord / HitraceData 写回 ftrace 数据行 txt 文件(不含 tracer: 与注释头)。使用 saveHitraceDataToTxt 逐行流式写入,避免将全部内容拼接为单个 string(受 V8 max string 长度限制):

导出 说明
formatHitraceRecordLine(record) 单条记录 → 一行 ftrace 文本(不含行尾换行)
saveHitraceDataToTxt(filePath, data, options?) 逐行写入文件;默认 \n 分隔且末行后追加换行
SaveHitraceDataToTxtOptions lineEndingtrailingLineEndingencoding

写入后可通过 parseHitraceFile / parseHitraceText 再次解析,字段语义与导出前一致(往返测试已覆盖)。function.content 为空时,导出行在事件名后追加 :

典型流程:解析 → 按 tgid 过滤 → 写入文件:

import {
  parseHitraceText,
  filterHitraceRecordsByTgid,
  saveHitraceDataToTxt,
} from "hmtrace-parser";

const data = parseHitraceText(hitraceText);
const records = filterHitraceRecordsByTgid(data.records, 952);
await saveHitraceDataToTxt("out.hitrace", { records });
解码与侧车快照
导出 说明
buildHitraceIndexSnapshot(input, formats, options?) 解析 + 解码 + 线程分桶,产出 HitraceThreadIndexSnapshot

低层工具(如 decodeHitraceFunctionsbuildHitraceThreadIndexregistryByEventName)仍存在于 src/lib/hitrace/,供本库内部与测试使用,未从主入口导出。FFRT 聚合见上文 FFRT gid 聚合 小节。

Backtrack

在已解码、且 traceFieldDict 已填充的 RecordSample 上做 backtrack 配对。默认按 revoke_id 配对:vfs_snapshot_dump_monitor(backtrack op)与 FS checkpoint_revoked(invalid op)。

历史 devid + fs + index 配对须显式传入 backtrackStrategy: backtrackStrategyDefault(未从主入口导出,测试/internal 使用)。

导出 说明
backtrackFS(perfData | samples, options?) 返回 BacktrackResult[recordSamples, map]
backtrackStrategyRls 默认 revoke_id 配对策略
resolveBacktrackStrategy(profile?) 解析策略(当前恒为 RLS)

开发

npm install
npm run build   # 输出到 dist/
npm test        # 单元测试
npm run test:all  # 单元测试 + perf 测试

许可证

MIT

Keywords