Merge pull request #1891 from github/henrymercer/fix-cgroup-limits
Fix reading `cgroup` limits on `ubuntu-20.04`
This commit is contained in:
commit
f3a128e0fe
13 changed files with 129 additions and 44 deletions
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
|
|
@ -73,7 +73,7 @@ jobs:
|
|||
needs: [check-codeql-versions]
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest,windows-latest,macos-latest]
|
||||
os: [ubuntu-20.04,ubuntu-22.04,windows-2019,windows-2022,macos-11,macos-12,macos-13]
|
||||
tools: ${{ fromJson(needs.check-codeql-versions.outputs.versions) }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
|
|
|||
2
lib/analyze-action.js
generated
2
lib/analyze-action.js
generated
|
|
@ -163,7 +163,7 @@ async function run() {
|
|||
const repositoryNwo = (0, repository_1.parseRepositoryNwo)(util.getRequiredEnvParam("GITHUB_REPOSITORY"));
|
||||
const gitHubVersion = await (0, api_client_1.getGitHubVersion)();
|
||||
const features = new feature_flags_1.Features(gitHubVersion, repositoryNwo, actionsUtil.getTemporaryDirectory(), logger);
|
||||
const memory = util.getMemoryFlag(actionsUtil.getOptionalInput("ram") || process.env["CODEQL_RAM"]);
|
||||
const memory = util.getMemoryFlag(actionsUtil.getOptionalInput("ram") || process.env["CODEQL_RAM"], logger);
|
||||
await runAutobuildIfLegacyGoWorkflow(config, logger);
|
||||
dbCreationTimings = await (0, analyze_1.runFinalize)(outputDir, threads, memory, config, logger, features);
|
||||
if (actionsUtil.getRequiredInput("skip-queries") !== "true") {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
2
lib/init-action.js
generated
2
lib/init-action.js
generated
|
|
@ -173,7 +173,7 @@ async function run() {
|
|||
// options at https://codeql.github.com/docs/codeql-cli/manual/database-trace-command/
|
||||
// for details.
|
||||
core.exportVariable("CODEQL_RAM", process.env["CODEQL_RAM"] ||
|
||||
(0, util_1.getMemoryFlagValue)((0, actions_util_1.getOptionalInput)("ram")).toString());
|
||||
(0, util_1.getMemoryFlagValue)((0, actions_util_1.getOptionalInput)("ram"), logger).toString());
|
||||
core.exportVariable("CODEQL_THREADS", (0, util_1.getThreadsFlagValue)((0, actions_util_1.getOptionalInput)("threads"), logger).toString());
|
||||
// Disable Kotlin extractor if feature flag set
|
||||
if (await features.getValue(feature_flags_1.Feature.DisableKotlinAnalysisEnabled)) {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
59
lib/util.js
generated
59
lib/util.js
generated
|
|
@ -59,6 +59,11 @@ exports.DEFAULT_DEBUG_DATABASE_NAME = "db";
|
|||
* The default fraction of the total RAM above 8 GB that should be reserved for the system.
|
||||
*/
|
||||
const DEFAULT_RESERVED_RAM_SCALING_FACTOR = 0.05;
|
||||
/**
|
||||
* The minimum amount of memory imposed by a cgroup limit that we will consider. Memory limits below
|
||||
* this amount are ignored.
|
||||
*/
|
||||
const MINIMUM_CGROUP_MEMORY_LIMIT_BYTES = 1024 * 1024;
|
||||
/**
|
||||
* Get the extra options for the codeql commands.
|
||||
*/
|
||||
|
|
@ -152,22 +157,44 @@ exports.getMemoryFlagValueForPlatform = getMemoryFlagValueForPlatform;
|
|||
* Get the total amount of memory available to the Action, taking into account constraints imposed
|
||||
* by cgroups on Linux.
|
||||
*/
|
||||
function getTotalMemoryAvailable() {
|
||||
function getTotalMemoryBytes(logger) {
|
||||
const limits = [os.totalmem()];
|
||||
if (os.platform() === "linux") {
|
||||
// Respect constraints imposed by Linux cgroups v1 and v2
|
||||
for (const limitFile of [
|
||||
limits.push(...[
|
||||
"/sys/fs/cgroup/memory/memory.limit_in_bytes",
|
||||
"/sys/fs/cgroup/memory.max",
|
||||
]) {
|
||||
if (fs.existsSync(limitFile)) {
|
||||
const limit = Number(fs.readFileSync(limitFile, "utf8"));
|
||||
if (Number.isInteger(limit)) {
|
||||
return limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
.map((file) => getCgroupMemoryLimitBytes(file, logger))
|
||||
.filter((limit) => limit !== undefined)
|
||||
.map((limit) => limit));
|
||||
}
|
||||
return os.totalmem();
|
||||
const limit = Math.min(...limits);
|
||||
logger.debug(`While resolving RAM, determined that the total memory available to the Action is ${limit / (1024 * 1024)} MiB.`);
|
||||
return limit;
|
||||
}
|
||||
/**
|
||||
* Gets the number of bytes of available memory specified by the cgroup limit file at the given path.
|
||||
*
|
||||
* May be greater than the total memory reported by the operating system if there is no cgroup limit.
|
||||
*/
|
||||
function getCgroupMemoryLimitBytes(limitFile, logger) {
|
||||
if (!fs.existsSync(limitFile)) {
|
||||
logger.debug(`While resolving RAM, did not find a cgroup memory limit at ${limitFile}.`);
|
||||
return undefined;
|
||||
}
|
||||
const limit = Number(fs.readFileSync(limitFile, "utf8"));
|
||||
if (!Number.isInteger(limit)) {
|
||||
logger.debug(`While resolving RAM, ignored the file ${limitFile} that may contain a cgroup memory limit ` +
|
||||
"as this file did not contain an integer.");
|
||||
return undefined;
|
||||
}
|
||||
const displayLimit = `${Math.floor(limit / (1024 * 1024))} MiB`;
|
||||
if (limit < MINIMUM_CGROUP_MEMORY_LIMIT_BYTES) {
|
||||
logger.info(`While resolving RAM, ignored a cgroup limit of ${displayLimit} in ${limitFile} as it was below ${MINIMUM_CGROUP_MEMORY_LIMIT_BYTES / (1024 * 1024)} MiB.`);
|
||||
return undefined;
|
||||
}
|
||||
logger.info(`While resolving RAM, found a cgroup limit of ${displayLimit} in ${limitFile}.`);
|
||||
return limit;
|
||||
}
|
||||
/**
|
||||
* Get the value of the codeql `--ram` flag as configured by the `ram` input.
|
||||
|
|
@ -176,8 +203,8 @@ function getTotalMemoryAvailable() {
|
|||
*
|
||||
* @returns {number} the amount of RAM to use, in megabytes
|
||||
*/
|
||||
function getMemoryFlagValue(userInput) {
|
||||
return getMemoryFlagValueForPlatform(userInput, getTotalMemoryAvailable(), process.platform);
|
||||
function getMemoryFlagValue(userInput, logger) {
|
||||
return getMemoryFlagValueForPlatform(userInput, getTotalMemoryBytes(logger), process.platform);
|
||||
}
|
||||
exports.getMemoryFlagValue = getMemoryFlagValue;
|
||||
/**
|
||||
|
|
@ -187,8 +214,8 @@ exports.getMemoryFlagValue = getMemoryFlagValue;
|
|||
*
|
||||
* @returns string
|
||||
*/
|
||||
function getMemoryFlag(userInput) {
|
||||
const megabytes = getMemoryFlagValue(userInput);
|
||||
function getMemoryFlag(userInput, logger) {
|
||||
const megabytes = getMemoryFlagValue(userInput, logger);
|
||||
return `--ram=${megabytes}`;
|
||||
}
|
||||
exports.getMemoryFlag = getMemoryFlag;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
2
lib/util.test.js
generated
2
lib/util.test.js
generated
|
|
@ -97,7 +97,7 @@ for (const { input, totalMemoryMb, platform, expectedMemoryValue, reservedPercen
|
|||
}
|
||||
(0, ava_1.default)("getMemoryFlag() throws if the ram input is < 0 or NaN", async (t) => {
|
||||
for (const input of ["-1", "hello!"]) {
|
||||
t.throws(() => util.getMemoryFlag(input));
|
||||
t.throws(() => util.getMemoryFlag(input, (0, logging_1.getRunnerLogger)(true)));
|
||||
}
|
||||
});
|
||||
(0, ava_1.default)("getAddSnippetsFlag() should return the correct flag", (t) => {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -233,6 +233,7 @@ async function run() {
|
|||
|
||||
const memory = util.getMemoryFlag(
|
||||
actionsUtil.getOptionalInput("ram") || process.env["CODEQL_RAM"],
|
||||
logger,
|
||||
);
|
||||
|
||||
await runAutobuildIfLegacyGoWorkflow(config, logger);
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ async function run() {
|
|||
core.exportVariable(
|
||||
"CODEQL_RAM",
|
||||
process.env["CODEQL_RAM"] ||
|
||||
getMemoryFlagValue(getOptionalInput("ram")).toString(),
|
||||
getMemoryFlagValue(getOptionalInput("ram"), logger).toString(),
|
||||
);
|
||||
core.exportVariable(
|
||||
"CODEQL_THREADS",
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ for (const {
|
|||
|
||||
test("getMemoryFlag() throws if the ram input is < 0 or NaN", async (t) => {
|
||||
for (const input of ["-1", "hello!"]) {
|
||||
t.throws(() => util.getMemoryFlag(input));
|
||||
t.throws(() => util.getMemoryFlag(input, getRunnerLogger(true)));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
93
src/util.ts
93
src/util.ts
|
|
@ -42,6 +42,12 @@ export const DEFAULT_DEBUG_DATABASE_NAME = "db";
|
|||
*/
|
||||
const DEFAULT_RESERVED_RAM_SCALING_FACTOR = 0.05;
|
||||
|
||||
/**
|
||||
* The minimum amount of memory imposed by a cgroup limit that we will consider. Memory limits below
|
||||
* this amount are ignored.
|
||||
*/
|
||||
const MINIMUM_CGROUP_MEMORY_LIMIT_BYTES = 1024 * 1024;
|
||||
|
||||
export interface SarifFile {
|
||||
version?: string | null;
|
||||
runs: SarifRun[];
|
||||
|
|
@ -215,22 +221,67 @@ export function getMemoryFlagValueForPlatform(
|
|||
* Get the total amount of memory available to the Action, taking into account constraints imposed
|
||||
* by cgroups on Linux.
|
||||
*/
|
||||
function getTotalMemoryAvailable(): number {
|
||||
function getTotalMemoryBytes(logger: Logger): number {
|
||||
const limits = [os.totalmem()];
|
||||
if (os.platform() === "linux") {
|
||||
// Respect constraints imposed by Linux cgroups v1 and v2
|
||||
for (const limitFile of [
|
||||
"/sys/fs/cgroup/memory/memory.limit_in_bytes",
|
||||
"/sys/fs/cgroup/memory.max",
|
||||
]) {
|
||||
if (fs.existsSync(limitFile)) {
|
||||
const limit = Number(fs.readFileSync(limitFile, "utf8"));
|
||||
if (Number.isInteger(limit)) {
|
||||
return limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
limits.push(
|
||||
...[
|
||||
"/sys/fs/cgroup/memory/memory.limit_in_bytes",
|
||||
"/sys/fs/cgroup/memory.max",
|
||||
]
|
||||
.map((file) => getCgroupMemoryLimitBytes(file, logger))
|
||||
.filter((limit) => limit !== undefined)
|
||||
.map((limit) => limit as number),
|
||||
);
|
||||
}
|
||||
return os.totalmem();
|
||||
const limit = Math.min(...limits);
|
||||
logger.debug(
|
||||
`While resolving RAM, determined that the total memory available to the Action is ${
|
||||
limit / (1024 * 1024)
|
||||
} MiB.`,
|
||||
);
|
||||
return limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of bytes of available memory specified by the cgroup limit file at the given path.
|
||||
*
|
||||
* May be greater than the total memory reported by the operating system if there is no cgroup limit.
|
||||
*/
|
||||
function getCgroupMemoryLimitBytes(
|
||||
limitFile: string,
|
||||
logger: Logger,
|
||||
): number | undefined {
|
||||
if (!fs.existsSync(limitFile)) {
|
||||
logger.debug(
|
||||
`While resolving RAM, did not find a cgroup memory limit at ${limitFile}.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const limit = Number(fs.readFileSync(limitFile, "utf8"));
|
||||
if (!Number.isInteger(limit)) {
|
||||
logger.debug(
|
||||
`While resolving RAM, ignored the file ${limitFile} that may contain a cgroup memory limit ` +
|
||||
"as this file did not contain an integer.",
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const displayLimit = `${Math.floor(limit / (1024 * 1024))} MiB`;
|
||||
if (limit < MINIMUM_CGROUP_MEMORY_LIMIT_BYTES) {
|
||||
logger.info(
|
||||
`While resolving RAM, ignored a cgroup limit of ${displayLimit} in ${limitFile} as it was below ${
|
||||
MINIMUM_CGROUP_MEMORY_LIMIT_BYTES / (1024 * 1024)
|
||||
} MiB.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`While resolving RAM, found a cgroup limit of ${displayLimit} in ${limitFile}.`,
|
||||
);
|
||||
return limit;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -240,10 +291,13 @@ function getTotalMemoryAvailable(): number {
|
|||
*
|
||||
* @returns {number} the amount of RAM to use, in megabytes
|
||||
*/
|
||||
export function getMemoryFlagValue(userInput: string | undefined): number {
|
||||
export function getMemoryFlagValue(
|
||||
userInput: string | undefined,
|
||||
logger: Logger,
|
||||
): number {
|
||||
return getMemoryFlagValueForPlatform(
|
||||
userInput,
|
||||
getTotalMemoryAvailable(),
|
||||
getTotalMemoryBytes(logger),
|
||||
process.platform,
|
||||
);
|
||||
}
|
||||
|
|
@ -255,8 +309,11 @@ export function getMemoryFlagValue(userInput: string | undefined): number {
|
|||
*
|
||||
* @returns string
|
||||
*/
|
||||
export function getMemoryFlag(userInput: string | undefined): string {
|
||||
const megabytes = getMemoryFlagValue(userInput);
|
||||
export function getMemoryFlag(
|
||||
userInput: string | undefined,
|
||||
logger: Logger,
|
||||
): string {
|
||||
const megabytes = getMemoryFlagValue(userInput, logger);
|
||||
return `--ram=${megabytes}`;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue