hiperf_txt_parser 1.0.12 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/serializer.d.ts +7 -5
- package/dist/serializer.js +26 -7
- package/dist/traceFormat.d.ts +10 -1
- package/dist/traceFormat.js +126 -62
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export { parsePerfData, filterByTgid } from "./parser.js";
|
|
|
2
2
|
export { formatPerfDataToText, formatPerfDataToJson } from "./serializer.js";
|
|
3
3
|
export { toBackTraceStack, toBackTraceStacks } from "./backtrace.js";
|
|
4
4
|
export { parseTraceFormat, parseCommonFieldsFromRaw, parseAllFieldsFromRaw, rawHexLinesToBuffer, bufferToRawHexLines, buildTraceParserRegistry, decodeRawByRegistry, decodePerfRawData, } from "./traceFormat.js";
|
|
5
|
-
export type { ParsedTraceFormat, TraceFormatField, TraceParserRegistry, DecodedRawSample, DecodePerfRawDataOptions, Endian, } from "./traceFormat.js";
|
|
5
|
+
export type { ParsedTraceFormat, TraceFormatField, TraceParserRegistry, DecodedRawSample, DecodePerfRawDataOptions, DecodeRawByRegistryOptions, TracePrintMode, Endian, } from "./traceFormat.js";
|
|
6
6
|
export type { PerfData, RecordSample } from "./types.js";
|
|
7
7
|
export type { RecordSampleJsonExportItem } from "./serializer.js";
|
package/dist/serializer.d.ts
CHANGED
|
@@ -3,13 +3,15 @@ import type { PerfData } from "./types.js";
|
|
|
3
3
|
* 将 PerfData 导出为与 sample/perf_data.txt 相同格式的文本(含缩进与每行前缀)
|
|
4
4
|
*/
|
|
5
5
|
export declare function formatPerfDataToText(data: PerfData): string;
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* 导出用 JSON 单项:固定含 `issuce`、`call_chain`;若存在 `traceFieldDict`,其键值对与二者**同级**平铺。
|
|
8
|
+
*/
|
|
9
|
+
export type RecordSampleJsonExportItem = {
|
|
8
10
|
issuce: "unknow";
|
|
9
11
|
call_chain: string;
|
|
10
|
-
}
|
|
12
|
+
} & Record<string, string>;
|
|
11
13
|
/**
|
|
12
|
-
* 将 PerfData 转为导出用 JSON
|
|
13
|
-
* call_chain
|
|
14
|
+
* 将 PerfData 转为导出用 JSON 结构:顶层为数组。
|
|
15
|
+
* call_chain 由 callchainFrames.frames 拼接;有 traceFieldDict 时各字段平铺在对象顶层(与 call_chain 同级)。
|
|
14
16
|
*/
|
|
15
17
|
export declare function formatPerfDataToJson(data: PerfData): RecordSampleJsonExportItem[];
|
package/dist/serializer.js
CHANGED
|
@@ -19,7 +19,18 @@ function serializeOneSample(sample) {
|
|
|
19
19
|
lines.push(` ${addr}`);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
if (sample.raw) {
|
|
22
|
+
if (sample.traceFieldDict && Object.keys(sample.traceFieldDict).length > 0 && sample.raw) {
|
|
23
|
+
lines.push(` raw size=${sample.raw.size}`);
|
|
24
|
+
for (const [k, v] of Object.entries(sample.traceFieldDict)) {
|
|
25
|
+
lines.push(` ${k}: ${v}`);
|
|
26
|
+
}
|
|
27
|
+
const n = Object.keys(sample.traceFieldDict).length;
|
|
28
|
+
for (let i = n; i < sample.raw.lines.length; i++) {
|
|
29
|
+
const { hex, short } = sample.raw.lines[i];
|
|
30
|
+
lines.push(short ? ` ${hex} (${short})` : ` ${hex}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (sample.raw) {
|
|
23
34
|
lines.push(` raw size=${sample.raw.size}`);
|
|
24
35
|
for (const { hex, short } of sample.raw.lines) {
|
|
25
36
|
lines.push(short ? ` ${hex} (${short})` : ` ${hex}`);
|
|
@@ -47,12 +58,20 @@ export function formatPerfDataToText(data) {
|
|
|
47
58
|
return data.recordSamples.map(serializeOneSample).join("\n\n");
|
|
48
59
|
}
|
|
49
60
|
/**
|
|
50
|
-
* 将 PerfData 转为导出用 JSON
|
|
51
|
-
* call_chain
|
|
61
|
+
* 将 PerfData 转为导出用 JSON 结构:顶层为数组。
|
|
62
|
+
* call_chain 由 callchainFrames.frames 拼接;有 traceFieldDict 时各字段平铺在对象顶层(与 call_chain 同级)。
|
|
52
63
|
*/
|
|
53
64
|
export function formatPerfDataToJson(data) {
|
|
54
|
-
return data.recordSamples.map((s) =>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
return data.recordSamples.map((s) => {
|
|
66
|
+
const item = {
|
|
67
|
+
issuce: "unknow",
|
|
68
|
+
call_chain: s.callchainFrames ? s.callchainFrames.frames.join("\n") : "",
|
|
69
|
+
};
|
|
70
|
+
if (s.traceFieldDict) {
|
|
71
|
+
for (const [k, v] of Object.entries(s.traceFieldDict)) {
|
|
72
|
+
item[k] = v;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return item;
|
|
76
|
+
});
|
|
58
77
|
}
|
package/dist/traceFormat.d.ts
CHANGED
|
@@ -28,11 +28,19 @@ export interface DecodedRawSample {
|
|
|
28
28
|
commonFields: Record<string, number | bigint>;
|
|
29
29
|
eventName?: string;
|
|
30
30
|
renderedText?: string;
|
|
31
|
+
/** 当 tracePrintMode 为 fieldDict 时:各 print 参数按对应格式符格式化后的键值对 */
|
|
32
|
+
renderedFieldDict?: Record<string, string>;
|
|
31
33
|
skipped: boolean;
|
|
32
34
|
}
|
|
35
|
+
/** printf:整条 print fmt 渲染为一句;fieldDict:按格式符逐项生成字段名 → 格式化字符串 */
|
|
36
|
+
export type TracePrintMode = "printf" | "fieldDict";
|
|
37
|
+
export interface DecodeRawByRegistryOptions {
|
|
38
|
+
tracePrintMode?: TracePrintMode;
|
|
39
|
+
}
|
|
33
40
|
export interface DecodePerfRawDataOptions {
|
|
34
41
|
/** 是否在替换后的 raw 内容中保留 common_* 信息 */
|
|
35
42
|
keepCommonFields?: boolean;
|
|
43
|
+
tracePrintMode?: TracePrintMode;
|
|
36
44
|
}
|
|
37
45
|
export type Endian = "le" | "be";
|
|
38
46
|
/**
|
|
@@ -71,7 +79,7 @@ export declare function buildTraceParserRegistry(formatTexts: string[]): TracePa
|
|
|
71
79
|
* 对一条 raw 数据先解 common_*,再按 common_type 选择解析器,最后按 print fmt 渲染。
|
|
72
80
|
* 找不到解析器时返回 skipped=true,并打印 common_type。
|
|
73
81
|
*/
|
|
74
|
-
export declare function decodeRawByRegistry(raw: Uint8Array, registry: TraceParserRegistry): Omit<DecodedRawSample, "sampleIndex">;
|
|
82
|
+
export declare function decodeRawByRegistry(raw: Uint8Array, registry: TraceParserRegistry, options?: DecodeRawByRegistryOptions): Omit<DecodedRawSample, "sampleIndex">;
|
|
75
83
|
/**
|
|
76
84
|
* 批量处理 perfData 的 raw 数据。仅处理有 raw 的 sample。
|
|
77
85
|
*/
|
|
@@ -80,6 +88,7 @@ export declare function decodeRawByRegistry(raw: Uint8Array, registry: TracePars
|
|
|
80
88
|
*
|
|
81
89
|
* 规则:
|
|
82
90
|
* - 若找到 common_type 对应解析器:将 print fmt 渲染结果写入 `sample.raw.lines`(一行)。
|
|
91
|
+
* - fieldDict:写入 `traceFieldDict`,`raw.lines` 为每行一条 `key: value`;`raw.size` 为行数(含可选 common 行)。
|
|
83
92
|
* - 若找不到解析器:放弃解析,并将 `common_type` 写入 `sample.raw.lines`(一行)。
|
|
84
93
|
*/
|
|
85
94
|
export declare function decodePerfRawData(perfData: PerfData, registry: TraceParserRegistry, options?: DecodePerfRawDataOptions): PerfData;
|
package/dist/traceFormat.js
CHANGED
|
@@ -274,6 +274,7 @@ export function buildTraceParserRegistry(formatTexts) {
|
|
|
274
274
|
}
|
|
275
275
|
return { byEventId, commonFormat };
|
|
276
276
|
}
|
|
277
|
+
const PRINT_FMT_SPEC_RE = /%[0-9]*[lh]*([duxXsS])/g;
|
|
277
278
|
function formatArgBySpecifier(value, spec) {
|
|
278
279
|
if (spec.toLowerCase() === "s") {
|
|
279
280
|
return typeof value === "string" ? value : String(value);
|
|
@@ -290,79 +291,107 @@ function formatArgBySpecifier(value, spec) {
|
|
|
290
291
|
}
|
|
291
292
|
return isBig ? value.toString(10) : Math.trunc(value).toString(10);
|
|
292
293
|
}
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
294
|
+
function normalizePrintExpr(exprRaw) {
|
|
295
|
+
let expr = exprRaw.trim();
|
|
296
|
+
while (expr.startsWith("("))
|
|
297
|
+
expr = expr.slice(1).trim();
|
|
298
|
+
while (expr.endsWith(")"))
|
|
299
|
+
expr = expr.slice(0, -1).trim();
|
|
300
|
+
expr = expr.replace(/\s+/g, "");
|
|
301
|
+
return expr;
|
|
302
|
+
}
|
|
303
|
+
function evalPrintArgExpr(exprRaw, fieldMap) {
|
|
304
|
+
const expr = normalizePrintExpr(exprRaw);
|
|
305
|
+
let m = expr.match(/^REC->(\w+)&0xffff$/);
|
|
306
|
+
if (m) {
|
|
307
|
+
const v = fieldMap[m[1]];
|
|
308
|
+
const n = typeof v === "number" ? v : 0;
|
|
309
|
+
return n & 0xffff;
|
|
304
310
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
311
|
+
m = expr.match(/^REC->(\w+)>>16$/);
|
|
312
|
+
if (m) {
|
|
313
|
+
const v = fieldMap[m[1]];
|
|
314
|
+
const n = typeof v === "number" ? v : 0;
|
|
315
|
+
return (n >>> 16) & 0xffff;
|
|
316
|
+
}
|
|
317
|
+
m = expr.match(/^REC->(\w+)(?:\[(\d+)])?$/);
|
|
318
|
+
if (!m)
|
|
319
|
+
return 0;
|
|
320
|
+
const name = m[1];
|
|
321
|
+
const idxRaw = m[2];
|
|
322
|
+
const v = fieldMap[name];
|
|
323
|
+
if (idxRaw !== undefined) {
|
|
324
|
+
const idx = parseInt(idxRaw, 10);
|
|
325
|
+
if (Array.isArray(v) && idx >= 0 && idx < v.length) {
|
|
326
|
+
return v[idx];
|
|
314
327
|
}
|
|
315
|
-
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
if (Array.isArray(v))
|
|
331
|
+
return v[0] ?? 0;
|
|
332
|
+
return v ?? 0;
|
|
333
|
+
}
|
|
334
|
+
function buildPrintArgValues(printArgs, fieldMap) {
|
|
335
|
+
const normalizedArgs = (printArgs ?? []).map(normalizePrintExpr);
|
|
336
|
+
const values = (printArgs ?? []).map((e) => evalPrintArgExpr(e, fieldMap));
|
|
337
|
+
return { normalizedArgs, values };
|
|
338
|
+
}
|
|
339
|
+
/** 与 renderPrintFmt 中 replace 顺序一致:依次取出每个格式符的类型字母 */
|
|
340
|
+
function extractPrintfSpecChars(printFmt) {
|
|
341
|
+
const specs = [];
|
|
342
|
+
printFmt.replace(PRINT_FMT_SPEC_RE, (_all, spec) => {
|
|
343
|
+
specs.push(spec);
|
|
344
|
+
return _all;
|
|
345
|
+
});
|
|
346
|
+
return specs;
|
|
347
|
+
}
|
|
348
|
+
function formatOnePrintArg(idx, spec, fieldMap, normalizedArgs, values) {
|
|
349
|
+
const v = values[idx] ?? 0;
|
|
350
|
+
const argExpr = normalizedArgs[idx] ?? "";
|
|
351
|
+
if (spec.toLowerCase() === "s") {
|
|
352
|
+
const m = argExpr.match(/^REC->__data_loc_(\w+)(?:&0xffff)?(?:>>16)?$/);
|
|
316
353
|
if (m) {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
// REC->field 或 REC->field[idx]
|
|
322
|
-
m = expr.match(/^REC->(\w+)(?:\[(\d+)])?$/);
|
|
323
|
-
if (!m)
|
|
324
|
-
return 0;
|
|
325
|
-
const name = m[1];
|
|
326
|
-
const idxRaw = m[2];
|
|
327
|
-
const v = fieldMap[name];
|
|
328
|
-
if (idxRaw !== undefined) {
|
|
329
|
-
const idx = parseInt(idxRaw, 10);
|
|
330
|
-
if (Array.isArray(v) && idx >= 0 && idx < v.length) {
|
|
331
|
-
return v[idx];
|
|
354
|
+
const s = fieldMap[m[1]];
|
|
355
|
+
if (typeof s === "string") {
|
|
356
|
+
return s;
|
|
332
357
|
}
|
|
333
|
-
return 0;
|
|
334
358
|
}
|
|
335
|
-
if (Array.isArray(v))
|
|
336
|
-
return v[0] ?? 0;
|
|
337
|
-
return v ?? 0;
|
|
338
359
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
360
|
+
return formatArgBySpecifier(v, spec);
|
|
361
|
+
}
|
|
362
|
+
function fieldKeyFromNormalizedArg(normalizedArg, index) {
|
|
363
|
+
let k = normalizedArg;
|
|
364
|
+
if (k.startsWith("REC->"))
|
|
365
|
+
k = k.slice(5);
|
|
366
|
+
return k.length > 0 ? k : `arg${index}`;
|
|
367
|
+
}
|
|
368
|
+
function renderPrintFmtAsFieldDict(printFmt, printArgs, fieldMap) {
|
|
369
|
+
const { normalizedArgs, values } = buildPrintArgValues(printArgs, fieldMap);
|
|
370
|
+
const specs = extractPrintfSpecChars(printFmt);
|
|
371
|
+
const out = {};
|
|
372
|
+
const seen = new Map();
|
|
373
|
+
for (let i = 0; i < specs.length; i++) {
|
|
374
|
+
const base = fieldKeyFromNormalizedArg(normalizedArgs[i] ?? "", i);
|
|
375
|
+
const n = (seen.get(base) ?? 0) + 1;
|
|
376
|
+
seen.set(base, n);
|
|
377
|
+
const key = n === 1 ? base : `${base}__${n}`;
|
|
378
|
+
out[key] = formatOnePrintArg(i, specs[i], fieldMap, normalizedArgs, values);
|
|
342
379
|
}
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
382
|
+
function renderPrintFmt(printFmt, printArgs, fieldMap) {
|
|
383
|
+
const { normalizedArgs, values } = buildPrintArgValues(printArgs, fieldMap);
|
|
343
384
|
let valueIdx = 0;
|
|
344
|
-
return printFmt.replace(
|
|
385
|
+
return printFmt.replace(PRINT_FMT_SPEC_RE, (_all, spec) => {
|
|
345
386
|
const idx = valueIdx++;
|
|
346
|
-
|
|
347
|
-
const argExpr = normalizedArgs[idx] ?? "";
|
|
348
|
-
// %s + REC->__data_loc_xxx 时,优先打印已解析出来的 xxx 字符串
|
|
349
|
-
if (spec.toLowerCase() === "s") {
|
|
350
|
-
const m = argExpr.match(/^REC->__data_loc_(\w+)(?:&0xffff)?(?:>>16)?$/);
|
|
351
|
-
if (m) {
|
|
352
|
-
const s = fieldMap[m[1]];
|
|
353
|
-
if (typeof s === "string") {
|
|
354
|
-
return s;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
return formatArgBySpecifier(v, spec);
|
|
387
|
+
return formatOnePrintArg(idx, spec, fieldMap, normalizedArgs, values);
|
|
359
388
|
});
|
|
360
389
|
}
|
|
361
390
|
/**
|
|
362
391
|
* 对一条 raw 数据先解 common_*,再按 common_type 选择解析器,最后按 print fmt 渲染。
|
|
363
392
|
* 找不到解析器时返回 skipped=true,并打印 common_type。
|
|
364
393
|
*/
|
|
365
|
-
export function decodeRawByRegistry(raw, registry) {
|
|
394
|
+
export function decodeRawByRegistry(raw, registry, options = {}) {
|
|
366
395
|
const commonType = raw.length >= 2 ? Number(new DataView(raw.buffer, raw.byteOffset, raw.byteLength).getUint16(0, true)) : undefined;
|
|
367
396
|
const commonFields = registry.commonFormat !== undefined
|
|
368
397
|
? parseCommonFieldsFromRaw(raw, registry.commonFormat)
|
|
@@ -383,9 +412,19 @@ export function decodeRawByRegistry(raw, registry) {
|
|
|
383
412
|
}
|
|
384
413
|
const fmt = registry.byEventId.get(eventId);
|
|
385
414
|
const allFields = parseAllFieldsFromRaw(raw, fmt);
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
415
|
+
const mode = options.tracePrintMode ?? "printf";
|
|
416
|
+
if (fmt.printFmt && mode === "fieldDict") {
|
|
417
|
+
const renderedFieldDict = renderPrintFmtAsFieldDict(fmt.printFmt, fmt.printArgs, allFields);
|
|
418
|
+
return {
|
|
419
|
+
commonType: eventId,
|
|
420
|
+
commonFields,
|
|
421
|
+
eventName: fmt.eventName,
|
|
422
|
+
renderedFieldDict,
|
|
423
|
+
renderedText: JSON.stringify(renderedFieldDict),
|
|
424
|
+
skipped: false,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const renderedText = fmt.printFmt ? renderPrintFmt(fmt.printFmt, fmt.printArgs, allFields) : undefined;
|
|
389
428
|
return {
|
|
390
429
|
commonType: eventId,
|
|
391
430
|
commonFields,
|
|
@@ -402,6 +441,7 @@ export function decodeRawByRegistry(raw, registry) {
|
|
|
402
441
|
*
|
|
403
442
|
* 规则:
|
|
404
443
|
* - 若找到 common_type 对应解析器:将 print fmt 渲染结果写入 `sample.raw.lines`(一行)。
|
|
444
|
+
* - fieldDict:写入 `traceFieldDict`,`raw.lines` 为每行一条 `key: value`;`raw.size` 为行数(含可选 common 行)。
|
|
405
445
|
* - 若找不到解析器:放弃解析,并将 `common_type` 写入 `sample.raw.lines`(一行)。
|
|
406
446
|
*/
|
|
407
447
|
export function decodePerfRawData(perfData, registry, options = {}) {
|
|
@@ -410,7 +450,9 @@ export function decodePerfRawData(perfData, registry, options = {}) {
|
|
|
410
450
|
if (!sample.raw || sample.raw.lines.length === 0)
|
|
411
451
|
return sample;
|
|
412
452
|
const raw = rawHexLinesToBuffer(sample.raw.lines);
|
|
413
|
-
const decoded = decodeRawByRegistry(raw, registry
|
|
453
|
+
const decoded = decodeRawByRegistry(raw, registry, {
|
|
454
|
+
tracePrintMode: options.tracePrintMode,
|
|
455
|
+
});
|
|
414
456
|
const base = decoded.renderedText ??
|
|
415
457
|
(decoded.commonType !== undefined ? String(decoded.commonType) : "");
|
|
416
458
|
if (!base)
|
|
@@ -420,8 +462,30 @@ export function decodePerfRawData(perfData, registry, options = {}) {
|
|
|
420
462
|
.map(([k, v]) => `${k}:${typeof v === "bigint" ? v.toString(10) : v}`)
|
|
421
463
|
.join(" ")
|
|
422
464
|
: "";
|
|
465
|
+
const dict = decoded.renderedFieldDict;
|
|
466
|
+
const useFieldDict = options.tracePrintMode === "fieldDict" &&
|
|
467
|
+
dict !== undefined &&
|
|
468
|
+
Object.keys(dict).length > 0;
|
|
469
|
+
if (useFieldDict) {
|
|
470
|
+
const kvLines = Object.entries(dict).map(([k, v]) => ({
|
|
471
|
+
hex: `${k}: ${v}`,
|
|
472
|
+
}));
|
|
473
|
+
const lines = options.keepCommonFields && commonLine.length > 0
|
|
474
|
+
? [...kvLines, { hex: commonLine }]
|
|
475
|
+
: kvLines;
|
|
476
|
+
return {
|
|
477
|
+
...sample,
|
|
478
|
+
traceFieldDict: dict,
|
|
479
|
+
raw: {
|
|
480
|
+
...sample.raw,
|
|
481
|
+
size: lines.length,
|
|
482
|
+
lines,
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
}
|
|
423
486
|
return {
|
|
424
487
|
...sample,
|
|
488
|
+
traceFieldDict: undefined,
|
|
425
489
|
raw: {
|
|
426
490
|
...sample.raw,
|
|
427
491
|
lines: options.keepCommonFields && commonLine.length > 0
|
package/dist/types.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export interface RecordSample {
|
|
|
39
39
|
period: number;
|
|
40
40
|
callchain?: CallchainEntry;
|
|
41
41
|
raw?: RawEntry;
|
|
42
|
+
/** tracePrintMode 为 fieldDict 且解码成功时:print 各参数键值(用于导出 txt/json) */
|
|
43
|
+
traceFieldDict?: Record<string, string>;
|
|
42
44
|
server?: ServerEntry;
|
|
43
45
|
callchainFrames?: CallchainFramesEntry;
|
|
44
46
|
}
|