catalyst-core 0.2.0-beta.2 → 0.3.0-beta.1
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.
- package/babel.native.config.cjs +8 -0
- package/bin/catalyst.js +49 -105
- package/changelog.md +8 -0
- package/dist/caching.js +1 -1
- package/dist/index.jsx +8 -0
- package/dist/logger.js +74 -3
- package/dist/native/androidProject/app/build.gradle.kts +1 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +143 -4
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +26 -6
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt +143 -17
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +6 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +26 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +56 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +48 -8
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfDeliveryState.kt +36 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfEventBuffer.kt +289 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/ProfilerNavigationState.kt +13 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ProfilerLifecycleTest.kt +51 -0
- package/dist/native/androidSetup.js +300 -8
- package/dist/native/bridge/WebBridge.js +481 -59
- package/dist/native/bridge/constants/NativeInterfaces.js +290 -44
- package/dist/native/bridge/errors.js +250 -26
- package/dist/native/bridge/hooks/index.js +125 -1
- package/dist/native/bridge/hooks/useCamera.js +347 -9
- package/dist/native/bridge/hooks/useDataProtection.js +200 -2
- package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
- package/dist/native/bridge/hooks/useFilePicker.js +429 -2
- package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
- package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
- package/dist/native/bridge/hooks/useIntent.js +95 -1
- package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
- package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
- package/dist/native/bridge/hooks/useNotification.js +318 -2
- package/dist/native/bridge/hooks/useSafeArea.js +84 -2
- package/dist/native/bridge/hooks/useVideoStream.js +434 -6
- package/dist/native/bridge/hooks/utils.js +27 -1
- package/dist/native/bridge/hooks.js +16 -1
- package/dist/native/bridge/perf/collectors/ApiCollector.js +85 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.js +215 -0
- package/dist/native/bridge/perf/collectors/HookCollector.js +165 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.js +505 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.js +273 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.js +124 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.js +294 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +183 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.js +284 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.js +111 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.js +111 -0
- package/dist/native/bridge/perf/core/constants.js +126 -0
- package/dist/native/bridge/perf/core/measure.js +189 -0
- package/dist/native/bridge/perf/core/session.js +100 -0
- package/dist/native/bridge/perf/core/store.js +554 -0
- package/dist/native/bridge/perf/core/summary.js +174 -0
- package/dist/native/bridge/perf/core/traceExport.js +240 -0
- package/dist/native/bridge/perf/core/waterfalls.js +146 -0
- package/dist/native/bridge/perf/index.js +402 -0
- package/dist/native/bridge/safeArea.js +53 -5
- package/dist/native/bridge/standardizedHooks.js +595 -82
- package/dist/native/bridge/useBaseHook.js +274 -38
- package/dist/native/bridge/utils/CameraUtils.js +201 -56
- package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
- package/dist/native/bridge/utils/NativeBridge.js +425 -141
- package/dist/native/buildAndroid/assets.js +510 -13
- package/dist/native/buildAndroid/build.js +296 -19
- package/dist/native/buildAndroid/config.js +87 -1
- package/dist/native/buildAndroid/index.js +374 -1
- package/dist/native/buildAppAndroid.js +34 -2
- package/dist/native/buildAppIos.js +62 -2
- package/dist/native/buildIos/assets.js +609 -8
- package/dist/native/buildIos/build.js +639 -20
- package/dist/native/buildIos/config.js +294 -12
- package/dist/native/buildIos/index.js +343 -6
- package/dist/native/buildIos/plugins.js +343 -16
- package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
- package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
- package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
- package/dist/native/internalPluginUtils.js +232 -7
- package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +19 -0
- package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +4 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/BootTimingUtility.swift +5 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +10 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/DisplayLinkPerfMonitor.swift +89 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +373 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +35 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +24 -11
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +9 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +126 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +75 -1
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +13 -0
- package/dist/native/package.json +3 -0
- package/dist/native/plugin-bridge/PluginBridge.js +227 -1
- package/dist/native/pluginComposerAndroid.js +407 -23
- package/dist/native/pluginComposerIos.js +435 -11
- package/dist/native/renameAndroidProject.js +1094 -88
- package/dist/native/setupEmulatorIos.js +289 -22
- package/dist/native/setupServer.js +160 -19
- package/dist/native/terminalProgress.js +199 -11
- package/dist/native/utils.js +193 -15
- package/dist/offline/catalyst-sw.js +145 -1
- package/dist/offline/registerServiceWorker.js +33 -1
- package/dist/otel.js +375 -46
- package/dist/router/ClientRouter.js +14 -2
- package/dist/router/ServerRouter.js +9 -1
- package/dist/router.js +1 -1
- package/dist/scripts/build.js +112 -3
- package/dist/scripts/checkVersion.js +3 -1
- package/dist/scripts/cleanCache.js +30 -1
- package/dist/scripts/generateOfflineManifest.js +116 -1
- package/dist/scripts/loadEnvironmentVariables.js +32 -6
- package/dist/scripts/plugins.js +309 -1
- package/dist/scripts/preServerInit.js +13 -0
- package/dist/scripts/scriptUtils.js +54 -3
- package/dist/scripts/serve.js +51 -4
- package/dist/scripts/start.js +47 -10
- package/dist/scripts/validator.js +149 -1
- package/dist/sentry.cjs +127 -0
- package/dist/sentry.js +125 -5
- package/dist/server/expressServer.js +250 -5
- package/dist/server/manifestCache.js +36 -0
- package/dist/server/renderer/ChunkExtractor.js +144 -0
- package/dist/server/renderer/document/Body.jsx +59 -0
- package/dist/server/renderer/document/FastRefresh.jsx +20 -0
- package/dist/server/renderer/document/Head.jsx +60 -0
- package/dist/server/renderer/document/index.jsx +2 -0
- package/dist/server/renderer/extract.js +149 -31
- package/dist/server/renderer/handler.jsx +433 -0
- package/dist/server/renderer/index.js +8 -1
- package/dist/server/renderer/render.js +47 -0
- package/dist/server/utils/botDetectionMiddleware.js +29 -0
- package/dist/server/utils/cjsRequire.js +10 -0
- package/dist/server/utils/userAgentUtil.js +87 -9
- package/dist/server/utils/validator.js +164 -1
- package/dist/vite/FastRefresh.jsx +20 -0
- package/dist/vite/inject-cache-key-plugin.js +276 -0
- package/dist/vite/loadCustomViteConfig.js +22 -0
- package/dist/vite/manifest-categorization-plugin.js +455 -0
- package/dist/vite/node-loader.mjs +458 -0
- package/dist/vite/scssParams.js +21 -0
- package/dist/vite/vite.config.client.js +130 -0
- package/dist/vite/vite.config.js +422 -0
- package/dist/vite/vite.config.server.js +84 -0
- package/dist/web-router/components/MetaTag.jsx +34 -0
- package/dist/web-router/components/RouterDataProvider.jsx +332 -0
- package/dist/web-router/components/Split.jsx +200 -0
- package/dist/web-router/components/SplitInview.jsx +106 -0
- package/dist/web-router/components/SsrRequestContext.jsx +14 -0
- package/dist/web-router/context.jsx +12 -0
- package/dist/web-router/hooks.jsx +72 -0
- package/dist/web-router/types/components/MetaTag.d.ts +2 -0
- package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
- package/dist/web-router/types/context.d.ts +14 -0
- package/dist/web-router/types/hooks.d.ts +1 -0
- package/dist/web-router/types/index.d.ts +3 -0
- package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
- package/dist/web-router/utils/metaDataUtils.jsx +119 -0
- package/mcp_v2/conversion-tasks.json +35 -17
- package/mcp_v2/knowledge-base.json +124 -13
- package/mcp_v2/lib/helpers.js +10 -0
- package/mcp_v2/mcp.js +4 -4
- package/mcp_v2/package.json +1 -0
- package/mcp_v2/tools/build.js +116 -20
- package/mcp_v2/tools/conversion.js +106 -5
- package/mcp_v2/tools/tasks.js +18 -14
- package/package.json +35 -68
- package/babel.config.js +0 -32
- package/dist/index.js +0 -1
- package/dist/scripts/devBuild.js +0 -3
- package/dist/scripts/devServe.js +0 -3
- package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
- package/dist/scripts/registerAliases.js +0 -6
- package/dist/server/renderer/document/Body.js +0 -11
- package/dist/server/renderer/document/Head.js +0 -9
- package/dist/server/renderer/document/index.js +0 -1
- package/dist/server/renderer/handler.js +0 -47
- package/dist/server/startServer.js +0 -57
- package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
- package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
- package/dist/webpack/babel.config.client.js +0 -1
- package/dist/webpack/babel.config.ssr.js +0 -1
- package/dist/webpack/base.babel.js +0 -7
- package/dist/webpack/development.client.babel.js +0 -10
- package/dist/webpack/production.client.babel.js +0 -4
- package/dist/webpack/production.ssr.babel.js +0 -8
- package/dist/webpack/scssParams.js +0 -9
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NetworkTimingCollector = void 0;
|
|
7
|
+
var _constants = require("../core/constants.js");
|
|
8
|
+
/**
|
|
9
|
+
* NetworkTimingCollector.js
|
|
10
|
+
*
|
|
11
|
+
* Adds lightweight Catalyst timeline spans for fetch() and XMLHttpRequest.
|
|
12
|
+
* Chrome already has detailed network timing; these spans exist to attach
|
|
13
|
+
* network work to Catalyst interaction sessions via sessionId.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const FETCH_WRAPPED = "__catalystNetworkTimingFetchWrapped";
|
|
17
|
+
const XHR_WRAPPED = "__catalystNetworkTimingXhrWrapped";
|
|
18
|
+
const XHR_META = "__catalystNetworkTimingMeta";
|
|
19
|
+
const MAX_LABEL_LENGTH = 72;
|
|
20
|
+
class NetworkTimingCollector {
|
|
21
|
+
constructor(measure) {
|
|
22
|
+
this._measure = measure;
|
|
23
|
+
this._getInteraction = null;
|
|
24
|
+
this._requestSeq = 0;
|
|
25
|
+
}
|
|
26
|
+
setInteractionSource(fn) {
|
|
27
|
+
this._getInteraction = fn;
|
|
28
|
+
}
|
|
29
|
+
setWaterfallSource(fn) {
|
|
30
|
+
this._notifyWaterfall = fn;
|
|
31
|
+
}
|
|
32
|
+
init() {
|
|
33
|
+
if (typeof window === "undefined") return;
|
|
34
|
+
this._wrapFetch();
|
|
35
|
+
this._wrapXhr();
|
|
36
|
+
}
|
|
37
|
+
_wrapFetch() {
|
|
38
|
+
const originalFetch = window.fetch;
|
|
39
|
+
if (typeof originalFetch !== "function" || originalFetch[FETCH_WRAPPED]) return;
|
|
40
|
+
const collector = this;
|
|
41
|
+
function catalystFetch(input, init) {
|
|
42
|
+
const span = collector._start("fetch", init?.method ?? input?.method ?? "GET", init?.url ?? input?.url ?? input);
|
|
43
|
+
try {
|
|
44
|
+
return originalFetch.apply(this, arguments).then(response => {
|
|
45
|
+
collector._finish(span, {
|
|
46
|
+
status: response?.status ?? null,
|
|
47
|
+
ok: response?.ok ?? null
|
|
48
|
+
});
|
|
49
|
+
return response;
|
|
50
|
+
}, error => {
|
|
51
|
+
collector._finish(span, {
|
|
52
|
+
outcome: "error",
|
|
53
|
+
ok: false
|
|
54
|
+
});
|
|
55
|
+
throw error;
|
|
56
|
+
});
|
|
57
|
+
} catch (error) {
|
|
58
|
+
collector._finish(span, {
|
|
59
|
+
outcome: "error",
|
|
60
|
+
ok: false
|
|
61
|
+
});
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catalystFetch[FETCH_WRAPPED] = true;
|
|
66
|
+
window.fetch = catalystFetch;
|
|
67
|
+
}
|
|
68
|
+
_wrapXhr() {
|
|
69
|
+
const proto = window.XMLHttpRequest?.prototype;
|
|
70
|
+
if (!proto || proto[XHR_WRAPPED]) return;
|
|
71
|
+
if (typeof proto.open !== "function" || typeof proto.send !== "function") return;
|
|
72
|
+
const originalOpen = proto.open;
|
|
73
|
+
const originalSend = proto.send;
|
|
74
|
+
const collector = this;
|
|
75
|
+
proto.open = function catalystXhrOpen(method, url) {
|
|
76
|
+
this[XHR_META] = {
|
|
77
|
+
method,
|
|
78
|
+
url
|
|
79
|
+
};
|
|
80
|
+
return originalOpen.apply(this, arguments);
|
|
81
|
+
};
|
|
82
|
+
proto.send = function catalystXhrSend() {
|
|
83
|
+
const meta = this[XHR_META] ?? {};
|
|
84
|
+
const span = collector._start("xhr", meta.method ?? "GET", meta.url ?? "unknown");
|
|
85
|
+
let emitted = false;
|
|
86
|
+
const finish = (outcome = "complete") => {
|
|
87
|
+
if (emitted) return;
|
|
88
|
+
emitted = true;
|
|
89
|
+
const status = this.status;
|
|
90
|
+
collector._finish(span, {
|
|
91
|
+
status,
|
|
92
|
+
ok: collector._statusOk(status),
|
|
93
|
+
outcome
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
this.addEventListener?.("loadend", () => finish(), {
|
|
97
|
+
once: true
|
|
98
|
+
});
|
|
99
|
+
try {
|
|
100
|
+
return originalSend.apply(this, arguments);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
finish("error");
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
proto[XHR_WRAPPED] = true;
|
|
107
|
+
}
|
|
108
|
+
_start(api, method, rawUrl) {
|
|
109
|
+
const startTime = performance.now();
|
|
110
|
+
const normalizedMethod = String(method || "GET").toUpperCase();
|
|
111
|
+
const url = this._normalizeUrl(rawUrl);
|
|
112
|
+
const label = this._labelForUrl(url);
|
|
113
|
+
const requestId = `${api}:${++this._requestSeq}`;
|
|
114
|
+
const startMark = `${_constants.PREFIX.NETWORK_TIMING}:${requestId}:start`;
|
|
115
|
+
performance.mark(startMark, {
|
|
116
|
+
startTime
|
|
117
|
+
});
|
|
118
|
+
const interaction = this._getInteraction?.();
|
|
119
|
+
const interactionId = interaction?.activeInteractionId ?? null;
|
|
120
|
+
if (interactionId) interaction?.onNetworkCall?.();
|
|
121
|
+
return {
|
|
122
|
+
api,
|
|
123
|
+
method: normalizedMethod,
|
|
124
|
+
url,
|
|
125
|
+
label,
|
|
126
|
+
requestId,
|
|
127
|
+
startMark,
|
|
128
|
+
startTime,
|
|
129
|
+
interactionId
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
_finish(span, result = {}) {
|
|
133
|
+
const endTime = performance.now();
|
|
134
|
+
const durationMs = Math.round(endTime - span.startTime);
|
|
135
|
+
const outcome = result.outcome ?? "complete";
|
|
136
|
+
const ok = result.ok ?? this._statusOk(result.status);
|
|
137
|
+
const color = outcome !== "complete" || ok === false || durationMs > _constants.THRESHOLD.FETCH_SLOW_MS ? _constants.COLOR.ERROR : _constants.COLOR.SECONDARY;
|
|
138
|
+
const detail = {
|
|
139
|
+
api: span.api,
|
|
140
|
+
method: span.method,
|
|
141
|
+
url: span.url,
|
|
142
|
+
status: result.status ?? null,
|
|
143
|
+
ok,
|
|
144
|
+
outcome,
|
|
145
|
+
durationMs,
|
|
146
|
+
sessionId: span.interactionId,
|
|
147
|
+
requestId: span.requestId,
|
|
148
|
+
simulatorValid: true
|
|
149
|
+
};
|
|
150
|
+
this._notifyWaterfall?.({
|
|
151
|
+
...detail,
|
|
152
|
+
startTime: span.startTime,
|
|
153
|
+
endTime
|
|
154
|
+
});
|
|
155
|
+
this._measure.emit(`${span.interactionId ? `[${span.interactionId}] ` : ""}Network: ${span.method} ${span.label} - ${durationMs}ms`, span.startMark, endTime, {
|
|
156
|
+
interactionId: span.interactionId,
|
|
157
|
+
...detail,
|
|
158
|
+
sessionId: undefined
|
|
159
|
+
}, _constants.TRACK.NETWORK, color);
|
|
160
|
+
}
|
|
161
|
+
_normalizeUrl(rawUrl) {
|
|
162
|
+
const value = rawUrl?.url ?? rawUrl ?? "unknown";
|
|
163
|
+
try {
|
|
164
|
+
const parsed = new URL(String(value), window.location?.href);
|
|
165
|
+
return `${parsed.origin}${parsed.pathname}`;
|
|
166
|
+
} catch {
|
|
167
|
+
return String(value).split("?")[0].split("#")[0];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
_labelForUrl(url) {
|
|
171
|
+
try {
|
|
172
|
+
const parsed = new URL(url, window.location?.href);
|
|
173
|
+
const label = parsed.origin === window.location?.origin ? parsed.pathname : `${parsed.host}${parsed.pathname}`;
|
|
174
|
+
return (label || "/").slice(0, MAX_LABEL_LENGTH);
|
|
175
|
+
} catch {
|
|
176
|
+
return String(url || "unknown").slice(0, MAX_LABEL_LENGTH);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
_statusOk(status) {
|
|
180
|
+
return typeof status === "number" && status >= 200 && status < 400;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.NetworkTimingCollector = NetworkTimingCollector;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.RenderCollector = void 0;
|
|
7
|
+
var _constants = require("../core/constants.js");
|
|
8
|
+
/**
|
|
9
|
+
* RenderCollector.js
|
|
10
|
+
*
|
|
11
|
+
* Instruments: bugs #1, #6, #10, #14, #15, #16
|
|
12
|
+
* #1 page slowness / cache miss → LoAF during navigation session = render blocked by loading
|
|
13
|
+
* #6 blank screens → LoAF blocks first render; LCP may never fire
|
|
14
|
+
* #10 footer flash on Drug/OTC → layout-shift fires just after navigation session closes
|
|
15
|
+
* #14 transitions not soft → layout-shift during nav = skeleton→content swap too abrupt
|
|
16
|
+
* #15 quantity selector slow → LoAF burst in the 2s after interaction (lazy chunk blocks)
|
|
17
|
+
* #16 prescription screen touch lag → same LoAF burst pattern after tap
|
|
18
|
+
*
|
|
19
|
+
* Spans emitted:
|
|
20
|
+
* catalyst:loaf|<ms>ms — long animation frame (Track: Render)
|
|
21
|
+
* detail: blockingDuration, scriptCount, sessionContext
|
|
22
|
+
* catalyst:layout-shift|<value> — unexpected layout shift (Track: Render)
|
|
23
|
+
* detail: value, sessionContext
|
|
24
|
+
* catalyst:fps-drop|<fps>fps-min|<ms>ms — native fps-drop episode (Track: Render)
|
|
25
|
+
* detail: minFps, avgFps, durationMs, sessionContext
|
|
26
|
+
* closes on recovery >55fps OR after 10s max
|
|
27
|
+
* catalyst:long-task|<ms>ms — native long task > 16ms (Track: Render)
|
|
28
|
+
* catalyst:mem/jvm|<MB>MB — JVM heap PSS — Kotlin/Java objects (Track: Render, blue)
|
|
29
|
+
* catalyst:mem/native|<MB>MB — native heap PSS — V8 + Blink + JNI, WebView proxy (Track: Render, orange)
|
|
30
|
+
* catalyst:mem/total|<MB>MB — total process PSS — what Android OOM killer sees (Track: Render, purple→red)
|
|
31
|
+
*
|
|
32
|
+
* Notifies:
|
|
33
|
+
* ScrollCollector.onLoaf() — so scroll session can increment its loafCount
|
|
34
|
+
* HookCollector.onLoaf() — so hook mount sessions can detect LoAF bursts
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
class RenderCollector {
|
|
38
|
+
constructor(measure, getSessionContext, nativeToWeb) {
|
|
39
|
+
this._measure = measure;
|
|
40
|
+
// getSessionContext() returns 'navigation'|'scroll'|'keyboard'|'none'
|
|
41
|
+
// provided by index.js so RenderCollector doesn't import other collectors
|
|
42
|
+
this._getSessionContext = getSessionContext;
|
|
43
|
+
this._nativeToWeb = nativeToWeb ?? (t => t);
|
|
44
|
+
this._loafListeners = []; // [fn] called on every LoAF
|
|
45
|
+
this._getInteraction = null; // set by index.js after construction
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Called by index.js to wire up interaction session context. */
|
|
49
|
+
setInteractionSource(fn) {
|
|
50
|
+
this._getInteraction = fn;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Called by index.js to wire up insights notifications. */
|
|
54
|
+
setInsightsSource(fn) {
|
|
55
|
+
this._notifyInsights = fn;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Called by index.js to feed the normalized perf store. */
|
|
59
|
+
setWaterfallSource(fn) {
|
|
60
|
+
this._notifyWaterfall = fn;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Allow other collectors to subscribe to LoAF events
|
|
64
|
+
addLoafListener(fn) {
|
|
65
|
+
this._loafListeners.push(fn);
|
|
66
|
+
}
|
|
67
|
+
init() {
|
|
68
|
+
this._observeLoAF();
|
|
69
|
+
this._observeLayoutShift();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── Native MetricsMonitor events ────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
onNativeEvent(event) {
|
|
75
|
+
const t = this._nativeToWeb(event.nativeTime);
|
|
76
|
+
switch (event.type) {
|
|
77
|
+
case "fps-drop-episode":
|
|
78
|
+
{
|
|
79
|
+
const start = Math.max(0, this._nativeToWeb(event.startNativeTime));
|
|
80
|
+
const end = Math.max(start, this._nativeToWeb(event.endNativeTime));
|
|
81
|
+
const markName = `${_constants.PREFIX.FPS_DROP}:${Math.round(start)}`;
|
|
82
|
+
performance.mark(markName, {
|
|
83
|
+
startTime: start
|
|
84
|
+
});
|
|
85
|
+
const interactionId = this._getInteraction?.()?.interactionIdForRange(start, end) ?? null;
|
|
86
|
+
if (interactionId) this._getInteraction?.()?.onFpsDrop();
|
|
87
|
+
this._measure.emit(`${interactionId ? `[${interactionId}] ` : ""}FPS drop: ${event.minFps.toFixed(1)}fps - ${Math.round(event.durationMs)}ms`, markName, end, {
|
|
88
|
+
interactionId,
|
|
89
|
+
minFps: event.minFps,
|
|
90
|
+
avgFps: event.avgFps,
|
|
91
|
+
durationMs: event.durationMs,
|
|
92
|
+
sessionContext: this._getSessionContext(),
|
|
93
|
+
thread: event.thread ?? null,
|
|
94
|
+
simulatorValid: false
|
|
95
|
+
}, _constants.TRACK.RENDER, event.minFps < 30 ? "error" : "tertiary");
|
|
96
|
+
this._notifyWaterfall?.({
|
|
97
|
+
metric: "fps-drop",
|
|
98
|
+
startTime: start,
|
|
99
|
+
endTime: end,
|
|
100
|
+
durationMs: event.durationMs,
|
|
101
|
+
minFps: event.minFps,
|
|
102
|
+
avgFps: event.avgFps,
|
|
103
|
+
sessionId: interactionId
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
interactionId,
|
|
107
|
+
startTime: start,
|
|
108
|
+
endTime: end
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
case "long-task":
|
|
112
|
+
{
|
|
113
|
+
const markName = `${_constants.PREFIX.LONG_TASK}:${Math.round(t)}`;
|
|
114
|
+
const interactionId = this._getInteraction?.()?.interactionIdForRange(t, t + event.durationMs) ?? null;
|
|
115
|
+
performance.mark(markName, {
|
|
116
|
+
startTime: t
|
|
117
|
+
});
|
|
118
|
+
this._measure.emit(`${interactionId ? `[${interactionId}] ` : ""}Long task: ${event.durationMs}ms`, markName, t + event.durationMs, {
|
|
119
|
+
interactionId,
|
|
120
|
+
durationMs: event.durationMs,
|
|
121
|
+
sessionContext: this._getSessionContext(),
|
|
122
|
+
thread: event.thread ?? null,
|
|
123
|
+
simulatorValid: true
|
|
124
|
+
}, _constants.TRACK.RENDER, event.durationMs > 100 ? "error" : "tertiary");
|
|
125
|
+
this._notifyWaterfall?.({
|
|
126
|
+
metric: "long-task",
|
|
127
|
+
startTime: t,
|
|
128
|
+
endTime: t + event.durationMs,
|
|
129
|
+
durationMs: event.durationMs,
|
|
130
|
+
sessionId: interactionId
|
|
131
|
+
});
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case "memory-snapshot":
|
|
135
|
+
{
|
|
136
|
+
const jvmMark = `catalyst:mem/jvm:${Math.round(t)}`;
|
|
137
|
+
performance.mark(jvmMark, {
|
|
138
|
+
startTime: t
|
|
139
|
+
});
|
|
140
|
+
this._measure.emit(`JVM memory: ${event.jvmMb.toFixed(1)}MB`, jvmMark, t + 1, {
|
|
141
|
+
memoryId: `memory-jvm-${Math.round(t)}`,
|
|
142
|
+
jvmMb: event.jvmMb,
|
|
143
|
+
peakMb: event.peakMb,
|
|
144
|
+
thread: event.thread ?? null,
|
|
145
|
+
simulatorValid: false,
|
|
146
|
+
...(event.coldStartMs != null ? {
|
|
147
|
+
coldStartMs: event.coldStartMs
|
|
148
|
+
} : {})
|
|
149
|
+
}, _constants.TRACK.MEMORY, "primary");
|
|
150
|
+
const webviewMemoryId = `memory-webview-${Math.round(t)}`;
|
|
151
|
+
const webviewMark = `catalyst:mem/webview:${Math.round(t)}`;
|
|
152
|
+
performance.mark(webviewMark, {
|
|
153
|
+
startTime: t
|
|
154
|
+
});
|
|
155
|
+
this._measure.emit(`WebView memory: ${event.webviewMb.toFixed(1)}MB`, webviewMark, t + 1, {
|
|
156
|
+
memoryId: webviewMemoryId,
|
|
157
|
+
webviewMb: event.webviewMb,
|
|
158
|
+
otherMb: event.otherMb,
|
|
159
|
+
peakMb: event.peakMb,
|
|
160
|
+
thread: event.thread ?? null,
|
|
161
|
+
simulatorValid: false,
|
|
162
|
+
...(event.coldStartMs != null ? {
|
|
163
|
+
coldStartMs: event.coldStartMs
|
|
164
|
+
} : {})
|
|
165
|
+
}, _constants.TRACK.MEMORY, "tertiary-dark");
|
|
166
|
+
const totalMark = `${_constants.PREFIX.MEMORY_TOTAL}:${Math.round(t)}`;
|
|
167
|
+
performance.mark(totalMark, {
|
|
168
|
+
startTime: t
|
|
169
|
+
});
|
|
170
|
+
this._measure.emit(`Total memory: ${event.totalMb.toFixed(1)}MB`, totalMark, t + 1, {
|
|
171
|
+
memoryId: `memory-total-${Math.round(t)}`,
|
|
172
|
+
totalMb: event.totalMb,
|
|
173
|
+
jvmMb: event.jvmMb,
|
|
174
|
+
webviewMb: event.webviewMb,
|
|
175
|
+
otherMb: event.otherMb,
|
|
176
|
+
peakMb: event.peakMb,
|
|
177
|
+
thread: event.thread ?? null,
|
|
178
|
+
simulatorValid: false,
|
|
179
|
+
...(event.coldStartMs != null ? {
|
|
180
|
+
coldStartMs: event.coldStartMs
|
|
181
|
+
} : {})
|
|
182
|
+
}, _constants.TRACK.MEMORY, event.totalMb > 400 ? "error" : "secondary");
|
|
183
|
+
this._notifyInsights?.({
|
|
184
|
+
currentMb: event.webviewMb,
|
|
185
|
+
startTime: t,
|
|
186
|
+
endTime: t + 1,
|
|
187
|
+
memoryId: webviewMemoryId
|
|
188
|
+
});
|
|
189
|
+
this._notifyWaterfall?.({
|
|
190
|
+
metric: "memory",
|
|
191
|
+
startTime: t,
|
|
192
|
+
endTime: t + 1,
|
|
193
|
+
durationMs: 1,
|
|
194
|
+
totalMb: event.totalMb,
|
|
195
|
+
webviewMb: event.webviewMb
|
|
196
|
+
});
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ─── Long Animation Frames ────────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
_observeLoAF() {
|
|
205
|
+
if (!PerformanceObserver.supportedEntryTypes?.includes("long-animation-frame")) return;
|
|
206
|
+
const obs = new PerformanceObserver(list => {
|
|
207
|
+
for (const entry of list.getEntries()) {
|
|
208
|
+
const start = entry.startTime;
|
|
209
|
+
const end = start + entry.duration;
|
|
210
|
+
const context = this._getSessionContext();
|
|
211
|
+
|
|
212
|
+
// Register with shared Measure so all collectors get overlap detection
|
|
213
|
+
this._measure.recordLoaf(start, end);
|
|
214
|
+
|
|
215
|
+
// Notify subscribers (ScrollCollector, HookCollector)
|
|
216
|
+
this._loafListeners.forEach(fn => fn(start, end));
|
|
217
|
+
const markName = `${_constants.PREFIX.LOAF}:${Math.round(start)}`;
|
|
218
|
+
performance.mark(markName, {
|
|
219
|
+
startTime: start
|
|
220
|
+
});
|
|
221
|
+
this._measure.emit(`${_constants.PREFIX.LOAF}|${Math.round(entry.duration)}ms`, markName, end, {
|
|
222
|
+
blockingDuration: entry.blockingDuration,
|
|
223
|
+
scriptCount: entry.scripts?.length ?? 0,
|
|
224
|
+
sessionContext: context,
|
|
225
|
+
simulatorValid: false
|
|
226
|
+
}, _constants.TRACK.RENDER);
|
|
227
|
+
this._notifyWaterfall?.({
|
|
228
|
+
metric: "loaf",
|
|
229
|
+
startTime: start,
|
|
230
|
+
endTime: end,
|
|
231
|
+
durationMs: entry.duration
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
obs.observe({
|
|
236
|
+
type: "long-animation-frame",
|
|
237
|
+
buffered: true
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ─── Layout shift ─────────────────────────────────────────────────────────
|
|
242
|
+
|
|
243
|
+
_observeLayoutShift() {
|
|
244
|
+
if (!PerformanceObserver.supportedEntryTypes?.includes("layout-shift")) return;
|
|
245
|
+
const obs = new PerformanceObserver(list => {
|
|
246
|
+
for (const entry of list.getEntries()) {
|
|
247
|
+
if (entry.hadRecentInput) continue; // user-initiated shift, ignore
|
|
248
|
+
|
|
249
|
+
const context = this._getSessionContext();
|
|
250
|
+
const markName = `catalyst:cls:${Math.round(entry.startTime)}`;
|
|
251
|
+
performance.mark(markName, {
|
|
252
|
+
startTime: entry.startTime
|
|
253
|
+
});
|
|
254
|
+
if (entry.value > 0.1) {
|
|
255
|
+
this._notifyInsights?.({
|
|
256
|
+
value: entry.value,
|
|
257
|
+
sessionContext: context,
|
|
258
|
+
startTime: entry.startTime,
|
|
259
|
+
endTime: entry.startTime + 1
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
this._measure.emit(`${_constants.PREFIX.LAYOUT_SHIFT}|${entry.value.toFixed(4)}`, markName, entry.startTime + 1,
|
|
263
|
+
// point-in-time event, minimal duration
|
|
264
|
+
{
|
|
265
|
+
value: entry.value,
|
|
266
|
+
sessionContext: context,
|
|
267
|
+
simulatorValid: false
|
|
268
|
+
}, _constants.TRACK.RENDER);
|
|
269
|
+
this._notifyWaterfall?.({
|
|
270
|
+
metric: "layout-shift",
|
|
271
|
+
startTime: entry.startTime,
|
|
272
|
+
endTime: entry.startTime + 1,
|
|
273
|
+
durationMs: 1,
|
|
274
|
+
value: entry.value
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
obs.observe({
|
|
279
|
+
type: "layout-shift",
|
|
280
|
+
buffered: true
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
exports.RenderCollector = RenderCollector;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ScrollCollector = void 0;
|
|
7
|
+
var _constants = require("../core/constants.js");
|
|
8
|
+
var _session = require("../core/session.js");
|
|
9
|
+
/**
|
|
10
|
+
* ScrollCollector.js
|
|
11
|
+
*
|
|
12
|
+
* Instruments: bugs #3, #13
|
|
13
|
+
* #3 keyboard sticky scroll bug → scroll fires during keyboard session → sticky recalc → layout thrash
|
|
14
|
+
* #13 cart computation jank → fps-drop-episode during scroll → scroll session marked janky
|
|
15
|
+
*
|
|
16
|
+
* Spans emitted:
|
|
17
|
+
* catalyst:session/scroll — scroll-start → scroll-end (Track: Input, shared with keyboard)
|
|
18
|
+
* detail: frameDropCount, minFps, janky, scrollDuration
|
|
19
|
+
*
|
|
20
|
+
* Native events consumed (via index.js _routeNativeEvent):
|
|
21
|
+
* scroll-start { nativeTime } — GestureDetector first onScroll() in CustomWebview.kt
|
|
22
|
+
* scroll-end { nativeTime } — GestureDetector ACTION_UP / ACTION_CANCEL
|
|
23
|
+
*
|
|
24
|
+
* fps-drop-episode routed here (Option B) when scroll session is open:
|
|
25
|
+
* index.js checks this._scroll._session.isOpen before calling onFpsDrop(event)
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
class ScrollCollector {
|
|
29
|
+
constructor(measure, nativeToWeb) {
|
|
30
|
+
this._measure = measure;
|
|
31
|
+
this._nativeToWeb = nativeToWeb;
|
|
32
|
+
this._session = new _session.Session(_constants.PREFIX.SESSION_SCROLL, measure, _constants.TRACK.INPUT);
|
|
33
|
+
this._insightsCb = null; // set by index.js via setInsightsSource
|
|
34
|
+
this._frameDropCount = 0;
|
|
35
|
+
this._minFps = 60;
|
|
36
|
+
}
|
|
37
|
+
init() {
|
|
38
|
+
// No DOM listener — scroll events come from native GestureDetector
|
|
39
|
+
}
|
|
40
|
+
setInsightsSource(cb) {
|
|
41
|
+
this._insightsCb = cb;
|
|
42
|
+
}
|
|
43
|
+
setWaterfallSource(fn) {
|
|
44
|
+
this._notifyWaterfall = fn;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Called by RenderCollector when a LoAF fires (shared notification, kept for compat)
|
|
48
|
+
onLoaf() {
|
|
49
|
+
if (this._session.isOpen) {
|
|
50
|
+
this._session.increment("loafCount");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ─── Native event handlers ────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
onScrollStart(event) {
|
|
57
|
+
if (this._session.isOpen) return; // already tracking, ignore duplicate start
|
|
58
|
+
const webNow = this._nativeToWeb(event.nativeTime);
|
|
59
|
+
this._frameDropCount = 0;
|
|
60
|
+
this._minFps = 60;
|
|
61
|
+
this._session.open({
|
|
62
|
+
loafCount: 0
|
|
63
|
+
}, webNow);
|
|
64
|
+
}
|
|
65
|
+
onScrollEnd(event) {
|
|
66
|
+
if (!this._session.isOpen) return;
|
|
67
|
+
const startTime = this._session.startTime;
|
|
68
|
+
const webNow = this._nativeToWeb(event.nativeTime);
|
|
69
|
+
const loafCount = this._session.get("loafCount") ?? 0;
|
|
70
|
+
const janky = this._frameDropCount > 0 || this._minFps < 55;
|
|
71
|
+
const durationMs = Math.round(webNow - startTime);
|
|
72
|
+
this._session.close({
|
|
73
|
+
loafCount,
|
|
74
|
+
frameDropCount: this._frameDropCount,
|
|
75
|
+
minFps: this._minFps,
|
|
76
|
+
janky,
|
|
77
|
+
simulatorValid: false
|
|
78
|
+
}, webNow);
|
|
79
|
+
if (janky && this._insightsCb) {
|
|
80
|
+
this._insightsCb({
|
|
81
|
+
minFps: this._minFps,
|
|
82
|
+
frameDropCount: this._frameDropCount,
|
|
83
|
+
startTime,
|
|
84
|
+
endTime: webNow
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
this._notifyWaterfall?.({
|
|
88
|
+
kind: "scroll",
|
|
89
|
+
startTime,
|
|
90
|
+
endTime: webNow,
|
|
91
|
+
durationMs,
|
|
92
|
+
loafCount,
|
|
93
|
+
frameDropCount: this._frameDropCount,
|
|
94
|
+
minFps: this._minFps,
|
|
95
|
+
janky
|
|
96
|
+
});
|
|
97
|
+
this._frameDropCount = 0;
|
|
98
|
+
this._minFps = 60;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ─── fps-drop-episode correlation (Option B) ──────────────────────────────
|
|
102
|
+
// Called from index.js _routeNativeEvent when scroll session is open.
|
|
103
|
+
|
|
104
|
+
onFpsDrop(event) {
|
|
105
|
+
this._frameDropCount++;
|
|
106
|
+
if (event.minFps != null && event.minFps < this._minFps) {
|
|
107
|
+
this._minFps = event.minFps;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.ScrollCollector = ScrollCollector;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ThreadCollector = void 0;
|
|
7
|
+
var _constants = require("../core/constants.js");
|
|
8
|
+
/**
|
|
9
|
+
* ThreadCollector.js
|
|
10
|
+
*
|
|
11
|
+
* Instruments: thread visibility gap during cache serving.
|
|
12
|
+
*
|
|
13
|
+
* Problem: when shouldInterceptRequest serves from cache it calls
|
|
14
|
+
* disableHardwareAcceleration() / enableHardwareAcceleration() on the WebView.
|
|
15
|
+
* This creates a software-render window that is invisible in the Render track
|
|
16
|
+
* because it happens on a bg thread and neither the thread name nor the
|
|
17
|
+
* hw-accel state appears in any existing span.
|
|
18
|
+
*
|
|
19
|
+
* Native events received (via index.js router):
|
|
20
|
+
* hw-accel-change { state: 'off'|'on', trigger: 'cache-serve'|'cleanup',
|
|
21
|
+
* thread, nativeTime }
|
|
22
|
+
*
|
|
23
|
+
* Spans emitted (on Render track):
|
|
24
|
+
* catalyst:hw-accel/off — software-render window open (error = bad, overlaps frames)
|
|
25
|
+
* catalyst:hw-accel/on — hardware acceleration restored (primary)
|
|
26
|
+
*
|
|
27
|
+
* Overlap detection:
|
|
28
|
+
* If a LoAF overlaps a software-render window → notifyInsights so
|
|
29
|
+
* InsightsCollector can fire an 'hw-accel-during-frame' critical insight.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
class ThreadCollector {
|
|
33
|
+
constructor(measure, nativeToWeb) {
|
|
34
|
+
this._measure = measure;
|
|
35
|
+
this._nativeToWeb = nativeToWeb;
|
|
36
|
+
|
|
37
|
+
// Track open software-render windows: { startMark, startTime, thread }
|
|
38
|
+
this._openWindow = null;
|
|
39
|
+
this._notifyInsights = null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Called by index.js to wire up insights notifications. */
|
|
43
|
+
setInsightsSource(fn) {
|
|
44
|
+
this._notifyInsights = fn;
|
|
45
|
+
}
|
|
46
|
+
init() {
|
|
47
|
+
// No web-side observers — all events come from native via onNativeEvent()
|
|
48
|
+
}
|
|
49
|
+
onNativeEvent(event) {
|
|
50
|
+
if (event.type !== "hw-accel-change") return;
|
|
51
|
+
const {
|
|
52
|
+
state,
|
|
53
|
+
trigger,
|
|
54
|
+
thread,
|
|
55
|
+
nativeTime
|
|
56
|
+
} = event;
|
|
57
|
+
const webTime = nativeTime != null ? this._nativeToWeb(nativeTime) : performance.now();
|
|
58
|
+
if (state === "off") {
|
|
59
|
+
// Software-render window opens — record start, wait for 'on' to close span
|
|
60
|
+
const markName = `${_constants.PREFIX.HW_ACCEL}/off:start:${Math.round(webTime)}`;
|
|
61
|
+
performance.mark(markName, {
|
|
62
|
+
startTime: webTime
|
|
63
|
+
});
|
|
64
|
+
this._openWindow = {
|
|
65
|
+
startMark: markName,
|
|
66
|
+
startTime: webTime,
|
|
67
|
+
thread: thread ?? null,
|
|
68
|
+
trigger: trigger ?? null
|
|
69
|
+
};
|
|
70
|
+
} else if (state === "on") {
|
|
71
|
+
const endTime = webTime;
|
|
72
|
+
if (this._openWindow) {
|
|
73
|
+
const {
|
|
74
|
+
startMark,
|
|
75
|
+
startTime,
|
|
76
|
+
thread: offThread,
|
|
77
|
+
trigger: offTrigger
|
|
78
|
+
} = this._openWindow;
|
|
79
|
+
this._openWindow = null;
|
|
80
|
+
const loafOverlap = this._measure.overlapsLoaf(startTime, endTime);
|
|
81
|
+
this._measure.emit(`${_constants.PREFIX.HW_ACCEL}/sw-render-window|${offTrigger ?? "unknown"}`, startMark, endTime, {
|
|
82
|
+
trigger: offTrigger ?? null,
|
|
83
|
+
thread: offThread ?? null,
|
|
84
|
+
loafOverlap,
|
|
85
|
+
simulatorValid: false
|
|
86
|
+
}, _constants.TRACK.RENDER, loafOverlap ? _constants.COLOR.ERROR : _constants.COLOR.SECONDARY_DARK);
|
|
87
|
+
if (loafOverlap) {
|
|
88
|
+
this._notifyInsights?.({
|
|
89
|
+
durationMs: endTime - startTime,
|
|
90
|
+
thread: offThread,
|
|
91
|
+
trigger: offTrigger,
|
|
92
|
+
startTime,
|
|
93
|
+
endTime
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Emit a point-in-time marker for hw-accel restored
|
|
99
|
+
const onMark = `${_constants.PREFIX.HW_ACCEL}/on:${Math.round(endTime)}`;
|
|
100
|
+
performance.mark(onMark, {
|
|
101
|
+
startTime: endTime
|
|
102
|
+
});
|
|
103
|
+
this._measure.emit(`${_constants.PREFIX.HW_ACCEL}/restored`, onMark, endTime + 1, {
|
|
104
|
+
trigger: trigger ?? null,
|
|
105
|
+
thread: thread ?? null,
|
|
106
|
+
simulatorValid: false
|
|
107
|
+
}, _constants.TRACK.RENDER, _constants.COLOR.SECONDARY);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.ThreadCollector = ThreadCollector;
|