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 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 indent = getIndent(line);
48
- const trimmed = line.trimStart();
49
- if (indent === 0 && trimmed.length > 0) {
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 (indent === 2) {
53
- if (trimmed.startsWith("sample_type:")) {
54
- sample.sample_type = trimmed.replace(/^sample_type:\s*/, "").trim();
55
- }
56
- else if (trimmed.startsWith("ID ")) {
57
- sample.id = parseInt(trimmed.replace(/^ID\s+/, ""), 10) || 0;
58
- }
59
- else if (trimmed.startsWith("ip ")) {
60
- sample.ip = trimmed.replace(/^ip\s+/, "").trim();
61
- }
62
- else if (trimmed.startsWith("pid ") && trimmed.includes(", tid ")) {
63
- const pidMatch = trimmed.match(/pid\s+(\d+).*tid\s+(\d+)/);
64
- if (pidMatch) {
65
- sample.pid = parseInt(pidMatch[1], 10);
66
- sample.tid = parseInt(pidMatch[2], 10);
67
- }
68
- }
69
- else if (trimmed.startsWith("time ")) {
70
- sample.time = parseInt(trimmed.replace(/^time\s+/, ""), 10) || 0;
71
- }
72
- else if (trimmed.startsWith("stream_id ")) {
73
- sample.stream_id = parseInt(trimmed.replace(/^stream_id\s+/, ""), 10) || 0;
74
- }
75
- else if (trimmed.startsWith("cpu ") && trimmed.includes(", res ")) {
76
- const cpuMatch = trimmed.match(/cpu\s+(\d+).*res\s+(\d+)/);
77
- if (cpuMatch) {
78
- sample.cpu = parseInt(cpuMatch[1], 10);
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
- else if (trimmed.startsWith("period ")) {
83
- sample.period = parseInt(trimmed.replace(/^period\s+/, ""), 10) || 0;
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
- else if (trimmed.startsWith("callchain nr=")) {
86
- const nrMatch = trimmed.match(/callchain\s+nr=(\d+)/);
87
- const nr = nrMatch ? parseInt(nrMatch[1], 10) : 0;
88
- const addresses = [];
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
- else if (trimmed.startsWith("raw size=")) {
101
- const sizeMatch = trimmed.match(/raw\s+size=(\d+)/);
102
- const size = sizeMatch ? parseInt(sizeMatch[1], 10) : 0;
103
- const entries = [];
104
- i++;
105
- while (i < lines.length && getIndent(lines[i]) >= 4) {
106
- const rawLine = lines[i].trim();
107
- const hexShort = rawLine.match(/^(0x[0-9a-fA-F]+)\s*\(([0-9a-fA-F]+)\)$/);
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
- sample.raw = { size, lines: entries };
117
- continue;
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
- else if (trimmed.startsWith("callchain: ")) {
135
- const countMatch = trimmed.match(/callchain:\s*(\d+)/);
136
- const count = countMatch ? parseInt(countMatch[1], 10) : 0;
137
- const frames = [];
138
- i++;
139
- while (i < lines.length && getIndent(lines[i]) >= 4) {
140
- frames.push(lines[i].trim());
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
- sample.callchainFrames = { count, frames };
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
- const indent = getIndent(line);
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) && indent === 0) {
224
+ if (trimmed.startsWith(RECORD_COMM_PREFIX)) {
170
225
  if (current.length > 0) {
171
226
  blocks.push(current);
172
227
  current = [];
@@ -33,9 +33,9 @@ function serializeOneSample(sample) {
33
33
  }
34
34
  if (sample.callchainFrames) {
35
35
  lines.push(` `);
36
- lines.push(` callchain: ${sample.callchainFrames.count}`);
36
+ lines.push(` callchain: ${sample.callchainFrames.count}`);
37
37
  for (const frame of sample.callchainFrames.frames) {
38
- lines.push(` ${frame}`);
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.1",
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/filterByTgid.test.mjs"
20
+ "test": "npm run build && node --test tests/*.test.mjs"
21
21
  },
22
22
  "keywords": [
23
23
  "perf",