npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hiperf_txt_parser

v2.3.3

Published

Parse perf data.txt and output structured TypeScript data

Readme

hiperf_txt_parser

将 perf 文本中的 record sample 段解析为结构化数据,并支持导出为:

  • perf 原文本格式(保留缩进和行前缀;若存在 traceFieldDict,会在 raw hex 行前输出 key: value 行)
  • JSON 数组格式(每项为 { "call_chain": "...", ...traceFields })
  • .xlsx(formatPerfDataToExcel,依赖包内 exceljs)

本项目为 纯 lib 库:src/ 为库实现,tests/ 为回归与性能用例,scripts/ 为本地基准;无 CLI、无 demo 子项目。发布到 npm 时仅包含 dist/(见 package.json 的 files)。

安装

npm install hiperf_txt_parser

在仓库根目录本地联调:

npm install .
npm run build

对外 API

import {
  parsePerfData,
  parsePerfDataFromFile,
  loadPerfData,
  formatPerfDataToText,
  formatPerfDataToJson,
  formatPerfDataToExcel,
  savePerfDataToText,
  savePerfDataToJson,
  savePerfDataToExcel,
  importPerfDataFromExportedJson,
  normalizeRecordSampleFromExportedText,
  filterByTgid,
  toBackTraceStack,
  toBackTraceStacks,
  buildTraceParserRegistry,
  decodePerfRawData,
  decodePerfRawDataSync,
  findHitraceSnapshot,
  findHiperfRecordsForHitraceSnapshot,
  hitraceThreadKeyForSample,
} from "hiperf_txt_parser";
import type {
  HitraceSidecarOptions,
  HitraceThreadIndexSnapshot,
} from "hiperf_txt_parser";
  • parsePerfData(text: string): PerfData
  • parsePerfDataFromFile(filePath: string): PerfData(扩展名为 .json 不区分大小写时按导出 JSON 解析,其它按 perf 文本解析)
  • formatPerfDataToText(data: PerfData): string
  • formatPerfDataToJson(data: PerfData): RecordSampleJsonExportItem[](call_chain 与 formatPerfDataToExcel 的 callchain 列同源:由 callchainFrames.frames 以换行拼接)
  • formatPerfDataToExcel(data: PerfData): Promise<Buffer>(.xlsx:traceFieldDict 的键集合相同的样本归到同一工作表;无字典或空字典时仅一列 callchain;有字典时表头为键名排序后的列并在末尾追加 callchain;recordSamples 为空会抛错)
  • importPerfDataFromExportedJson(jsonText: string): PerfData
  • normalizeRecordSampleFromExportedText(sample: RecordSample): RecordSample(从导出 txt 的 raw key: value 行回填 traceFieldDict)
  • filterByTgid(data: PerfData, tgid: number): PerfData(仅保留 pid === tgid 的 RecordSample)
  • toBackTraceStack(sample, options?): string
  • toBackTraceStacks(data, options?): PerfData
  • parseTraceFormat(text: string): ParsedTraceFormat(解析 sample/trace_format 风格文本)
  • parseCommonFieldsFromRaw(raw: Uint8Array, format: ParsedTraceFormat): Record<string, number | bigint>(按 format 仅解析 common_* 字段,小端)
  • rawHexLinesToBuffer(lines): Uint8Array(将 perf 文本里 raw 段的 hex 行拼成字节缓冲,便于喂给 parseCommonFieldsFromRaw)
  • buildTraceParserRegistry(formatTexts)(从 trace format 文本构建 event 解析注册表,支持按 format 绑定 fieldDict 回调)
  • decodePerfRawData(perfData, registry, options)(按注册表解码 raw,并可在 fieldDict 模式写回 key/value;成功时写入 traceEventName / tracePrintContent;字段解码失败降级为 decode_failed 而不中断整批;可选 options.hitrace 合并预处理 hitrace 索引;workerCount > 1 时返回 Promise)
  • decodePerfRawDataSync(perfData, registry, options)(始终在主线程解码,等价于 workerCount <= 1 的 decodePerfRawData)
  • decodePerfRawDataOneRow(sample, registry, options) / decodePerfRawDataOneRowWithHitrace(...)(单条 sample 解码,供自定义流水线复用)
  • findHiperfRecordsForHitraceSnapshot(samples, snap, thread, index)(反向查找:返回会被 findHitraceSnapshot 命中的 hiperf 记录)
  • loadPerfData(filePath, options?) / savePerfDataToText / savePerfDataToJson / savePerfDataToExcel(大文件流式读写;workerCount > 1 时走 Worker 池。TXT 解析在返回前会关闭并等待底层 ReadStream,调用方可立即覆盖或 rename 源文件)
  • traceFormatTextsNeedMainThreadDecode / traceFormatTextsToWorkerStrings(判断 formatTexts 是否必须主线程或可否传入 Worker)
  • compareAll / createCompositeCompare / defaultRecordSampleCompare / pickPerfDataMatchingOther / filterPerfData(RecordSample 比较与过滤)
  • dedupePerfData / dedupeItemsByCompare(按比较函数去重)

