benchforge 0.1.11 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +99 -294
  3. package/bin/benchforge +1 -2
  4. package/dist/AnalyzeArchive-8NCJhmhS.mjs +145 -0
  5. package/dist/AnalyzeArchive-8NCJhmhS.mjs.map +1 -0
  6. package/dist/BenchMatrix-BZVrBB_h.mjs +1050 -0
  7. package/dist/BenchMatrix-BZVrBB_h.mjs.map +1 -0
  8. package/dist/{BenchRunner-BzyUfiyB.d.mts → BenchRunner-DglX1NOn.d.mts} +119 -66
  9. package/dist/CoverageSampler-D5T9DRqe.mjs +27 -0
  10. package/dist/CoverageSampler-D5T9DRqe.mjs.map +1 -0
  11. package/dist/Formatters-BWj3d4sv.mjs +95 -0
  12. package/dist/Formatters-BWj3d4sv.mjs.map +1 -0
  13. package/dist/{HeapSampler-B8dtKHn1.mjs → HeapSampler-Dq-hpXem.mjs} +4 -4
  14. package/dist/HeapSampler-Dq-hpXem.mjs.map +1 -0
  15. package/dist/RunBenchCLI-C17DrJz8.mjs +3075 -0
  16. package/dist/RunBenchCLI-C17DrJz8.mjs.map +1 -0
  17. package/dist/StatisticalUtils-BD92crgM.mjs +255 -0
  18. package/dist/StatisticalUtils-BD92crgM.mjs.map +1 -0
  19. package/dist/TimeSampler-Ds8n7l2B.mjs +29 -0
  20. package/dist/TimeSampler-Ds8n7l2B.mjs.map +1 -0
  21. package/dist/ViewerServer-BJhdnxlN.mjs +639 -0
  22. package/dist/ViewerServer-BJhdnxlN.mjs.map +1 -0
  23. package/dist/ViewerServer-CuMNdNBz.mjs +2 -0
  24. package/dist/bin/benchforge.mjs +4 -5
  25. package/dist/bin/benchforge.mjs.map +1 -1
  26. package/dist/index.d.mts +711 -558
  27. package/dist/index.mjs +98 -3
  28. package/dist/index.mjs.map +1 -0
  29. package/dist/runners/WorkerScript.d.mts +12 -4
  30. package/dist/runners/WorkerScript.mjs +77 -105
  31. package/dist/runners/WorkerScript.mjs.map +1 -1
  32. package/dist/viewer/assets/CIPlot-BkOvMoMa.js +1 -0
  33. package/dist/viewer/assets/HistogramKde-CmSyUFY0.js +1 -0
  34. package/dist/viewer/assets/LegendUtils-BJpbn_jr.js +55 -0
  35. package/dist/viewer/assets/SampleTimeSeries-C4VBhXr3.js +1 -0
  36. package/dist/viewer/assets/index-Br9bp_cX.js +153 -0
  37. package/dist/viewer/assets/index-NzXXe_CC.css +1 -0
  38. package/dist/viewer/index.html +19 -0
  39. package/dist/viewer/speedscope/LICENSE +21 -0
  40. package/dist/viewer/speedscope/SourceCodePro-Regular.ttf-ILST5JV6.woff2 +0 -0
  41. package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js +2 -0
  42. package/dist/viewer/speedscope/favicon-16x16-V2DMIAZS.js.map +7 -0
  43. package/dist/viewer/speedscope/favicon-16x16-VSI62OPJ.png +0 -0
  44. package/dist/viewer/speedscope/favicon-32x32-3EB2YCUY.png +0 -0
  45. package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js +2 -0
  46. package/dist/viewer/speedscope/favicon-32x32-THY3JDJL.js.map +7 -0
  47. package/dist/viewer/speedscope/favicon-FOKUP5Y5.ico +0 -0
  48. package/dist/viewer/speedscope/favicon-M34RF7BI.js +2 -0
  49. package/dist/viewer/speedscope/favicon-M34RF7BI.js.map +7 -0
  50. package/dist/viewer/speedscope/file-format-schema.json +274 -0
  51. package/dist/viewer/speedscope/index.html +19 -0
  52. package/dist/viewer/speedscope/jfrview_bg-BLJXNNQB.wasm +0 -0
  53. package/dist/viewer/speedscope/perf-vertx-stacks-01-collapsed-all-ZNUIGAJL.txt +199 -0
  54. package/dist/viewer/speedscope/release.txt +3 -0
  55. package/dist/viewer/speedscope/source-code-pro.LICENSE.md +93 -0
  56. package/dist/viewer/speedscope/speedscope-GHPHNKXC.css +2 -0
  57. package/dist/viewer/speedscope/speedscope-GHPHNKXC.css.map +7 -0
  58. package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js +212 -0
  59. package/dist/viewer/speedscope/speedscope-QZFMJ7VP.js.map +7 -0
  60. package/package.json +52 -27
  61. package/src/bin/benchforge.ts +2 -2
  62. package/src/cli/AnalyzeArchive.ts +232 -0
  63. package/src/cli/BrowserBench.ts +322 -0
  64. package/src/cli/CliArgs.ts +164 -51
  65. package/src/cli/CliExport.ts +179 -0
  66. package/src/cli/CliOptions.ts +147 -0
  67. package/src/cli/CliReport.ts +197 -0
  68. package/src/cli/FilterBenchmarks.ts +18 -30
  69. package/src/cli/RunBenchCLI.ts +132 -866
  70. package/src/cli/SuiteRunner.ts +160 -0
  71. package/src/cli/ViewerServer.ts +282 -0
  72. package/src/export/AllocExport.ts +121 -0
  73. package/src/export/ArchiveExport.ts +146 -0
  74. package/src/export/ArchiveFormat.ts +50 -0
  75. package/src/export/CoverageExport.ts +148 -0
  76. package/src/export/EditorUri.ts +10 -0
  77. package/src/export/PerfettoExport.ts +64 -99
  78. package/src/export/SpeedscopeTypes.ts +98 -0
  79. package/src/export/TimeExport.ts +115 -0
  80. package/src/index.ts +86 -67
  81. package/src/matrix/BenchMatrix.ts +230 -0
  82. package/src/matrix/CaseLoader.ts +8 -6
  83. package/src/matrix/MatrixDirRunner.ts +153 -0
  84. package/src/matrix/MatrixFilter.ts +49 -47
  85. package/src/matrix/MatrixInlineRunner.ts +50 -0
  86. package/src/matrix/MatrixReport.ts +90 -250
  87. package/src/matrix/VariantLoader.ts +5 -5
  88. package/src/profiling/browser/BenchLoop.ts +51 -0
  89. package/src/profiling/browser/BrowserCDP.ts +133 -0
  90. package/src/profiling/browser/BrowserGcStats.ts +33 -0
  91. package/src/profiling/browser/BrowserProfiler.ts +160 -0
  92. package/src/profiling/browser/CdpClient.ts +82 -0
  93. package/src/profiling/browser/CdpPage.ts +138 -0
  94. package/src/profiling/browser/ChromeLauncher.ts +158 -0
  95. package/src/profiling/browser/ChromeTraceEvent.ts +28 -0
  96. package/src/profiling/browser/PageLoadMode.ts +61 -0
  97. package/src/profiling/node/CoverageSampler.ts +27 -0
  98. package/src/profiling/node/CoverageTypes.ts +23 -0
  99. package/src/profiling/node/HeapSampleReport.ts +261 -0
  100. package/src/{heap-sample → profiling/node}/HeapSampler.ts +1 -2
  101. package/src/{heap-sample → profiling/node}/ResolvedProfile.ts +18 -9
  102. package/src/profiling/node/TimeSampler.ts +57 -0
  103. package/src/report/BenchmarkReport.ts +146 -0
  104. package/src/report/Colors.ts +9 -0
  105. package/src/report/Formatters.ts +110 -0
  106. package/src/report/GcSections.ts +151 -0
  107. package/src/{GitUtils.ts → report/GitUtils.ts} +18 -19
  108. package/src/report/HtmlReport.ts +223 -0
  109. package/src/report/ParseStats.ts +73 -0
  110. package/src/report/StandardSections.ts +147 -0
  111. package/src/report/ViewerSections.ts +286 -0
  112. package/src/report/text/TableReport.ts +253 -0
  113. package/src/report/text/TextReport.ts +123 -0
  114. package/src/runners/AdaptiveWrapper.ts +116 -236
  115. package/src/runners/BenchRunner.ts +20 -15
  116. package/src/{Benchmark.ts → runners/BenchmarkSpec.ts} +5 -6
  117. package/src/runners/CreateRunner.ts +5 -7
  118. package/src/runners/GcStats.ts +47 -50
  119. package/src/{MeasuredResults.ts → runners/MeasuredResults.ts} +43 -37
  120. package/src/runners/MergeBatches.ts +123 -0
  121. package/src/{NodeGC.ts → runners/NodeGC.ts} +2 -3
  122. package/src/runners/RunnerOrchestrator.ts +127 -243
  123. package/src/runners/RunnerUtils.ts +75 -1
  124. package/src/runners/SampleStats.ts +100 -0
  125. package/src/runners/TimingRunner.ts +244 -0
  126. package/src/runners/TimingUtils.ts +3 -2
  127. package/src/runners/WorkerScript.ts +135 -151
  128. package/src/stats/BootstrapDifference.ts +282 -0
  129. package/src/{PermutationTest.ts → stats/PermutationTest.ts} +8 -17
  130. package/src/stats/StatisticalUtils.ts +445 -0
  131. package/src/{tests → test}/AdaptiveConvergence.test.ts +10 -10
  132. package/src/test/AdaptiveRunner.test.ts +39 -41
  133. package/src/{tests → test}/AdaptiveSampling.test.ts +9 -9
  134. package/src/test/AdaptiveStatistics.integration.ts +2 -2
  135. package/src/{tests → test}/BenchMatrix.test.ts +19 -16
  136. package/src/test/BenchmarkReport.test.ts +63 -13
  137. package/src/test/BrowserBench.e2e.test.ts +186 -17
  138. package/src/test/BrowserBench.test.ts +10 -5
  139. package/src/test/BuildTimeSection.test.ts +130 -0
  140. package/src/test/CapSamples.test.ts +82 -0
  141. package/src/test/CoverageExport.test.ts +115 -0
  142. package/src/test/CoverageSampler.test.ts +33 -0
  143. package/src/test/HeapAttribution.test.ts +14 -14
  144. package/src/{tests → test}/MatrixFilter.test.ts +1 -1
  145. package/src/{tests → test}/MatrixReport.test.ts +1 -1
  146. package/src/test/PermutationTest.test.ts +1 -1
  147. package/src/{tests → test}/RealDataValidation.test.ts +6 -6
  148. package/src/test/RunBenchCLI.test.ts +39 -38
  149. package/src/test/RunnerOrchestrator.test.ts +12 -12
  150. package/src/test/StatisticalUtils.test.ts +48 -12
  151. package/src/{table-util/test → test}/TableReport.test.ts +2 -2
  152. package/src/test/TestUtils.ts +12 -7
  153. package/src/test/TimeExport.test.ts +139 -0
  154. package/src/test/TimeSampler.test.ts +37 -0
  155. package/src/test/ViewerLive.e2e.test.ts +159 -0
  156. package/src/test/ViewerStatic.static.e2e.test.ts +137 -0
  157. package/src/{tests → test}/fixtures/baseline/impl.ts +1 -1
  158. package/src/{tests → test}/fixtures/bevy30-samples.ts +3 -1
  159. package/src/test/fixtures/cases/asyncCases.ts +9 -0
  160. package/src/{tests → test}/fixtures/cases/cases.ts +5 -2
  161. package/src/test/fixtures/cases/variants/product.ts +2 -0
  162. package/src/test/fixtures/cases/variants/sum.ts +2 -0
  163. package/src/test/fixtures/discover/fast.ts +1 -0
  164. package/src/{tests → test}/fixtures/discover/slow.ts +1 -1
  165. package/src/test/fixtures/invalid/bad.ts +1 -0
  166. package/src/test/fixtures/loader/fast.ts +1 -0
  167. package/src/{tests → test}/fixtures/loader/slow.ts +1 -1
  168. package/src/test/fixtures/loader/stateful.ts +2 -0
  169. package/src/test/fixtures/stateful/stateful.ts +2 -0
  170. package/src/test/fixtures/variants/extra.ts +1 -0
  171. package/src/test/fixtures/variants/impl.ts +1 -0
  172. package/src/test/fixtures/worker/fast.ts +1 -0
  173. package/src/{tests → test}/fixtures/worker/slow.ts +1 -1
  174. package/src/viewer/DateFormat.ts +30 -0
  175. package/src/viewer/Helpers.ts +23 -0
  176. package/src/viewer/LineData.ts +120 -0
  177. package/src/viewer/Providers.ts +191 -0
  178. package/src/viewer/ReportData.ts +123 -0
  179. package/src/viewer/State.ts +49 -0
  180. package/src/viewer/Theme.ts +15 -0
  181. package/src/viewer/components/App.tsx +73 -0
  182. package/src/viewer/components/DropZone.tsx +71 -0
  183. package/src/viewer/components/LazyPlot.ts +33 -0
  184. package/src/viewer/components/SamplesPanel.tsx +214 -0
  185. package/src/viewer/components/Shell.tsx +26 -0
  186. package/src/viewer/components/SourcePanel.tsx +216 -0
  187. package/src/viewer/components/SummaryPanel.tsx +332 -0
  188. package/src/viewer/components/TabBar.tsx +131 -0
  189. package/src/viewer/components/TabContent.tsx +46 -0
  190. package/src/viewer/components/ThemeToggle.tsx +50 -0
  191. package/src/viewer/index.html +20 -0
  192. package/src/viewer/main.tsx +4 -0
  193. package/src/viewer/plots/CIPlot.ts +313 -0
  194. package/src/{html/browser → viewer/plots}/HistogramKde.ts +33 -38
  195. package/src/viewer/plots/LegendUtils.ts +134 -0
  196. package/src/viewer/plots/PlotTypes.ts +85 -0
  197. package/src/viewer/plots/RenderPlots.ts +230 -0
  198. package/src/viewer/plots/SampleTimeSeries.ts +306 -0
  199. package/src/viewer/plots/SvgHelpers.ts +136 -0
  200. package/src/viewer/plots/TimeSeriesMarks.ts +319 -0
  201. package/src/viewer/report.css +427 -0
  202. package/src/viewer/shell.css +357 -0
  203. package/src/viewer/tsconfig.json +11 -0
  204. package/dist/BrowserHeapSampler-B6asLKWQ.mjs +0 -202
  205. package/dist/BrowserHeapSampler-B6asLKWQ.mjs.map +0 -1
  206. package/dist/GcStats-wX7Xyblu.mjs +0 -77
  207. package/dist/GcStats-wX7Xyblu.mjs.map +0 -1
  208. package/dist/HeapSampler-B8dtKHn1.mjs.map +0 -1
  209. package/dist/TimingUtils-DwOwkc8G.mjs +0 -597
  210. package/dist/TimingUtils-DwOwkc8G.mjs.map +0 -1
  211. package/dist/browser/index.js +0 -914
  212. package/dist/src-B-DDaCa9.mjs +0 -3108
  213. package/dist/src-B-DDaCa9.mjs.map +0 -1
  214. package/src/BenchMatrix.ts +0 -380
  215. package/src/BenchmarkReport.ts +0 -161
  216. package/src/HtmlDataPrep.ts +0 -148
  217. package/src/StandardSections.ts +0 -261
  218. package/src/StatisticalUtils.ts +0 -175
  219. package/src/TypeUtil.ts +0 -8
  220. package/src/browser/BrowserGcStats.ts +0 -44
  221. package/src/browser/BrowserHeapSampler.ts +0 -271
  222. package/src/export/JsonExport.ts +0 -103
  223. package/src/export/JsonFormat.ts +0 -91
  224. package/src/export/SpeedscopeExport.ts +0 -202
  225. package/src/heap-sample/HeapSampleReport.ts +0 -269
  226. package/src/html/HtmlReport.ts +0 -131
  227. package/src/html/HtmlTemplate.ts +0 -284
  228. package/src/html/Types.ts +0 -88
  229. package/src/html/browser/CIPlot.ts +0 -287
  230. package/src/html/browser/LegendUtils.ts +0 -163
  231. package/src/html/browser/RenderPlots.ts +0 -263
  232. package/src/html/browser/SampleTimeSeries.ts +0 -389
  233. package/src/html/browser/Types.ts +0 -96
  234. package/src/html/browser/index.ts +0 -1
  235. package/src/html/index.ts +0 -17
  236. package/src/runners/BasicRunner.ts +0 -364
  237. package/src/table-util/ConvergenceFormatters.ts +0 -19
  238. package/src/table-util/Formatters.ts +0 -157
  239. package/src/table-util/README.md +0 -70
  240. package/src/table-util/TableReport.ts +0 -293
  241. package/src/tests/fixtures/cases/asyncCases.ts +0 -7
  242. package/src/tests/fixtures/cases/variants/product.ts +0 -2
  243. package/src/tests/fixtures/cases/variants/sum.ts +0 -2
  244. package/src/tests/fixtures/discover/fast.ts +0 -1
  245. package/src/tests/fixtures/invalid/bad.ts +0 -1
  246. package/src/tests/fixtures/loader/fast.ts +0 -1
  247. package/src/tests/fixtures/loader/stateful.ts +0 -2
  248. package/src/tests/fixtures/stateful/stateful.ts +0 -2
  249. package/src/tests/fixtures/variants/extra.ts +0 -1
  250. package/src/tests/fixtures/variants/impl.ts +0 -1
  251. package/src/tests/fixtures/worker/fast.ts +0 -1
  252. /package/src/{table-util/test → test}/TableValueExtractor.test.ts +0 -0
  253. /package/src/{table-util/test → test}/TableValueExtractor.ts +0 -0
