From a6fe31cfa71431a44e850524abf55ab62014d20c Mon Sep 17 00:00:00 2001 From: neverland Date: Mon, 12 May 2025 10:18:05 +0800 Subject: [PATCH] feat: align tracing implementation with Rspack CLI --- .gitignore | 1 + e2e/cases/rspack-profile/index.test.ts | 8 +- packages/core/src/plugins/rspackProfile.ts | 117 +++++++++++++----- .../docs/en/guide/debug/build-profiling.mdx | 2 +- .../docs/zh/guide/debug/build-profiling.mdx | 2 +- 5 files changed, 93 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index e24548b8c5..2974dd7e4e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ doc_build/ playwright-report/ tsconfig.tsbuildinfo .swc +.rspack-profile-*/ # CSS Modules dist files *.css.d.ts diff --git a/e2e/cases/rspack-profile/index.test.ts b/e2e/cases/rspack-profile/index.test.ts index c5b96a6c00..f7323a3a06 100644 --- a/e2e/cases/rspack-profile/index.test.ts +++ b/e2e/cases/rspack-profile/index.test.ts @@ -33,12 +33,12 @@ rspackOnlyTest( logs.some((log) => log.includes('profile file saved to')), ).toBeTruthy(); - const profileDir = logs + const profileFile = logs .find((log) => log.includes('profile file saved to')) ?.split('profile file saved to')[1] ?.trim(); - expect(fs.existsSync(path.join(profileDir!, 'trace.json'))).toBeTruthy(); + expect(fs.existsSync(profileFile!)).toBeTruthy(); devProcess.kill(); }, ); @@ -69,12 +69,12 @@ rspackOnlyTest( logs.some((log) => log.includes('profile file saved to')), ).toBeTruthy(); - const profileDir = logs + const profileFile = logs .find((log) => log.includes('profile file saved to')) ?.split('profile file saved to')[1] ?.trim(); - expect(fs.existsSync(path.join(profileDir!, 'trace.json'))).toBeTruthy(); + expect(fs.existsSync(profileFile!)).toBeTruthy(); buildProcess.kill(); }, ); diff --git a/packages/core/src/plugins/rspackProfile.ts b/packages/core/src/plugins/rspackProfile.ts index 33f40bb978..c9c39686cd 100644 --- a/packages/core/src/plugins/rspackProfile.ts +++ b/packages/core/src/plugins/rspackProfile.ts @@ -1,10 +1,82 @@ import fs from 'node:fs'; import path from 'node:path'; import rspack from '@rspack/core'; -import { color, isFileExists } from '../helpers'; +import { color } from '../helpers'; import { logger } from '../logger'; import type { RsbuildPlugin } from '../types'; +enum TracePreset { + OVERVIEW = 'OVERVIEW', // contains overview trace events + ALL = 'ALL', // contains all trace events +} + +function resolveLayer(value: string): string { + const overviewTraceFilter = 'info'; + const allTraceFilter = 'trace'; + + if (value === TracePreset.OVERVIEW) { + return overviewTraceFilter; + } + if (value === TracePreset.ALL) { + return allTraceFilter; + } + + return value; +} + +async function ensureFileDir(outputFilePath: string) { + const dir = path.dirname(outputFilePath); + await fs.promises.mkdir(dir, { recursive: true }); +} + +/** + * `RSPACK_PROFILE=ALL` overview trace events + * `RSPACK_PROFILE=OVERVIEW` // all trace event + * `RSPACK_PROFILE=warn,tokio::net=info` // trace filter from https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax + */ +async function applyProfile( + root: string, + filterValue: string, + traceLayer = 'chrome', + traceOutput?: string, +) { + if (traceLayer !== 'chrome' && traceLayer !== 'logger') { + throw new Error(`unsupported trace layer: ${traceLayer}`); + } + + if (!traceOutput) { + const timestamp = Date.now(); + const defaultOutputDir = path.join( + root, + `.rspack-profile-${timestamp}-${process.pid}`, + ); + const defaultRustTraceChromeOutput = path.join( + defaultOutputDir, + 'trace.json', + ); + const defaultRustTraceLoggerOutput = 'stdout'; + + const defaultTraceOutput = + traceLayer === 'chrome' + ? defaultRustTraceChromeOutput + : defaultRustTraceLoggerOutput; + + // biome-ignore lint/style/noParameterAssign: setting default value makes sense + traceOutput = defaultTraceOutput; + } + + const filter = resolveLayer(filterValue); + + await ensureFileDir(traceOutput); + await rspack.experiments.globalTrace.register( + filter, + traceLayer, + traceOutput, + ); + + return traceOutput; +} + // Referenced from Rspack CLI // https://github.com/web-infra-dev/rspack/blob/v1.3.9/packages/rspack-cli/src/utils/profile.ts export const pluginRspackProfile = (): RsbuildPlugin => ({ @@ -15,38 +87,20 @@ export const pluginRspackProfile = (): RsbuildPlugin => ({ return; } - /** - * RSPACK_PROFILE=ALL - * RSPACK_PROFILE=TRACE - */ - const RSPACK_PROFILE = process.env.RSPACK_PROFILE?.toUpperCase(); - + const { RSPACK_PROFILE } = process.env; if (!RSPACK_PROFILE) { return; } - const timestamp = Date.now(); - const profileDirName = `rspack-profile-${timestamp}`; - - const enableProfileTrace = - RSPACK_PROFILE === 'ALL' || RSPACK_PROFILE.includes('TRACE'); + let traceOutput: string; const onStart = async () => { - // Note: Cannot obtain accurate `api.context.distPath` before config initialization - const profileDir = path.join(api.context.distPath, profileDirName); - const traceFilePath = path.join(profileDir, 'trace.json'); - - if (!(await isFileExists(profileDir))) { - await fs.promises.mkdir(profileDir, { recursive: true }); - } - - if (enableProfileTrace) { - rspack.experiments.globalTrace.register( - 'trace', - 'chrome', - traceFilePath, - ); - } + traceOutput = await applyProfile( + api.context.rootPath, + RSPACK_PROFILE, + process.env.RSPACK_TRACE_LAYER, + process.env.RSPACK_TRACE_OUTPUT, + ); }; api.onBeforeBuild(({ isFirstCompile }) => { @@ -57,11 +111,12 @@ export const pluginRspackProfile = (): RsbuildPlugin => ({ api.onBeforeStartDevServer(onStart); api.onExit(() => { - if (enableProfileTrace) { - rspack.experiments.globalTrace.cleanup(); + if (!traceOutput) { + return; } - const profileDir = path.join(api.context.distPath, profileDirName); - logger.info(`profile file saved to ${color.cyan(profileDir)}`); + + rspack.experiments.globalTrace.cleanup(); + logger.info(`profile file saved to ${color.cyan(traceOutput)}`); }); }, }); diff --git a/website/docs/en/guide/debug/build-profiling.mdx b/website/docs/en/guide/debug/build-profiling.mdx index 31e4575f98..76e2eaa3dd 100644 --- a/website/docs/en/guide/debug/build-profiling.mdx +++ b/website/docs/en/guide/debug/build-profiling.mdx @@ -62,7 +62,7 @@ As Windows does not support the above usage, you can also use [cross-env](https: } ``` -When the build command is finished, or the dev server is shut down, Rsbuild will create a `rspack-profile-${timestamp}` folder in the dist folder, containing a `trace.json` file, which is generated by Rspack based on [tracing](https://github.com/tokio-rs/tracing) and records the time spent on each phase at a granular level, and can be viewed using [ui.perfetto.dev](https://ui.perfetto.dev/). +When the build command is finished, or the dev server is shut down, Rsbuild will create a `rspack-profile-${timestamp}` folder in the current directory, containing a `trace.json` file, which is generated by Rspack based on [tracing](https://github.com/tokio-rs/tracing) and records the time spent on each phase at a granular level, and can be viewed using [ui.perfetto.dev](https://ui.perfetto.dev/). :::tip For more information about Rspack profiling, refer to [Rspack - Tracing](https://rspack.dev/contribute/development/tracing). diff --git a/website/docs/zh/guide/debug/build-profiling.mdx b/website/docs/zh/guide/debug/build-profiling.mdx index f2fb857a1e..a05253ea23 100644 --- a/website/docs/zh/guide/debug/build-profiling.mdx +++ b/website/docs/zh/guide/debug/build-profiling.mdx @@ -62,7 +62,7 @@ Rsbuild 支持使用 `RSPACK_PROFILE` 环境变量来对 Rspack 进行构建性 } ``` -当 build 命令执行完成,或是 dev server 被关闭时,Rsbuild 会在产物目录下生成一个 `rspack-profile-${timestamp}` 文件夹,其中包含 `trace.json` 文件,该文件由 Rspack 基于 [tracing](https://github.com/tokio-rs/tracing) 细粒度地记录了各个阶段的耗时,可以使用 [ui.perfetto.dev](https://ui.perfetto.dev/) 进行查看。 +当 build 命令执行完成,或是 dev server 被关闭时,Rsbuild 会在当前目录下生成一个 `rspack-profile-${timestamp}` 文件夹,其中包含 `trace.json` 文件,该文件由 Rspack 基于 [tracing](https://github.com/tokio-rs/tracing) 细粒度地记录了各个阶段的耗时,可以使用 [ui.perfetto.dev](https://ui.perfetto.dev/) 进行查看。 :::tip 关于 Rspack 性能分析的更多用法,可参考 [Rspack - Tracing](https://rspack.dev/zh/contribute/development/tracing)。