hm-pt-core 1.0.1 → 1.0.2
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/trace/decode_raw.js +5 -2
- package/dist/trace/parse.js +117 -39
- package/dist/trace/printf.d.ts +6 -2
- package/dist/trace/printf.js +42 -12
- package/package.json +1 -1
package/dist/trace/decode_raw.js
CHANGED
|
@@ -21,9 +21,10 @@ export function decodeRawByRegistry(raw, registry, options = {}) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
const allFields = parseAllFieldsFromRaw(raw, fmt);
|
|
24
|
+
const renderContext = { eventName: fmt.eventName, eventId };
|
|
24
25
|
const mode = options.tracePrintMode ?? "printf";
|
|
25
26
|
if (fmt.printFmt && mode === "fieldDict") {
|
|
26
|
-
let renderedFieldDict = renderPrintFmtAsFieldDict(fmt.printFmt, fmt.printArgs, allFields);
|
|
27
|
+
let renderedFieldDict = renderPrintFmtAsFieldDict(fmt.printFmt, fmt.printArgs, allFields, renderContext);
|
|
27
28
|
const transform = registry.transformFieldDictByEventId?.get(eventId);
|
|
28
29
|
if (transform) {
|
|
29
30
|
renderedFieldDict = transform({
|
|
@@ -42,7 +43,9 @@ export function decodeRawByRegistry(raw, registry, options = {}) {
|
|
|
42
43
|
skipped: false,
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
|
-
const renderedText = fmt.printFmt
|
|
46
|
+
const renderedText = fmt.printFmt
|
|
47
|
+
? renderPrintFmt(fmt.printFmt, fmt.printArgs, allFields, renderContext)
|
|
48
|
+
: undefined;
|
|
46
49
|
return {
|
|
47
50
|
commonType: eventId,
|
|
48
51
|
commonFields,
|
package/dist/trace/parse.js
CHANGED
|
@@ -222,13 +222,89 @@ function isCharType(typeName) {
|
|
|
222
222
|
const t = normalizeType(typeName);
|
|
223
223
|
return t === "char" || t === "signed char" || t === "unsigned char";
|
|
224
224
|
}
|
|
225
|
+
function isDataLocType(typeName) {
|
|
226
|
+
return normalizeType(typeName).includes("__data_loc");
|
|
227
|
+
}
|
|
228
|
+
function parseDataLocElementType(typeName, signed) {
|
|
229
|
+
const t = normalizeType(typeName);
|
|
230
|
+
const m = t.match(/^__data_loc\s+(.+)\[\]$/);
|
|
231
|
+
if (!m)
|
|
232
|
+
return undefined;
|
|
233
|
+
const elemTypeName = m[1].trim();
|
|
234
|
+
const elemSize = inferScalarSize(elemTypeName, signed);
|
|
235
|
+
if (elemSize === undefined)
|
|
236
|
+
return undefined;
|
|
237
|
+
return { elemTypeName, elemSize, signed: isSignedElementType(elemTypeName, signed) };
|
|
238
|
+
}
|
|
239
|
+
function isSignedElementType(elemTypeName, fieldSigned) {
|
|
240
|
+
const t = normalizeType(elemTypeName);
|
|
241
|
+
if (t.includes("unsigned"))
|
|
242
|
+
return false;
|
|
243
|
+
if (t === "char" && !fieldSigned)
|
|
244
|
+
return false;
|
|
245
|
+
return fieldSigned || t === "signed char" || t === "short" || t === "int" || t === "long" || t === "long long" || t === "__s64";
|
|
246
|
+
}
|
|
247
|
+
function inferScalarSize(typeName, signed) {
|
|
248
|
+
const t = normalizeType(typeName);
|
|
249
|
+
if (t.includes("double"))
|
|
250
|
+
return 8;
|
|
251
|
+
if (t.includes("long long") || t === "__s64")
|
|
252
|
+
return 8;
|
|
253
|
+
if (t.includes("long"))
|
|
254
|
+
return 8;
|
|
255
|
+
if (t.includes("short"))
|
|
256
|
+
return 2;
|
|
257
|
+
if (t.includes("char") || t.includes("int") || t.includes("float"))
|
|
258
|
+
return 4;
|
|
259
|
+
if (t.includes("unsigned") || signed)
|
|
260
|
+
return 4;
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
function assertFieldInBounds(fieldName, offset, size, rawLength) {
|
|
264
|
+
if (offset < 0 || offset + size > rawLength) {
|
|
265
|
+
throw new TraceFieldDecodeError(`Field "${fieldName}" out of bounds: offset=${offset}, size=${size}, raw.length=${rawLength}`, fieldName, offset, size, rawLength);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function readLocatorField(view, field) {
|
|
269
|
+
assertFieldInBounds(field.name, field.offset, field.size, view.byteLength);
|
|
270
|
+
const locField = {
|
|
271
|
+
...field,
|
|
272
|
+
typeName: "unsigned int",
|
|
273
|
+
signed: false,
|
|
274
|
+
size: 4,
|
|
275
|
+
};
|
|
276
|
+
const loc = readFieldScalarUnchecked(view, locField);
|
|
277
|
+
if (typeof loc !== "number") {
|
|
278
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" __data_loc locator is invalid`, field.name, field.offset, field.size, view.byteLength);
|
|
279
|
+
}
|
|
280
|
+
return loc;
|
|
281
|
+
}
|
|
282
|
+
function readDynamicRegionAsArray(raw, view, field, dataOffset, byteLen, elemTypeName, elemSize, elemSigned) {
|
|
283
|
+
if (byteLen === 0)
|
|
284
|
+
return [];
|
|
285
|
+
if (byteLen % elemSize !== 0) {
|
|
286
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" __data_loc length ${byteLen} is not divisible by element size ${elemSize}`, field.name, dataOffset, byteLen, raw.length);
|
|
287
|
+
}
|
|
288
|
+
assertFieldInBounds(field.name, dataOffset, byteLen, raw.length);
|
|
289
|
+
const count = byteLen / elemSize;
|
|
290
|
+
const values = [];
|
|
291
|
+
for (let i = 0; i < count; i++) {
|
|
292
|
+
const elemField = {
|
|
293
|
+
typeName: elemTypeName,
|
|
294
|
+
name: field.name,
|
|
295
|
+
offset: dataOffset + i * elemSize,
|
|
296
|
+
size: elemSize,
|
|
297
|
+
signed: elemSigned,
|
|
298
|
+
};
|
|
299
|
+
values.push(readFieldScalarUnchecked(view, elemField));
|
|
300
|
+
}
|
|
301
|
+
return values;
|
|
302
|
+
}
|
|
225
303
|
/**
|
|
226
304
|
* 从 little-endian 原始缓冲区按字段描述读一个标量
|
|
227
305
|
*/
|
|
228
|
-
function
|
|
306
|
+
function readFieldScalarUnchecked(view, field) {
|
|
229
307
|
const { offset, size, signed, typeName } = field;
|
|
230
|
-
if (offset + size > view.byteLength)
|
|
231
|
-
return undefined;
|
|
232
308
|
const t = normalizeType(typeName);
|
|
233
309
|
if (size === 1) {
|
|
234
310
|
if (signed || t === "signed char") {
|
|
@@ -260,7 +336,14 @@ function readFieldScalar(view, field) {
|
|
|
260
336
|
}
|
|
261
337
|
return view.getBigUint64(offset, true);
|
|
262
338
|
}
|
|
263
|
-
|
|
339
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" has unsupported scalar size ${size}`, field.name, offset, size, view.byteLength);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* 从 little-endian 原始缓冲区按字段描述读一个标量;越界时抛出 TraceFieldDecodeError。
|
|
343
|
+
*/
|
|
344
|
+
function readFieldScalar(view, field) {
|
|
345
|
+
assertFieldInBounds(field.name, field.offset, field.size, view.byteLength);
|
|
346
|
+
return readFieldScalarUnchecked(view, field);
|
|
264
347
|
}
|
|
265
348
|
function parseArrayName(name) {
|
|
266
349
|
const m = name.match(/^(\w+)\[(\d+)\]$/);
|
|
@@ -274,9 +357,10 @@ function readFieldValue(view, field) {
|
|
|
274
357
|
return readFieldScalar(view, field);
|
|
275
358
|
if (arr.len <= 0)
|
|
276
359
|
return [];
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
360
|
+
if (field.size % arr.len !== 0) {
|
|
361
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" size ${field.size} is not divisible by array length ${arr.len}`, field.name, field.offset, field.size, view.byteLength);
|
|
362
|
+
}
|
|
363
|
+
const elemSize = field.size / arr.len;
|
|
280
364
|
const values = [];
|
|
281
365
|
for (let i = 0; i < arr.len; i++) {
|
|
282
366
|
const elemField = {
|
|
@@ -285,10 +369,7 @@ function readFieldValue(view, field) {
|
|
|
285
369
|
offset: field.offset + i * elemSize,
|
|
286
370
|
size: elemSize,
|
|
287
371
|
};
|
|
288
|
-
|
|
289
|
-
if (v === undefined)
|
|
290
|
-
return undefined;
|
|
291
|
-
values.push(v);
|
|
372
|
+
values.push(readFieldScalar(view, elemField));
|
|
292
373
|
}
|
|
293
374
|
return values;
|
|
294
375
|
}
|
|
@@ -301,10 +382,9 @@ export function parseCommonFieldsFromRaw(raw, format) {
|
|
|
301
382
|
for (const field of format.fields) {
|
|
302
383
|
if (!field.name.startsWith("common_"))
|
|
303
384
|
continue;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
385
|
+
if (field.offset + field.size > view.byteLength)
|
|
386
|
+
continue;
|
|
387
|
+
out[field.name] = readFieldScalar(view, field);
|
|
308
388
|
}
|
|
309
389
|
return out;
|
|
310
390
|
}
|
|
@@ -318,26 +398,18 @@ export function parseAllFieldsFromRaw(raw, format) {
|
|
|
318
398
|
const arr = parseArrayName(field.name);
|
|
319
399
|
const key = arr ? arr.baseName : field.name;
|
|
320
400
|
const t = normalizeType(field.typeName);
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const offset = loc & 0xffff;
|
|
334
|
-
const len = (loc >>> 16) & 0xffff;
|
|
335
|
-
if (len > 0 && (offset < 0 || offset + len > raw.length)) {
|
|
336
|
-
throw new TraceFieldDecodeError(`Field "${field.name}" __data_loc out of bounds: offset=${offset}, len=${len}, raw.length=${raw.length}`, field.name, offset, len, raw.length);
|
|
337
|
-
}
|
|
338
|
-
if (offset >= 0 && len > 0 && offset + len <= raw.length) {
|
|
339
|
-
const bytes = raw.subarray(offset, offset + len);
|
|
340
|
-
// 去掉末尾 \0
|
|
401
|
+
if (isDataLocType(field.typeName)) {
|
|
402
|
+
const loc = readLocatorField(view, field);
|
|
403
|
+
const locKey = `__data_loc_${field.name}`;
|
|
404
|
+
out[locKey] = loc;
|
|
405
|
+
const dataOffset = loc & 0xffff;
|
|
406
|
+
const byteLen = (loc >>> 16) & 0xffff;
|
|
407
|
+
if (byteLen > 0 && (dataOffset < 0 || dataOffset + byteLen > raw.length)) {
|
|
408
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" __data_loc out of bounds: offset=${dataOffset}, len=${byteLen}, raw.length=${raw.length}`, field.name, dataOffset, byteLen, raw.length);
|
|
409
|
+
}
|
|
410
|
+
if (t.includes("char")) {
|
|
411
|
+
if (byteLen > 0) {
|
|
412
|
+
const bytes = raw.subarray(dataOffset, dataOffset + byteLen);
|
|
341
413
|
const nul = bytes.indexOf(0);
|
|
342
414
|
const slice = nul >= 0 ? bytes.subarray(0, nul) : bytes;
|
|
343
415
|
out[field.name] = UTF8_DECODER.decode(slice);
|
|
@@ -345,17 +417,20 @@ export function parseAllFieldsFromRaw(raw, format) {
|
|
|
345
417
|
else {
|
|
346
418
|
out[field.name] = "";
|
|
347
419
|
}
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
const elemInfo = parseDataLocElementType(field.typeName, field.signed);
|
|
423
|
+
if (!elemInfo) {
|
|
424
|
+
throw new TraceFieldDecodeError(`Field "${field.name}" uses unsupported __data_loc element type "${field.typeName}"`, field.name, field.offset, field.size, raw.length);
|
|
348
425
|
}
|
|
426
|
+
out[key] = readDynamicRegionAsArray(raw, view, field, dataOffset, byteLen, elemInfo.elemTypeName, elemInfo.elemSize, elemInfo.signed);
|
|
349
427
|
continue;
|
|
350
428
|
}
|
|
351
429
|
if (arr && isCharType(field.typeName)) {
|
|
352
430
|
out[key] = readCharArrayAsString(raw, field.offset, field.size, field.name);
|
|
353
431
|
continue;
|
|
354
432
|
}
|
|
355
|
-
|
|
356
|
-
if (v !== undefined) {
|
|
357
|
-
out[key] = v;
|
|
358
|
-
}
|
|
433
|
+
out[key] = readFieldValue(view, field);
|
|
359
434
|
}
|
|
360
435
|
return out;
|
|
361
436
|
}
|
|
@@ -450,6 +525,9 @@ export function buildTraceParserRegistry(formatTexts, options = {}) {
|
|
|
450
525
|
if (typeof src !== "string" && src.transformFieldDict) {
|
|
451
526
|
transformFieldDictByEventId.set(fmt.eventId, src.transformFieldDict);
|
|
452
527
|
}
|
|
528
|
+
else {
|
|
529
|
+
transformFieldDictByEventId.delete(fmt.eventId);
|
|
530
|
+
}
|
|
453
531
|
}
|
|
454
532
|
});
|
|
455
533
|
return {
|
package/dist/trace/printf.d.ts
CHANGED
|
@@ -29,6 +29,10 @@ export declare function tokenizePrintfFormat(printFmt: string): Array<{
|
|
|
29
29
|
kind: "spec";
|
|
30
30
|
spec: PrintfSpec;
|
|
31
31
|
}>;
|
|
32
|
+
export interface PrintFmtRenderContext {
|
|
33
|
+
eventName?: string;
|
|
34
|
+
eventId?: number;
|
|
35
|
+
}
|
|
32
36
|
/** 测试专用:重置 printf 解析缓存 */
|
|
33
37
|
export declare function resetPrintfCachesForTest(): void;
|
|
34
38
|
/** 测试专用:读取 printf 解析缓存条目数 */
|
|
@@ -36,6 +40,6 @@ export declare function getPrintfCacheSizesForTest(): {
|
|
|
36
40
|
tokens: number;
|
|
37
41
|
specs: number;
|
|
38
42
|
};
|
|
39
|
-
export declare function renderPrintFmtAsFieldDict(printFmt: string, printArgs: string[] | undefined, fieldMap: PrintFieldMap): Record<string, string>;
|
|
40
|
-
export declare function renderPrintFmt(printFmt: string, printArgs: string[] | undefined, fieldMap: PrintFieldMap): string;
|
|
43
|
+
export declare function renderPrintFmtAsFieldDict(printFmt: string, printArgs: string[] | undefined, fieldMap: PrintFieldMap, context?: PrintFmtRenderContext): Record<string, string>;
|
|
44
|
+
export declare function renderPrintFmt(printFmt: string, printArgs: string[] | undefined, fieldMap: PrintFieldMap, context?: PrintFmtRenderContext): string;
|
|
41
45
|
export {};
|
package/dist/trace/printf.js
CHANGED
|
@@ -158,11 +158,21 @@ function formatPrintfUnsignedRadix(unsigned, spec, radix, upper) {
|
|
|
158
158
|
: unsigned.toString(radix);
|
|
159
159
|
body = body.padStart(Math.max(prec, body.length), "0");
|
|
160
160
|
}
|
|
161
|
-
if (hasPrintfFlag(spec.flags, "#")
|
|
162
|
-
if (radix === 16)
|
|
161
|
+
if (hasPrintfFlag(spec.flags, "#")) {
|
|
162
|
+
if (radix === 16 && unsigned !== 0n) {
|
|
163
163
|
prefix = upper ? "0X" : "0x";
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
}
|
|
165
|
+
else if (radix === 8) {
|
|
166
|
+
if (unsigned === 0n && (prec === 0 || body === "")) {
|
|
167
|
+
body = "0";
|
|
168
|
+
}
|
|
169
|
+
else if (body === "") {
|
|
170
|
+
body = "0";
|
|
171
|
+
}
|
|
172
|
+
else if (!body.startsWith("0")) {
|
|
173
|
+
body = "0" + body;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
166
176
|
}
|
|
167
177
|
const zeroPad = hasPrintfFlag(spec.flags, "0") &&
|
|
168
178
|
!hasPrintfFlag(spec.flags, "-") &&
|
|
@@ -333,6 +343,16 @@ export function tokenizePrintfFormat(printFmt) {
|
|
|
333
343
|
flushText();
|
|
334
344
|
return out;
|
|
335
345
|
}
|
|
346
|
+
function assertPrintArgCount(printFmt, printArgs, context) {
|
|
347
|
+
const specCount = extractPrintfSpecs(printFmt).length;
|
|
348
|
+
const argCount = printArgs?.length ?? 0;
|
|
349
|
+
if (argCount < specCount) {
|
|
350
|
+
const eventLabel = context?.eventName !== undefined
|
|
351
|
+
? `"${context.eventName}"${context.eventId !== undefined ? ` (ID ${context.eventId})` : ""}`
|
|
352
|
+
: "unknown event";
|
|
353
|
+
throw new Error(`Print fmt for event ${eventLabel} requires ${specCount} args but got ${argCount} at spec index ${argCount}: "${printFmt}"`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
336
356
|
function extractPrintfSpecs(printFmt) {
|
|
337
357
|
const cached = PRINT_FMT_SPEC_CACHE.get(printFmt);
|
|
338
358
|
if (cached)
|
|
@@ -401,12 +421,20 @@ function evalPrintArgExpr(exprRaw, fieldMap) {
|
|
|
401
421
|
return v;
|
|
402
422
|
throw new Error(`__get_str(${m[1]}) requires a string field, got ${typeof v}`);
|
|
403
423
|
}
|
|
404
|
-
m = expr.match(/^__get_dynamic_array\((\w+)\)
|
|
424
|
+
m = expr.match(/^__get_dynamic_array\((\w+)\)(?:\[(\d+)])?$/);
|
|
405
425
|
if (m) {
|
|
406
426
|
const v = fieldMap[m[1]];
|
|
407
|
-
if (Array.isArray(v))
|
|
408
|
-
|
|
409
|
-
|
|
427
|
+
if (!Array.isArray(v)) {
|
|
428
|
+
throw new Error(`__get_dynamic_array(${m[1]}) requires an array field, got ${typeof v}`);
|
|
429
|
+
}
|
|
430
|
+
if (m[2] !== undefined) {
|
|
431
|
+
const idx = parseInt(m[2], 10);
|
|
432
|
+
if (idx < 0 || idx >= v.length) {
|
|
433
|
+
throw new Error(`Invalid print arg index for __get_dynamic_array(${m[1]})[${m[2]}]`);
|
|
434
|
+
}
|
|
435
|
+
return v[idx];
|
|
436
|
+
}
|
|
437
|
+
return v;
|
|
410
438
|
}
|
|
411
439
|
m = expr.match(/^REC->(\w+)&0xffff$/);
|
|
412
440
|
if (m) {
|
|
@@ -481,9 +509,9 @@ function fieldKeyFromNormalizedArg(normalizedArg, index) {
|
|
|
481
509
|
if (mGetStr) {
|
|
482
510
|
return mGetStr[1];
|
|
483
511
|
}
|
|
484
|
-
const mGetArr = k.match(/^__get_dynamic_array\((\w+)\)
|
|
512
|
+
const mGetArr = k.match(/^__get_dynamic_array\((\w+)\)(?:\[(\d+)])?$/);
|
|
485
513
|
if (mGetArr) {
|
|
486
|
-
return mGetArr[1];
|
|
514
|
+
return mGetArr[2] !== undefined ? `${mGetArr[1]}__${mGetArr[2]}` : mGetArr[1];
|
|
487
515
|
}
|
|
488
516
|
// REC->__data_loc_path&0xffff 在 fieldDict 中收敛为 path 作为键名
|
|
489
517
|
const mDataLocOff = k.match(/^__data_loc_(\w+)&0xffff$/);
|
|
@@ -492,7 +520,8 @@ function fieldKeyFromNormalizedArg(normalizedArg, index) {
|
|
|
492
520
|
}
|
|
493
521
|
return k.length > 0 ? k : `arg${index}`;
|
|
494
522
|
}
|
|
495
|
-
export function renderPrintFmtAsFieldDict(printFmt, printArgs, fieldMap) {
|
|
523
|
+
export function renderPrintFmtAsFieldDict(printFmt, printArgs, fieldMap, context) {
|
|
524
|
+
assertPrintArgCount(printFmt, printArgs, context);
|
|
496
525
|
const { normalizedArgs, values } = buildPrintArgValues(printArgs, fieldMap);
|
|
497
526
|
const specs = extractPrintfSpecs(printFmt);
|
|
498
527
|
const out = {};
|
|
@@ -506,7 +535,8 @@ export function renderPrintFmtAsFieldDict(printFmt, printArgs, fieldMap) {
|
|
|
506
535
|
}
|
|
507
536
|
return out;
|
|
508
537
|
}
|
|
509
|
-
export function renderPrintFmt(printFmt, printArgs, fieldMap) {
|
|
538
|
+
export function renderPrintFmt(printFmt, printArgs, fieldMap, context) {
|
|
539
|
+
assertPrintArgCount(printFmt, printArgs, context);
|
|
510
540
|
const { normalizedArgs, values } = buildPrintArgValues(printArgs, fieldMap);
|
|
511
541
|
const tokens = getCachedPrintfTokens(printFmt);
|
|
512
542
|
let argIdx = 0;
|