@@ -0,0 +1,357 @@
1
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
2
+
3
+ :root {
4
+ --tab-bar-bg: #f8f8f8;
5
+ --tab-border: #ccc;
6
+ --tab-color: #999;
7
+ --tab-active-color: #222;
8
+ --tab-accent: #5f61d8;
9
+ --body-bg: #fff;
10
+ --source-bg: #fff;
11
+ --source-color: #333;
12
+ --source-highlight: #fffbdd;
13
+ --source-line-color: #999;
14
+ }
15
+
16
+ @media (prefers-color-scheme: dark) {
17
+ :root:not([data-theme="light"]) {
18
+ --tab-bar-bg: #1e1e1e;
19
+ --tab-border: #555;
20
+ --tab-color: #999;
21
+ --tab-active-color: #fff;
22
+ --tab-accent: #7b7dea;
23
+ --body-bg: #181818;
24
+ --source-bg: #1e1e1e;
25
+ --source-color: #d4d4d4;
26
+ --source-highlight: #3a3a00;
27
+ --source-line-color: #666;
28
+ }
29
+ }
30
+
31
+ :root[data-theme="dark"] {
32
+ --tab-bar-bg: #1e1e1e;
33
+ --tab-border: #555;
34
+ --tab-color: #999;
35
+ --tab-active-color: #fff;
36
+ --tab-accent: #7b7dea;
37
+ --body-bg: #181818;
38
+ --source-bg: #1e1e1e;
39
+ --source-color: #d4d4d4;
40
+ --source-highlight: #3a3a00;
41
+ --source-line-color: #666;
42
+ }
43
+
44
+ html, body {
45
+ height: 100%;
46
+ background: var(--body-bg);
47
+ font-family: system-ui, sans-serif;
48
+ color: var(--source-color);
49
+ }
50
+
51
+ body { display: flex; flex-direction: column; }
52
+ #app { display: contents; }
53
+
54
+ .tab-bar {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: 0;
58
+ padding: 6px 8px;
59
+ background: var(--tab-bar-bg);
60
+ flex-shrink: 0;
61
+ position: relative;
62
+ z-index: 10;
63
+ }
64
+
65
+ .tab-bar::after {
66
+ content: "";
67
+ position: absolute;
68
+ bottom: 0;
69
+ left: 0;
70
+ right: 0;
71
+ height: 1px;
72
+ background: var(--tab-border);
73
+ }
74
+
75
+ .tab {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 6px;
79
+ padding: 5px 12px;
80
+ background: transparent;
81
+ border: none;
82
+ border-radius: 0;
83
+ color: var(--tab-color);
84
+ cursor: pointer;
85
+ position: relative;
86
+ font-size: 13px;
87
+ font-family: system-ui, sans-serif;
88
+ white-space: nowrap;
89
+ }
90
+
91
+ .tab:hover:not(:disabled) { color: var(--tab-active-color); }
92
+
93
+ .tab:disabled {
94
+ opacity: 0.4;
95
+ cursor: not-allowed;
96
+ }
97
+
98
+ .tab.active {
99
+ color: var(--tab-accent);
100
+ font-weight: 600;
101
+ padding-bottom: calc(5px + 6px + 0.5px);
102
+ margin-bottom: calc(-1 * (6px + 0.5px));
103
+ position: relative;
104
+ z-index: 1;
105
+ border-bottom: 2px solid var(--tab-accent);
106
+ }
107
+
108
+ .tab .tab-close {
109
+ width: 16px;
110
+ height: 16px;
111
+ padding: 0;
112
+ border: none;
113
+ background: transparent;
114
+ color: inherit;
115
+ cursor: pointer;
116
+ opacity: 0;
117
+ font-size: 16px;
118
+ line-height: 1;
119
+ display: inline-flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ }
123
+
124
+ .tab:hover .tab-close { opacity: 0.6; }
125
+ .tab .tab-close:hover { opacity: 1; }
126
+
127
+ .tab-spacer { flex: 1; }
128
+
129
+ .theme-toggle { display: flex; align-items: center; gap: 2px; margin-right: 8px; }
130
+
131
+ .theme-btn {
132
+ background: none;
133
+ border: none;
134
+ cursor: pointer;
135
+ padding: 4px;
136
+ border-radius: 4px;
137
+ color: var(--tab-color);
138
+ opacity: 0.6;
139
+ display: flex;
140
+ align-items: center;
141
+ }
142
+ .theme-btn:hover { opacity: 1; }
143
+ .theme-btn.active {
144
+ color: var(--tab-accent);
145
+ opacity: 1;
146
+ background: color-mix(in srgb, var(--tab-accent) 15%, transparent);
147
+ }
148
+
149
+ .archive-btn {
150
+ font-size: 12px;
151
+ color: var(--tab-color);
152
+ opacity: 0.8;
153
+ }
154
+ .archive-btn:hover { opacity: 1; }
155
+
156
+ .tab-content {
157
+ flex: 1;
158
+ position: relative;
159
+ min-height: 0;
160
+ }
161
+
162
+ #speedscope-iframe,
163
+ #time-speedscope-iframe {
164
+ width: 100%;
165
+ height: calc(100% - 20px);
166
+ border: none;
167
+ position: absolute;
168
+ top: 20px;
169
+ left: 0;
170
+ }
171
+
172
+ .source-panel {
173
+ position: absolute;
174
+ top: 0; left: 0; right: 0; bottom: 0;
175
+ overflow: auto;
176
+ background: var(--source-bg);
177
+ padding: 0 24px 16px;
178
+ display: none;
179
+ }
180
+
181
+ .source-panel.active { display: block; }
182
+
183
+ .source-header {
184
+ display: flex;
185
+ align-items: center;
186
+ gap: 12px;
187
+ padding: 10px 0;
188
+ border-bottom: 1px solid var(--tab-border);
189
+ margin-bottom: 8px;
190
+ font-family: system-ui, sans-serif;
191
+ font-size: 13px;
192
+ }
193
+
194
+ .source-path {
195
+ color: var(--tab-color);
196
+ overflow: hidden;
197
+ text-overflow: ellipsis;
198
+ white-space: nowrap;
199
+ flex: 1;
200
+ }
201
+
202
+ .source-editor-link {
203
+ color: var(--tab-accent);
204
+ text-decoration: none;
205
+ white-space: nowrap;
206
+ font-size: 12px;
207
+ }
208
+ .source-editor-link:hover { text-decoration: underline; }
209
+
210
+ .source-code pre {
211
+ margin: 0;
212
+ counter-reset: line;
213
+ font-family: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
214
+ font-size: 13px;
215
+ line-height: 1.6;
216
+ white-space: normal;
217
+ }
218
+
219
+ .source-code code {
220
+ white-space: normal;
221
+ }
222
+
223
+ .source-code .line {
224
+ display: block;
225
+ counter-increment: line;
226
+ padding: 0 16px 0 60px;
227
+ position: relative;
228
+ white-space: pre;
229
+ }
230
+
231
+ .source-code .line::before {
232
+ content: counter(line);
233
+ position: absolute;
234
+ left: 0;
235
+ width: 48px;
236
+ text-align: right;
237
+ color: var(--source-line-color);
238
+ font-size: 12px;
239
+ user-select: none;
240
+ padding-right: 8px;
241
+ }
242
+
243
+ .source-code .line.highlighted {
244
+ background: var(--source-highlight);
245
+ }
246
+
247
+ .source-code .line .gutter.heat {
248
+ background: rgba(234, 179, 8, calc(var(--heat, 0) * 0.18));
249
+ }
250
+
251
+ .source-code .line .gutter {
252
+ display: inline-block;
253
+ width: 64px;
254
+ text-align: right;
255
+ font-size: 11px;
256
+ padding-right: 8px;
257
+ user-select: none;
258
+ opacity: 0.85;
259
+ }
260
+
261
+ .source-code .line .gutter:last-of-type {
262
+ margin-right: 8px;
263
+ }
264
+
265
+ .source-code .line .gutter-count { color: #888; }
266
+ .source-code .line .gutter-alloc { color: #b45309; }
267
+ .source-code .line .gutter-time { color: #2563eb; }
268
+
269
+ @media (prefers-color-scheme: dark) {
270
+ :root:not([data-theme="light"]) .source-code .line .gutter.heat {
271
+ background: rgba(234, 179, 8, calc(var(--heat, 0) * 0.12));
272
+ }
273
+ :root:not([data-theme="light"]) .source-code .line .gutter-count { color: #aaa; }
274
+ :root:not([data-theme="light"]) .source-code .line .gutter-alloc { color: #fbbf24; }
275
+ :root:not([data-theme="light"]) .source-code .line .gutter-time { color: #60a5fa; }
276
+ }
277
+
278
+ :root[data-theme="dark"] .source-code .line .gutter.heat {
279
+ background: rgba(234, 179, 8, calc(var(--heat, 0) * 0.12));
280
+ }
281
+ :root[data-theme="dark"] .source-code .line .gutter-count { color: #aaa; }
282
+ :root[data-theme="dark"] .source-code .line .gutter-alloc { color: #fbbf24; }
283
+ :root[data-theme="dark"] .source-code .line .gutter-time { color: #60a5fa; }
284
+
285
+ /* Shiki dual-theme wiring */
286
+ .shiki { color: var(--shiki-light); background-color: var(--shiki-light-bg); }
287
+ .shiki span { color: var(--shiki-light); }
288
+
289
+ @media (prefers-color-scheme: dark) {
290
+ :root:not([data-theme="light"]) .shiki { color: var(--shiki-dark); background-color: var(--shiki-dark-bg); }
291
+ :root:not([data-theme="light"]) .shiki span { color: var(--shiki-dark); }
292
+ }
293
+
294
+ :root[data-theme="dark"] .shiki { color: var(--shiki-dark); background-color: var(--shiki-dark-bg); }
295
+ :root[data-theme="dark"] .shiki span { color: var(--shiki-dark); }
296
+
297
+ .source-placeholder {
298
+ color: var(--tab-color);
299
+ font-family: system-ui, sans-serif;
300
+ font-size: 14px;
301
+ padding-top: 16px;
302
+ }
303
+
304
+ .source-placeholder code {
305
+ background: var(--tab-border);
306
+ padding: 2px 6px;
307
+ border-radius: 3px;
308
+ font-size: 13px;
309
+ }
310
+
311
+ /* Drop zone (hosted viewer mode) */
312
+
313
+ .drop-zone {
314
+ position: fixed;
315
+ inset: 0;
316
+ display: flex;
317
+ align-items: center;
318
+ justify-content: center;
319
+ background: var(--body-bg);
320
+ z-index: 100;
321
+ }
322
+
323
+ .drop-zone.drag-over { background: var(--tab-bar-bg); }
324
+
325
+ .drop-zone-content {
326
+ text-align: center;
327
+ color: var(--source-color);
328
+ }
329
+
330
+ .drop-zone-content h2 { margin: 16px 0 8px; font-size: 24px; }
331
+ .drop-zone-content p { color: var(--tab-color); margin: 8px 0; }
332
+
333
+ .drop-zone-content code {
334
+ background: var(--tab-border);
335
+ padding: 2px 6px;
336
+ border-radius: 3px;
337
+ font-size: 13px;
338
+ }
339
+
340
+ .drop-zone-divider {
341
+ color: var(--tab-color);
342
+ margin: 16px 0;
343
+ font-size: 13px;
344
+ }
345
+
346
+ .drop-zone-browse {
347
+ display: inline-block;
348
+ padding: 8px 20px;
349
+ background: var(--tab-accent);
350
+ color: white;
351
+ border-radius: 6px;
352
+ cursor: pointer;
353
+ font-size: 14px;
354
+ }
355
+
356
+ .drop-zone-browse:hover { opacity: 0.9; }
357
+ p.drop-zone-error { color: #ef4444; margin-top: 5em; }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "jsx": "react-jsx",
5
+ "jsxImportSource": "preact",
6
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
7
+ "isolatedDeclarations": false
8
+ },
9
+ "include": ["**/*.ts", "**/*.tsx"],
10
+ "exclude": []
11
+ }
@@ -1,202 +0,0 @@
1
- import { t as aggregateGcStats } from "./GcStats-wX7Xyblu.mjs";
2
- import { chromium } from "playwright";
3
-
4
- //#region src/browser/BrowserGcStats.ts
5
- /** Parse CDP trace events (MinorGC/MajorGC) into GcEvent[] */
6
- function parseGcTraceEvents(traceEvents) {
7
- return traceEvents.flatMap((e) => {
8
- if (e.ph !== "X") return [];
9
- const type = gcType(e.name);
10
- if (!type) return [];
11
- const durUs = e.dur ?? 0;
12
- const heapBefore = e.args?.usedHeapSizeBefore ?? 0;
13
- const heapAfter = e.args?.usedHeapSizeAfter ?? 0;
14
- return [{
15
- type,
16
- pauseMs: durUs / 1e3,
17
- collected: Math.max(0, heapBefore - heapAfter)
18
- }];
19
- });
20
- }
21
- /** Parse CDP trace events and aggregate into GcStats */
22
- function browserGcStats(traceEvents) {
23
- return aggregateGcStats(parseGcTraceEvents(traceEvents));
24
- }
25
- function gcType(name) {
26
- if (name === "MinorGC") return "scavenge";
27
- if (name === "MajorGC") return "mark-compact";
28
- }
29
-
30
- //#endregion
31
- //#region src/browser/BrowserHeapSampler.ts
32
- /** Run browser benchmark, auto-detecting page API mode.
33
- * Bench function (window.__bench): CLI controls iteration and timing.
34
- * Lap mode (__start/__lap/__done): page controls the measured region. */
35
- async function profileBrowser(params) {
36
- const { url, headless = true, chromeArgs, timeout = 60 } = params;
37
- const { gcStats: collectGc } = params;
38
- const { samplingInterval = 32768 } = params.heapOptions ?? {};
39
- const server = await chromium.launchServer({
40
- headless,
41
- args: chromeArgs
42
- });
43
- pipeChromeOutput(server);
44
- const browser = await chromium.connect(server.wsEndpoint());
45
- try {
46
- const page = await browser.newPage();
47
- page.setDefaultTimeout(timeout * 1e3);
48
- const cdp = await page.context().newCDPSession(page);
49
- const pageErrors = [];
50
- page.on("pageerror", (err) => pageErrors.push(err.message));
51
- const traceEvents = collectGc ? await startGcTracing(cdp) : [];
52
- const lapMode = await setupLapMode(page, cdp, params, samplingInterval, timeout, pageErrors);
53
- await page.goto(url, { waitUntil: "load" });
54
- const hasBench = await page.evaluate(() => typeof globalThis.__bench === "function");
55
- let result;
56
- if (hasBench) {
57
- lapMode.cancel();
58
- lapMode.promise.catch(() => {});
59
- result = await runBenchLoop(page, cdp, params, samplingInterval);
60
- } else {
61
- result = await lapMode.promise;
62
- lapMode.cancel();
63
- }
64
- if (collectGc) result = {
65
- ...result,
66
- gcStats: await collectTracing(cdp, traceEvents)
67
- };
68
- return result;
69
- } finally {
70
- await browser.close();
71
- await server.close();
72
- }
73
- }
74
- /** Forward Chrome's stdout/stderr to the terminal so V8 flag output is visible. */
75
- function pipeChromeOutput(server) {
76
- const proc = server.process();
77
- const pipe = (stream) => stream?.on("data", (chunk) => {
78
- for (const line of chunk.toString().split("\n")) {
79
- const text = line.trim();
80
- if (text) process.stderr.write(`[chrome] ${text}\n`);
81
- }
82
- });
83
- pipe(proc.stdout);
84
- pipe(proc.stderr);
85
- }
86
- /** Start CDP GC tracing, returns the event collector array. */
87
- async function startGcTracing(cdp) {
88
- const events = [];
89
- cdp.on("Tracing.dataCollected", ({ value }) => {
90
- for (const e of value) events.push(e);
91
- });
92
- await cdp.send("Tracing.start", { traceConfig: { includedCategories: ["v8", "v8.gc"] } });
93
- return events;
94
- }
95
- /** Inject __start/__lap as in-page functions, expose __done for results collection.
96
- * __start/__lap are pure in-page (zero CDP overhead). First __start() triggers
97
- * instrument start. __done() stops instruments and collects timing data. */
98
- async function setupLapMode(page, cdp, params, samplingInterval, timeout, pageErrors) {
99
- const { heapSample } = params;
100
- const { promise, resolve, reject } = Promise.withResolvers();
101
- let instrumentsStarted = false;
102
- await page.exposeFunction("__benchInstrumentStart", async () => {
103
- if (instrumentsStarted) return;
104
- instrumentsStarted = true;
105
- if (heapSample) await cdp.send("HeapProfiler.startSampling", heapSamplingParams(samplingInterval));
106
- });
107
- await page.exposeFunction("__benchCollect", async (samples, wallTimeMs) => {
108
- let heapProfile;
109
- if (heapSample && instrumentsStarted) heapProfile = (await cdp.send("HeapProfiler.stopSampling")).profile;
110
- resolve({
111
- samples,
112
- heapProfile,
113
- wallTimeMs
114
- });
115
- });
116
- await page.addInitScript(injectLapFunctions);
117
- const timer = setTimeout(() => {
118
- const lines = [`Timed out after ${timeout}s`];
119
- if (pageErrors.length) lines.push("Page JS errors:", ...pageErrors.map((e) => ` ${e}`));
120
- else lines.push("Page did not call __done() or define window.__bench");
121
- reject(new Error(lines.join("\n")));
122
- }, timeout * 1e3);
123
- return {
124
- promise,
125
- cancel: () => clearTimeout(timer)
126
- };
127
- }
128
- /** Bench function mode: run window.__bench in a timed iteration loop. */
129
- async function runBenchLoop(page, cdp, params, samplingInterval) {
130
- const { heapSample } = params;
131
- const maxTime = params.maxTime ?? 642;
132
- const maxIter = params.maxIterations ?? Number.MAX_SAFE_INTEGER;
133
- if (heapSample) await cdp.send("HeapProfiler.startSampling", heapSamplingParams(samplingInterval));
134
- const { samples, totalMs } = await page.evaluate(async ({ maxTime, maxIter }) => {
135
- const bench = globalThis.__bench;
136
- const samples = [];
137
- const startAll = performance.now();
138
- const deadline = startAll + maxTime;
139
- for (let i = 0; i < maxIter && performance.now() < deadline; i++) {
140
- const t0 = performance.now();
141
- await bench();
142
- samples.push(performance.now() - t0);
143
- }
144
- return {
145
- samples,
146
- totalMs: performance.now() - startAll
147
- };
148
- }, {
149
- maxTime,
150
- maxIter
151
- });
152
- let heapProfile;
153
- if (heapSample) heapProfile = (await cdp.send("HeapProfiler.stopSampling")).profile;
154
- return {
155
- samples,
156
- heapProfile,
157
- wallTimeMs: totalMs
158
- };
159
- }
160
- /** Stop CDP tracing and parse GC events into GcStats. */
161
- async function collectTracing(cdp, traceEvents) {
162
- const complete = new Promise((resolve) => cdp.once("Tracing.tracingComplete", () => resolve()));
163
- await cdp.send("Tracing.end");
164
- await complete;
165
- return browserGcStats(traceEvents);
166
- }
167
- function heapSamplingParams(samplingInterval) {
168
- return {
169
- samplingInterval,
170
- includeObjectsCollectedByMajorGC: true,
171
- includeObjectsCollectedByMinorGC: true
172
- };
173
- }
174
- /** In-page timing functions injected via addInitScript (zero CDP overhead).
175
- * __start/__lap collect timestamps, __done delegates to exposed __benchCollect. */
176
- function injectLapFunctions() {
177
- const g = globalThis;
178
- g.__benchSamples = [];
179
- g.__benchLastTime = 0;
180
- g.__benchFirstStart = 0;
181
- g.__start = () => {
182
- const now = performance.now();
183
- g.__benchLastTime = now;
184
- if (!g.__benchFirstStart) {
185
- g.__benchFirstStart = now;
186
- return g.__benchInstrumentStart();
187
- }
188
- };
189
- g.__lap = () => {
190
- const now = performance.now();
191
- g.__benchSamples.push(now - g.__benchLastTime);
192
- g.__benchLastTime = now;
193
- };
194
- g.__done = () => {
195
- const wall = g.__benchFirstStart ? performance.now() - g.__benchFirstStart : 0;
196
- return g.__benchCollect(g.__benchSamples.slice(), wall);
197
- };
198
- }
199
-
200
- //#endregion
201
- export { profileBrowser, profileBrowser as profileBrowserHeap };
202
- //# sourceMappingURL=BrowserHeapSampler-B6asLKWQ.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BrowserHeapSampler-B6asLKWQ.mjs","names":[],"sources":["../src/browser/BrowserGcStats.ts","../src/browser/BrowserHeapSampler.ts"],"sourcesContent":["import {\n aggregateGcStats,\n type GcEvent,\n type GcStats,\n} from \"../runners/GcStats.ts\";\n\n/** CDP trace event from Tracing.dataCollected */\nexport interface TraceEvent {\n cat: string;\n name: string;\n ph: string;\n dur?: number; // microseconds\n args?: Record<string, any>;\n}\n\n/** Parse CDP trace events (MinorGC/MajorGC) into GcEvent[] */\nexport function parseGcTraceEvents(traceEvents: TraceEvent[]): GcEvent[] {\n return traceEvents.flatMap(e => {\n if (e.ph !== \"X\") return [];\n const type = gcType(e.name);\n if (!type) return [];\n const durUs = e.dur ?? 0;\n const heapBefore: number = e.args?.usedHeapSizeBefore ?? 0;\n const heapAfter: number = e.args?.usedHeapSizeAfter ?? 0;\n return [\n {\n type,\n pauseMs: durUs / 1000,\n collected: Math.max(0, heapBefore - heapAfter),\n },\n ];\n });\n}\n\n/** Parse CDP trace events and aggregate into GcStats */\nexport function browserGcStats(traceEvents: TraceEvent[]): GcStats {\n return aggregateGcStats(parseGcTraceEvents(traceEvents));\n}\n\nfunction gcType(name: string): GcEvent[\"type\"] | undefined {\n if (name === \"MinorGC\") return \"scavenge\";\n if (name === \"MajorGC\") return \"mark-compact\";\n return undefined;\n}\n","import {\n type BrowserServer,\n type CDPSession,\n chromium,\n type Page,\n} from \"playwright\";\nimport type {\n HeapProfile,\n HeapSampleOptions,\n} from \"../heap-sample/HeapSampler.ts\";\nimport type { GcStats } from \"../runners/GcStats.ts\";\nimport { browserGcStats, type TraceEvent } from \"./BrowserGcStats.ts\";\n\nexport interface BrowserProfileParams {\n url: string;\n heapSample?: boolean;\n heapOptions?: HeapSampleOptions;\n gcStats?: boolean;\n headless?: boolean;\n chromeArgs?: string[];\n timeout?: number; // seconds\n maxTime?: number; // ms, bench function iteration time limit\n maxIterations?: number; // exact iteration count (bench function mode)\n}\n\nexport interface BrowserProfileResult {\n heapProfile?: HeapProfile;\n gcStats?: GcStats;\n /** Wall-clock ms (lap mode: first start to done, bench function: total loop) */\n wallTimeMs?: number;\n /** Per-iteration timing samples (ms) from bench function or lap mode */\n samples?: number[];\n}\n\ninterface LapModeHandle {\n promise: Promise<BrowserProfileResult>;\n cancel: () => void;\n}\n\n/** Run browser benchmark, auto-detecting page API mode.\n * Bench function (window.__bench): CLI controls iteration and timing.\n * Lap mode (__start/__lap/__done): page controls the measured region. */\nexport async function profileBrowser(\n params: BrowserProfileParams,\n): Promise<BrowserProfileResult> {\n const { url, headless = true, chromeArgs, timeout = 60 } = params;\n const { gcStats: collectGc } = params;\n const { samplingInterval = 32768 } = params.heapOptions ?? {};\n\n const server = await chromium.launchServer({ headless, args: chromeArgs });\n pipeChromeOutput(server);\n const browser = await chromium.connect(server.wsEndpoint());\n try {\n const page = await browser.newPage();\n page.setDefaultTimeout(timeout * 1000);\n const cdp = await page.context().newCDPSession(page);\n\n const pageErrors: string[] = [];\n page.on(\"pageerror\", err => pageErrors.push(err.message));\n\n const traceEvents = collectGc ? await startGcTracing(cdp) : [];\n const lapMode = await setupLapMode(\n page,\n cdp,\n params,\n samplingInterval,\n timeout,\n pageErrors,\n );\n\n await page.goto(url, { waitUntil: \"load\" });\n const hasBench = await page.evaluate(\n () => typeof (globalThis as any).__bench === \"function\",\n );\n\n let result: BrowserProfileResult;\n if (hasBench) {\n lapMode.cancel();\n lapMode.promise.catch(() => {}); // suppress unused rejection\n result = await runBenchLoop(page, cdp, params, samplingInterval);\n } else {\n result = await lapMode.promise;\n lapMode.cancel();\n }\n\n if (collectGc) {\n result = { ...result, gcStats: await collectTracing(cdp, traceEvents) };\n }\n return result;\n } finally {\n await browser.close();\n await server.close();\n }\n}\n\n/** Forward Chrome's stdout/stderr to the terminal so V8 flag output is visible. */\nfunction pipeChromeOutput(server: BrowserServer): void {\n const proc = server.process();\n const pipe = (stream: NodeJS.ReadableStream | null) =>\n stream?.on(\"data\", (chunk: Buffer) => {\n for (const line of chunk.toString().split(\"\\n\")) {\n const text = line.trim();\n if (text) process.stderr.write(`[chrome] ${text}\\n`);\n }\n });\n pipe(proc.stdout);\n pipe(proc.stderr);\n}\n\n/** Start CDP GC tracing, returns the event collector array. */\nasync function startGcTracing(cdp: CDPSession): Promise<TraceEvent[]> {\n const events: TraceEvent[] = [];\n cdp.on(\"Tracing.dataCollected\", ({ value }) => {\n for (const e of value) events.push(e as unknown as TraceEvent);\n });\n await cdp.send(\"Tracing.start\", {\n traceConfig: { includedCategories: [\"v8\", \"v8.gc\"] },\n });\n return events;\n}\n\n/** Inject __start/__lap as in-page functions, expose __done for results collection.\n * __start/__lap are pure in-page (zero CDP overhead). First __start() triggers\n * instrument start. __done() stops instruments and collects timing data. */\nasync function setupLapMode(\n page: Page,\n cdp: CDPSession,\n params: BrowserProfileParams,\n samplingInterval: number,\n timeout: number,\n pageErrors: string[],\n): Promise<LapModeHandle> {\n const { heapSample } = params;\n const { promise, resolve, reject } =\n Promise.withResolvers<BrowserProfileResult>();\n let instrumentsStarted = false;\n\n await page.exposeFunction(\"__benchInstrumentStart\", async () => {\n if (instrumentsStarted) return;\n instrumentsStarted = true;\n if (heapSample) {\n await cdp.send(\n \"HeapProfiler.startSampling\",\n heapSamplingParams(samplingInterval),\n );\n }\n });\n\n await page.exposeFunction(\n \"__benchCollect\",\n async (samples: number[], wallTimeMs: number) => {\n let heapProfile: HeapProfile | undefined;\n if (heapSample && instrumentsStarted) {\n const result = await cdp.send(\"HeapProfiler.stopSampling\");\n heapProfile = result.profile as unknown as HeapProfile;\n }\n resolve({ samples, heapProfile, wallTimeMs });\n },\n );\n\n await page.addInitScript(injectLapFunctions);\n\n const timer = setTimeout(() => {\n const lines = [`Timed out after ${timeout}s`];\n if (pageErrors.length) {\n lines.push(\"Page JS errors:\", ...pageErrors.map(e => ` ${e}`));\n } else {\n lines.push(\"Page did not call __done() or define window.__bench\");\n }\n reject(new Error(lines.join(\"\\n\")));\n }, timeout * 1000);\n\n return { promise, cancel: () => clearTimeout(timer) };\n}\n\n/** Bench function mode: run window.__bench in a timed iteration loop. */\nasync function runBenchLoop(\n page: Page,\n cdp: CDPSession,\n params: BrowserProfileParams,\n samplingInterval: number,\n): Promise<BrowserProfileResult> {\n const { heapSample } = params;\n const maxTime = params.maxTime ?? 642;\n const maxIter = params.maxIterations ?? Number.MAX_SAFE_INTEGER;\n\n if (heapSample) {\n await cdp.send(\n \"HeapProfiler.startSampling\",\n heapSamplingParams(samplingInterval),\n );\n }\n\n const { samples, totalMs } = await page.evaluate(\n async ({ maxTime, maxIter }) => {\n const bench = (globalThis as any).__bench;\n const samples: number[] = [];\n const startAll = performance.now();\n const deadline = startAll + maxTime;\n for (let i = 0; i < maxIter && performance.now() < deadline; i++) {\n const t0 = performance.now();\n await bench();\n samples.push(performance.now() - t0);\n }\n return { samples, totalMs: performance.now() - startAll };\n },\n { maxTime, maxIter },\n );\n\n let heapProfile: HeapProfile | undefined;\n if (heapSample) {\n const result = await cdp.send(\"HeapProfiler.stopSampling\");\n heapProfile = result.profile as unknown as HeapProfile;\n }\n\n return { samples, heapProfile, wallTimeMs: totalMs };\n}\n\n/** Stop CDP tracing and parse GC events into GcStats. */\nasync function collectTracing(\n cdp: CDPSession,\n traceEvents: TraceEvent[],\n): Promise<GcStats> {\n const complete = new Promise<void>(resolve =>\n cdp.once(\"Tracing.tracingComplete\", () => resolve()),\n );\n await cdp.send(\"Tracing.end\");\n await complete;\n return browserGcStats(traceEvents);\n}\n\nfunction heapSamplingParams(samplingInterval: number) {\n return {\n samplingInterval,\n includeObjectsCollectedByMajorGC: true,\n includeObjectsCollectedByMinorGC: true,\n };\n}\n\n/** In-page timing functions injected via addInitScript (zero CDP overhead).\n * __start/__lap collect timestamps, __done delegates to exposed __benchCollect. */\nfunction injectLapFunctions(): void {\n const g = globalThis as any;\n g.__benchSamples = [];\n g.__benchLastTime = 0;\n g.__benchFirstStart = 0;\n\n g.__start = () => {\n const now = performance.now();\n g.__benchLastTime = now;\n if (!g.__benchFirstStart) {\n g.__benchFirstStart = now;\n return g.__benchInstrumentStart();\n }\n };\n\n g.__lap = () => {\n const now = performance.now();\n g.__benchSamples.push(now - g.__benchLastTime);\n g.__benchLastTime = now;\n };\n\n g.__done = () => {\n const wall = g.__benchFirstStart\n ? performance.now() - g.__benchFirstStart\n : 0;\n return g.__benchCollect(g.__benchSamples.slice(), wall);\n };\n}\n\nexport { profileBrowser as profileBrowserHeap };\n"],"mappings":"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"}
@@ -1,77 +0,0 @@
1
- //#region src/runners/GcStats.ts
2
- /** Parse a single --trace-gc-nvp stderr line */
3
- function parseGcLine(line) {
4
- if (!line.includes("pause=")) return void 0;
5
- const fields = parseNvpFields(line);
6
- if (!fields.gc) return void 0;
7
- const int = (k) => Number.parseInt(fields[k] || "0", 10);
8
- const type = parseGcType(fields.gc);
9
- const pauseMs = Number.parseFloat(fields.pause || "0");
10
- const allocated = int("allocated");
11
- const promoted = int("promoted");
12
- const survived = int("new_space_survived") || int("survived");
13
- const startSize = int("start_object_size");
14
- const endSize = int("end_object_size");
15
- const collected = startSize > endSize ? startSize - endSize : 0;
16
- if (Number.isNaN(pauseMs)) return void 0;
17
- return {
18
- type,
19
- pauseMs,
20
- allocated,
21
- collected,
22
- promoted,
23
- survived
24
- };
25
- }
26
- /** Aggregate GC events into summary stats */
27
- function aggregateGcStats(events) {
28
- let scavenges = 0;
29
- let markCompacts = 0;
30
- let gcPauseTime = 0;
31
- let totalCollected = 0;
32
- let hasNodeFields = false;
33
- let totalAllocated = 0;
34
- let totalPromoted = 0;
35
- let totalSurvived = 0;
36
- for (const e of events) {
37
- if (e.type === "scavenge" || e.type === "minor-ms") scavenges++;
38
- else if (e.type === "mark-compact") markCompacts++;
39
- gcPauseTime += e.pauseMs;
40
- totalCollected += e.collected;
41
- if (e.allocated != null) {
42
- hasNodeFields = true;
43
- totalAllocated += e.allocated;
44
- totalPromoted += e.promoted ?? 0;
45
- totalSurvived += e.survived ?? 0;
46
- }
47
- }
48
- return {
49
- scavenges,
50
- markCompacts,
51
- totalCollected,
52
- gcPauseTime,
53
- ...hasNodeFields && {
54
- totalAllocated,
55
- totalPromoted,
56
- totalSurvived
57
- }
58
- };
59
- }
60
- /** Parse name=value pairs from trace-gc-nvp line */
61
- function parseNvpFields(line) {
62
- const fields = {};
63
- const matches = line.matchAll(/(\w+)=([^\s,]+)/g);
64
- for (const [, key, value] of matches) fields[key] = value;
65
- return fields;
66
- }
67
- /** Map V8 gc type codes to our types */
68
- function parseGcType(gcField) {
69
- if (gcField === "s" || gcField === "scavenge") return "scavenge";
70
- if (gcField === "mc" || gcField === "ms" || gcField === "mark-compact") return "mark-compact";
71
- if (gcField === "mmc" || gcField === "minor-mc" || gcField === "minor-ms") return "minor-ms";
72
- return "unknown";
73
- }
74
-
75
- //#endregion
76
- export { parseGcLine as n, aggregateGcStats as t };
77
- //# sourceMappingURL=GcStats-wX7Xyblu.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GcStats-wX7Xyblu.mjs","names":[],"sources":["../src/runners/GcStats.ts"],"sourcesContent":["/** GC statistics aggregated from V8 trace events.\n * Node (--trace-gc-nvp) provides all fields.\n * Browser (CDP Tracing) provides counts, collected, and pause only. */\nexport interface GcStats {\n scavenges: number;\n markCompacts: number;\n totalCollected: number; // bytes freed\n gcPauseTime: number; // total pause time (ms)\n totalAllocated?: number; // bytes allocated (Node only)\n totalPromoted?: number; // bytes promoted to old gen (Node only)\n totalSurvived?: number; // bytes survived in young gen (Node only)\n}\n\n/** Single GC event. Node provides all fields; browser provides type, pauseMs, collected. */\nexport interface GcEvent {\n type: \"scavenge\" | \"mark-compact\" | \"minor-ms\" | \"unknown\";\n pauseMs: number;\n collected: number;\n allocated?: number; // Node only\n promoted?: number; // Node only\n survived?: number; // Node only\n}\n\n/** Parse a single --trace-gc-nvp stderr line */\nexport function parseGcLine(line: string): GcEvent | undefined {\n // V8 format: [pid:addr:gen] N ms: pause=X gc=s ... allocated=N promoted=N ...\n if (!line.includes(\"pause=\")) return undefined;\n\n const fields = parseNvpFields(line);\n if (!fields.gc) return undefined;\n\n const int = (k: string) => Number.parseInt(fields[k] || \"0\", 10);\n const type = parseGcType(fields.gc);\n const pauseMs = Number.parseFloat(fields.pause || \"0\");\n const allocated = int(\"allocated\");\n const promoted = int(\"promoted\");\n // V8 uses \"new_space_survived\" not \"survived\"\n const survived = int(\"new_space_survived\") || int(\"survived\");\n // Calculate collected from start/end object size if available\n const startSize = int(\"start_object_size\");\n const endSize = int(\"end_object_size\");\n const collected = startSize > endSize ? startSize - endSize : 0;\n\n if (Number.isNaN(pauseMs)) return undefined;\n\n return { type, pauseMs, allocated, collected, promoted, survived };\n}\n\n/** Aggregate GC events into summary stats */\nexport function aggregateGcStats(events: GcEvent[]): GcStats {\n let scavenges = 0;\n let markCompacts = 0;\n let gcPauseTime = 0;\n let totalCollected = 0;\n let hasNodeFields = false;\n let totalAllocated = 0;\n let totalPromoted = 0;\n let totalSurvived = 0;\n\n for (const e of events) {\n if (e.type === \"scavenge\" || e.type === \"minor-ms\") scavenges++;\n else if (e.type === \"mark-compact\") markCompacts++;\n gcPauseTime += e.pauseMs;\n totalCollected += e.collected;\n if (e.allocated != null) {\n hasNodeFields = true;\n totalAllocated += e.allocated;\n totalPromoted += e.promoted ?? 0;\n totalSurvived += e.survived ?? 0;\n }\n }\n\n return {\n scavenges,\n markCompacts,\n totalCollected,\n gcPauseTime,\n ...(hasNodeFields && { totalAllocated, totalPromoted, totalSurvived }),\n };\n}\n\n/** @return GcStats with all counters zeroed */\nexport function emptyGcStats(): GcStats {\n return { scavenges: 0, markCompacts: 0, totalCollected: 0, gcPauseTime: 0 };\n}\n\n/** Parse name=value pairs from trace-gc-nvp line */\nfunction parseNvpFields(line: string): Record<string, string> {\n const fields: Record<string, string> = {};\n // Format: \"key=value, key=value, ...\" or \"key=value key=value\"\n const matches = line.matchAll(/(\\w+)=([^\\s,]+)/g);\n for (const [, key, value] of matches) {\n fields[key] = value;\n }\n return fields;\n}\n\n/** Map V8 gc type codes to our types */\nfunction parseGcType(gcField: string): GcEvent[\"type\"] {\n // V8 uses: s=scavenge, mc=mark-compact, mmc=minor-mc (young gen mark-compact)\n if (gcField === \"s\" || gcField === \"scavenge\") return \"scavenge\";\n if (gcField === \"mc\" || gcField === \"ms\" || gcField === \"mark-compact\")\n return \"mark-compact\";\n if (gcField === \"mmc\" || gcField === \"minor-mc\" || gcField === \"minor-ms\")\n return \"minor-ms\";\n return \"unknown\";\n}\n"],"mappings":";;AAwBA,SAAgB,YAAY,MAAmC;AAE7D,KAAI,CAAC,KAAK,SAAS,SAAS,CAAE,QAAO;CAErC,MAAM,SAAS,eAAe,KAAK;AACnC,KAAI,CAAC,OAAO,GAAI,QAAO;CAEvB,MAAM,OAAO,MAAc,OAAO,SAAS,OAAO,MAAM,KAAK,GAAG;CAChE,MAAM,OAAO,YAAY,OAAO,GAAG;CACnC,MAAM,UAAU,OAAO,WAAW,OAAO,SAAS,IAAI;CACtD,MAAM,YAAY,IAAI,YAAY;CAClC,MAAM,WAAW,IAAI,WAAW;CAEhC,MAAM,WAAW,IAAI,qBAAqB,IAAI,IAAI,WAAW;CAE7D,MAAM,YAAY,IAAI,oBAAoB;CAC1C,MAAM,UAAU,IAAI,kBAAkB;CACtC,MAAM,YAAY,YAAY,UAAU,YAAY,UAAU;AAE9D,KAAI,OAAO,MAAM,QAAQ,CAAE,QAAO;AAElC,QAAO;EAAE;EAAM;EAAS;EAAW;EAAW;EAAU;EAAU;;;AAIpE,SAAgB,iBAAiB,QAA4B;CAC3D,IAAI,YAAY;CAChB,IAAI,eAAe;CACnB,IAAI,cAAc;CAClB,IAAI,iBAAiB;CACrB,IAAI,gBAAgB;CACpB,IAAI,iBAAiB;CACrB,IAAI,gBAAgB;CACpB,IAAI,gBAAgB;AAEpB,MAAK,MAAM,KAAK,QAAQ;AACtB,MAAI,EAAE,SAAS,cAAc,EAAE,SAAS,WAAY;WAC3C,EAAE,SAAS,eAAgB;AACpC,iBAAe,EAAE;AACjB,oBAAkB,EAAE;AACpB,MAAI,EAAE,aAAa,MAAM;AACvB,mBAAgB;AAChB,qBAAkB,EAAE;AACpB,oBAAiB,EAAE,YAAY;AAC/B,oBAAiB,EAAE,YAAY;;;AAInC,QAAO;EACL;EACA;EACA;EACA;EACA,GAAI,iBAAiB;GAAE;GAAgB;GAAe;GAAe;EACtE;;;AASH,SAAS,eAAe,MAAsC;CAC5D,MAAM,SAAiC,EAAE;CAEzC,MAAM,UAAU,KAAK,SAAS,mBAAmB;AACjD,MAAK,MAAM,GAAG,KAAK,UAAU,QAC3B,QAAO,OAAO;AAEhB,QAAO;;;AAIT,SAAS,YAAY,SAAkC;AAErD,KAAI,YAAY,OAAO,YAAY,WAAY,QAAO;AACtD,KAAI,YAAY,QAAQ,YAAY,QAAQ,YAAY,eACtD,QAAO;AACT,KAAI,YAAY,SAAS,YAAY,cAAc,YAAY,WAC7D,QAAO;AACT,QAAO"}