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