// This file parses ASCII text-encoded dbus message dump. function extractUsec(line) { let i0 = line.indexOf('time='); if (i0 == -1) { return BigInt(-1); } let line1 = line.substr(i0); let i1 = line1.indexOf(' '); if (i1 == -1) { return BigInt(-1); } let line2 = line1.substr(5, i1 - 5); let sp = line2.split('.'); return BigInt(sp[0]) * BigInt(1000000) + BigInt(sp[1]); } function extractInt(line, kw) { let N = kw.length; let i0 = line.indexOf(kw); if (i0 == -1) { return null; } let line1 = line.substr(i0); let i1 = line1.indexOf(' '); if (i1 == -1) { i1 = line.length; } let line2 = line1.substr(N, i1 - N); return parseInt(line2); } function extractSerial(line) { return extractInt(line, 'serial='); } function extractReplySerial(line) { return extractInt(line, 'reply_serial='); } // Returns [byte, i+1] if successful // Returns [null, i ] if unsuccessful function munchByte(lines, i) { if (i >= lines.length) { return [null, i] } let l = lines[i]; let idx = l.indexOf('byte'); if (idx != -1) { return [parseInt(l.substr(idx + 4), 10), i + 1]; } else { return [null, i]; } } // array of bytes "@" function munchArrayOfBytes1(lines, i) { let l = lines[i]; let idx = l.indexOf('array of bytes "'); if (idx != -1) { let the_ch = l.substr(idx + 16, 1); return [[the_ch.charCodeAt(0)], i + 1]; } else { return [null, i]; } } function munchArrayOfBytes2(lines, i) { let l = lines[i]; let idx = l.indexOf('array of bytes ['); if (idx == -1) { idx = l.indexOf('array ['); } if (idx != -1) { let j = i + 1; let payload = []; while (true) { if (j >= lines.length) { break; } l = lines[j]; let sp = l.trim().split(' '); let ok = true; for (let k = 0; k < sp.length; k++) { let b = parseInt(sp[k], 16); if (isNaN(b)) { ok = false; break; } else { payload.push(b); } } if (!ok) { j--; break; } else j++; } return [payload, j]; } else { return [null, i]; } } function munchArrayOfBytes(lines, i) { if (i >= lines.length) return [null, i]; let x = munchArrayOfBytes1(lines, i); if (x[0] != null) { return x; } x = munchArrayOfBytes2(lines, i); if (x[0] != null) { return x; } return [null, i]; } // ReceivedMessage function munchLegacyMessageStart(lines, i) { let entry = { netfn: 0, lun: 0, cmd: 0, serial: 0, start_usec: 0, end_usec: 0, request: [], response: [] }; let ts = extractUsec(lines[i]); entry.start_usec = ts; let x = munchByte(lines, i + 1); if (x[0] == null) { return [null, i]; } entry.serial = x[0]; let j = x[1]; x = munchByte(lines, j); if (x[0] == null) { return [null, i]; } entry.netfn = x[0]; j = x[1]; x = munchByte(lines, j); if (x[0] == null) { return [null, i]; } entry.lun = x[0]; j = x[1]; x = munchByte(lines, j); if (x[0] == null) { return [null, i]; } entry.cmd = x[0]; j = x[1]; x = munchArrayOfBytes(lines, j); if (x[0] == null) { return [null, i]; } entry.request = x[0]; j = x[1]; return [entry, j]; } function munchLegacyMessageEnd(lines, i, in_flight, parsed_entries) { let ts = extractUsec(lines[i]); let x = munchByte(lines, i + 1); if (x[0] == null) { return [null, i]; } // serial let serial = x[0]; let j = x[1]; let entry = null; if (serial in in_flight) { entry = in_flight[serial]; delete in_flight[serial]; } else { return [null, i]; } entry.end_usec = ts; x = munchByte(lines, j); // netfn if (x[0] == null) { return [null, i]; } if (entry.netfn + 1 == x[0]) { } else { return [null, i]; } j = x[1]; x = munchByte(lines, j); // lun (not used right now) if (x[0] == null) { return [null, i]; } j = x[1]; x = munchByte(lines, j); // cmd if (x[0] == null) { return [null, i]; } if (entry.cmd == x[0]) { } else { return [null, i]; } j = x[1]; x = munchByte(lines, j); // cc if (x[0] == null) { return [null, i]; } j = x[1]; x = munchArrayOfBytes(lines, j); if (x[0] == null) { entry.response = []; } else { entry.response = x[0]; } j = x[1]; parsed_entries.push(entry); return [entry, j]; } function munchNewMessageStart(lines, i, in_flight) { let ts = extractUsec(lines[i]); let serial = extractSerial(lines[i]); let entry = { netfn: 0, lun: 0, cmd: 0, serial: -999, start_usec: 0, end_usec: 0, request: [], response: [] }; entry.start_usec = ts; entry.serial = serial; let x = munchByte(lines, i + 1); if (x[0] == null) { return [null, i]; } entry.netfn = x[0]; let j = x[1]; x = munchByte(lines, j); if (x[0] == null) { return [null, i]; } entry.lun = x[0]; j = x[1]; x = munchByte(lines, j); if (x[0] == null) { return [null, i]; } entry.cmd = x[0]; j = x[1]; x = munchArrayOfBytes(lines, j); if (x[0] == null) { entry.request = []; } // Truncated entry.request = x[0]; j = x[1]; return [entry, j]; } function munchNewMessageEnd(lines, i, in_flight, parsed_entries) { let ts = extractUsec(lines[i]); let reply_serial = extractReplySerial(lines[i]); let entry = null; if (reply_serial in in_flight) { entry = in_flight[reply_serial]; delete in_flight[reply_serial]; } else { return [null, i]; } entry.end_usec = ts; let x = munchByte(lines, i + 2); // Skip "struct {" if (x[0] == null) { return [null, i]; } // NetFN if (entry.netfn + 1 != x[0]) { return [null, i]; } let j = x[1]; x = munchByte(lines, j); // LUN if (x[0] == null) { return [null, i]; } j = x[1]; x = munchByte(lines, j); // CMD if (x[0] == null) { return [null, i]; } if (entry.cmd != x[0]) { return [null, i]; } j = x[1]; x = munchByte(lines, j); // cc if (x[0] == null) { return [null, i]; } j = x[1]; x = munchArrayOfBytes(lines, j); if (x[0] == null) { entry.response = []; } else { entry.response = x[0]; } j = x[1]; parsed_entries.push(entry); return [entry, j]; } // Parsing state let g_ipmi_parse_buf = ''; let g_ipmi_parse_lines = []; let g_ipmi_in_flight = {}; let g_ipmi_parsed_entries = []; function StartParseIPMIDump() { g_ipmi_parse_lines = []; g_ipmi_parsed_entries = []; g_ipmi_in_flight = {}; g_ipmi_parse_buf = ''; } function AppendToParseBuffer(x) { g_ipmi_parse_buf += x; } function MunchLines() { // 1. Extract all lines from the buffer let chars_munched = 0; while (true) { let idx = g_ipmi_parse_buf.indexOf('\n'); if (idx == -1) break; let l = g_ipmi_parse_buf.substr(0, idx); g_ipmi_parse_lines.push(l); g_ipmi_parse_buf = g_ipmi_parse_buf.substr(idx + 1); chars_munched += (idx + 1); } console.log(chars_munched + ' chars munched'); // 2. Parse as many lines as possible let lidx_last = 0; let i = 0; while (i < g_ipmi_parse_lines.length) { let line = g_ipmi_parse_lines[i]; if (line.indexOf('interface=org.openbmc.HostIpmi') != -1 && line.indexOf('member=ReceivedMessage') != -1) { let x = munchLegacyMessageStart(g_ipmi_parse_lines, i); let entry = x[0]; if (i != x[1]) lidx_last = x[1]; // Munch success! i = x[1]; if (entry != null) { g_ipmi_in_flight[entry.serial] = entry; } } else if ( line.indexOf('interface=org.openbmc.HostIpmi') != -1 && line.indexOf('member=sendMessage') != -1) { let x = munchLegacyMessageEnd( g_ipmi_parse_lines, i, g_ipmi_in_flight, g_ipmi_parsed_entries); if (i != x[1]) lidx_last = x[1]; // Munch success! i = x[1]; } else if ( line.indexOf('interface=xyz.openbmc_project.Ipmi.Server') != -1 && line.indexOf('member=execute') != -1) { let x = munchNewMessageStart(g_ipmi_parse_lines, i); let entry = x[0]; if (i != x[1]) lidx_last = x[1]; i = x[1]; if (entry != null) { g_ipmi_in_flight[entry.serial] = entry; } } else if (line.indexOf('method return') != -1) { let x = munchNewMessageEnd( g_ipmi_parse_lines, i, g_ipmi_in_flight, g_ipmi_parsed_entries); if (i != x[1]) lidx_last = x[1]; // Munch success i = x[1]; } i++; } g_ipmi_parse_lines = g_ipmi_parse_lines.slice( lidx_last, g_ipmi_parse_lines.length); // Remove munched lines console.log( lidx_last + ' lines munched, |lines|=' + g_ipmi_parse_lines.length + ', |entries|=' + g_ipmi_parsed_entries.length, ', |inflight|=' + Object.keys(g_ipmi_in_flight).length); } let last_update_time = 0; // Millis since Unix Epoch function UpdateLayout(level) { const this_update_time = new Date().getTime(); const over_1s = (this_update_time - last_update_time > 1000); if (!over_1s) { if (level > 0) { setTimeout(function() { UpdateLayout(level - 1); }, 1000); } else { return; } } if (g_ipmi_parsed_entries.length > 0) { last_update_time = this_update_time; // Write to Data_IPMI let ts0 = g_ipmi_parsed_entries[0].start_usec; let ts1 = g_ipmi_parsed_entries[g_ipmi_parsed_entries.length - 1].end_usec; // When calling from DBus PCap loader, the following happens // >> OnGroupByConditionChanged // >> Preprocess <-- Time shift will happen here // So, we don't do time-shifting here let time_shift; if (g_StartingSec != undefined) { time_shift = BigInt(0); } else { // This is during live capture mode time_shift = ts0; } Data_IPMI = []; for (i = 0; i < g_ipmi_parsed_entries.length; i++) { let entry = g_ipmi_parsed_entries[i]; let x = [ entry.netfn, entry.cmd, parseInt(entry.start_usec - time_shift), parseInt(entry.end_usec - time_shift), entry.request, entry.response ]; Data_IPMI.push(x); } // Re-calculate time range RANGE_LEFT_INIT = 0; RANGE_RIGHT_INIT = parseInt((ts1 - ts0) / BigInt(1000000) / BigInt(10)) * 10 + 10; IsCanvasDirty = true; OnGroupByConditionChanged(); ComputeHistogram(); } else { console.log('No entries parsed'); } } function ParseIPMIDump(data) { StartParseIPMIDump(); AppendToParseBuffer(data); MunchLines(); UpdateLayout(); }