Skip to content

Commit

Permalink
Fix profiling for 'ui5 serve'
Browse files Browse the repository at this point in the history
  • Loading branch information
RandomByte committed Dec 3, 2024
1 parent 426d3f6 commit b1dae76
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 24 deletions.
14 changes: 10 additions & 4 deletions bin/ui5.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,19 @@ const ui5 = {
},

async invokeCLI(pkg) {
let profile;
if (process.env.UI5_CLI_PROFILE) {
const profile = await import("../lib/utils/profile.js");
profile = await import("../lib/utils/profile.js");
await profile.start();
}
const {default: cli} = await import("../lib/cli/cli.js");
await cli(pkg);
const {command} = await cli(pkg);

// Stop profiling after CLI finished execution
// Except for "serve" command, which continues running and only stops on sigint (see profile.js)
if (profile && command !== "serve") {
await profile.stop();
}
},

async main() {
Expand All @@ -105,8 +112,7 @@ const ui5 = {
} else {
const localInstallationInvoked = await ui5.invokeLocalInstallation(pkg);
if (!localInstallationInvoked) {
const exitCode = await ui5.invokeCLI(pkg);
process.exit(exitCode);
await ui5.invokeCLI(pkg);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/cli/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,6 @@ export default function(cli) {
process.stderr.write(chalk.dim(`See 'ui5 --help'`));
process.stderr.write("\n");
}
process.exit(1);
});
}
20 changes: 4 additions & 16 deletions lib/cli/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,8 @@ export default async (pkg) => {
// Format terminal output to full available width
cli.wrap(cli.terminalWidth());

let exitCode = 0;
try {
// Call parse to run yargs
await cli.parse();
} catch (err) {
// Error is already handled via .fail callback in ./base.js
exitCode = 1;
} finally {
// Stop profiling after CLI finished execution
if (process.env.UI5_CLI_PROFILE) {
const profile = await import("../utils/profile.js");
await profile.stop();
}
}

return exitCode;
const {_} = await cli.argv;
return {
command: _[0]
};
};
58 changes: 54 additions & 4 deletions lib/utils/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {writeFile} from "node:fs/promises";
import {Session} from "node:inspector";

let session;
let processSignals;

export async function start() {
if (session) {
Expand All @@ -11,25 +12,44 @@ export async function start() {
session.connect();
await new Promise((resolve) => {
session.post("Profiler.enable", () => {
console.log(`Recording CPU profile...`);

Check failure on line 15 in lib/utils/profile.js

View workflow job for this annotation

GitHub Actions / General checks, tests and coverage reporting

Unexpected console statement
session.post("Profiler.start", () => {
processSignals = registerSigHooks();
resolve();
});
});
});
}

async function writeProfile(profile) {
const d = new Date();
const timestamp =
`${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()}_${d.getHours()}-${d.getMinutes()}-${d.getSeconds()}`;
const formatter = new Intl.DateTimeFormat("en-GB", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
const dateParts = Object.create(null);
const parts = formatter.formatToParts(new Date());
parts.forEach((p) => {
dateParts[p.type] = p.value;
});

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

Check failure on line 41 in lib/utils/profile.js

View workflow job for this annotation

GitHub Actions / General checks, tests and coverage reporting

Unexpected console statement
await writeFile(fileName, JSON.stringify(profile));
}

export async function stop() {
if (!session) {
return;
}
if (processSignals) {
deregisterSigHooks(processSignals);
processSignals = null;
}
const profile = await new Promise((resolve) => {
session.post("Profiler.stop", (err, {profile}) => {
if (err) {
Expand All @@ -38,8 +58,38 @@ export async function stop() {
resolve(profile);
}
});
session = null;
});
if (profile) {
await writeProfile(profile);
}
}

function registerSigHooks() {
function createListener(exitCode) {
return function() {
// Gracefully end profiling, then exit
stop().then(() => {
process.exit(exitCode);
});
};
}

const processSignals = {
"SIGHUP": createListener(128 + 1),
"SIGINT": createListener(128 + 2),
"SIGTERM": createListener(128 + 15),
"SIGBREAK": createListener(128 + 21)
};

for (const signal of Object.keys(processSignals)) {
process.on(signal, processSignals[signal]);
}
return processSignals;
}

function deregisterSigHooks(signals) {
for (const signal of Object.keys(signals)) {
process.removeListener(signal, signals[signal]);
}
}

0 comments on commit b1dae76

Please sign in to comment.