完整导出见 src/index.ts。

快速示例

import {
  parsePerfData,
  filterByTgid,
  formatPerfDataToJson,
  formatPerfDataToText,
} from "hiperf_txt_parser";

const input = `record sample: type 9, misc 2, size 520\n  sample_type: 0x8000107e7\n  ID 13`;

const parsed = parsePerfData(input);
const filtered = filterByTgid(parsed, 1234);
const jsonArray = formatPerfDataToJson(filtered);
const txt = formatPerfDataToText(parsed);

按文件路径解析(txt/json 自动分发)

import { parsePerfDataFromFile } from "hiperf_txt_parser";

const fromTxt = parsePerfDataFromFile("./sample/perf_data.txt");
const fromJson = parsePerfDataFromFile("./out/decoded.json");

fieldDict 回调示例(按 format 绑定)

import {
  buildTraceParserRegistry,
  decodePerfRawData,
  formatPerfDataToJson,
} from "hiperf_txt_parser";

const traceFormatText = `name: foo
ID: 77
format:
  field:unsigned short common_type; offset:0; size:2; signed:0;
  field:unsigned char common_flags; offset:2; size:1; signed:0;
  field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
  field:int common_pid; offset:4; size:4; signed:1;
  field:__data_loc char[] path; offset:8; size:4; signed:0;
  field:u32 len; offset:12; size:4; signed:0;
print fmt: "path=%s len=%u", REC->path, REC->len
`;

// 将回调与 format 直接绑定:只影响该 eventId(77)
const registry = buildTraceParserRegistry([
  {
    text: traceFormatText,
    transformFieldDict: ({ fieldDict }) => {
      // 仅在 tracePrintMode: "fieldDict" 时触发
      return {
        path: fieldDict.path ?? "",
        len: fieldDict.len ?? "0",
      };
    },
  },
]);

// perfData 为 parsePerfData(...) 的结果
const decoded = decodePerfRawData(perfData, registry, {
  tracePrintMode: "fieldDict",
});

// JSON 导出时,traceFieldDict 会平铺到 call_chain 同级
const jsonOut = formatPerfDataToJson(decoded);

hitrace 侧车合并(可选)

当 perf 样本的 raw 解码失败或需补充字段时,可传入由外部工具(如 hmtrace-parser 的 buildHitraceIndexSnapshot)预处理好的线程索引,在解码阶段按时间戳对齐合并。

  • 索引键:${sample.pid}:${sample.tid}(hiperf 中 pid 为进程 / tgid,tid 为线程;与 hitrace 桶键一致)。可用 hitraceThreadKeyForSample(sample) 生成。
  • 对齐:findHitraceSnapshot(index, sample) 在同线程桶内取 timestampNs <= sample.time 的最后一条(二分)。
  • 反向查找:findHiperfRecordsForHitraceSnapshot(samples, snap, thread, index) 按线程 → 时间窗口 [snap.ts, nextSnap.ts) → fieldDict 子集,找出会被正向对齐命中的 hiperf 记录。
  • 策略 HitraceSidecarOptions(显式字段,无 strategy 枚举):

