diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 988f19c5f..b559174d6 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -214,7 +214,7 @@ page.on("console", async e => { switch (tag) { case "WebpackInterceptor:": - const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!; + const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module|took [\d.]+?ms) \(Module id is (.+?)\): (.+)/)!; if (!patchFailMatch) break; console.error(await getText()); diff --git a/src/debug/Tracer.ts b/src/debug/Tracer.ts index 7d80f425c..be65cd54b 100644 --- a/src/debug/Tracer.ts +++ b/src/debug/Tracer.ts @@ -23,35 +23,63 @@ if (IS_DEV || IS_REPORTER) { var logger = new Logger("Tracer", "#FFD166"); } -const noop = function () { }; +const Noop = () => { }; -export const beginTrace = !(IS_DEV || IS_REPORTER) ? noop : +export const beginTrace = !(IS_DEV || IS_REPORTER) ? Noop : function beginTrace(name: string, ...args: any[]) { - if (name in traces) + if (name in traces) { throw new Error(`Trace ${name} already exists!`); + } traces[name] = [performance.now(), args]; }; -export const finishTrace = !(IS_DEV || IS_REPORTER) ? noop : function finishTrace(name: string) { - const end = performance.now(); +export const finishTrace = !(IS_DEV || IS_REPORTER) ? () => 0 : + function finishTrace(name: string) { + const end = performance.now(); - const [start, args] = traces[name]; - delete traces[name]; + const [start, args] = traces[name]; + delete traces[name]; - logger.debug(`${name} took ${end - start}ms`, args); -}; + const totalTime = end - start; + logger.debug(`${name} took ${totalTime}ms`, args); + + return totalTime; + }; type Func = (...args: any[]) => any; type TraceNameMapper = (...args: Parameters) => string; -const noopTracer = - (name: string, f: F, mapper?: TraceNameMapper) => f; +function noopTracerWithResults(name: string, f: F, mapper?: TraceNameMapper) { + return function (this: unknown, ...args: Parameters): [ReturnType, number] { + return [f.apply(this, args), 0]; + }; +} + +function noopTracer(name: string, f: F, mapper?: TraceNameMapper) { + return f; +} + +export const traceFunctionWithResults = !(IS_DEV || IS_REPORTER) + ? noopTracerWithResults + : function traceFunctionWithResults(name: string, f: F, mapper?: TraceNameMapper): (this: unknown, ...args: Parameters) => [ReturnType, number] { + return function (this: unknown, ...args: Parameters) { + const traceName = mapper?.(...args) ?? name; + + beginTrace(traceName, ...arguments); + try { + return [f.apply(this, args), finishTrace(traceName)]; + } catch (e) { + finishTrace(traceName); + throw e; + } + }; + }; export const traceFunction = !(IS_DEV || IS_REPORTER) ? noopTracer : function traceFunction(name: string, f: F, mapper?: TraceNameMapper): F { - return function (this: any, ...args: Parameters) { + return function (this: unknown, ...args: Parameters) { const traceName = mapper?.(...args) ?? name; beginTrace(traceName, ...arguments); diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 73cb2d71d..37b492c99 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -47,6 +47,12 @@ async function runReporter() { } } + for (const [plugin, moduleId, match, totalTime] of Vencord.WebpackPatcher.patchTimings) { + if (totalTime > 3) { + new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${totalTime}ms (Module id is ${String(moduleId)}): ${match}`); + } + } + await Promise.all(Webpack.webpackSearchHistory.map(async ([searchType, args]) => { args = [...args]; diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 951fad46f..5bb173e54 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -10,7 +10,7 @@ import { interpolateIfDefined } from "@utils/misc"; import { canonicalizeReplacement } from "@utils/patches"; import { PatchReplacement } from "@utils/types"; -import { traceFunction } from "../debug/Tracer"; +import { traceFunctionWithResults } from "../debug/Tracer"; import { patches } from "../plugins"; import { _initWebpack, AnyModuleFactory, AnyWebpackRequire, factoryListeners, moduleListeners, waitForSubscriptions, WebpackRequire, WrappedModuleFactory, wreq } from "."; @@ -21,6 +21,8 @@ export const allWebpackInstances = new Set(); /** Whether we tried to fallback to factory WebpackRequire, or disabled patches */ let wreqFallbackApplied = false; +export const patchTimings = [] as Array<[plugin: string, moduleId: PropertyKey, match: string | RegExp, totalTime: number]>; + type Define = typeof Reflect.defineProperty; const define: Define = (target, p, attributes) => { if (Object.hasOwn(attributes, "value")) { @@ -391,7 +393,7 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory) { patchedBy.add(patch.plugin); - const executePatch = traceFunction(`patch by ${patch.plugin}`, (match: string | RegExp, replace: string) => code.replace(match, replace)); + const executePatch = traceFunctionWithResults(`patch by ${patch.plugin}`, (match: string | RegExp, replace: string) => code.replace(match, replace)); const previousCode = code; const previousFactory = factory; @@ -403,7 +405,12 @@ function patchFactory(id: PropertyKey, factory: AnyModuleFactory) { canonicalizeReplacement(replacement, patch.plugin); try { - const newCode = executePatch(replacement.match, replacement.replace as string); + const [newCode, totalTime] = executePatch(replacement.match, replacement.replace as string); + + if (IS_REPORTER) { + patchTimings.push([patch.plugin, id, replacement.match, totalTime]); + } + if (newCode === code) { if (!patch.noWarn) { logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${String(id)}): ${replacement.match}`);