hiperf_txt_parser 1.0.12 → 1.0.13
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/traceFormat.d.ts +9 -1
- package/dist/traceFormat.js +103 -62
- 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/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
|
*/
|
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,
|
|
@@ -410,7 +449,9 @@ export function decodePerfRawData(perfData, registry, options = {}) {
|
|
|
410
449
|
if (!sample.raw || sample.raw.lines.length === 0)
|
|
411
450
|
return sample;
|
|
412
451
|
const raw = rawHexLinesToBuffer(sample.raw.lines);
|
|
413
|
-
const decoded = decodeRawByRegistry(raw, registry
|
|
452
|
+
const decoded = decodeRawByRegistry(raw, registry, {
|
|
453
|
+
tracePrintMode: options.tracePrintMode,
|
|
454
|
+
});
|
|
414
455
|
const base = decoded.renderedText ??
|
|
415
456
|
(decoded.commonType !== undefined ? String(decoded.commonType) : "");
|
|
416
457
|
if (!base)
|