| 402 | |
| 403 | // Find ALL CrRendererMain threads, return the one with the most events |
| 404 | function findMainRendererThread(events) { |
| 405 | const threadNames = new Map(); |
| 406 | |
| 407 | for (const event of events) { |
| 408 | if (event.name === 'thread_name' && event.ph === 'M' && event.args && event.args.name) { |
| 409 | const key = `${event.pid}-${event.tid}`; |
| 410 | |
| 411 | threadNames.set(key, event.args.name); |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | const rendererThreads = []; |
| 416 | |
| 417 | for (const [key, name] of threadNames.entries()) { |
| 418 | if (name === 'CrRendererMain') { |
| 419 | const [pid, tid] = key.split('-').map(Number); |
| 420 | |
| 421 | rendererThreads.push({ pid, tid }); |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | if (rendererThreads.length === 0) { |
| 426 | return null; |
| 427 | } |
| 428 | if (rendererThreads.length === 1) { |
| 429 | return rendererThreads[0]; |
| 430 | } |
| 431 | |
| 432 | // Multiple renderer threads: pick the one with the most complete events |
| 433 | let best = null; |
| 434 | let bestCount = 0; |
| 435 | |
| 436 | for (const thread of rendererThreads) { |
| 437 | const count = events.filter(e => |
| 438 | e.ph === 'X' && e.pid === thread.pid && e.tid === thread.tid |
| 439 | ).length; |
| 440 | |
| 441 | if (count > bestCount) { |
| 442 | bestCount = count; |
| 443 | best = thread; |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | return best; |
| 448 | } |
| 449 | |
| 450 | // Compute trace bounds matching DevTools MetaHandler logic: |
| 451 | // - min = TracingStartedInBrowser.ts (if present), else min of all event timestamps |