1// This file parses ASCII text-encoded dbus message dump.
2
3function extractUsec(line) {
4  let i0 = line.indexOf('time=');
5  if (i0 == -1) {
6    return BigInt(-1);
7  }
8  let line1 = line.substr(i0);
9  let i1 = line1.indexOf(' ');
10  if (i1 == -1) {
11    return BigInt(-1);
12  }
13  let line2 = line1.substr(5, i1 - 5);
14  let sp = line2.split('.');
15  return BigInt(sp[0]) * BigInt(1000000) + BigInt(sp[1]);
16}
17
18function extractInt(line, kw) {
19  let N = kw.length;
20  let i0 = line.indexOf(kw);
21  if (i0 == -1) {
22    return null;
23  }
24  let line1 = line.substr(i0);
25  let i1 = line1.indexOf(' ');
26  if (i1 == -1) {
27    i1 = line.length;
28  }
29  let line2 = line1.substr(N, i1 - N);
30  return parseInt(line2);
31}
32
33function extractSerial(line) {
34  return extractInt(line, 'serial=');
35}
36
37function extractReplySerial(line) {
38  return extractInt(line, 'reply_serial=');
39}
40
41// Returns [byte, i+1] if successful
42// Returns [null, i  ] if unsuccessful
43function munchByte(lines, i) {
44  if (i >= lines.length) {
45    return [null, i]
46  }
47  let l = lines[i];
48  let idx = l.indexOf('byte');
49  if (idx != -1) {
50    return [parseInt(l.substr(idx + 4), 10), i + 1];
51  } else {
52    return [null, i];
53  }
54}
55
56// array of bytes "@"
57function munchArrayOfBytes1(lines, i) {
58  let l = lines[i];
59  let idx = l.indexOf('array of bytes "');
60  if (idx != -1) {
61    let the_ch = l.substr(idx + 16, 1);
62    return [[the_ch.charCodeAt(0)], i + 1];
63  } else {
64    return [null, i];
65  }
66}
67
68function munchArrayOfBytes2(lines, i) {
69  let l = lines[i];
70  let idx = l.indexOf('array of bytes [');
71  if (idx == -1) {
72    idx = l.indexOf('array [');
73  }
74  if (idx != -1) {
75    let j = i + 1;
76    let payload = [];
77    while (true) {
78      if (j >= lines.length) {
79        break;
80      }
81      l = lines[j];
82      let sp = l.trim().split(' ');
83      let ok = true;
84      for (let k = 0; k < sp.length; k++) {
85        let b = parseInt(sp[k], 16);
86        if (isNaN(b)) {
87          ok = false;
88          break;
89        } else {
90          payload.push(b);
91        }
92      }
93      if (!ok) {
94        j--;
95        break;
96      } else
97        j++;
98    }
99    return [payload, j];
100  } else {
101    return [null, i];
102  }
103}
104
105function munchArrayOfBytes(lines, i) {
106  if (i >= lines.length) return [null, i];
107
108  let x = munchArrayOfBytes1(lines, i);
109  if (x[0] != null) {
110    return x;
111  }
112  x = munchArrayOfBytes2(lines, i);
113  if (x[0] != null) {
114    return x;
115  }
116  return [null, i];
117}
118
119// ReceivedMessage
120function munchLegacyMessageStart(lines, i) {
121  let entry = {
122    netfn: 0,
123    lun: 0,
124    cmd: 0,
125    serial: 0,
126    start_usec: 0,
127    end_usec: 0,
128    request: [],
129    response: []
130  };
131
132  let ts = extractUsec(lines[i]);
133  entry.start_usec = ts;
134
135  let x = munchByte(lines, i + 1);
136  if (x[0] == null) {
137    return [null, i];
138  }
139  entry.serial = x[0];
140  let j = x[1];
141
142  x = munchByte(lines, j);
143  if (x[0] == null) {
144    return [null, i];
145  }
146  entry.netfn = x[0];
147  j = x[1];
148
149  x = munchByte(lines, j);
150  if (x[0] == null) {
151    return [null, i];
152  }
153  entry.lun = x[0];
154  j = x[1];
155
156  x = munchByte(lines, j);
157  if (x[0] == null) {
158    return [null, i];
159  }
160  entry.cmd = x[0];
161  j = x[1];
162
163  x = munchArrayOfBytes(lines, j);
164  if (x[0] == null) {
165    return [null, i];
166  }
167  entry.request = x[0];
168  j = x[1];
169
170  return [entry, j];
171}
172
173function munchLegacyMessageEnd(lines, i, in_flight, parsed_entries) {
174  let ts = extractUsec(lines[i]);
175
176  let x = munchByte(lines, i + 1);
177  if (x[0] == null) {
178    return [null, i];
179  }  // serial
180  let serial = x[0];
181  let j = x[1];
182
183  let entry = null;
184  if (serial in in_flight) {
185    entry = in_flight[serial];
186    delete in_flight[serial];
187  } else {
188    return [null, i];
189  }
190
191  entry.end_usec = ts;
192
193  x = munchByte(lines, j);  // netfn
194  if (x[0] == null) {
195    return [null, i];
196  }
197  if (entry.netfn + 1 == x[0]) {
198  } else {
199    return [null, i];
200  }
201  j = x[1];
202
203  x = munchByte(lines, j);  // lun (not used right now)
204  if (x[0] == null) {
205    return [null, i];
206  }
207  j = x[1];
208
209  x = munchByte(lines, j);  // cmd
210  if (x[0] == null) {
211    return [null, i];
212  }
213  if (entry.cmd == x[0]) {
214  } else {
215    return [null, i];
216  }
217  j = x[1];
218
219  x = munchByte(lines, j);  // cc
220  if (x[0] == null) {
221    return [null, i];
222  }
223  j = x[1];
224
225  x = munchArrayOfBytes(lines, j);
226  if (x[0] == null) {
227    entry.response = [];
228  } else {
229    entry.response = x[0];
230  }
231  j = x[1];
232
233  parsed_entries.push(entry);
234
235  return [entry, j];
236}
237
238function munchNewMessageStart(lines, i, in_flight) {
239  let ts = extractUsec(lines[i]);
240  let serial = extractSerial(lines[i]);
241
242  let entry = {
243    netfn: 0,
244    lun: 0,
245    cmd: 0,
246    serial: -999,
247    start_usec: 0,
248    end_usec: 0,
249    request: [],
250    response: []
251  };
252  entry.start_usec = ts;
253  entry.serial = serial;
254
255  let x = munchByte(lines, i + 1);
256  if (x[0] == null) {
257    return [null, i];
258  }
259  entry.netfn = x[0];
260  let j = x[1];
261
262  x = munchByte(lines, j);
263  if (x[0] == null) {
264    return [null, i];
265  }
266  entry.lun = x[0];
267  j = x[1];
268
269  x = munchByte(lines, j);
270  if (x[0] == null) {
271    return [null, i];
272  }
273  entry.cmd = x[0];
274  j = x[1];
275
276  x = munchArrayOfBytes(lines, j);
277  if (x[0] == null) {
278    entry.request = [];
279  }  // Truncated
280  entry.request = x[0];
281  j = x[1];
282
283  return [entry, j];
284}
285
286function munchNewMessageEnd(lines, i, in_flight, parsed_entries) {
287  let ts = extractUsec(lines[i]);
288  let reply_serial = extractReplySerial(lines[i]);
289
290  let entry = null;
291  if (reply_serial in in_flight) {
292    entry = in_flight[reply_serial];
293    delete in_flight[reply_serial];
294  } else {
295    return [null, i];
296  }
297
298  entry.end_usec = ts;
299
300  let x = munchByte(lines, i + 2);  // Skip "struct {"
301  if (x[0] == null) {
302    return [null, i];
303  }  // NetFN
304  if (entry.netfn + 1 != x[0]) {
305    return [null, i];
306  }
307  let j = x[1];
308
309  x = munchByte(lines, j);  // LUN
310  if (x[0] == null) {
311    return [null, i];
312  }
313  j = x[1];
314
315  x = munchByte(lines, j);  // CMD
316  if (x[0] == null) {
317    return [null, i];
318  }
319  if (entry.cmd != x[0]) {
320    return [null, i];
321  }
322  j = x[1];
323
324  x = munchByte(lines, j);  // cc
325  if (x[0] == null) {
326    return [null, i];
327  }
328  j = x[1];
329
330  x = munchArrayOfBytes(lines, j);
331  if (x[0] == null) {
332    entry.response = [];
333  } else {
334    entry.response = x[0];
335  }
336  j = x[1];
337
338  parsed_entries.push(entry);
339
340  return [entry, j];
341}
342
343// Parsing state
344let g_ipmi_parse_buf = '';
345let g_ipmi_parse_lines = [];
346let g_ipmi_in_flight = {};
347let g_ipmi_parsed_entries = [];
348function StartParseIPMIDump() {
349  g_ipmi_parse_lines = [];
350  g_ipmi_parsed_entries = [];
351  g_ipmi_in_flight = {};
352  g_ipmi_parse_buf = '';
353}
354function AppendToParseBuffer(x) {
355  g_ipmi_parse_buf += x;
356}
357function MunchLines() {
358  // 1. Extract all lines from the buffer
359  let chars_munched = 0;
360  while (true) {
361    let idx = g_ipmi_parse_buf.indexOf('\n');
362    if (idx == -1) break;
363    let l = g_ipmi_parse_buf.substr(0, idx);
364    g_ipmi_parse_lines.push(l);
365    g_ipmi_parse_buf = g_ipmi_parse_buf.substr(idx + 1);
366    chars_munched += (idx + 1);
367  }
368  console.log(chars_munched + ' chars munched');
369
370  // 2. Parse as many lines as possible
371  let lidx_last = 0;
372  let i = 0;
373  while (i < g_ipmi_parse_lines.length) {
374    let line = g_ipmi_parse_lines[i];
375    if (line.indexOf('interface=org.openbmc.HostIpmi') != -1 &&
376        line.indexOf('member=ReceivedMessage') != -1) {
377      let x = munchLegacyMessageStart(g_ipmi_parse_lines, i);
378      let entry = x[0];
379      if (i != x[1]) lidx_last = x[1];  // Munch success!
380      i = x[1];
381      if (entry != null) {
382        g_ipmi_in_flight[entry.serial] = entry;
383      }
384    } else if (
385        line.indexOf('interface=org.openbmc.HostIpmi') != -1 &&
386        line.indexOf('member=sendMessage') != -1) {
387      let x = munchLegacyMessageEnd(
388          g_ipmi_parse_lines, i, g_ipmi_in_flight, g_ipmi_parsed_entries);
389      if (i != x[1]) lidx_last = x[1];  // Munch success!
390      i = x[1];
391
392    } else if (
393        line.indexOf('interface=xyz.openbmc_project.Ipmi.Server') != -1 &&
394        line.indexOf('member=execute') != -1) {
395      let x = munchNewMessageStart(g_ipmi_parse_lines, i);
396      let entry = x[0];
397      if (i != x[1]) lidx_last = x[1];
398      i = x[1];
399      if (entry != null) {
400        g_ipmi_in_flight[entry.serial] = entry;
401      }
402    } else if (line.indexOf('method return') != -1) {
403      let x = munchNewMessageEnd(
404          g_ipmi_parse_lines, i, g_ipmi_in_flight, g_ipmi_parsed_entries);
405      if (i != x[1]) lidx_last = x[1];  // Munch success
406      i = x[1];
407    }
408    i++;
409  }
410  g_ipmi_parse_lines = g_ipmi_parse_lines.slice(
411      lidx_last,
412      g_ipmi_parse_lines.length);  // Remove munched lines
413  console.log(
414      lidx_last + ' lines munched, |lines|=' + g_ipmi_parse_lines.length +
415          ', |entries|=' + g_ipmi_parsed_entries.length,
416      ', |inflight|=' + Object.keys(g_ipmi_in_flight).length);
417}
418
419let last_update_time = 0;  // Millis since Unix Epoch
420function UpdateLayout(level) {
421  const this_update_time = new Date().getTime();
422  const over_1s = (this_update_time - last_update_time > 1000);
423  if (!over_1s) {
424    if (level > 0) {
425      setTimeout(function() {
426        UpdateLayout(level - 1);
427      }, 1000);
428    } else {
429      return;
430    }
431  }
432
433  if (g_ipmi_parsed_entries.length > 0) {
434    last_update_time = this_update_time;
435    // Write to Data_IPMI
436    let ts0 = g_ipmi_parsed_entries[0].start_usec;
437    let ts1 = g_ipmi_parsed_entries[g_ipmi_parsed_entries.length - 1].end_usec;
438
439    // When calling from DBus PCap loader, the following happens
440    // >> OnGroupByConditionChanged
441    //   >> Preprocess  <-- Time shift will happen here
442    // So, we don't do time-shifting here
443    let time_shift;
444    if (g_StartingSec != undefined) {
445      time_shift = BigInt(0);
446    } else {  // This is during live capture mode
447      time_shift = ts0;
448    }
449
450    Data_IPMI = [];
451    for (i = 0; i < g_ipmi_parsed_entries.length; i++) {
452      let entry = g_ipmi_parsed_entries[i];
453      let x = [
454        entry.netfn, entry.cmd, parseInt(entry.start_usec - time_shift),
455        parseInt(entry.end_usec - time_shift), entry.request, entry.response
456      ];
457      Data_IPMI.push(x);
458    }
459
460    // Re-calculate time range
461    RANGE_LEFT_INIT = 0;
462    RANGE_RIGHT_INIT =
463        parseInt((ts1 - ts0) / BigInt(1000000) / BigInt(10)) * 10 + 10;
464
465    IsCanvasDirty = true;
466    OnGroupByConditionChanged();
467
468    ComputeHistogram();
469  } else {
470    console.log('No entries parsed');
471  }
472}
473
474function ParseIPMIDump(data) {
475  StartParseIPMIDump();
476  AppendToParseBuffer(data);
477  MunchLines();
478  UpdateLayout();
479}
480