Compare commits

...

3 Commits

Author SHA1 Message Date
Rie Takahashi
9bc373d87a fix: merge tti components, use css file 2023-09-17 20:45:04 +01:00
Rie Takahashi
fb7e1c64fd fix missing $ in regex 2023-09-17 19:42:59 +01:00
Rie Takahashi
1128e8f3ad feat(startup timings): add Time-To-Interactive info 2023-09-17 19:37:03 +01:00
2 changed files with 129 additions and 6 deletions

@ -0,0 +1,23 @@
.vc-startuptimings-server-trace {
color: var(--header-primary);
user-select: text;
}
.vc-startuptimings-grid {
color: var(--header-primary);
display: grid;
gap: 2px 10px;
user-select: text;
}
.vc-startuptimings-4-cols {
grid-template-columns: repeat(3, auto) 1fr;
}
.vc-startuptimings-3-cols {
grid-template-columns: repeat(2, auto) 1fr;
}
.vc-startuptimings-2-cols {
grid-template-columns: auto 1fr;
}

@ -16,11 +16,35 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import "./StartupTimingPage.css";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { Margins } from "@utils/margins";
import { classes } from "@utils/misc";
import { findByPropsLazy } from "@webpack";
import { Forms, React } from "@webpack/common";
export const cl = classNameFactory("vc-startuptimings-");
interface ITTITrackerEvent {
emoji: string;
name: string;
start: number;
end: number;
hasData(): boolean;
}
interface ITTITracker {
serializeTTITracker(): Record<string, string | number | boolean | null | undefined>;
[event: string]: ITTITrackerEvent | string | boolean | null | any;
}
/** Time-To-Interactive Tracker */
const TTITracker: ITTITracker = findByPropsLazy("serializeTTITracker");
interface AppStartPerformance {
prefix: string;
logs: Log[];
@ -91,11 +115,11 @@ function TimingSection({ title, logs, traceEnd }: TimingSectionProps) {
<Forms.FormSection title={title} tag="h1">
<code>
{traceEnd && (
<div style={{ color: "var(--header-primary)", marginBottom: 5, userSelect: "text" }}>
<div className={cl("server-trace")} style={{ marginBottom: 5 }}>
Trace ended at: {(new Date(traceEnd)).toTimeString()}
</div>
)}
<div style={{ color: "var(--header-primary)", display: "grid", gridTemplateColumns: "repeat(3, auto) 1fr", gap: "2px 10px", userSelect: "text" }}>
<div className={classes(cl("grid"), cl("4-cols"))}>
<span>Start</span>
<span>Interval</span>
<span>Delta</span>
@ -119,7 +143,7 @@ function ServerTrace({ trace }: ServerTraceProps) {
return (
<Forms.FormSection title="Server Trace" tag="h2">
<code>
<Flex flexDirection="column" style={{ color: "var(--header-primary)", gap: 5, userSelect: "text" }}>
<Flex flexDirection="column" className={cl("server-trace")} style={{ gap: 5 }}>
{lines.map(line => (
<span>{line}</span>
))}
@ -129,11 +153,70 @@ function ServerTrace({ trace }: ServerTraceProps) {
);
}
function TTIAnalytics() {
const analytics = TTITracker.serializeTTITracker();
const filteredAnalytics = Object.entries(analytics).filter(([key, value]) => value != null && !/_start$|_end$/.test(key));
return (
<ErrorBoundary>
<Forms.FormSection title="TTI Analytics" tag="h2">
<code>
<div className={classes(cl("grid"), cl("2-cols"))}>
{filteredAnalytics.map(([key, value]) => (
<React.Fragment>
<span><pre>{key}</pre></span>
<span><pre>{`${value}`}</pre></span>
</React.Fragment>
))}
</div>
</code>
</Forms.FormSection>
</ErrorBoundary>
);
}
interface TTITimingsProps {
records: [string, ITTITrackerEvent][];
title: string;
type: "registered" | "unregistered";
}
function TTITimings({ records, title, type }: TTITimingsProps) {
const isRegistered = type === "registered";
return (
<ErrorBoundary>
<Forms.FormSection title={title} tag="h2">
<code>
<div className={classes(cl("grid"), cl(isRegistered ? "3-cols" : "2-cols"))}>
{isRegistered && <span>Duration</span>}
<span>Key</span>
<span style={{ marginBottom: 5 }}>Event</span>
{records.map(([key, event]) => (
<React.Fragment key={key}>
{isRegistered && <span><pre>{event.end - event.start}ms</pre></span>}
<span><pre>{key}</pre></span>
<span><pre>{event.emoji} {event.name}</pre></span>
</React.Fragment>
))}
</div>
</code>
</Forms.FormSection>
</ErrorBoundary>
);
}
function StartupTimingPage() {
if (!AppStartPerformance?.logs) return <div>Loading...</div>;
const serverTrace = AppStartPerformance.logGroups.find(g => g.serverTrace)?.serverTrace;
const registeredTTITimings: [string, ITTITrackerEvent][] = (Object.entries(TTITracker))
.filter(([, value]) => value?.hasData?.());
const unregisteredTTITimings: [string, ITTITrackerEvent][] = (Object.entries(TTITracker))
.filter(([, value]) => value?.hasData && !value.hasData());
return (
<React.Fragment>
<TimingSection
@ -141,9 +224,26 @@ function StartupTimingPage() {
logs={AppStartPerformance.logs}
traceEnd={AppStartPerformance.endTime_}
/>
{/* Lazy Divider */}
<div style={{ marginTop: 5 }}>&nbsp;</div>
<Forms.FormDivider className={classes(Margins.top16, Margins.bottom16)} />
{serverTrace && <ServerTrace trace={serverTrace} />}
<Forms.FormDivider className={classes(Margins.top16, Margins.bottom16)} />
<TTIAnalytics />
<Forms.FormDivider className={classes(Margins.top16, Margins.bottom16)} />
<TTITimings
title="Registered TTI Timings"
records={registeredTTITimings}
type="registered"
/>
<Forms.FormDivider className={classes(Margins.top16, Margins.bottom16)} />
<TTITimings
title="Unregistered TTI Timings"
records={unregisteredTTITimings}
type="unregistered"
/>
</React.Fragment>
);
}