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