Skip to content

Commit

Permalink
Merge pull request #62 from DeterminateSystems/dont-pull-microstackshots
Browse files Browse the repository at this point in the history
Don't collect microstackshots, or stale errors
  • Loading branch information
grahamc authored Aug 26, 2024
2 parents e8f6e8f + 21e6930 commit 817e4d4
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 28 deletions.
2 changes: 2 additions & 0 deletions dist/index.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 45 additions & 14 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

39 changes: 26 additions & 13 deletions src/backtrace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,27 @@ import { isLinux, isMacOS } from "./actions-core-platform.js";
import { stringifyError } from "./errors.js";
import * as actionsCore from "@actions/core";
import * as exec from "@actions/exec";
import { readFile, readdir } from "node:fs/promises";
import { readFile, readdir, stat } from "node:fs/promises";
import { promisify } from "node:util";
import { gzip } from "node:zlib";

export async function collectBacktraces(
prefixes: string[],
startTimestampMs: number,
): Promise<Map<string, string>> {
if (isMacOS) {
return await collectBacktracesMacOS(prefixes);
return await collectBacktracesMacOS(prefixes, startTimestampMs);
}
if (isLinux) {
return await collectBacktracesSystemd(prefixes);
return await collectBacktracesSystemd(prefixes, startTimestampMs);
}

return new Map();
}

export async function collectBacktracesMacOS(
prefixes: string[],
startTimestampMs: number,
): Promise<Map<string, string>> {
const backtraces: Map<string, string> = new Map();

Expand Down Expand Up @@ -71,19 +73,28 @@ export async function collectBacktracesMacOS(
];

for (const [source, dir] of dirs) {
const fileNames = (await readdir(dir)).filter((fileName) => {
return prefixes.some((prefix) => fileName.startsWith(prefix));
});
const fileNames = (await readdir(dir))
.filter((fileName) => {
return prefixes.some((prefix) => fileName.startsWith(prefix));
})
.filter((fileName) => {
// macOS creates .diag files periodically, which are called "microstackshots".
// We don't necessarily want those, and they're definitely not crashes.
// See: https://patents.google.com/patent/US20140237219A1/en
return !fileName.endsWith(".diag");
});

const doGzip = promisify(gzip);
for (const fileName of fileNames) {
try {
const logText = await readFile(`${dir}/${fileName}`);
const buf = await doGzip(logText);
backtraces.set(
`backtrace_value_${source}_${fileName}`,
buf.toString("base64"),
);
if ((await stat(`${dir}/${fileName}`)).ctimeMs >= startTimestampMs) {
const logText = await readFile(`${dir}/${fileName}`);
const buf = await doGzip(logText);
backtraces.set(
`backtrace_value_${source}_${fileName}`,
buf.toString("base64"),
);
}
} catch (innerError: unknown) {
backtraces.set(
`backtrace_failure_${source}_${fileName}`,
Expand All @@ -103,15 +114,17 @@ type SystemdCoreDumpInfo = {

export async function collectBacktracesSystemd(
prefixes: string[],
startTimestampMs: number,
): Promise<Map<string, string>> {
const sinceSeconds = Math.ceil((Date.now() - startTimestampMs) / 1000);
const backtraces: Map<string, string> = new Map();

const coredumps: SystemdCoreDumpInfo[] = [];

try {
const { stdout: coredumpjson } = await exec.getExecOutput(
"coredumpctl",
["--json=pretty", "list", "--since", "1 hour ago"],
["--json=pretty", "list", "--since", `${sinceSeconds} seconds ago`],
{
silent: true,
},
Expand Down
33 changes: 33 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ const FACT_NIX_STORE_CHECK_ERROR = "nix_store_check_error";
const STATE_KEY_EXECUTION_PHASE = "detsys_action_execution_phase";
const STATE_KEY_NIX_NOT_FOUND = "detsys_action_nix_not_found";
const STATE_NOT_FOUND = "not-found";
const STATE_KEY_CROSS_PHASE_ID = "detsys_cross_phase_id";
const STATE_BACKTRACE_START_TIMESTAMP = "detsys_backtrace_start_timestamp";

const DIAGNOSTIC_ENDPOINT_TIMEOUT_MS = 30_000; // 30 seconds in milliseconds
const CHECK_IN_ENDPOINT_TIMEOUT_MS = 5_000; // 5 seconds in milliseconds
Expand Down Expand Up @@ -198,6 +200,9 @@ export abstract class DetSysAction {
this.featureEventMetadata = {};
this.events = [];

this.getCrossPhaseId();
this.collectBacktraceSetup();

// JSON sent to server
/* eslint-disable camelcase */
this.facts = {
Expand Down Expand Up @@ -325,6 +330,18 @@ export abstract class DetSysAction {
);
}

// This ID will be saved in the action's state, to be persisted across phase steps
getCrossPhaseId(): string {
let crossPhaseId = actionsCore.getState(STATE_KEY_CROSS_PHASE_ID);

if (crossPhaseId === "") {
crossPhaseId = randomUUID();
actionsCore.saveState(STATE_KEY_CROSS_PHASE_ID, crossPhaseId);
}

return crossPhaseId;
}

getCorrelationHashes(): correlation.AnonymizedCorrelationHashes {
return this.identity;
}
Expand Down Expand Up @@ -765,10 +782,26 @@ export abstract class DetSysAction {
}
}

private collectBacktraceSetup(): void {
if (process.env.DETSYS_BACKTRACE_COLLECTOR === "") {
actionsCore.exportVariable(
"DETSYS_BACKTRACE_COLLECTOR",
this.getCrossPhaseId(),
);

actionsCore.saveState(STATE_BACKTRACE_START_TIMESTAMP, Date.now());
}
}

private async collectBacktraces(): Promise<void> {
try {
if (process.env.DETSYS_BACKTRACE_COLLECTOR !== this.getCrossPhaseId()) {
return;
}

const backtraces = await collectBacktraces(
this.actionOptions.binaryNamePrefixes,
parseInt(actionsCore.getState(STATE_BACKTRACE_START_TIMESTAMP)),
);
actionsCore.debug(`Backtraces identified: ${backtraces.size}`);
if (backtraces.size > 0) {
Expand Down

0 comments on commit 817e4d4

Please sign in to comment.