mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
191 lines
6.0 KiB
TypeScript
191 lines
6.0 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { readdirSync, readFileSync } from "fs";
|
|
import { bunEnv, bunExe, tempDir } from "harness";
|
|
import { join } from "path";
|
|
|
|
describe.concurrent("--cpu-prof", () => {
|
|
test("generates CPU profile with default name", async () => {
|
|
using dir = tempDir("cpu-prof", {
|
|
"test.js": `
|
|
// CPU-intensive task
|
|
function fibonacci(n) {
|
|
if (n <= 1) return n;
|
|
return fibonacci(n - 1) + fibonacci(n - 2);
|
|
}
|
|
|
|
const now = performance.now();
|
|
while (now + 50 > performance.now()) {
|
|
Bun.inspect(fibonacci(20));
|
|
}
|
|
`,
|
|
});
|
|
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "--cpu-prof", "test.js"],
|
|
cwd: String(dir),
|
|
env: bunEnv,
|
|
stdout: "inherit",
|
|
stderr: "inherit",
|
|
});
|
|
|
|
const exitCode = await proc.exited;
|
|
|
|
// Check that a .cpuprofile file was created
|
|
const files = readdirSync(String(dir));
|
|
const profileFiles = files.filter(f => f.endsWith(".cpuprofile"));
|
|
|
|
expect(profileFiles.length).toBeGreaterThan(0);
|
|
expect(exitCode).toBe(0);
|
|
|
|
// Read and validate the profile
|
|
const profilePath = join(String(dir), profileFiles[0]);
|
|
const profileContent = readFileSync(profilePath, "utf-8");
|
|
const profile = JSON.parse(profileContent);
|
|
|
|
// Validate Chrome CPU Profiler format
|
|
expect(profile).toHaveProperty("nodes");
|
|
expect(profile).toHaveProperty("startTime");
|
|
expect(profile).toHaveProperty("endTime");
|
|
expect(profile).toHaveProperty("samples");
|
|
expect(profile).toHaveProperty("timeDeltas");
|
|
|
|
expect(Array.isArray(profile.nodes)).toBe(true);
|
|
expect(Array.isArray(profile.samples)).toBe(true);
|
|
expect(Array.isArray(profile.timeDeltas)).toBe(true);
|
|
|
|
// Validate root node
|
|
expect(profile.nodes.length).toBeGreaterThan(0);
|
|
const rootNode = profile.nodes[0];
|
|
expect(rootNode.id).toBe(1);
|
|
expect(rootNode.callFrame.functionName).toBe("(root)");
|
|
|
|
// Validate node structure
|
|
profile.nodes.forEach((node: any) => {
|
|
expect(node).toHaveProperty("id");
|
|
expect(node).toHaveProperty("callFrame");
|
|
expect(node).toHaveProperty("hitCount");
|
|
expect(node.callFrame).toHaveProperty("functionName");
|
|
expect(node.callFrame).toHaveProperty("scriptId");
|
|
expect(node.callFrame).toHaveProperty("url");
|
|
expect(node.callFrame).toHaveProperty("lineNumber");
|
|
expect(node.callFrame).toHaveProperty("columnNumber");
|
|
});
|
|
|
|
// Validate samples point to valid nodes
|
|
const nodeIds = new Set(profile.nodes.map((n: any) => n.id));
|
|
profile.samples.forEach((sample: number) => {
|
|
expect(nodeIds.has(sample)).toBe(true);
|
|
});
|
|
|
|
// Validate time deltas
|
|
expect(profile.timeDeltas.length).toBe(profile.samples.length);
|
|
// For very fast programs, start and end times might be equal or very close
|
|
expect(profile.startTime).toBeLessThanOrEqual(profile.endTime);
|
|
|
|
// CRITICAL: Validate timestamps are positive and in microseconds
|
|
// Chrome DevTools requires timestamps in microseconds since Unix epoch
|
|
// A valid timestamp should be > 1000000000000000 (around year 2001)
|
|
// and < 3000000000000000 (around year 2065)
|
|
expect(profile.startTime).toBeGreaterThan(1000000000000000);
|
|
expect(profile.startTime).toBeLessThan(3000000000000000);
|
|
expect(profile.endTime).toBeGreaterThan(1000000000000000);
|
|
expect(profile.endTime).toBeLessThan(3000000000000000);
|
|
});
|
|
|
|
test("--cpu-prof-name sets custom filename", async () => {
|
|
using dir = tempDir("cpu-prof-name", {
|
|
"test.js": `
|
|
function loop() {
|
|
const end = Date.now() + 32;
|
|
while (Date.now() < end) {}
|
|
}
|
|
loop();
|
|
`,
|
|
});
|
|
|
|
const customName = "my-profile.cpuprofile";
|
|
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "--cpu-prof", "--cpu-prof-name", customName, "test.js"],
|
|
cwd: String(dir),
|
|
env: bunEnv,
|
|
stdout: "inherit",
|
|
stderr: "inherit",
|
|
});
|
|
|
|
const exitCode = await proc.exited;
|
|
|
|
const files = readdirSync(String(dir));
|
|
expect(files).toContain(customName);
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("--cpu-prof-dir sets custom directory", async () => {
|
|
using dir = tempDir("cpu-prof-dir", {
|
|
"test.js": `
|
|
function loop() {
|
|
const end = Date.now() + 32;
|
|
while (Date.now() < end) {}
|
|
}
|
|
loop();
|
|
`,
|
|
"profiles": {},
|
|
});
|
|
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "--cpu-prof", "--cpu-prof-dir", "profiles", "test.js"],
|
|
cwd: String(dir),
|
|
env: bunEnv,
|
|
stdout: "inherit",
|
|
stderr: "inherit",
|
|
});
|
|
|
|
const exitCode = await proc.exited;
|
|
|
|
const profilesDir = join(String(dir), "profiles");
|
|
const files = readdirSync(profilesDir);
|
|
const profileFiles = files.filter(f => f.endsWith(".cpuprofile"));
|
|
|
|
expect(profileFiles.length).toBeGreaterThan(0);
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("profile captures function names", async () => {
|
|
using dir = tempDir("cpu-prof-functions", {
|
|
"test.js": `
|
|
function myFunction() {
|
|
let sum = 0;
|
|
for (let i = 0; i < 1000000; i++) {
|
|
sum += i;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
myFunction();
|
|
`,
|
|
});
|
|
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "--cpu-prof", "test.js"],
|
|
cwd: String(dir),
|
|
env: bunEnv,
|
|
stdout: "inherit",
|
|
stderr: "inherit",
|
|
});
|
|
|
|
const exitCode = await proc.exited;
|
|
|
|
const files = readdirSync(String(dir));
|
|
const profileFiles = files.filter(f => f.endsWith(".cpuprofile"));
|
|
expect(profileFiles.length).toBeGreaterThan(0);
|
|
|
|
const profilePath = join(String(dir), profileFiles[0]);
|
|
const profile = JSON.parse(readFileSync(profilePath, "utf-8"));
|
|
|
|
// Check that we captured some meaningful function names
|
|
const functionNames = profile.nodes.map((n: any) => n.callFrame.functionName);
|
|
expect(functionNames.some((name: string) => name !== "(root)" && name !== "(program)")).toBe(true);
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
});
|