Skip to content

Commit 617a318

Browse files
authored
Native profiling (#892)
1 parent 3fe7d4e commit 617a318

File tree

15 files changed

+345
-2
lines changed

15 files changed

+345
-2
lines changed

.changeset/weak-beds-happen.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@atlaspack/types-internal': minor
3+
'@atlaspack/profiler': minor
4+
'@atlaspack/utils': minor
5+
'@atlaspack/core': minor
6+
'@atlaspack/cli': minor
7+
---
8+
9+
Added a new `shouldProfileNative` option that provides a way to pause and connect a native profiler to Atlaspack.

docs/Atlaspack-API/Configuration-Guide.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ interface InitialAtlaspackOptions {
6363
shouldAutoInstall?: boolean;
6464
logLevel?: LogLevel;
6565
shouldProfile?: boolean;
66+
nativeProfiler?: 'instruments' | 'samply';
6667
shouldTrace?: boolean;
6768
shouldPatchConsole?: boolean;
6869
additionalReporters?: Array<{
@@ -681,6 +682,42 @@ const atlaspack = new Atlaspack({
681682
});
682683
```
683684

685+
### `nativeProfiler`
686+
687+
**Type**: `'instruments' | 'samply' | undefined`
688+
689+
**Default**: `undefined`
690+
691+
**Description**: Enable native build profiling. When enabled, Atlaspack will display a banner with the PID and command to run the profiler. The profiler type can be:
692+
693+
- `'instruments'` - Use Instruments (macOS only, uses `xcrun xctrace`)
694+
- `'samply'` - Use samply profiler (cross-platform)
695+
696+
If `--profile-native` is provided on the CLI without a profiler name, defaults to `'samply'`.
697+
698+
**Examples**:
699+
700+
```javascript
701+
// Enable native profiling with instruments (macOS)
702+
const atlaspack = new Atlaspack({
703+
entries: ['src/index.html'],
704+
nativeProfiler: 'instruments',
705+
});
706+
707+
// Enable native profiling with samply
708+
const atlaspack = new Atlaspack({
709+
entries: ['src/index.html'],
710+
nativeProfiler: 'samply',
711+
});
712+
713+
// Environment-based native profiling
714+
const atlaspack = new Atlaspack({
715+
entries: ['src/index.html'],
716+
nativeProfiler:
717+
process.env.NATIVE_PROFILE === 'samply' ? 'samply' : 'instruments',
718+
});
719+
```
720+
684721
### `shouldTrace`
685722

686723
**Type**: `boolean`

docs/cli/build-commands.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ These options are available for `atlaspack serve`, `atlaspack watch`, and `atlas
1616
- `--dist-dir <dir>` - Output directory when unspecified by targets
1717
- `--no-autoinstall` - Disable autoinstall
1818
- `--profile` - Enable sampling build profiling
19+
- `--profile-native [instruments|samply]` - Enable native build profiling (defaults to samply if no profiler name is provided)
1920
- `--trace` - Enable build tracing
2021
- `-V, --version` - Output the version number
2122
- `--detailed-report [count]` - Print asset timings and sizes in the build report

packages/core/cli/src/normalizeOptions.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {FeatureFlags} from '@atlaspack/feature-flags';
66
import type {InitialAtlaspackOptions, LogLevel} from '@atlaspack/types';
77
import {INTERNAL_ORIGINAL_CONSOLE} from '@atlaspack/logger';
88
import path from 'path';
9+
import os from 'os';
910

1011
function parsePort(portValue: string): number {
1112
let parsedPort = Number(portValue);
@@ -47,6 +48,7 @@ export interface Options {
4748
config?: string;
4849
logLevel?: LogLevel;
4950
profile?: boolean;
51+
profileNative?: string | boolean;
5052
contentHash?: boolean;
5153
featureFlag?: Partial<FeatureFlags>;
5254
optimize?: boolean;
@@ -175,6 +177,22 @@ export async function normalizeOptions(
175177
return input.split(',').map((value) => value.trim());
176178
};
177179

180+
let nativeProfiler: 'instruments' | 'samply' | undefined;
181+
if (typeof command.profileNative === 'string') {
182+
if (
183+
command.profileNative === 'instruments' ||
184+
command.profileNative === 'samply'
185+
) {
186+
nativeProfiler = command.profileNative as 'instruments' | 'samply';
187+
} else {
188+
nativeProfiler = undefined;
189+
}
190+
} else if (command.profileNative) {
191+
nativeProfiler = os.platform() === 'darwin' ? 'instruments' : 'samply';
192+
} else {
193+
nativeProfiler = undefined;
194+
}
195+
178196
return {
179197
shouldDisableCache: command.cache === false,
180198
cacheDir: command.cacheDir,
@@ -192,6 +210,7 @@ export async function normalizeOptions(
192210
shouldAutoInstall: command.autoinstall ?? true,
193211
logLevel: command.logLevel,
194212
shouldProfile: command.profile,
213+
nativeProfiler,
195214
shouldTrace: command.trace,
196215
shouldBuildLazily: typeof command.lazy !== 'undefined',
197216
lazyIncludes: normalizeIncludeExcludeList(command.lazy),

packages/core/cli/src/options.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ export const commonOptions: OptionsDefinition = {
6060
'output directory to write to when unspecified by targets',
6161
'--no-autoinstall': 'disable autoinstall',
6262
'--profile': 'enable sampling build profiling',
63+
'--profile-native [instruments|samply]':
64+
'enable native build profiling (defaults to instruments on macOS, samply otherwise)',
6365
'--trace': 'enable build tracing',
6466
'-V, --version': 'output the version number',
6567
'--detailed-report [count]': [

packages/core/core/src/Atlaspack.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import {
5757
toProjectPath,
5858
fromProjectPathRelative,
5959
} from './projectPath';
60-
import {tracer} from '@atlaspack/profiler';
60+
import {tracer, NativeProfiler} from '@atlaspack/profiler';
6161
import {setFeatureFlags, DEFAULT_FEATURE_FLAGS} from '@atlaspack/feature-flags';
6262
import {AtlaspackV3, FileSystemV3} from './atlaspack-v3';
6363
import createAssetGraphRequestJS from './requests/AssetGraphRequest';
@@ -407,6 +407,10 @@ export default class Atlaspack {
407407
if (options.shouldProfile) {
408408
await this.startProfiling();
409409
}
410+
if (options.nativeProfiler) {
411+
const nativeProfiler = new NativeProfiler();
412+
await nativeProfiler.startProfiling(options.nativeProfiler);
413+
}
410414
if (options.shouldTrace) {
411415
tracer.enable();
412416
// We need to ensure the tracer is disabled when Atlaspack is disposed as it is a module level object.

packages/core/core/src/resolveOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ export default async function resolveOptions(
269269
: false,
270270
shouldDisableCache: initialOptions.shouldDisableCache ?? false,
271271
shouldProfile: initialOptions.shouldProfile ?? false,
272+
nativeProfiler: initialOptions.nativeProfiler,
272273
shouldTrace: initialOptions.shouldTrace ?? false,
273274
cacheDir,
274275
watchDir,

packages/core/core/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ export type AtlaspackOptions = {
335335
logLevel: LogLevel;
336336
projectRoot: FilePath;
337337
shouldProfile: boolean;
338+
nativeProfiler: 'instruments' | 'samply' | undefined;
338339
shouldTrace: boolean;
339340
shouldPatchConsole: boolean;
340341
detailedReport?: DetailedReportOptions | null | undefined;

packages/core/profiler/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
"@atlaspack/diagnostic": "2.14.4",
2424
"@atlaspack/events": "2.14.4",
2525
"@atlaspack/types-internal": "2.20.8",
26+
"@atlaspack/logger": "2.14.30",
27+
"@atlaspack/utils": "3.1.2",
28+
"chalk": "^4.1.2",
2629
"chrome-trace-event": "^1.0.2"
2730
},
2831
"type": "commonjs"

0 commit comments

Comments
 (0)