| 字段 | 含义 | |------|------| | index | 线程 → 按时间升序的快照列表 | | order | perfOnly | hitraceFirst | perfFirst:路径执行顺序 | | afterPerf | 仅 order: "perfFirst":perf 之后何时 try hitrace(见下) | | overrideDecodedPerf | afterPerf: "always" 且 perf 已 decoded 时是否允许 hitrace 覆盖 | | timeThreshold | 默认 none;否则校验 sample.time - snap.timestampNs(ms),超出可 drop(视为未命中)或 ignore |

hitracePolicyPerfFirst 默认 afterPerf: whenPerfNotDecoded。其它取值:

  • never:从不 apply hitrace
  • whenPerfNotDecoded:perf.kind !== "decoded"
  • whenPerfNeedsFallback:无 raw / 解码失败 / 空渲染
  • always:有 snap 即尝试;覆盖已 decoded 的 perf 需 overrideDecodedPerf: true

order 语义:

  • perfOnly:等同不传 hitrace
  • hitraceFirst:先 apply,成功则返回(不先解 perf raw)
  • perfFirst:先 decodePerfRawDataOneRow,再按 afterPerf 决定是否 apply

便捷工厂:hitracePolicyPerfOnly、hitracePolicyHitraceFirst、hitracePolicyPerfFirst。

import {
  buildTraceParserRegistry,
  decodePerfRawData,
  hitracePolicyPerfFirst,
  hitraceThreadKeyForSample,
} from "hiperf_txt_parser";
import type { HitraceThreadIndexSnapshot } from "hiperf_txt_parser";

const registry = buildTraceParserRegistry([traceFormatText]);

const hitraceIndex: HitraceThreadIndexSnapshot = {
  [hitraceThreadKeyForSample(perfData.recordSamples[0]!)]: [
    {
      timestampNs: perfData.recordSamples[0]!.time,
      functionName: "my_event",
      fieldDict: { path: "/data", len: "4" },
    },
  ],
};

const decoded = await decodePerfRawData(perfData, registry, {
  tracePrintMode: "fieldDict",
  formatTexts: [traceFormatText],
  workerCount: 4,
  hitrace: hitracePolicyPerfFirst(hitraceIndex),
  // 或手写:{ index, order: "perfFirst", afterPerf: "whenPerfNeedsFallback" }
});

hitrace.index 可 structuredClone 后随 Worker 传入;order === "perfOnly" 时不下发侧车。

批量解码结束后会打 info 汇总(decodePerfRawData hitrace merge results: …),含 rows、snapFound/snapMissing 与各 hitraceMerge 结果计数。单行决策见结果上的 hitraceMerge / hitraceSnapFound;更细逐步日志设 HIPERF_LOG_LEVEL=trace。

Excel 导出(.xlsx)

import { parsePerfData, formatPerfDataToExcel } from "hiperf_txt_parser";
import fs from "node:fs";

const perfData = parsePerfData(perfText);
const buf = await formatPerfDataToExcel(perfData);
fs.writeFileSync("out/samples.xlsx", buf);

JSON 导入说明

formatPerfDataToJson 导出的结构可直接被 importPerfDataFromExportedJson 读回:

import {
  formatPerfDataToJson,
  importPerfDataFromExportedJson,
} from "hiperf_txt_parser";

const arr = formatPerfDataToJson(perfData);
const restored = importPerfDataFromExportedJson(JSON.stringify(arr));

Node 并行(worker_threads,经 options 开启)

