(traceJson)
| 824 | } |
| 825 | |
| 826 | export function parseTrace(traceJson) { |
| 827 | const events = traceJson.traceEvents || []; |
| 828 | |
| 829 | // Find main renderer thread |
| 830 | const mainThread = findMainRendererThread(events); |
| 831 | |
| 832 | if (!mainThread) { |
| 833 | throw new Error('Could not find main renderer thread (CrRendererMain)'); |
| 834 | } |
| 835 | |
| 836 | // Compute trace bounds (DevTools MetaHandler logic) |
| 837 | const traceBounds = computeTraceBounds(events); |
| 838 | const traceBoundsUs = { |
| 839 | min: traceBounds.minUs, |
| 840 | max: traceBounds.maxUs, |
| 841 | range: traceBounds.maxUs - traceBounds.minUs, |
| 842 | }; |
| 843 | |
| 844 | // Get ALL main thread events (ph=X with positive duration) for window calculation |
| 845 | // This matches DevTools's topMostMainThreadAppender.getEntries() behavior |
| 846 | const allMainThreadEntries = events.filter(e => |
| 847 | e.ph === 'X' && e.dur > 0 && |
| 848 | e.pid === mainThread.pid && |
| 849 | e.tid === mainThread.tid |
| 850 | ); |
| 851 | |
| 852 | // Calculate the auto-zoomed window using DevTools's MainThreadActivity.calculateWindow |
| 853 | const windowUs = calculateWindow(traceBoundsUs, allMainThreadEntries); |
| 854 | const windowMinMs = windowUs.min / 1000; |
| 855 | const windowMaxMs = windowUs.max / 1000; |
| 856 | const windowRangeMs = windowUs.range / 1000; |
| 857 | |
| 858 | // Filter to VISIBLE events only for stats computation |
| 859 | const mainThreadEvents = events.filter(e => |
| 860 | e.ph === 'X' && |
| 861 | e.dur && e.dur > 0 && |
| 862 | e.pid === mainThread.pid && |
| 863 | e.tid === mainThread.tid && |
| 864 | isVisibleEvent(e) |
| 865 | ); |
| 866 | |
| 867 | if (mainThreadEvents.length === 0) { |
| 868 | throw new Error('No visible events found for main thread'); |
| 869 | } |
| 870 | |
| 871 | // Calculate stats using DevTools algorithm with absolute ms timestamps |
| 872 | const stats = statsForTimeRange(mainThreadEvents, windowMinMs, windowMaxMs); |
| 873 | |
| 874 | // Add ProfileCall scripting contribution from CPU profile synthesis |
| 875 | // (SamplesIntegrator synthesizes ProfileCall events for non-idle CPU samples |
| 876 | // that fall in gaps within RunTask intervals not covered by visible scripting events) |
| 877 | const profileCallMs = computeProfileCallScripting( |
| 878 | events, mainThread.pid, mainThread.tid, |
| 879 | windowUs.min, windowUs.max |
| 880 | ); |
| 881 | |
| 882 | if (profileCallMs > 0) { |
| 883 | stats.scripting = (stats.scripting || 0) + profileCallMs; |
no test coverage detected
searching dependent graphs…