blob: dc7d32192fecfe368c93d490e149c4eed2e882ed [file] [log] [blame]
Sui Chenb65280f2020-06-30 18:14:03 -07001// This file performs the file reading step
2// Actual preprocessing is done in dbus_timeline_vis.js
3
4function MyFloatMillisToBigIntUsec(x) {
5 x = ('' + x).split('.');
6 ret = BigInt(x[0]) * BigInt(1000);
7 return ret;
8}
9
10// When the Open File dialog is completed, the name of the opened file will be passed
11// to this routine. Then the program will do the following:
12// 1. Launch "linecount.py" to get the total packet count in the PCAP file for
13// progress
14// 2. Launch "dbus-pcap" to get the timestamps of each DBus message
15// 3. Launch "dbus-pcap" to get the JSON representation of each DBus message
16//
17function OpenDBusPcapFile(file_name) {
18 // First try to parse using dbus-pcap
19
20 ShowBlocker('Determining the number of packets in the pcap file ...');
21 const num_lines_py =
22 spawn('python3', ['linecount.py', file_name]);
23 let stdout_num_lines = '';
24 num_lines_py.stdout.on('data', (data) => {
25 stdout_num_lines += data;
26 });
27
28 num_lines_py.on('close', (code) => {
29 let num_packets = parseInt(stdout_num_lines.trim());
30 ShowBlocker('Running dbus-pcap (Pass 1/2, packet timestamps) ...');
31 const dbus_pcap1 =
32 //spawn('python3', ['dbus-pcap', file_name, '--json', '--progress']);
33 spawn('python3', ['dbus-pcap', file_name]);
34 let stdout1 = '';
35 let timestamps = [];
36 let count1 = 0; // In the first phase, consecutive newlines indicate a new entry
37 //const r = new RegExp('([0-9]+/[0-9]+) [0-9]+\.[0-9]+:.*');
38 const r = new RegExp('([0-9]+\.[0-9]+):.*');
39
40 let is_last_newline = false; // Whether the last scanned character is a newline
41 let last_update_millis = 0;
42 dbus_pcap1.stdout.on('data', (data) => {
43 const s = data.toString();
44 stdout1 += s;
45 for (let i=0; i<s.length; i++) {
46 const ch = s[i];
47 let is_new_line = false;
48 if (ch == '\n' || ch == '\r') {
49 is_new_line = true;
50 }
51 if (!is_last_newline && is_new_line) {
52 count1 ++;
53 }
54 is_last_newline = is_new_line;
55 }
56 const millis = Date.now();
57 if (millis - last_update_millis > 100) { // Refresh at most 10 times per second
58 let pct = parseInt(count1 * 100 / num_packets);
59 ShowBlocker('Running dbus-pcap (Pass 1/2, packet timestamps): ' + count1 + '/' + num_packets + ' (' + pct + '%)');
60 last_update_millis = millis;
61 }
62 });
63
Sui Chen27cf9332021-11-03 16:20:28 -070064 dbus_pcap1.stderr.on('data', (data) => {
Sui Chene8c12082022-03-06 09:53:14 -080065 console.error(data.toString());
Sui Chen27cf9332021-11-03 16:20:28 -070066 });
67
Sui Chenb65280f2020-06-30 18:14:03 -070068 dbus_pcap1.on('close', (code) => {
69 ShowBlocker('Running dbus-pcap (Pass 2/2, packet contents) ...');
70 let stdout2 = '';
71 let count2 = 0;
72 is_last_newline = false;
73 const dbus_pcap2 =
74 spawn('python3', ['dbus-pcap', file_name, '--json']);
75 dbus_pcap2.stdout.on('data', (data) => {
76 const s = data.toString();
77 stdout2 += s;
78 for (let i=0; i<s.length; i++) {
79 const ch = s[i];
80 let is_new_line = false;
81 if (ch == '\n' || ch == '\r') {
82 is_new_line = true;
83 }
84 if (!is_last_newline && is_new_line) {
85 count2 ++;
86 }
87 is_last_newline = is_new_line;
88 }
89 const millis = Date.now();
90 if (millis - last_update_millis > 100) { // Refresh at most 10 times per second
91 let pct = parseInt(count2 * 100 / num_packets);
92 ShowBlocker('Running dbus-pcap (Pass 2/2, packet contents): ' + count2 + '/' + num_packets + ' (' + pct + '%)');
93 last_update_millis = millis;
94 }
95 });
96
Sui Chen27cf9332021-11-03 16:20:28 -070097 dbus_pcap2.stderr.on('data', (data) => {
Sui Chene8c12082022-03-06 09:53:14 -080098 console.error(data.toString());
Sui Chen27cf9332021-11-03 16:20:28 -070099 });
100
Sui Chenb65280f2020-06-30 18:14:03 -0700101 dbus_pcap2.on('close', (code) => {
102 { ShowBlocker('Processing dbus-pcap output ... '); }
103
104 let packets = [];
105 // Parse timestamps
106 let lines = stdout1.split('\n');
107 for (let i=0; i<lines.length; i++) {
108 let l = lines[i].trim();
109 if (l.length > 0) {
110 // Timestamp
111 l = l.substr(0, l.indexOf(':'));
112 const ts_usec = parseFloat(l) * 1000.0;
113 if (!isNaN(ts_usec)) {
114 timestamps.push(ts_usec);
115 } else {
116 console.log('not a number: ' + l);
117 }
118 }
119 }
120
121 // JSON
122 lines = stdout2.split('\n');
123 for (let i=0; i<lines.length; i++) {
124 let l = lines[i].trim();
125 let parsed = undefined;
126 if (l.length > 0) {
127 try {
128 parsed = JSON.parse(l);
129 } catch (e) {
130 console.log(e);
131 }
132
133 if (parsed == undefined) {
134 try {
135 l = l.replace("NaN", "null");
136 parsed = JSON.parse(l);
137 } catch (e) {
138 console.log(e);
139 }
140 }
141
142 if (parsed != undefined) {
143 packets.push(parsed);
144 }
145 }
146 }
147
148 Timestamps_DBus = timestamps;
149
150 Data_DBus = packets.slice();
151 OnGroupByConditionChanged_DBus();
152 const v = dbus_timeline_view;
153
154 // Will return 2 packages
155 // 1) sensor PropertyChagned signal emissions
156 // 2) everything else
157 let preproc = Preprocess_DBusPcap(packets, timestamps);
158
159 let grouped = Group_DBus(preproc, v.GroupBy);
160 GenerateTimeLine_DBus(grouped);
161
162 dbus_timeline_view.IsCanvasDirty = true;
163 if (dbus_timeline_view.IsEmpty() == false ||
164 ipmi_timeline_view.IsEmpty() == false) {
165 dbus_timeline_view.CurrentFileName = file_name;
166 ipmi_timeline_view.CurrentFileName = file_name;
167 HideWelcomeScreen();
168 ShowDBusTimeline();
169 ShowIPMITimeline();
170 ShowNavigation();
171 UpdateFileNamesString();
172 }
173 HideBlocker();
174
175 g_btn_zoom_reset.click(); // Zoom to capture time range
176 });
177 });
178 })
179}
180
181// Input: data and timestamps obtained from
182// Output: Two arrays
183// The first is sensor PropertyChanged emissions only
184// The second is all other DBus message types
185//
186// This function also determines the starting timestamp of the capture
187//
188function Preprocess_DBusPcap(data, timestamps) {
189 // Also clear IPMI entries
190 g_ipmi_parsed_entries = [];
191
192 let ret = [];
193
194 let in_flight = {};
195 let in_flight_ipmi = {};
196
197 for (let i = 0; i < data.length; i++) {
198 const packet = data[i];
199
200 // Fields we are interested in
201 const fixed_header = packet[0]; // is an [Array(5), Array(6)]
202
203 if (fixed_header == undefined) { // for hacked dbus-pcap
204 console.log(packet);
205 continue;
206 }
207
208 const payload = packet[1];
209 const ty = fixed_header[0][1];
210 let timestamp = timestamps[i];
211 let timestamp_end = undefined;
212 const IDX_TIMESTAMP_END = 8;
213 const IDX_MC_OUTCOME = 9; // Outcome of method call
214
215 let serial, path, member, iface, destination, sender, signature = '';
216 // Same as the format of the Dummy data set
217
218 switch (ty) {
219 case 4: { // signal
220 serial = fixed_header[0][5];
221 path = fixed_header[1][0][1];
222 iface = fixed_header[1][1][1];
223 member = fixed_header[1][2][1];
224 // signature = fixed_header[1][3][1];
225 // fixed_header[1] can have variable length.
226 // For example: signal from org.freedesktop.PolicyKit1.Authority can
227 // have only 4 elements, while most others are 5
228 const idx = fixed_header[1].length - 1;
229 sender = fixed_header[1][idx][1];
230
231 // Ugly fix for:
232 if (sender == "s" || sender == "sss") {
233 sender = packet[1][0];
234 if (fixed_header[1].length == 6) {
235 // Example: fixed_header is
236 // 0: (2) [7, "org.freedesktop.DBus"]
237 // 1: (2) [6, ":1.1440274"]
238 // 2: (2) [1, "/org/freedesktop/DBus"]
239 // 3: (2) [2, "org.freedesktop.DBus"]
240 // 4: (2) [3, "NameLost"]
241 // 5: (2) [8, "s"]
242 path = fixed_header[1][2][1];
243 iface = fixed_header[1][3][1];
244 member = fixed_header[1][4][1];
245 } else if (fixed_header[1].length == 5) {
246 // Example: fixed_header is
247 // 0: (2) [7, "org.freedesktop.DBus"]
248 // 1: (2) [1, "/org/freedesktop/DBus"]
249 // 2: (2) [2, "org.freedesktop.DBus"]
250 // 3: (2) [3, "NameOwnerChanged"]
251 // 4: (2) [8, "sss"]
252 path = fixed_header[1][1][1];
253 iface = fixed_header[1][2][1];
254 member = fixed_header[1][3][1];
255 }
256 }
257
258
259 destination = '<none>';
260 timestamp_end = timestamp;
261 let entry = [
262 'sig', timestamp, serial, sender, destination, path, iface, member,
263 timestamp_end, payload
264 ];
265
266 // Legacy IPMI interface uses signal for IPMI request
267 if (iface == 'org.openbmc.HostIpmi' && member == 'ReceivedMessage') {
268 console.log('Legacy IPMI interface, request');
269 }
270
271 ret.push(entry);
272 break;
273 }
274 case 1: { // method call
275 serial = fixed_header[0][5];
276 path = fixed_header[1][0][1];
277 member = fixed_header[1][1][1];
278 iface = fixed_header[1][2][1];
279 destination = fixed_header[1][3][1];
280 if (fixed_header[1].length > 5) {
281 sender = fixed_header[1][5][1];
282 signature = fixed_header[1][4][1];
283 } else {
284 sender = fixed_header[1][4][1];
285 }
286 let entry = [
287 'mc', timestamp, serial, sender, destination, path, iface, member,
Sui Chenc403b032022-03-06 18:03:12 -0800288 timestamp_end, payload, packet, ''
Sui Chenb65280f2020-06-30 18:14:03 -0700289 ];
290
291 // Legacy IPMI interface uses method call for IPMI response
292 if (iface == 'org.openbmc.HostIpmi' && member == 'sendMessge') {
293 console.log('Legacy IPMI interface, response')
294 } else if (
295 iface == 'xyz.openbmc_project.Ipmi.Server' && member == 'execute') {
296 let ipmi_entry = {
297 netfn: payload[0],
298 lun: payload[1],
299 cmd: payload[2],
300 request: payload[3],
301 start_usec: MyFloatMillisToBigIntUsec(timestamp),
302 end_usec: 0,
303 response: []
304 };
305 in_flight_ipmi[serial] = (ipmi_entry);
306 }
307
308
309 ret.push(entry);
310 in_flight[serial] = (entry);
311 break;
312 }
313 case 2: { // method reply
314 let reply_serial = fixed_header[1][0][1];
315 if (reply_serial in in_flight) {
316 let x = in_flight[reply_serial];
317 delete in_flight[reply_serial];
318 x[IDX_TIMESTAMP_END] = timestamp;
319 x[IDX_MC_OUTCOME] = 'ok';
320 }
321
322 if (reply_serial in in_flight_ipmi) {
323 let x = in_flight_ipmi[reply_serial];
324 delete in_flight_ipmi[reply_serial];
325 if (payload[0] != undefined && payload[0][4] != undefined) {
326 x.response = payload[0][4];
327 }
328 x.end_usec = MyFloatMillisToBigIntUsec(timestamp);
329 g_ipmi_parsed_entries.push(x);
330 }
331 break;
332 }
333 case 3: { // error reply
334 let reply_serial = fixed_header[1][0][1];
335 if (reply_serial in in_flight) {
336 let x = in_flight[reply_serial];
337 delete in_flight[reply_serial];
338 x[IDX_TIMESTAMP_END] = timestamp;
339 x[IDX_MC_OUTCOME] = 'error';
340 }
341 }
342 }
343 }
344
345 if (g_ipmi_parsed_entries.length > 0) UpdateLayout();
346 return ret;
347}