Sui Chen | b65280f | 2020-06-30 18:14:03 -0700 | [diff] [blame] | 1 | // This file performs the file reading step |
| 2 | // Actual preprocessing is done in dbus_timeline_vis.js |
| 3 | |
| 4 | function 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 | // |
| 17 | function 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 Chen | 27cf933 | 2021-11-03 16:20:28 -0700 | [diff] [blame] | 64 | dbus_pcap1.stderr.on('data', (data) => { |
Sui Chen | e8c1208 | 2022-03-06 09:53:14 -0800 | [diff] [blame] | 65 | console.error(data.toString()); |
Sui Chen | 27cf933 | 2021-11-03 16:20:28 -0700 | [diff] [blame] | 66 | }); |
| 67 | |
Sui Chen | b65280f | 2020-06-30 18:14:03 -0700 | [diff] [blame] | 68 | 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 Chen | 27cf933 | 2021-11-03 16:20:28 -0700 | [diff] [blame] | 97 | dbus_pcap2.stderr.on('data', (data) => { |
Sui Chen | e8c1208 | 2022-03-06 09:53:14 -0800 | [diff] [blame] | 98 | console.error(data.toString()); |
Sui Chen | 27cf933 | 2021-11-03 16:20:28 -0700 | [diff] [blame] | 99 | }); |
| 100 | |
Sui Chen | b65280f | 2020-06-30 18:14:03 -0700 | [diff] [blame] | 101 | 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 | // |
| 188 | function 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 Chen | c403b03 | 2022-03-06 18:03:12 -0800 | [diff] [blame] | 288 | timestamp_end, payload, packet, '' |
Sui Chen | b65280f | 2020-06-30 18:14:03 -0700 | [diff] [blame] | 289 | ]; |
| 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 | } |