hiperf_txt_parser 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/parser.js +148 -93
- package/dist/serializer.js +2 -2
- package/package.json +2 -2
package/dist/parser.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
const RECORD_SAMPLE_PREFIX = "record sample:";
|
|
2
2
|
const RECORD_COMM_PREFIX = "record comm";
|
|
3
|
-
/**
|
|
4
|
-
* 获取行首空格数量(缩进)
|
|
5
|
-
*/
|
|
6
|
-
function getIndent(line) {
|
|
7
|
-
const m = line.match(/^(\s*)/);
|
|
8
|
-
return m ? m[1].length : 0;
|
|
9
|
-
}
|
|
10
3
|
/**
|
|
11
4
|
* 解析 "record sample: type 9, misc 2, size 520" 行
|
|
12
5
|
*/
|
|
@@ -23,6 +16,20 @@ function parseHeaderLine(line) {
|
|
|
23
16
|
/**
|
|
24
17
|
* 解析单个 record sample 块(已按行切分好的行数组)
|
|
25
18
|
*/
|
|
19
|
+
function isTopLevelField(trimmed) {
|
|
20
|
+
return (trimmed.startsWith("sample_type:") ||
|
|
21
|
+
trimmed.startsWith("ID ") ||
|
|
22
|
+
trimmed.startsWith("ip ") ||
|
|
23
|
+
(trimmed.startsWith("pid ") && trimmed.includes(", tid ")) ||
|
|
24
|
+
trimmed.startsWith("time ") ||
|
|
25
|
+
trimmed.startsWith("stream_id ") ||
|
|
26
|
+
(trimmed.startsWith("cpu ") && trimmed.includes(", res ")) ||
|
|
27
|
+
trimmed.startsWith("period ") ||
|
|
28
|
+
trimmed.startsWith("callchain nr=") ||
|
|
29
|
+
trimmed.startsWith("raw size=") ||
|
|
30
|
+
trimmed.startsWith("server nr=") ||
|
|
31
|
+
trimmed.startsWith("callchain: "));
|
|
32
|
+
}
|
|
26
33
|
function parseOneBlock(lines) {
|
|
27
34
|
if (lines.length === 0 || !lines[0].trimStart().startsWith(RECORD_SAMPLE_PREFIX)) {
|
|
28
35
|
throw new Error("Invalid record sample block");
|
|
@@ -41,108 +48,157 @@ function parseOneBlock(lines) {
|
|
|
41
48
|
res: 0,
|
|
42
49
|
period: 0,
|
|
43
50
|
};
|
|
51
|
+
let mode = null;
|
|
44
52
|
let i = 1;
|
|
45
53
|
while (i < lines.length) {
|
|
46
54
|
const line = lines[i];
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
const trimmed = line.trim();
|
|
56
|
+
if (!trimmed) {
|
|
57
|
+
i++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (trimmed.startsWith(RECORD_SAMPLE_PREFIX) ||
|
|
61
|
+
trimmed.startsWith(RECORD_COMM_PREFIX)) {
|
|
50
62
|
break;
|
|
51
63
|
}
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
sample.res = parseInt(cpuMatch[2], 10);
|
|
80
|
-
}
|
|
64
|
+
if (mode && isTopLevelField(trimmed)) {
|
|
65
|
+
mode = null;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (trimmed.startsWith("sample_type:")) {
|
|
69
|
+
sample.sample_type = trimmed.replace(/^sample_type:\s*/, "").trim();
|
|
70
|
+
mode = null;
|
|
71
|
+
i++;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (trimmed.startsWith("ID ")) {
|
|
75
|
+
sample.id = parseInt(trimmed.replace(/^ID\s+/, ""), 10) || 0;
|
|
76
|
+
mode = null;
|
|
77
|
+
i++;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (trimmed.startsWith("ip ")) {
|
|
81
|
+
sample.ip = trimmed.replace(/^ip\s+/, "").trim();
|
|
82
|
+
mode = null;
|
|
83
|
+
i++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (trimmed.startsWith("pid ") && trimmed.includes(", tid ")) {
|
|
87
|
+
const pidMatch = trimmed.match(/pid\s+(\d+).*tid\s+(\d+)/);
|
|
88
|
+
if (pidMatch) {
|
|
89
|
+
sample.pid = parseInt(pidMatch[1], 10);
|
|
90
|
+
sample.tid = parseInt(pidMatch[2], 10);
|
|
81
91
|
}
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
mode = null;
|
|
93
|
+
i++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (trimmed.startsWith("time ")) {
|
|
97
|
+
sample.time = parseInt(trimmed.replace(/^time\s+/, ""), 10) || 0;
|
|
98
|
+
mode = null;
|
|
99
|
+
i++;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (trimmed.startsWith("stream_id ")) {
|
|
103
|
+
sample.stream_id = parseInt(trimmed.replace(/^stream_id\s+/, ""), 10) || 0;
|
|
104
|
+
mode = null;
|
|
105
|
+
i++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (trimmed.startsWith("cpu ") && trimmed.includes(", res ")) {
|
|
109
|
+
const cpuMatch = trimmed.match(/cpu\s+(\d+).*res\s+(\d+)/);
|
|
110
|
+
if (cpuMatch) {
|
|
111
|
+
sample.cpu = parseInt(cpuMatch[1], 10);
|
|
112
|
+
sample.res = parseInt(cpuMatch[2], 10);
|
|
84
113
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
114
|
+
mode = null;
|
|
115
|
+
i++;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (trimmed.startsWith("period ")) {
|
|
119
|
+
sample.period = parseInt(trimmed.replace(/^period\s+/, ""), 10) || 0;
|
|
120
|
+
mode = null;
|
|
121
|
+
i++;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (trimmed.startsWith("callchain nr=")) {
|
|
125
|
+
const nrMatch = trimmed.match(/callchain\s+nr=(\d+)/);
|
|
126
|
+
const nr = nrMatch ? parseInt(nrMatch[1], 10) : 0;
|
|
127
|
+
sample.callchain = { nr, addresses: [] };
|
|
128
|
+
mode = "callchainAddr";
|
|
129
|
+
i++;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (trimmed.startsWith("raw size=")) {
|
|
133
|
+
const sizeMatch = trimmed.match(/raw\s+size=(\d+)/);
|
|
134
|
+
const size = sizeMatch ? parseInt(sizeMatch[1], 10) : 0;
|
|
135
|
+
sample.raw = { size, lines: [] };
|
|
136
|
+
mode = "raw";
|
|
137
|
+
i++;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (trimmed.startsWith("server nr=")) {
|
|
141
|
+
const nrMatch = trimmed.match(/server\s+nr=(\d+)/);
|
|
142
|
+
const nr = nrMatch ? parseInt(nrMatch[1], 10) : 0;
|
|
143
|
+
sample.server = { nr, pids: [] };
|
|
144
|
+
mode = "server";
|
|
145
|
+
i++;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (trimmed.startsWith("callchain: ")) {
|
|
149
|
+
const countMatch = trimmed.match(/callchain:\s*(\d+)/);
|
|
150
|
+
const count = countMatch ? parseInt(countMatch[1], 10) : 0;
|
|
151
|
+
sample.callchainFrames = { count, frames: [] };
|
|
152
|
+
mode = "frames";
|
|
153
|
+
i++;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (mode === "callchainAddr") {
|
|
157
|
+
if (/^0x[0-9a-fA-F]+$/.test(trimmed) && sample.callchain) {
|
|
158
|
+
sample.callchain.addresses.push(trimmed);
|
|
89
159
|
i++;
|
|
90
|
-
while (i < lines.length && getIndent(lines[i]) >= 4) {
|
|
91
|
-
const addr = lines[i].trim();
|
|
92
|
-
if (addr && /^0x[0-9a-fA-F]+$/.test(addr)) {
|
|
93
|
-
addresses.push(addr);
|
|
94
|
-
}
|
|
95
|
-
i++;
|
|
96
|
-
}
|
|
97
|
-
sample.callchain = { nr, addresses };
|
|
98
160
|
continue;
|
|
99
161
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (hexShort) {
|
|
109
|
-
entries.push({ hex: hexShort[1], short: hexShort[2] });
|
|
110
|
-
}
|
|
111
|
-
else if (rawLine.startsWith("0x")) {
|
|
112
|
-
entries.push({ hex: rawLine });
|
|
113
|
-
}
|
|
162
|
+
mode = null;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (mode === "raw") {
|
|
166
|
+
if (sample.raw) {
|
|
167
|
+
const hexShort = trimmed.match(/^(0x[0-9a-fA-F]+)\s*\(([0-9a-fA-F]+)\)$/);
|
|
168
|
+
if (hexShort) {
|
|
169
|
+
sample.raw.lines.push({ hex: hexShort[1], short: hexShort[2] });
|
|
114
170
|
i++;
|
|
171
|
+
continue;
|
|
115
172
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
else if (trimmed.startsWith("server nr=")) {
|
|
120
|
-
const nrMatch = trimmed.match(/server\s+nr=(\d+)/);
|
|
121
|
-
const nr = nrMatch ? parseInt(nrMatch[1], 10) : 0;
|
|
122
|
-
const pids = [];
|
|
123
|
-
i++;
|
|
124
|
-
while (i < lines.length && getIndent(lines[i]) >= 4) {
|
|
125
|
-
const pidMatch = lines[i].trim().match(/pid:\s*(\d+)/);
|
|
126
|
-
if (pidMatch) {
|
|
127
|
-
pids.push(parseInt(pidMatch[1], 10));
|
|
128
|
-
}
|
|
173
|
+
if (/^0x[0-9a-fA-F]+$/.test(trimmed)) {
|
|
174
|
+
sample.raw.lines.push({ hex: trimmed });
|
|
129
175
|
i++;
|
|
176
|
+
continue;
|
|
130
177
|
}
|
|
131
|
-
sample.server = { nr, pids };
|
|
132
|
-
continue;
|
|
133
178
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
179
|
+
mode = null;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (mode === "server") {
|
|
183
|
+
if (sample.server) {
|
|
184
|
+
const pidMatch = trimmed.match(/^pid:\s*(\d+)$/);
|
|
185
|
+
if (pidMatch) {
|
|
186
|
+
sample.server.pids.push(parseInt(pidMatch[1], 10));
|
|
141
187
|
i++;
|
|
188
|
+
continue;
|
|
142
189
|
}
|
|
143
|
-
|
|
190
|
+
}
|
|
191
|
+
mode = null;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (mode === "frames") {
|
|
195
|
+
if (sample.callchainFrames) {
|
|
196
|
+
sample.callchainFrames.frames.push(trimmed);
|
|
197
|
+
i++;
|
|
144
198
|
continue;
|
|
145
199
|
}
|
|
200
|
+
mode = null;
|
|
201
|
+
continue;
|
|
146
202
|
}
|
|
147
203
|
i++;
|
|
148
204
|
}
|
|
@@ -158,15 +214,14 @@ function extractRecordSampleBlocks(text) {
|
|
|
158
214
|
for (let i = 0; i < lines.length; i++) {
|
|
159
215
|
const line = lines[i];
|
|
160
216
|
const trimmed = line.trimStart();
|
|
161
|
-
|
|
162
|
-
if (trimmed.startsWith(RECORD_SAMPLE_PREFIX) && indent === 0) {
|
|
217
|
+
if (trimmed.startsWith(RECORD_SAMPLE_PREFIX)) {
|
|
163
218
|
if (current.length > 0) {
|
|
164
219
|
blocks.push(current);
|
|
165
220
|
}
|
|
166
221
|
current = [line];
|
|
167
222
|
continue;
|
|
168
223
|
}
|
|
169
|
-
if (trimmed.startsWith(RECORD_COMM_PREFIX)
|
|
224
|
+
if (trimmed.startsWith(RECORD_COMM_PREFIX)) {
|
|
170
225
|
if (current.length > 0) {
|
|
171
226
|
blocks.push(current);
|
|
172
227
|
current = [];
|
package/dist/serializer.js
CHANGED
|
@@ -33,9 +33,9 @@ function serializeOneSample(sample) {
|
|
|
33
33
|
}
|
|
34
34
|
if (sample.callchainFrames) {
|
|
35
35
|
lines.push(` `);
|
|
36
|
-
lines.push(`
|
|
36
|
+
lines.push(` callchain: ${sample.callchainFrames.count}`);
|
|
37
37
|
for (const frame of sample.callchainFrames.frames) {
|
|
38
|
-
lines.push(`
|
|
38
|
+
lines.push(` ${frame}`);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
return lines.join("\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hiperf_txt_parser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Parse perf data.txt and output structured TypeScript data",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc",
|
|
19
19
|
"prepublishOnly": "npm run build",
|
|
20
|
-
"test": "npm run build && node --test tests
|
|
20
|
+
"test": "npm run build && node --test tests/*.test.mjs"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"perf",
|