以下能力 仅适用于 Node.js(依赖 worker_threads),通过原有 API 的 workerCount(默认 1) 选择串行或并行:<= 1 不创建 Worker;> 1 使用 Worker 池。

  • decodePerfRawData(perfData, registry, options?):options.workerCount > 1 时返回 Promise,并需 options.formatTexts(与 buildTraceParserRegistry 入参相同)。含不可序列化的 transformFieldDict 时自动回退主线程。options.hitrace 在 order !== "perfOnly" 时传入 Worker(index 须可序列化)。
  • loadPerfData(filePath, options?):perf 文本在 workerCount > 1 时块级并行 parseOneBlock;.json 仍一次性读入。串行与并行都会在返回前 destroy 输入流并等待 close(遇到 records summary 提前结束时尤其重要),避免 Windows 上随后覆盖同一 TXT 失败。
  • savePerfDataToText / savePerfDataToJson:options.workerCount > 1 时并行序列化后按序写盘。

实现位于 src/parallel/(decodeParallel、loadParallel、saveParallel)与 src/workers/(decodeWorker、parseBlockWorker、saveSerializeWorker),勿作为稳定公共路径直接引用。

源码布局(src/)

| 路径 | 说明 | |------|------| | parser.ts / serializer.ts | perf 文本解析与导入导出 | | traceFormat.ts | trace format 解析与 raw 解码主逻辑 | | hitrace/types.ts | hitrace 侧车类型契约 | | hitrace/merge.ts | perf 与 hitrace 按时间戳合并 | | parallel/ | Node Worker 池(decode / load / save) | | workers/ | Worker 线程入口脚本 | | index.ts | 对外 re-export 聚合 |

对比串行 wall time 与加速比:

npm run bench:parallel -- --samples=8000 --rounds=2 --worker-count=4

开发与验证

npm test

tests/ 覆盖解析、trace 解码、hitrace 合并、并行一致性、导入导出、TXT 读流生命周期与性能阈值。本地可用 sample/(已 gitignore)放置 perf 样例,通过 loadPerfData / parsePerfDataFromFile 自行验证。

性能基准

提供了一个本地基准脚本,用于评估大数据量下的核心链路吞吐:

npm run bench -- --samples=20000 --rounds=5

并行对比(见上一节「Node 并行 API」):

npm run bench:parallel -- --samples=8000 --rounds=2 --worker-count=4

多档 samples × worker-count 扫描(输出 Markdown 表,可选 CSV):

npm run bench:scale-sweep
npm run bench:scale-sweep -- --samples=4000,15000,50000,120000 --workers=2,4,8 --rounds=3 --csv=out/bench-scale.csv

speedup = 串行 avg / 并行 avg(> 1 表示并行更快)。合成数据 raw 较小时并行可能慢于串行,属 Worker 调度与拷贝开销;请在真实大 raw 载荷与本机核数下复测。

只跑特定 profile(可逗号分隔):

npm run bench -- --samples=20000 --rounds=5 --profiles=fieldDict

跳过 Core Stages(仅跑 decode/pipeline profile):

npm run bench -- --samples=20000 --rounds=5 --profiles=fieldDict --skip-core
  • samples:每轮构造的 record sample 数量(默认 20000)
  • rounds:测试轮数(默认 3)
  • profiles:可选,指定 decode profile;可选值为 printf、fieldDict、fieldDict+keepCommon(默认全跑)
  • skip-core:可选,跳过 Core Stages(仅输出 Decode Profiles)
  • 输出分为两段:
    • Core Stages:parsePerfData、formatPerfDataToText
    • Decode Profiles:printf / fieldDict / fieldDict+keepCommon 三种模式下的 decodePerfRawData 与端到端 pipeline
  • 每项同时输出 avg、p50、p95、吞吐(samples/s)、heapΔ 与 heapPeak

输出结构说明

  • 解析结构(parsePerfData):{ recordSamples: RecordSample[] }
  • 类型 RecordSampleJsonExportItem:{ call_chain: string } & Record<string, string>(除 call_chain 外的顶层键来自 traceFieldDict)
  • JSON 导出(formatPerfDataToJson):
[
  {
    "call_chain": "frame1\\nframe2\\nframe3"
  }
]