@flanksource/clicky-ui
Version:
Flanksource Clicky UI — React component library built on shadcn/ui with light/dark and density theming.
191 lines (190 loc) • 7.04 kB
JavaScript
const headerRe = /^"(?<name>[^"]*)"(?<rest>.*)$/;
const stateRe = /^\s*java\.lang\.Thread\.State:\s+(?<state>[A-Z_]+)(?:\s+\((?<sub>[^)]+)\))?/;
const frameRe = /^\s*at\s+(?<fn>[^\s(]+)\((?<src>[^)]+)\)\s*$/;
const srcLineRe = /^(?<file>[^:]+):(?<line>\d+)$/;
const annotationRe = /^\s*-\s+(?<kind>locked|waiting on|waiting to lock|parking to wait for)\b(?<rest>.*)$/;
function parseJvmThreadDump(text) {
const trimmed = text.trim();
if (!trimmed) return [];
const blocks = splitIntoThreadBlocks(trimmed);
const threads = [];
for (const block of blocks) {
const parsed = parseThreadBlock(block);
if (parsed) threads.push(parsed);
}
return threads;
}
function splitIntoThreadBlocks(text) {
const out = [];
let current = [];
const lines = text.split("\n");
for (const raw of lines) {
const line = raw.replace(/\r$/, "");
if (line.startsWith('"') && current.length > 0) {
out.push(current.join("\n"));
current = [line];
} else {
current.push(line);
}
}
if (current.length > 0) out.push(current.join("\n"));
return out;
}
function parseThreadBlock(block) {
var _a, _b, _c, _d;
const lines = block.split("\n");
const header = ((_a = lines[0]) == null ? void 0 : _a.trim()) ?? "";
const headerMatch = headerRe.exec(header);
if (!(headerMatch == null ? void 0 : headerMatch.groups)) return null;
const name = headerMatch.groups.name;
if (!name) return null;
const rest = headerMatch.groups.rest ?? "";
const idMatch = /#(\d+)\b/.exec(rest);
const prioMatch = /\bprio=(\d+)/.exec(rest);
const nidMatch = /\bnid=(0x[0-9a-f]+)/i.exec(rest);
const daemon = /\bdaemon\b/.test(rest);
const headerStateTrail = extractHeaderStateTrail(rest);
let rawState = headerStateTrail ?? "";
let state = normalizeJvmState(rawState);
const frames = [];
for (let i = 1; i < lines.length; i++) {
const line = lines[i];
if (line == null) continue;
if (!line.trim()) continue;
const stateMatch = stateRe.exec(line);
if (stateMatch == null ? void 0 : stateMatch.groups) {
const primary = stateMatch.groups.state;
if (!primary) continue;
const sub = stateMatch.groups.sub;
rawState = sub ? `${primary} (${sub})` : primary;
state = normalizeJvmState(primary);
continue;
}
const frameMatch = frameRe.exec(line);
if (frameMatch == null ? void 0 : frameMatch.groups) {
const functionName = frameMatch.groups.fn;
const src = frameMatch.groups.src;
if (!functionName || !src) continue;
const frame = {
functionName,
displayName: sanitizeJvmFunctionName(functionName),
kind: "frame",
runtime: isJvmRuntimeFrame(functionName),
nativeMethod: src === "Native Method"
};
const srcMatch = srcLineRe.exec(src);
if (((_b = srcMatch == null ? void 0 : srcMatch.groups) == null ? void 0 : _b.file) && srcMatch.groups.line) {
frame.file = srcMatch.groups.file;
frame.line = Number(srcMatch.groups.line);
frame.location = `${frame.file}:${frame.line}`;
} else if (src === "Native Method") {
frame.location = "Native Method";
} else {
frame.location = src;
}
frames.push(frame);
continue;
}
const annoMatch = annotationRe.exec(line);
if ((_c = annoMatch == null ? void 0 : annoMatch.groups) == null ? void 0 : _c.kind) {
const annotationKind = annoMatch.groups.kind;
const kind = mapAnnotationKind(annotationKind);
frames.push({
functionName: annotationKind,
displayName: annotationKind,
kind,
runtime: false,
nativeMethod: false,
annotationText: (annoMatch.groups.rest ?? "").trim()
});
continue;
}
}
const userFrameCount = frames.filter((f) => f.kind === "frame" && !f.runtime).length;
const topFunction = (_d = frames.find((f) => f.kind === "frame")) == null ? void 0 : _d.functionName;
const searchText = [
header,
...frames.map((f) => `${f.functionName} ${f.location ?? ""} ${f.annotationText ?? ""}`)
].join("\n").toLowerCase();
return {
id: idMatch ? Number(idMatch[1]) : deriveSyntheticId(name, rest),
name,
state: state || "unknown",
rawState: rawState || "",
daemon,
frames,
raw: block,
userFrameCount,
searchText,
...(nidMatch == null ? void 0 : nidMatch[1]) !== void 0 ? { nid: nidMatch[1] } : {},
...(prioMatch == null ? void 0 : prioMatch[1]) !== void 0 ? { priority: Number(prioMatch[1]) } : {},
...topFunction !== void 0 ? { topFunction } : {}
};
}
function extractHeaderStateTrail(rest) {
var _a, _b;
const tail = rest.match(/nid=0x[0-9a-f]+\s+(?<desc>[^[]+?)(?:\s+\[0x[0-9a-f]+\])?\s*$/i);
return (_b = (_a = tail == null ? void 0 : tail.groups) == null ? void 0 : _a.desc) == null ? void 0 : _b.trim();
}
function deriveSyntheticId(name, rest) {
const tid = /\btid=(0x[0-9a-f]+)/i.exec(rest);
if (tid == null ? void 0 : tid[1]) {
const hex = tid[1].slice(2);
const n = Number.parseInt(hex.slice(-8), 16);
if (Number.isFinite(n)) return n;
}
let h = 0;
for (let i = 0; i < name.length; i++) h = h * 31 + name.charCodeAt(i) | 0;
return Math.abs(h);
}
function mapAnnotationKind(kind) {
switch (kind) {
case "locked":
return "locked";
case "waiting on":
return "waiting_on";
case "waiting to lock":
return "waiting_to_lock";
case "parking to wait for":
return "parking";
default:
return "frame";
}
}
function countThreadsByState(threads) {
const counts = /* @__PURE__ */ new Map();
for (const t of threads) counts.set(t.state, (counts.get(t.state) ?? 0) + 1);
return counts;
}
function normalizeJvmState(value) {
if (!value) return "";
const upper = value.trim().toUpperCase();
if (upper.startsWith("RUNNABLE")) return "runnable";
if (upper.startsWith("TIMED_WAITING")) return "timed_waiting";
if (upper.startsWith("WAITING")) return "waiting";
if (upper.startsWith("BLOCKED")) return "blocked";
if (upper.startsWith("NEW")) return "new";
if (upper.startsWith("TERMINATED")) return "terminated";
const lower = value.trim().toLowerCase();
if (lower.includes("runnable")) return "runnable";
if (lower.includes("waiting on condition") || lower.includes("sleeping")) return "timed_waiting";
if (lower.includes("waiting")) return "waiting";
if (lower.includes("blocked")) return "blocked";
return lower.split(/\s+/)[0] ?? "";
}
const runtimePrefixes = ["java.", "javax.", "sun.", "jdk.", "com.sun.", "oracle.jrockit."];
function isJvmRuntimeFrame(functionName) {
return runtimePrefixes.some((p) => functionName.startsWith(p));
}
function sanitizeJvmFunctionName(functionName) {
const parts = functionName.split(".");
if (parts.length < 2) return functionName;
const method = parts[parts.length - 1];
const cls = parts[parts.length - 2];
return `${cls}.${method}`;
}
export {
countThreadsByState,
parseJvmThreadDump
};
//# sourceMappingURL=jvm-stacktrace.js.map