Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ doc_build/
playwright-report/
tsconfig.tsbuildinfo
.swc
.rspack-profile-*/

# CSS Modules dist files
*.css.d.ts
Expand Down
8 changes: 4 additions & 4 deletions e2e/cases/rspack-profile/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
},
);
Expand Down Expand Up @@ -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();
},
);
117 changes: 86 additions & 31 deletions packages/core/src/plugins/rspackProfile.ts
Original file line number Diff line number Diff line change
@@ -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 => ({
Expand All @@ -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 }) => {
Expand All @@ -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)}`);
});
},
});
2 changes: 1 addition & 1 deletion website/docs/en/guide/debug/build-profiling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
2 changes: 1 addition & 1 deletion website/docs/zh/guide/debug/build-profiling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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)。
Expand Down
Loading