hiperf_txt_parser 1.0.13 → 1.0.15
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 +2 -2
- package/dist/index.js +1 -1
- package/dist/serializer.d.ts +7 -5
- package/dist/serializer.js +26 -7
- package/dist/traceFormat.d.ts +25 -0
- package/dist/traceFormat.js +265 -31
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { parsePerfData, filterByTgid } from "./parser.js";
|
|
2
2
|
export { formatPerfDataToText, formatPerfDataToJson } from "./serializer.js";
|
|
3
3
|
export { toBackTraceStack, toBackTraceStacks } from "./backtrace.js";
|
|
4
|
-
export { parseTraceFormat, parseCommonFieldsFromRaw, parseAllFieldsFromRaw, rawHexLinesToBuffer, bufferToRawHexLines, buildTraceParserRegistry, decodeRawByRegistry, decodePerfRawData, } from "./traceFormat.js";
|
|
5
|
-
export type { ParsedTraceFormat, TraceFormatField, TraceParserRegistry, DecodedRawSample, DecodePerfRawDataOptions, DecodeRawByRegistryOptions, TracePrintMode, Endian, } from "./traceFormat.js";
|
|
4
|
+
export { parseTraceFormat, parseCommonFieldsFromRaw, parseAllFieldsFromRaw, rawHexLinesToBuffer, bufferToRawHexLines, buildTraceParserRegistry, decodeRawByRegistry, decodePerfRawData, formatPrintfValue, tokenizePrintfFormat, } from "./traceFormat.js";
|
|
5
|
+
export type { ParsedTraceFormat, TraceFormatField, TraceParserRegistry, DecodedRawSample, DecodePerfRawDataOptions, DecodeRawByRegistryOptions, TracePrintMode, PrintfSpec, Endian, } from "./traceFormat.js";
|
|
6
6
|
export type { PerfData, RecordSample } from "./types.js";
|
|
7
7
|
export type { RecordSampleJsonExportItem } from "./serializer.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { parsePerfData, filterByTgid } from "./parser.js";
|
|
2
2
|
export { formatPerfDataToText, formatPerfDataToJson } from "./serializer.js";
|
|
3
3
|
export { toBackTraceStack, toBackTraceStacks } from "./backtrace.js";
|
|
4
|
-
export { parseTraceFormat, parseCommonFieldsFromRaw, parseAllFieldsFromRaw, rawHexLinesToBuffer, bufferToRawHexLines, buildTraceParserRegistry, decodeRawByRegistry, decodePerfRawData, } from "./traceFormat.js";
|
|
4
|
+
export { parseTraceFormat, parseCommonFieldsFromRaw, parseAllFieldsFromRaw, rawHexLinesToBuffer, bufferToRawHexLines, buildTraceParserRegistry, decodeRawByRegistry, decodePerfRawData, formatPrintfValue, tokenizePrintfFormat, } from "./traceFormat.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
|
@@ -75,6 +75,30 @@ export declare function bufferToRawHexLines(raw: Uint8Array, bytesPerLine?: numb
|
|
|
75
75
|
* 从多个 trace_format 文本构建解析器集合(按 event ID 索引)
|
|
76
76
|
*/
|
|
77
77
|
export declare function buildTraceParserRegistry(formatTexts: string[]): TraceParserRegistry;
|
|
78
|
+
/** 与 C printf 接近的转换说明符(不含 * 动态宽度等) */
|
|
79
|
+
export interface PrintfSpec {
|
|
80
|
+
flags: string;
|
|
81
|
+
width?: number;
|
|
82
|
+
precision?: number;
|
|
83
|
+
length: string;
|
|
84
|
+
conv: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 按 C printf 子集格式化:标志 -+ #0 空格,宽度,精度,长度 hl ll hh,转换 d i u x X o s p。
|
|
88
|
+
*/
|
|
89
|
+
export declare function formatPrintfValue(value: number | bigint | string, spec: PrintfSpec): string;
|
|
90
|
+
/**
|
|
91
|
+
* 将 print fmt 拆成文本、`%%`、与带参数的转换说明符。
|
|
92
|
+
*/
|
|
93
|
+
export declare function tokenizePrintfFormat(printFmt: string): Array<{
|
|
94
|
+
kind: "text";
|
|
95
|
+
text: string;
|
|
96
|
+
} | {
|
|
97
|
+
kind: "literal_pct";
|
|
98
|
+
} | {
|
|
99
|
+
kind: "spec";
|
|
100
|
+
spec: PrintfSpec;
|
|
101
|
+
}>;
|
|
78
102
|
/**
|
|
79
103
|
* 对一条 raw 数据先解 common_*,再按 common_type 选择解析器,最后按 print fmt 渲染。
|
|
80
104
|
* 找不到解析器时返回 skipped=true,并打印 common_type。
|
|
@@ -88,6 +112,7 @@ export declare function decodeRawByRegistry(raw: Uint8Array, registry: TracePars
|
|
|
88
112
|
*
|
|
89
113
|
* 规则:
|
|
90
114
|
* - 若找到 common_type 对应解析器:将 print fmt 渲染结果写入 `sample.raw.lines`(一行)。
|
|
115
|
+
* - fieldDict:写入 `traceFieldDict`,`raw.lines` 为每行一条 `key: value`;`raw.size` 为行数(含可选 common 行)。
|
|
91
116
|
* - 若找不到解析器:放弃解析,并将 `common_type` 写入 `sample.raw.lines`(一行)。
|
|
92
117
|
*/
|
|
93
118
|
export declare function decodePerfRawData(perfData: PerfData, registry: TraceParserRegistry, options?: DecodePerfRawDataOptions): PerfData;
|
package/dist/traceFormat.js
CHANGED
|
@@ -274,22 +274,232 @@ export function buildTraceParserRegistry(formatTexts) {
|
|
|
274
274
|
}
|
|
275
275
|
return { byEventId, commonFormat };
|
|
276
276
|
}
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
const PRINT_FLAG_CHARS = new Set(["-", "+", "#", "0", " "]);
|
|
278
|
+
const VALID_PRINTF_CONV = new Set(["d", "i", "u", "x", "X", "o", "s", "S", "p", "P"]);
|
|
279
|
+
function hasPrintfFlag(flags, f) {
|
|
280
|
+
return flags.includes(f);
|
|
281
|
+
}
|
|
282
|
+
function toBigIntForPrintf(value) {
|
|
283
|
+
if (typeof value === "bigint")
|
|
284
|
+
return value;
|
|
285
|
+
if (typeof value === "string")
|
|
286
|
+
return 0n;
|
|
287
|
+
if (!Number.isFinite(value))
|
|
288
|
+
return 0n;
|
|
289
|
+
return BigInt(Math.trunc(value));
|
|
290
|
+
}
|
|
291
|
+
/** 按 64 位无符号解释(与内核 trace 常见 long 一致) */
|
|
292
|
+
function asUInt64(n) {
|
|
293
|
+
return n & ((1n << 64n) - 1n);
|
|
294
|
+
}
|
|
295
|
+
function applyWidth(s, width, leftAlign, zeroPad) {
|
|
296
|
+
if (width === undefined || s.length >= width)
|
|
297
|
+
return s;
|
|
298
|
+
const padLen = width - s.length;
|
|
299
|
+
const ch = zeroPad && !leftAlign ? "0" : " ";
|
|
300
|
+
if (leftAlign)
|
|
301
|
+
return s + ch.repeat(padLen);
|
|
302
|
+
return ch.repeat(padLen) + s;
|
|
303
|
+
}
|
|
304
|
+
/** 带符号十进制:支持 precision、0 宽度、+- 空格 */
|
|
305
|
+
function formatPrintfD(n, spec) {
|
|
306
|
+
const neg = n < 0n;
|
|
307
|
+
const abs = neg ? -n : n;
|
|
308
|
+
let digits = abs.toString(10);
|
|
309
|
+
const prec = spec.precision !== undefined ? spec.precision : 1;
|
|
310
|
+
digits = digits.padStart(Math.max(prec, digits.length), "0");
|
|
311
|
+
let sign = "";
|
|
312
|
+
if (neg)
|
|
313
|
+
sign = "-";
|
|
314
|
+
else if (hasPrintfFlag(spec.flags, "+"))
|
|
315
|
+
sign = "+";
|
|
316
|
+
else if (hasPrintfFlag(spec.flags, " "))
|
|
317
|
+
sign = " ";
|
|
318
|
+
const body = sign + digits;
|
|
319
|
+
if (spec.width === undefined || body.length >= spec.width)
|
|
320
|
+
return body;
|
|
321
|
+
const padLen = spec.width - body.length;
|
|
322
|
+
if (hasPrintfFlag(spec.flags, "0") && !hasPrintfFlag(spec.flags, "-") && spec.precision === undefined) {
|
|
323
|
+
if (sign)
|
|
324
|
+
return sign + "0".repeat(padLen) + digits;
|
|
325
|
+
return "0".repeat(padLen) + body;
|
|
326
|
+
}
|
|
327
|
+
if (hasPrintfFlag(spec.flags, "-"))
|
|
328
|
+
return body + " ".repeat(padLen);
|
|
329
|
+
return " ".repeat(padLen) + body;
|
|
330
|
+
}
|
|
331
|
+
function formatPrintfUnsignedRadix(unsigned, spec, radix, upper) {
|
|
332
|
+
let body = radix === 16
|
|
333
|
+
? upper
|
|
334
|
+
? unsigned.toString(16).toUpperCase()
|
|
335
|
+
: unsigned.toString(16)
|
|
336
|
+
: unsigned.toString(radix);
|
|
337
|
+
const precDefault = 1;
|
|
338
|
+
const prec = spec.precision !== undefined ? spec.precision : precDefault;
|
|
339
|
+
body = body.padStart(Math.max(prec, body.length), "0");
|
|
340
|
+
if (hasPrintfFlag(spec.flags, "#")) {
|
|
341
|
+
if (radix === 16)
|
|
342
|
+
body = (upper ? "0X" : "0x") + body;
|
|
343
|
+
else if (radix === 8 && body !== "0" && !body.startsWith("0"))
|
|
344
|
+
body = "0" + body;
|
|
345
|
+
}
|
|
346
|
+
const zeroPad = hasPrintfFlag(spec.flags, "0") &&
|
|
347
|
+
!hasPrintfFlag(spec.flags, "-") &&
|
|
348
|
+
spec.precision === undefined;
|
|
349
|
+
return applyWidth(body, spec.width, hasPrintfFlag(spec.flags, "-"), zeroPad);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* 按 C printf 子集格式化:标志 -+ #0 空格,宽度,精度,长度 hl ll hh,转换 d i u x X o s p。
|
|
353
|
+
*/
|
|
354
|
+
export function formatPrintfValue(value, spec) {
|
|
355
|
+
const conv = spec.conv;
|
|
356
|
+
const cl = conv.toLowerCase();
|
|
357
|
+
if (conv === "s" || conv === "S") {
|
|
358
|
+
let s = typeof value === "string" ? value : String(value);
|
|
359
|
+
if (spec.precision !== undefined) {
|
|
360
|
+
s = s.slice(0, spec.precision);
|
|
361
|
+
}
|
|
362
|
+
return applyWidth(s, spec.width, hasPrintfFlag(spec.flags, "-"), false);
|
|
281
363
|
}
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
364
|
+
const n = toBigIntForPrintf(value);
|
|
365
|
+
if (conv === "p" || conv === "P") {
|
|
366
|
+
const u = asUInt64(n);
|
|
367
|
+
const upper = conv === "P";
|
|
368
|
+
let body = upper ? u.toString(16).toUpperCase() : u.toString(16);
|
|
369
|
+
const prec = spec.precision !== undefined ? spec.precision : 1;
|
|
370
|
+
body = body.padStart(Math.max(prec, body.length), "0");
|
|
371
|
+
const zeroPad = hasPrintfFlag(spec.flags, "0") &&
|
|
372
|
+
!hasPrintfFlag(spec.flags, "-") &&
|
|
373
|
+
spec.precision === undefined;
|
|
374
|
+
return applyWidth(body, spec.width, hasPrintfFlag(spec.flags, "-"), zeroPad);
|
|
286
375
|
}
|
|
287
|
-
if (
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
376
|
+
if (cl === "d" || cl === "i") {
|
|
377
|
+
return formatPrintfD(n, spec);
|
|
378
|
+
}
|
|
379
|
+
if (cl === "u" || cl === "x" || cl === "X" || cl === "o") {
|
|
380
|
+
const u = asUInt64(n);
|
|
381
|
+
if (cl === "u") {
|
|
382
|
+
let body = u.toString(10);
|
|
383
|
+
const prec = spec.precision !== undefined ? spec.precision : 1;
|
|
384
|
+
body = body.padStart(Math.max(prec, body.length), "0");
|
|
385
|
+
const zeroPad = hasPrintfFlag(spec.flags, "0") &&
|
|
386
|
+
!hasPrintfFlag(spec.flags, "-") &&
|
|
387
|
+
spec.precision === undefined;
|
|
388
|
+
return applyWidth(body, spec.width, hasPrintfFlag(spec.flags, "-"), zeroPad);
|
|
389
|
+
}
|
|
390
|
+
if (cl === "o") {
|
|
391
|
+
return formatPrintfUnsignedRadix(u, spec, 8, false);
|
|
392
|
+
}
|
|
393
|
+
return formatPrintfUnsignedRadix(u, spec, 16, conv === "X");
|
|
394
|
+
}
|
|
395
|
+
return String(value);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* 将 print fmt 拆成文本、`%%`、与带参数的转换说明符。
|
|
399
|
+
*/
|
|
400
|
+
export function tokenizePrintfFormat(printFmt) {
|
|
401
|
+
const out = [];
|
|
402
|
+
let i = 0;
|
|
403
|
+
let textBuf = "";
|
|
404
|
+
const flushText = () => {
|
|
405
|
+
if (textBuf.length > 0) {
|
|
406
|
+
out.push({ kind: "text", text: textBuf });
|
|
407
|
+
textBuf = "";
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
while (i < printFmt.length) {
|
|
411
|
+
if (printFmt[i] !== "%") {
|
|
412
|
+
textBuf += printFmt[i];
|
|
413
|
+
i++;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
flushText();
|
|
417
|
+
const pctPos = i;
|
|
418
|
+
if (i + 1 >= printFmt.length) {
|
|
419
|
+
textBuf += "%";
|
|
420
|
+
i++;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (printFmt[i + 1] === "%") {
|
|
424
|
+
out.push({ kind: "literal_pct" });
|
|
425
|
+
i += 2;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
i++;
|
|
429
|
+
let flags = "";
|
|
430
|
+
while (i < printFmt.length && PRINT_FLAG_CHARS.has(printFmt[i])) {
|
|
431
|
+
flags += printFmt[i];
|
|
432
|
+
i++;
|
|
433
|
+
}
|
|
434
|
+
let width;
|
|
435
|
+
if (i < printFmt.length && printFmt[i] >= "0" && printFmt[i] <= "9") {
|
|
436
|
+
let w = 0;
|
|
437
|
+
while (i < printFmt.length && printFmt[i] >= "0" && printFmt[i] <= "9") {
|
|
438
|
+
w = w * 10 + (printFmt[i].charCodeAt(0) - 48);
|
|
439
|
+
i++;
|
|
440
|
+
}
|
|
441
|
+
width = w;
|
|
442
|
+
}
|
|
443
|
+
let precision;
|
|
444
|
+
if (i < printFmt.length && printFmt[i] === ".") {
|
|
445
|
+
i++;
|
|
446
|
+
if (i < printFmt.length && printFmt[i] >= "0" && printFmt[i] <= "9") {
|
|
447
|
+
let p = 0;
|
|
448
|
+
while (i < printFmt.length && printFmt[i] >= "0" && printFmt[i] <= "9") {
|
|
449
|
+
p = p * 10 + (printFmt[i].charCodeAt(0) - 48);
|
|
450
|
+
i++;
|
|
451
|
+
}
|
|
452
|
+
precision = p;
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
precision = 0;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
let length = "";
|
|
459
|
+
if (i < printFmt.length) {
|
|
460
|
+
if (printFmt[i] === "h") {
|
|
461
|
+
length = "h";
|
|
462
|
+
i++;
|
|
463
|
+
if (i < printFmt.length && printFmt[i] === "h") {
|
|
464
|
+
length = "hh";
|
|
465
|
+
i++;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else if (printFmt[i] === "l") {
|
|
469
|
+
length = "l";
|
|
470
|
+
i++;
|
|
471
|
+
if (i < printFmt.length && printFmt[i] === "l") {
|
|
472
|
+
length = "ll";
|
|
473
|
+
i++;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
else if (printFmt[i] === "L" ||
|
|
477
|
+
printFmt[i] === "j" ||
|
|
478
|
+
printFmt[i] === "z" ||
|
|
479
|
+
printFmt[i] === "t") {
|
|
480
|
+
length = printFmt[i];
|
|
481
|
+
i++;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (i >= printFmt.length) {
|
|
485
|
+
textBuf += printFmt.slice(pctPos, i);
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
const conv = printFmt[i];
|
|
489
|
+
i++;
|
|
490
|
+
if (!VALID_PRINTF_CONV.has(conv)) {
|
|
491
|
+
textBuf += printFmt.slice(pctPos, i);
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
out.push({ kind: "spec", spec: { flags, width, precision, length, conv } });
|
|
291
495
|
}
|
|
292
|
-
|
|
496
|
+
flushText();
|
|
497
|
+
return out;
|
|
498
|
+
}
|
|
499
|
+
function extractPrintfSpecs(printFmt) {
|
|
500
|
+
return tokenizePrintfFormat(printFmt)
|
|
501
|
+
.filter((t) => t.kind === "spec")
|
|
502
|
+
.map((t) => t.spec);
|
|
293
503
|
}
|
|
294
504
|
function normalizePrintExpr(exprRaw) {
|
|
295
505
|
let expr = exprRaw.trim();
|
|
@@ -336,28 +546,19 @@ function buildPrintArgValues(printArgs, fieldMap) {
|
|
|
336
546
|
const values = (printArgs ?? []).map((e) => evalPrintArgExpr(e, fieldMap));
|
|
337
547
|
return { normalizedArgs, values };
|
|
338
548
|
}
|
|
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
549
|
function formatOnePrintArg(idx, spec, fieldMap, normalizedArgs, values) {
|
|
349
550
|
const v = values[idx] ?? 0;
|
|
350
551
|
const argExpr = normalizedArgs[idx] ?? "";
|
|
351
|
-
if (spec.
|
|
552
|
+
if (spec.conv === "s" || spec.conv === "S") {
|
|
352
553
|
const m = argExpr.match(/^REC->__data_loc_(\w+)(?:&0xffff)?(?:>>16)?$/);
|
|
353
554
|
if (m) {
|
|
354
555
|
const s = fieldMap[m[1]];
|
|
355
556
|
if (typeof s === "string") {
|
|
356
|
-
return s;
|
|
557
|
+
return formatPrintfValue(s, spec);
|
|
357
558
|
}
|
|
358
559
|
}
|
|
359
560
|
}
|
|
360
|
-
return
|
|
561
|
+
return formatPrintfValue(v, spec);
|
|
361
562
|
}
|
|
362
563
|
function fieldKeyFromNormalizedArg(normalizedArg, index) {
|
|
363
564
|
let k = normalizedArg;
|
|
@@ -367,7 +568,7 @@ function fieldKeyFromNormalizedArg(normalizedArg, index) {
|
|
|
367
568
|
}
|
|
368
569
|
function renderPrintFmtAsFieldDict(printFmt, printArgs, fieldMap) {
|
|
369
570
|
const { normalizedArgs, values } = buildPrintArgValues(printArgs, fieldMap);
|
|
370
|
-
const specs =
|
|
571
|
+
const specs = extractPrintfSpecs(printFmt);
|
|
371
572
|
const out = {};
|
|
372
573
|
const seen = new Map();
|
|
373
574
|
for (let i = 0; i < specs.length; i++) {
|
|
@@ -381,11 +582,21 @@ function renderPrintFmtAsFieldDict(printFmt, printArgs, fieldMap) {
|
|
|
381
582
|
}
|
|
382
583
|
function renderPrintFmt(printFmt, printArgs, fieldMap) {
|
|
383
584
|
const { normalizedArgs, values } = buildPrintArgValues(printArgs, fieldMap);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
585
|
+
const tokens = tokenizePrintfFormat(printFmt);
|
|
586
|
+
let argIdx = 0;
|
|
587
|
+
let out = "";
|
|
588
|
+
for (const tok of tokens) {
|
|
589
|
+
if (tok.kind === "text") {
|
|
590
|
+
out += tok.text;
|
|
591
|
+
}
|
|
592
|
+
else if (tok.kind === "literal_pct") {
|
|
593
|
+
out += "%";
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
out += formatOnePrintArg(argIdx++, tok.spec, fieldMap, normalizedArgs, values);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return out;
|
|
389
600
|
}
|
|
390
601
|
/**
|
|
391
602
|
* 对一条 raw 数据先解 common_*,再按 common_type 选择解析器,最后按 print fmt 渲染。
|
|
@@ -441,6 +652,7 @@ export function decodeRawByRegistry(raw, registry, options = {}) {
|
|
|
441
652
|
*
|
|
442
653
|
* 规则:
|
|
443
654
|
* - 若找到 common_type 对应解析器:将 print fmt 渲染结果写入 `sample.raw.lines`(一行)。
|
|
655
|
+
* - fieldDict:写入 `traceFieldDict`,`raw.lines` 为每行一条 `key: value`;`raw.size` 为行数(含可选 common 行)。
|
|
444
656
|
* - 若找不到解析器:放弃解析,并将 `common_type` 写入 `sample.raw.lines`(一行)。
|
|
445
657
|
*/
|
|
446
658
|
export function decodePerfRawData(perfData, registry, options = {}) {
|
|
@@ -461,8 +673,30 @@ export function decodePerfRawData(perfData, registry, options = {}) {
|
|
|
461
673
|
.map(([k, v]) => `${k}:${typeof v === "bigint" ? v.toString(10) : v}`)
|
|
462
674
|
.join(" ")
|
|
463
675
|
: "";
|
|
676
|
+
const dict = decoded.renderedFieldDict;
|
|
677
|
+
const useFieldDict = options.tracePrintMode === "fieldDict" &&
|
|
678
|
+
dict !== undefined &&
|
|
679
|
+
Object.keys(dict).length > 0;
|
|
680
|
+
if (useFieldDict) {
|
|
681
|
+
const kvLines = Object.entries(dict).map(([k, v]) => ({
|
|
682
|
+
hex: `${k}: ${v}`,
|
|
683
|
+
}));
|
|
684
|
+
const lines = options.keepCommonFields && commonLine.length > 0
|
|
685
|
+
? [...kvLines, { hex: commonLine }]
|
|
686
|
+
: kvLines;
|
|
687
|
+
return {
|
|
688
|
+
...sample,
|
|
689
|
+
traceFieldDict: dict,
|
|
690
|
+
raw: {
|
|
691
|
+
...sample.raw,
|
|
692
|
+
size: lines.length,
|
|
693
|
+
lines,
|
|
694
|
+
},
|
|
695
|
+
};
|
|
696
|
+
}
|
|
464
697
|
return {
|
|
465
698
|
...sample,
|
|
699
|
+
traceFieldDict: undefined,
|
|
466
700
|
raw: {
|
|
467
701
|
...sample.raw,
|
|
468
702
|
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
|
}
|