Skip to content

Commit 68598ab

Browse files
committed
Fix profiling for 'ui5 serve'
1 parent 3e19cb1 commit 68598ab

File tree

4 files changed

+69
-24
lines changed

4 files changed

+69
-24
lines changed

bin/ui5.cjs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,19 @@ const ui5 = {
7777
},
7878

7979
async invokeCLI(pkg) {
80+
let profile;
8081
if (process.env.UI5_CLI_PROFILE) {
81-
const profile = await import("../lib/utils/profile.js");
82+
profile = await import("../lib/utils/profile.js");
8283
await profile.start();
8384
}
8485
const {default: cli} = await import("../lib/cli/cli.js");
85-
await cli(pkg);
86+
const {command} = await cli(pkg);
87+
88+
// Stop profiling after CLI finished execution
89+
// Except for "serve" command, which continues running and only stops on sigint (see profile.js)
90+
if (profile && command !== "serve") {
91+
await profile.stop();
92+
}
8693
},
8794

8895
async main() {
@@ -92,8 +99,7 @@ const ui5 = {
9299
} else {
93100
const localInstallationInvoked = await ui5.invokeLocalInstallation(pkg);
94101
if (!localInstallationInvoked) {
95-
const exitCode = await ui5.invokeCLI(pkg);
96-
process.exit(exitCode);
102+
await ui5.invokeCLI(pkg);
97103
}
98104
}
99105
}

lib/cli/base.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,6 @@ export default function(cli) {
123123
console.log("");
124124
console.log(chalk.dim(`See 'ui5 --help'`));
125125
}
126+
process.exit(1);
126127
});
127128
}

lib/cli/cli.js

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,8 @@ export default async (pkg) => {
6868
// Format terminal output to full available width
6969
cli.wrap(cli.terminalWidth());
7070

71-
let exitCode = 0;
72-
try {
73-
// Call parse to run yargs
74-
await cli.parse();
75-
} catch (err) {
76-
// Error is already handled via .fail callback in ./base.js
77-
exitCode = 1;
78-
} finally {
79-
// Stop profiling after CLI finished execution
80-
if (process.env.UI5_CLI_PROFILE) {
81-
const profile = await import("../utils/profile.js");
82-
await profile.stop();
83-
}
84-
}
85-
86-
return exitCode;
71+
const {_} = await cli.argv;
72+
return {
73+
command: _[0]
74+
};
8775
};

lib/utils/profile.js

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {writeFile} from "node:fs/promises";
22
import {Session} from "node:inspector";
33

44
let session;
5+
let processSignals;
56

67
export async function start() {
78
if (session) {
@@ -11,25 +12,44 @@ export async function start() {
1112
session.connect();
1213
await new Promise((resolve) => {
1314
session.post("Profiler.enable", () => {
15+
console.log(`Recording CPU profile...`);
1416
session.post("Profiler.start", () => {
17+
processSignals = registerSigHooks();
1518
resolve();
1619
});
1720
});
1821
});
1922
}
2023

2124
async function writeProfile(profile) {
22-
const d = new Date();
23-
const timestamp =
24-
`${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()}_${d.getHours()}-${d.getMinutes()}-${d.getSeconds()}`;
25+
const formatter = new Intl.DateTimeFormat("en-GB", {
26+
year: "numeric",
27+
month: "2-digit",
28+
day: "2-digit",
29+
hour: "2-digit",
30+
minute: "2-digit",
31+
second: "2-digit",
32+
});
33+
const dateParts = Object.create(null);
34+
const parts = formatter.formatToParts(new Date());
35+
parts.forEach((p) => {
36+
dateParts[p.type] = p.value;
37+
});
2538

26-
await writeFile(`./ui5_${timestamp}.cpuprofile`, JSON.stringify(profile));
39+
const fileName = `./ui5_${dateParts.year}-${dateParts.month}-${dateParts.day}_` +
40+
`${dateParts.hour}-${dateParts.minute}-${dateParts.second}.cpuprofile`;
41+
console.log(`\nSaving CPU profile to ${fileName}...`);
42+
await writeFile(fileName, JSON.stringify(profile));
2743
}
2844

2945
export async function stop() {
3046
if (!session) {
3147
return;
3248
}
49+
if (processSignals) {
50+
deregisterSigHooks(processSignals);
51+
processSignals = null;
52+
}
3353
const profile = await new Promise((resolve) => {
3454
session.post("Profiler.stop", (err, {profile}) => {
3555
if (err) {
@@ -38,8 +58,38 @@ export async function stop() {
3858
resolve(profile);
3959
}
4060
});
61+
session = null;
4162
});
4263
if (profile) {
4364
await writeProfile(profile);
4465
}
4566
}
67+
68+
function registerSigHooks() {
69+
function createListener(exitCode) {
70+
return function() {
71+
// Gracefully end profiling, then exit
72+
stop().then(() => {
73+
process.exit(exitCode);
74+
});
75+
};
76+
}
77+
78+
const processSignals = {
79+
"SIGHUP": createListener(128 + 1),
80+
"SIGINT": createListener(128 + 2),
81+
"SIGTERM": createListener(128 + 15),
82+
"SIGBREAK": createListener(128 + 21)
83+
};
84+
85+
for (const signal of Object.keys(processSignals)) {
86+
process.on(signal, processSignals[signal]);
87+
}
88+
return processSignals;
89+
}
90+
91+
function deregisterSigHooks(signals) {
92+
for (const signal of Object.keys(signals)) {
93+
process.removeListener(signal, signals[signal]);
94+
}
95+
}

0 commit comments

Comments
 (0)