From 65848d232bf1b2e92692e94aeae3dc1556751ff7 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 29 Jul 2026 23:42:12 -0700 Subject: [PATCH] test: measure memory via harness rss() instead of process.memoryUsage.rss() On macOS, RSS includes MADV_FREE_REUSABLE pages the process has already released, so leak tests report the high-water mark rather than what is actually retained, and the deltas drift with allocator/GC timing. This makes RSS-delta leak tests flaky on darwin. Add an rss() export to test/harness.ts that uses Bun.unsafe.memoryFootprint() (phys_footprint) on darwin and falls back to process.memoryUsage.rss() elsewhere, and switch the leak tests over to it. Test files import it from harness; spawned fixtures and inline -e scripts define the same helper locally, with a typeof Bun guard where the fixture can also run under Node. No-Verification-Needed: test-only change --- test/bundler/bun-build-api.test.ts | 10 ++++--- test/cli/hot/hot.test.ts | 4 +-- test/cli/run/cjs-fixture-leak-small.js | 8 +++-- test/cli/run/esm-bug-leak-fixture.mjs | 8 +++-- test/cli/run/esm-fixture-leak-small.mjs | 8 +++-- .../cli/run/require-cache-bug-leak-fixture.js | 8 +++-- test/cli/run/require-cache.test.ts | 28 +++++++++-------- test/harness.ts | 5 ++++ test/js/bun/archive-extract-leak-repro.ts | 9 ++++-- test/js/bun/archive.test.ts | 5 ++-- test/js/bun/css/small-list-grow.test.ts | 5 ++-- .../bun/css/token-list-backtracking.test.ts | 5 ++-- test/js/bun/glob/leak.test.ts | 20 ++++++++----- test/js/bun/http/body-leak-test-fixture.ts | 13 +++++--- test/js/bun/http/bun-serve-file.test.ts | 6 ++-- test/js/bun/http/bun-serve-static-helpers.ts | 22 +++++++------- .../bun/http/proxy-stress-memory-fixture.ts | 5 +++- test/js/bun/http/req-url-leak-fixture.js | 8 +++-- .../http/request-constructor-leak-fixture.js | 8 +++-- .../http/response-constructor-leak-fixture.js | 8 +++-- .../serve-request-extra-memory-fixture.ts | 7 ++++- ...serve-response-stream-sink-leak-fixture.ts | 7 ++++- .../http/server-fetch-string-leak-fixture.js | 8 +++-- test/js/bun/http/tls-bunfile-leak-fixture.js | 8 +++-- .../js/bun/http/tls-keepalive-leak-fixture.js | 8 +++-- test/js/bun/image/image-adversarial.test.ts | 10 +++---- test/js/bun/io/bun-write-leak-fixture.js | 10 +++++-- test/js/bun/json5/json5.test.ts | 5 ++-- .../resolve/load-file-loader-a-lot.test.ts | 6 ++-- test/js/bun/s3/bun-write-leak-fixture.js | 10 +++++-- test/js/bun/s3/s3-stream-leak-fixture.js | 12 +++++--- test/js/bun/s3/s3-text-leak-fixture.js | 10 +++++-- test/js/bun/s3/s3-write-leak-fixture.js | 10 +++++-- test/js/bun/s3/s3-writer-leak-fixture.js | 10 +++++-- test/js/bun/shell/bunshell.test.ts | 6 ++-- test/js/bun/shell/commands/seq.test.ts | 7 +++-- test/js/bun/shell/leak.test.ts | 3 +- test/js/bun/shell/shell-leak-args.test.ts | 20 ++++++------- test/js/bun/spawn/spawn-noread-leak.test.ts | 6 ++-- test/js/bun/spawn/spawn-pipe-leak.test.ts | 12 ++++---- .../spawn-stdout-iterate-leak.fixture.ts | 6 +++- .../transpiler-comma-chain-oom.test.ts | 5 ++-- test/js/bun/util/filesystem_router.test.ts | 5 ++-- test/js/bun/util/inspect-error-leak.test.js | 6 ++-- test/js/bun/util/password.test.ts | 10 ++++--- test/js/bun/util/zstd.test.ts | 4 +-- .../js/node/buffer-from-encoding-leak.test.ts | 6 ++-- ...ort-signal-leak-read-write-file-fixture.ts | 8 ++--- ...eaddirSync-recursive-error-leak-fixture.js | 9 ++++-- .../fixtures/http.compress.leak.server.ts | 10 +++++-- test/js/node/http2/node-http2-memory-leak.js | 8 +++-- .../js/node/module/node-module-module.test.js | 5 ++-- test/js/node/net/handle-leak.test.ts | 10 +++---- .../node/net/net-mongodb-pattern-leak.test.ts | 4 +-- test/js/node/net/socketaddress.spec.ts | 7 +++-- test/js/node/process/process-stdin.test.ts | 10 ++++--- .../tls/node-tls-getpeercert-leak.test.ts | 6 ++-- .../tls/node-tls-set-session-leak.fixture.ts | 8 +++-- .../node/tls/tls-connect-socket-churn.test.ts | 6 ++-- .../js/node/url/pathToFileURL-leak-fixture.js | 10 +++++-- test/js/node/vm/script-leak.test.ts | 6 ++-- test/js/node/vm/sourcetextmodule-leak.test.ts | 6 ++-- test/js/node/watch/fs.watch.test.ts | 5 ++-- .../eval-source-leak-fixture.js | 8 +++-- .../worker_threads/worker_thread_check.ts | 6 +++- test/js/node/zlib/leak.test.ts | 26 ++++++++-------- .../sql/sql-mysql-query-string-leak.test.ts | 5 ++-- test/js/sql/sql.test.ts | 10 +++---- .../express-memory-leak-fixture.mjs | 7 ++++- test/js/third_party/prisma/prisma.test.ts | 14 ++++----- test/js/web/encoding/text-decoder.test.js | 15 ++++++++-- test/js/web/fetch/abortsignal-leak-fixture.ts | 9 +++--- test/js/web/fetch/blob.test.ts | 5 ++-- test/js/web/fetch/body.test.ts | 9 +++--- .../fetch/fetch-abort-stream-leak-fixture.ts | 9 ++++-- test/js/web/fetch/fetch-backpressure.test.ts | 5 ++-- .../web/fetch/fetch-http2-adversarial.test.ts | 7 +++-- .../js/web/fetch/fetch-leak-test-fixture-2.js | 9 ++++-- .../js/web/fetch/fetch-leak-test-fixture-4.js | 6 +++- .../js/web/fetch/fetch-leak-test-fixture-5.js | 8 +++-- .../js/web/fetch/fetch-leak-test-fixture-6.js | 8 +++-- test/js/web/fetch/fetch-leak.test.ts | 30 +++++++++++-------- test/js/web/fetch/fetch-redirect.test.ts | 3 +- test/js/web/fetch/fetch.test.ts | 5 ++-- .../html/FormData-file-error-leak-fixture.ts | 8 +++-- .../js/web/request/request-clone-leak.test.ts | 10 +++---- test/js/web/streams/streams-leak.test.ts | 8 ++--- .../js/web/structured-clone-blob-file.test.ts | 6 ++-- test/js/web/structured-clone-fastpath.test.ts | 14 ++++----- .../js/web/timers/setInterval-leak-fixture.js | 7 +++-- ...tTimeout-clear-in-callback-leak-fixture.js | 8 +++-- .../web/timers/setinterval-cancel-fixture.js | 6 +++- test/js/web/websocket/websocket.test.js | 5 ++-- .../workers/message-port-closed-leak.test.ts | 10 ++++--- .../message-port-context-destroy-leak.test.ts | 5 ++-- .../workers/performance-observer-leak.test.ts | 5 ++-- test/js/workerd/html-rewriter-leak.test.ts | 3 +- test/napi/napi-app/leak-fixture.js | 7 ++++- test/napi/napi.test.ts | 5 ++-- test/regression/brotli-reset-leak.test.ts | 10 +++---- test/regression/issue/26088.test.ts | 6 ++-- test/regression/issue/28632.test.ts | 6 ++-- test/regression/issue/28756.test.ts | 5 ++-- 103 files changed, 546 insertions(+), 320 deletions(-) diff --git a/test/bundler/bun-build-api.test.ts b/test/bundler/bun-build-api.test.ts index 2b7fa720b654..e33c00657af5 100644 --- a/test/bundler/bun-build-api.test.ts +++ b/test/bundler/bun-build-api.test.ts @@ -1334,6 +1334,7 @@ test.skipIf(!isDebug && !isASAN)( const dir = tempDirWithFiles("bun-build-inline-sourcemap-leak", { "entry.ts": "export const a = 1;\n/* " + Buffer.alloc(30 * 1024 * 1024, "x").toString() + " */\n", "run.ts": ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const entry = process.argv[2]; async function build() { const res = await Bun.build({ entrypoints: [entry], sourcemap: "inline" }); @@ -1344,10 +1345,10 @@ test.skipIf(!isDebug && !isASAN)( } for (let i = 0; i < 2; i++) await build(); await settle(); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 8; i++) await build(); await settle(); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(JSON.stringify({ before, after, growth: after - before })); `, }); @@ -1412,6 +1413,7 @@ test.skip("Bun.build NumberRenamer does not leak intermediate NumberScope.name_c const dir = tempDirWithFiles("bun-build-number-renamer-leak", { "entry.js": entry, "run.ts": ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const entry = process.argv[2]; async function build() { // No identifier minification → NumberRenamer path (not MinifyRenamer). @@ -1425,10 +1427,10 @@ test.skip("Bun.build NumberRenamer does not leak intermediate NumberScope.name_c // steady-state so the measured window only reflects per-build retention. for (let i = 0; i < 2; i++) await build(); await settle(); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 20; i++) await build(); await settle(); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(JSON.stringify({ before, after, growth: after - before })); `, }); diff --git a/test/cli/hot/hot.test.ts b/test/cli/hot/hot.test.ts index 832e61e79cc8..8ab6f31dd9e6 100644 --- a/test/cli/hot/hot.test.ts +++ b/test/cli/hot/hot.test.ts @@ -701,7 +701,7 @@ it( bundleIn, `// ${long_comment} // -console.error("RSS: %s", process.memoryUsage().rss); +console.error("RSS: %s", process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint() : process.memoryUsage.rss()); throw new Error('0');`, ); await using bundler = spawn({ @@ -745,7 +745,7 @@ throw new Error('0');`, writeHotFileAtomicSync( bundleIn, `// ${long_comment} -console.error("RSS: %s", process.memoryUsage().rss); +console.error("RSS: %s", process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint() : process.memoryUsage.rss()); // ${Buffer.alloc(counter * 2, " ").toString()}throw new Error(${counter});`, ); diff --git a/test/cli/run/cjs-fixture-leak-small.js b/test/cli/run/cjs-fixture-leak-small.js index dc3fae6c5e93..0e3a23d759a4 100644 --- a/test/cli/run/cjs-fixture-leak-small.js +++ b/test/cli/run/cjs-fixture-leak-small.js @@ -2,6 +2,10 @@ const dest = require.resolve("./leak-fixture-small-ast.js"); // ASAN's quarantine retains freed allocations (default 256 MB) so RSS deltas // run far higher under bun-asan; widen the threshold to avoid false positives. const isASAN = process.execPath.includes("bun-asan"); +const rss = + process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; let gc = globalThis.gc; if (typeof Bun !== "undefined") { @@ -18,7 +22,7 @@ for (let i = 0; i < 5; i++) { require(dest); } gc(); -const baseline = process.memoryUsage.rss(); +const baseline = rss(); for (let i = 0; i < 10000; i++) { delete require.cache[dest]; @@ -27,7 +31,7 @@ for (let i = 0; i < 10000; i++) { gc(); setTimeout(() => { - let diff = process.memoryUsage.rss() - baseline; + let diff = rss() - baseline; diff = (diff / 1024 / 1024) | 0; console.log({ leaked: diff + " MB" }); if (diff > (isASAN ? 320 : 48)) { diff --git a/test/cli/run/esm-bug-leak-fixture.mjs b/test/cli/run/esm-bug-leak-fixture.mjs index 1c4d9343f757..9884aa394222 100644 --- a/test/cli/run/esm-bug-leak-fixture.mjs +++ b/test/cli/run/esm-bug-leak-fixture.mjs @@ -4,6 +4,10 @@ const dest = await import.meta.resolve("./esm-leak-fixture-large-ast.mjs"); // ASAN's quarantine retains freed allocations (default 256 MB) so RSS deltas // run far higher under bun-asan; widen the threshold to avoid false positives. const isASAN = process.execPath.includes("bun-asan"); +const rss = + process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; if (typeof Bun !== "undefined") Bun.gc(true); for (let i = 0; i < 5; i++) { @@ -11,7 +15,7 @@ for (let i = 0; i < 5; i++) { await import(dest); } if (typeof Bun !== "undefined") Bun.gc(true); -const baseline = process.memoryUsage.rss(); +const baseline = rss(); for (let i = 0; i < 50; i++) { delete require.cache[dest]; @@ -20,7 +24,7 @@ for (let i = 0; i < 50; i++) { if (typeof Bun !== "undefined") Bun.gc(true); setTimeout(() => { - let diff = process.memoryUsage.rss() - baseline; + let diff = rss() - baseline; diff = (diff / 1024 / 1024) | 0; console.log({ leaked: diff + " MB" }); if (diff > (isASAN ? 400 : 120)) { diff --git a/test/cli/run/esm-fixture-leak-small.mjs b/test/cli/run/esm-fixture-leak-small.mjs index 270efb9db3fd..71e0ad6ba9b1 100644 --- a/test/cli/run/esm-fixture-leak-small.mjs +++ b/test/cli/run/esm-fixture-leak-small.mjs @@ -4,6 +4,10 @@ const dest = require.resolve("./leak-fixture-small-ast.js"); // ASAN's quarantine retains freed allocations (default 256 MB) so RSS deltas // run far higher under bun-asan; widen the threshold to avoid false positives. const isASAN = process.execPath.includes("bun-asan"); +const rss = + process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; if (typeof Bun !== "undefined") Bun.gc(true); for (let i = 0; i < 5; i++) { @@ -11,7 +15,7 @@ for (let i = 0; i < 5; i++) { await import(dest); } if (typeof Bun !== "undefined") Bun.gc(true); -const baseline = process.memoryUsage.rss(); +const baseline = rss(); for (let i = 0; i < 100000; i++) { delete require.cache[dest]; @@ -20,7 +24,7 @@ for (let i = 0; i < 100000; i++) { if (typeof Bun !== "undefined") Bun.gc(true); setTimeout(() => { - let diff = process.memoryUsage.rss() - baseline; + let diff = rss() - baseline; diff = (diff / 1024 / 1024) | 0; console.log({ leaked: diff + " MB" }); // This test seems to be more flaky on slow filesystems. diff --git a/test/cli/run/require-cache-bug-leak-fixture.js b/test/cli/run/require-cache-bug-leak-fixture.js index bcb7fa8a07f6..e43fa3dbec92 100644 --- a/test/cli/run/require-cache-bug-leak-fixture.js +++ b/test/cli/run/require-cache-bug-leak-fixture.js @@ -2,6 +2,10 @@ const dest = require.resolve("./require-cache-bug-leak-fixture-large-ast.js"); // ASAN's quarantine retains freed allocations (default 256 MB) so RSS deltas // run far higher under bun-asan; widen the threshold to avoid false positives. const isASAN = process.execPath.includes("bun-asan"); +const rss = + process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; if (typeof Bun !== "undefined") Bun.gc(true); for (let i = 0; i < 5; i++) { @@ -9,7 +13,7 @@ for (let i = 0; i < 5; i++) { require(dest); } if (typeof Bun !== "undefined") Bun.gc(true); -const baseline = process.memoryUsage.rss(); +const baseline = rss(); for (let i = 0; i < 50; i++) { delete require.cache[dest]; @@ -18,7 +22,7 @@ for (let i = 0; i < 50; i++) { if (typeof Bun !== "undefined") Bun.gc(true); setTimeout(() => { - let diff = process.memoryUsage.rss() - baseline; + let diff = rss() - baseline; diff = (diff / 1024 / 1024) | 0; console.log({ leaked: diff + " MB" }); if (diff > (isASAN ? 400 : 120)) { diff --git a/test/cli/run/require-cache.test.ts b/test/cli/run/require-cache.test.ts index 46dca38a6f2e..1163eb9804bd 100644 --- a/test/cli/run/require-cache.test.ts +++ b/test/cli/run/require-cache.test.ts @@ -64,6 +64,7 @@ describe.concurrent("require.cache", () => { "require-cache-bug-leak-fixture.js": ` const path = require.resolve("./index.js"); const gc = global.gc || globalThis?.Bun?.gc || (() => {}); + const rss = process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const noChildren = module.children = { indexOf() { return 0; } }; // disable children tracking function bust() { const mod = require.cache[path]; @@ -79,14 +80,14 @@ describe.concurrent("require.cache", () => { bust(); } gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let i = 0; i < 500; i++) { require(path); bust(path); } gc(true); - const rss = process.memoryUsage.rss(); - const diff = rss - baseline; + const after = rss(); + const diff = after - baseline; console.log("RSS diff", (diff / 1024 / 1024) | 0, "MB"); console.log("RSS", (diff / 1024 / 1024) | 0, "MB"); if (diff > ${isASAN ? 400 : 100} * 1024 * 1024) { @@ -122,6 +123,7 @@ describe.concurrent("require.cache", () => { "require-cache-bug-leak-fixture.js": ` const path = require.resolve("./index.js"); const gc = global.gc || globalThis?.Bun?.gc || (() => {}); + const rss = process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; function bust() { delete require.cache[path]; } @@ -131,14 +133,14 @@ describe.concurrent("require.cache", () => { bust(); } gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let i = 0; i < 400; i++) { await import(path); bust(path); } gc(true); - const rss = process.memoryUsage.rss(); - const diff = rss - baseline; + const after = rss(); + const diff = after - baseline; console.log("RSS diff", (diff / 1024 / 1024) | 0, "MB"); console.log("RSS", (diff / 1024 / 1024) | 0, "MB"); if (diff > ${isASAN ? 320 : 64} * 1024 * 1024) { @@ -171,6 +173,7 @@ describe.concurrent("require.cache", () => { "require-cache-bug-leak-fixture.js": ` const path = require.resolve("./index.js"); const gc = global.gc || globalThis?.Bun?.gc || (() => {}); + const rss = process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; function bust() { delete require.cache[path]; } @@ -180,14 +183,14 @@ describe.concurrent("require.cache", () => { bust(); } gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let i = 0; i < 250; i++) { await import(path); bust(path); } gc(true); - const rss = process.memoryUsage.rss(); - const diff = rss - baseline; + const after = rss(); + const diff = after - baseline; console.log("RSS diff", (diff / 1024 / 1024) | 0, "MB"); console.log("RSS", (diff / 1024 / 1024) | 0, "MB"); if (diff > ${isASAN ? 320 : 64} * 1024 * 1024) { @@ -231,6 +234,7 @@ describe.concurrent("require.cache", () => { "require-cache-bug-leak-fixture.js": ` const path = require.resolve("./index.js"); const gc = global.gc || globalThis?.Bun?.gc || (() => {}); + const rss = process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; function bust() { const mod = require.cache[path]; if (mod) { @@ -245,14 +249,14 @@ describe.concurrent("require.cache", () => { bust(); } gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let i = 0; i < 400; i++) { require(path); bust(path); } gc(true); - const rss = process.memoryUsage.rss(); - const diff = rss - baseline; + const after = rss(); + const diff = after - baseline; console.log("RSS diff", (diff / 1024 / 1024) | 0, "MB"); console.log("RSS", (diff / 1024 / 1024) | 0, "MB"); if (diff > ${isASAN ? 320 : 64} * 1024 * 1024) { diff --git a/test/harness.ts b/test/harness.ts index 40917f5ffdb4..43537aaccc9d 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -2221,3 +2221,8 @@ export function compileFixture(sourcePath: string, options: { flags?: string[] } compiledFixtures.set(cacheKey, outPath); return outPath; } + +export const rss: () => number = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? (Bun.unsafe.memoryFootprint as () => number) + : process.memoryUsage.rss; diff --git a/test/js/bun/archive-extract-leak-repro.ts b/test/js/bun/archive-extract-leak-repro.ts index acdd63c2a918..411558294a2b 100644 --- a/test/js/bun/archive-extract-leak-repro.ts +++ b/test/js/bun/archive-extract-leak-repro.ts @@ -5,6 +5,11 @@ import { mkdtempSync, rmSync } from "fs"; import { tmpdir } from "os"; import { join } from "path"; +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; + const dir = mkdtempSync(join(tmpdir(), "archive-leak-")); const files = { @@ -26,8 +31,8 @@ for (let round = 0; round < 20; round++) { } Bun.gc(true); - const rss = process.memoryUsage.rss(); - console.log(`Round ${round + 1}: RSS = ${formatMB(rss)}`); + const rssNow = rss(); + console.log(`Round ${round + 1}: RSS = ${formatMB(rssNow)}`); } rmSync(dir, { recursive: true }); diff --git a/test/js/bun/archive.test.ts b/test/js/bun/archive.test.ts index 2ee76481bd67..1ec6edbbd73c 100644 --- a/test/js/bun/archive.test.ts +++ b/test/js/bun/archive.test.ts @@ -653,6 +653,7 @@ describe("Bun.Archive", () => { // iterations would leak ~190MB. A 64MB threshold comfortably separates // "fixed" (stable RSS) from "leaking". const code = /* ts */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; function ustarHeader(name, size) { const h = Buffer.alloc(512); h.write(name, 0, 100, "utf8"); @@ -691,10 +692,10 @@ describe("Bun.Archive", () => { for (let i = 0; i < 100; i++) await once(); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 1500; i++) await once(); Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; console.log("RSS growth: " + growthMB.toFixed(1) + " MB"); if (growthMB > 64) throw new Error("leaked " + growthMB.toFixed(1) + " MB"); `; diff --git a/test/js/bun/css/small-list-grow.test.ts b/test/js/bun/css/small-list-grow.test.ts index 5cbc471b2935..049c5e0953b9 100644 --- a/test/js/bun/css/small-list-grow.test.ts +++ b/test/js/bun/css/small-list-grow.test.ts @@ -36,7 +36,8 @@ test("CSS bundler doesn't over-allocate SmallList when growing past the first he const lastSel = `.r${numRules - 1}-s${selectorsPerRule - 1}`; const fixture = /* js */ ` - const baseline = process.memoryUsage.rss(); + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; + const baseline = rss(); const result = await Bun.build({ entrypoints: [${JSON.stringify(path.join(String(dir), "wide.css"))}], }); @@ -46,7 +47,7 @@ test("CSS bundler doesn't over-allocate SmallList when growing past the first he } const out = await result.outputs[0].text(); Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(JSON.stringify({ deltaMB: (after - baseline) / 1024 / 1024, hasFirst: out.includes(${JSON.stringify(firstSel)}), diff --git a/test/js/bun/css/token-list-backtracking.test.ts b/test/js/bun/css/token-list-backtracking.test.ts index 7f75ec99c47a..716c5f240783 100644 --- a/test/js/bun/css/token-list-backtracking.test.ts +++ b/test/js/bun/css/token-list-backtracking.test.ts @@ -28,11 +28,12 @@ function spawnMinify(css: string) { bunExe(), "-e", `const c = require("bun:internal-for-testing").cssInternals; + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const css = ${JSON.stringify(css)}; - const rssBefore = process.memoryUsage.rss(); + const rssBefore = rss(); let threw = false; try { c.minifyTest(css, ""); } catch { threw = true; } - const deltaMB = (process.memoryUsage.rss() - rssBefore) / 1024 / 1024; + const deltaMB = (rss() - rssBefore) / 1024 / 1024; if (deltaMB > 256) throw new Error("memory grew by " + deltaMB.toFixed(0) + "MB"); console.log("done threw=" + threw);`, ], diff --git a/test/js/bun/glob/leak.test.ts b/test/js/bun/glob/leak.test.ts index b9795bf80e70..a82f5e619a9e 100644 --- a/test/js/bun/glob/leak.test.ts +++ b/test/js/bun/glob/leak.test.ts @@ -24,13 +24,14 @@ describe("leaks", () => { await run( String(dir), /* ts */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const glob = new Bun.Glob("**/*"); for (let i = 0; i < 1000; i++) Array.from(glob.scanSync()); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 100000; i++) Array.from(glob.scanSync()); Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; if (growthMB > ${thresholdMB}) throw new Error("leaked " + growthMB.toFixed(2) + "MB"); `, ); @@ -45,13 +46,14 @@ describe("leaks", () => { await run( String(dir), /* ts */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const glob = new Bun.Glob("**/*"); for (let i = 0; i < 1000; i++) await Array.fromAsync(glob.scan()); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 100000; i++) await Array.fromAsync(glob.scan()); Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; if (growthMB > ${thresholdMB}) throw new Error("leaked " + growthMB.toFixed(2) + "MB"); `, ); @@ -66,13 +68,14 @@ describe("leaks", () => { await run( String(dir), /* ts */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const glob = new Bun.Glob("*.txt"); for (let i = 0; i < 1000; i++) Array.from(glob.scanSync()); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 100000; i++) Array.from(glob.scanSync()); Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; if (growthMB > ${thresholdMB}) throw new Error("leaked " + growthMB.toFixed(2) + "MB"); `, ); @@ -87,13 +90,14 @@ describe("leaks", () => { await run( String(dir), /* ts */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const glob = new Bun.Glob("*.txt"); for (let i = 0; i < 1000; i++) await Array.fromAsync(glob.scan()); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 100000; i++) await Array.fromAsync(glob.scan()); Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; if (growthMB > ${thresholdMB}) throw new Error("leaked " + growthMB.toFixed(2) + "MB"); `, ); diff --git a/test/js/bun/http/body-leak-test-fixture.ts b/test/js/bun/http/body-leak-test-fixture.ts index 988039ddf04f..36b992b4311e 100644 --- a/test/js/bun/http/body-leak-test-fixture.ts +++ b/test/js/bun/http/body-leak-test-fixture.ts @@ -1,9 +1,14 @@ // RSS on darwin keeps freed-but-mapped (MADV_FREE_REUSABLE) pages, so it only reports // the high-water mark; memoryFootprint() is what the process actually retains. +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; + async function memoryUsage() { Bun.gc(true); await Bun.sleep(50); - return Bun.unsafe.memoryFootprint() ?? process.memoryUsage.rss(); + return rss(); } const server = Bun.serve({ @@ -62,11 +67,11 @@ process?.send?.(server.url.href); if (!process.send) { setInterval(() => { Bun.gc(true); - const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; - console.log("RSS", rss, "MB"); + const rssMB = (rss() / 1024 / 1024) | 0; + console.log("RSS", rssMB, "MB"); console.log("Active requests", server.pendingRequests); - if (rss > 1024) { + if (rssMB > 1024) { require("v8").writeHeapSnapshot("/tmp/heap.heapsnapshot"); } }, 5000); diff --git a/test/js/bun/http/bun-serve-file.test.ts b/test/js/bun/http/bun-serve-file.test.ts index 9a9f7d971208..6b9fa842266d 100644 --- a/test/js/bun/http/bun-serve-file.test.ts +++ b/test/js/bun/http/bun-serve-file.test.ts @@ -1,6 +1,6 @@ import type { Server } from "bun"; import { afterAll, beforeAll, describe, expect, it, mock, test } from "bun:test"; -import { bunEnv, bunExe, isASAN, isWindows, rmScope, tempDir, tempDirWithFiles } from "harness"; +import { bunEnv, bunExe, isASAN, isWindows, rmScope, rss, tempDir, tempDirWithFiles } from "harness"; import { mkfifo } from "mkfifo"; import { unlinkSync } from "node:fs"; import { join } from "node:path"; @@ -619,7 +619,7 @@ describe("Bun.file in serve routes", () => { } Bun.gc(true); - const baseline = (process.memoryUsage.rss() / 1024 / 1024) | 0; + const baseline = (rss() / 1024 / 1024) | 0; // Make many requests to large file for (let i = 0; i < 50; i++) { @@ -629,7 +629,7 @@ describe("Bun.file in serve routes", () => { } Bun.gc(true); - const final = (process.memoryUsage.rss() / 1024 / 1024) | 0; + const final = (rss() / 1024 / 1024) | 0; const delta = final - baseline; // ASAN's quarantine retains freed allocations (default 256 MB) so RSS diff --git a/test/js/bun/http/bun-serve-static-helpers.ts b/test/js/bun/http/bun-serve-static-helpers.ts index 81fabaaaa7dd..6c75cf1437f9 100644 --- a/test/js/bun/http/bun-serve-static-helpers.ts +++ b/test/js/bun/http/bun-serve-static-helpers.ts @@ -1,6 +1,6 @@ -import { expect } from "bun:test"; import type { Server } from "bun"; -import { fillRepeating, isASAN, isDebug, isWindows } from "harness"; +import { expect } from "bun:test"; +import { fillRepeating, isASAN, isDebug, isWindows, rss } from "harness"; // /big is 4MB so that the first send() cannot drain the body in one write: the // static-route sender has to take the to_async + on_writable backpressure loop @@ -93,25 +93,25 @@ export async function runStress( } Bun.gc(true); - const baseline = (process.memoryUsage.rss() / 1024 / 1024) | 0; + const baseline = (rss() / 1024 / 1024) | 0; let lastRSS = baseline; console.log("Start RSS", baseline); for (let i = 0; i < iterations; i++) { await iterate(); - const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; - if (lastRSS + 50 < rss) { - console.log("RSS Growth", rss - lastRSS); + const rssMB = (rss() / 1024 / 1024) | 0; + if (lastRSS + 50 < rssMB) { + console.log("RSS Growth", rssMB - lastRSS); } - lastRSS = rss; + lastRSS = rssMB; } Bun.gc(true); - const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; - const delta = rss - baseline; - console.log("Final RSS", rss); + const finalRSS = (rss() / 1024 / 1024) | 0; + const delta = finalRSS - baseline; + console.log("Final RSS", finalRSS); console.log("Delta RSS", delta); // ASAN's shadow memory + quarantine raise the absolute RSS floor. - expect(rss).toBeLessThan(isASAN ? 6144 : 4092); + expect(finalRSS).toBeLessThan(isASAN ? 6144 : 4092); if (isASAN || isDebug) { // With the reduced iteration count the absolute ceiling alone would miss a // per-request body leak on the first /big case (4 iter * 64 * 4MB = 1GB is diff --git a/test/js/bun/http/proxy-stress-memory-fixture.ts b/test/js/bun/http/proxy-stress-memory-fixture.ts index 6dc6fc0c8cde..70b2bb02eb69 100644 --- a/test/js/bun/http/proxy-stress-memory-fixture.ts +++ b/test/js/bun/http/proxy-stress-memory-fixture.ts @@ -108,7 +108,10 @@ function recordError(i: number, e: unknown) { } } -const rss = () => process.memoryUsage.rss(); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; async function one(i: number): Promise { const path = mode === "redirect" ? "/start" : `/${i}`; diff --git a/test/js/bun/http/req-url-leak-fixture.js b/test/js/bun/http/req-url-leak-fixture.js index 25dbbdf06173..608e633a931a 100644 --- a/test/js/bun/http/req-url-leak-fixture.js +++ b/test/js/bun/http/req-url-leak-fixture.js @@ -1,3 +1,7 @@ +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; let existingPromise = null; const server = Bun.serve({ port: 0, @@ -10,12 +14,12 @@ const server = Bun.serve({ if (existingPromise === waitedUpon) { existingPromise = null; } - return new Response(process.memoryUsage.rss().toString()); + return new Response(rss().toString()); }, }); setInterval(() => { - console.log("RSS", (process.memoryUsage.rss() / 1024 / 1024) | 0); + console.log("RSS", (rss() / 1024 / 1024) | 0); }, 1000); console.log("Server started on", server.url.href); diff --git a/test/js/bun/http/request-constructor-leak-fixture.js b/test/js/bun/http/request-constructor-leak-fixture.js index eff0eb03ccca..a1fc1d6488e5 100644 --- a/test/js/bun/http/request-constructor-leak-fixture.js +++ b/test/js/bun/http/request-constructor-leak-fixture.js @@ -4,6 +4,10 @@ // - the headers leak // - the url leaks // +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const buf = new Uint8Array(1024 * 1024 * 16); for (var i = 0; i < 1000; i++) { @@ -21,7 +25,7 @@ for (var i = 0; i < 1000; i++) { } catch (e) {} } Bun.gc(true); -console.log("RSS:", (process.memoryUsage().rss / 1024 / 1024) | 0, "MB"); -if (process.memoryUsage.rss() > 1024 * 1024 * 1024) { +console.log("RSS:", (rss() / 1024 / 1024) | 0, "MB"); +if (rss() > 1024 * 1024 * 1024) { process.exit(1); } diff --git a/test/js/bun/http/response-constructor-leak-fixture.js b/test/js/bun/http/response-constructor-leak-fixture.js index c61544cd95f7..ab8ae337f3b2 100644 --- a/test/js/bun/http/response-constructor-leak-fixture.js +++ b/test/js/bun/http/response-constructor-leak-fixture.js @@ -4,6 +4,10 @@ // - the headers leak // +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const buf = new Uint8Array(1024 * 1024 * 32); for (var i = 0; i < 1000; i++) { @@ -22,7 +26,7 @@ for (var i = 0; i < 1000; i++) { } catch (e) {} } Bun.gc(true); -console.log("RSS:", (process.memoryUsage().rss / 1024 / 1024) | 0, "MB"); -if (process.memoryUsage.rss() > 1024 * 1024 * 1024) { +console.log("RSS:", (rss() / 1024 / 1024) | 0, "MB"); +if (rss() > 1024 * 1024 * 1024) { process.exit(1); } diff --git a/test/js/bun/http/serve-request-extra-memory-fixture.ts b/test/js/bun/http/serve-request-extra-memory-fixture.ts index 46ec2a224cc2..861b3a0e699e 100644 --- a/test/js/bun/http/serve-request-extra-memory-fixture.ts +++ b/test/js/bun/http/serve-request-extra-memory-fixture.ts @@ -1,10 +1,15 @@ +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; + const server = Bun.serve({ port: 0, fetch(req) { if (req.url.endsWith("/report")) { return Response.json({ external: process.memoryUsage().external, - rss: process.memoryUsage.rss(), + rss: rss(), }); } return new Response("Hello, World!"); diff --git a/test/js/bun/http/serve-response-stream-sink-leak-fixture.ts b/test/js/bun/http/serve-response-stream-sink-leak-fixture.ts index 6d265f1909ad..7ae3eed14572 100644 --- a/test/js/bun/http/serve-response-stream-sink-leak-fixture.ts +++ b/test/js/bun/http/serve-response-stream-sink-leak-fixture.ts @@ -4,6 +4,11 @@ // heap HTTPServerWritable/JSSink plus the promise plumbing used to wait for // the close, and all of it must be released when end() completes the // response; otherwise each request leaks the struct plus its buffer. +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; + let controller: any; let pulled: { promise: Promise; resolve: () => void }; @@ -43,7 +48,7 @@ async function settledRss() { for (let i = 0; i < 5; i++) { Bun.gc(true); await Bun.sleep(50); - min = Math.min(min, process.memoryUsage.rss()); + min = Math.min(min, rss()); } return min; } diff --git a/test/js/bun/http/server-fetch-string-leak-fixture.js b/test/js/bun/http/server-fetch-string-leak-fixture.js index fb94bc788af0..2792a06cad6e 100644 --- a/test/js/bun/http/server-fetch-string-leak-fixture.js +++ b/test/js/bun/http/server-fetch-string-leak-fixture.js @@ -3,6 +3,10 @@ // bun.String. Both code paths are exercised: // - absolute URL with a hostname (dupe branch) // - relative path with no hostname (append-to-base-url branch) +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const isASAN = process.execPath.includes("bun-asan"); using server = Bun.serve({ port: 0, @@ -20,7 +24,7 @@ for (let i = 0; i < 64; i++) { await server.fetch(longPath); } Bun.gc(true); -const before = process.memoryUsage.rss(); +const before = rss(); // Under ASAN the quarantine (default 256 MB) retains freed URL buffers, so the // no-leak baseline alone is ~256 MB. Run more iterations so the leak signature @@ -31,7 +35,7 @@ for (let i = 0; i < iterations; i++) { await server.fetch(longPath); } Bun.gc(true); -const after = process.memoryUsage.rss(); +const after = rss(); const deltaMB = (after - before) / 1024 / 1024; console.log("RSS delta:", deltaMB.toFixed(1), "MB"); diff --git a/test/js/bun/http/tls-bunfile-leak-fixture.js b/test/js/bun/http/tls-bunfile-leak-fixture.js index 346ad5e9f8d9..d6c4fb1f45f8 100644 --- a/test/js/bun/http/tls-bunfile-leak-fixture.js +++ b/test/js/bun/http/tls-bunfile-leak-fixture.js @@ -11,6 +11,10 @@ // // Env: TLS_CERT_PATH, TLS_KEY_PATH - paths to (large) PEM files +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const certPath = process.env.TLS_CERT_PATH; const keyPath = process.env.TLS_KEY_PATH; const iterations = parseInt(process.env.ITERATIONS || "100", 10); @@ -35,11 +39,11 @@ function iterate() { for (let i = 0; i < warmup; i++) iterate(); Bun.gc(true); -const baselineRss = process.memoryUsage.rss(); +const baselineRss = rss(); for (let i = 0; i < iterations; i++) iterate(); Bun.gc(true); -const finalRss = process.memoryUsage.rss(); +const finalRss = rss(); const growthMB = (finalRss - baselineRss) / (1024 * 1024); diff --git a/test/js/bun/http/tls-keepalive-leak-fixture.js b/test/js/bun/http/tls-keepalive-leak-fixture.js index aa485bc97fd5..9b1af820f44b 100644 --- a/test/js/bun/http/tls-keepalive-leak-fixture.js +++ b/test/js/bun/http/tls-keepalive-leak-fixture.js @@ -7,6 +7,10 @@ // WARMUP_REQUESTS - warmup iterations to stabilize the RSS baseline (default 20000) // MODE - "same" (same TLS config) or "distinct" (unique configs) +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const cert = process.env.TLS_CERT; const key = process.env.TLS_KEY; const numRequests = parseInt(process.env.NUM_REQUESTS || "50000", 10); @@ -36,7 +40,7 @@ for (let i = 0; i < warmupRequests; i++) { }).then(r => r.text()); } Bun.gc(true); -const baselineRss = process.memoryUsage.rss(); +const baselineRss = rss(); const requests = []; if (mode === "same") { @@ -61,7 +65,7 @@ await Bun.sleep(100); Bun.gc(true); await Bun.sleep(100); Bun.gc(true); -const finalRss = process.memoryUsage.rss(); +const finalRss = rss(); const growthMB = (finalRss - baselineRss) / (1024 * 1024); // Output as JSON for the parent test to parse diff --git a/test/js/bun/image/image-adversarial.test.ts b/test/js/bun/image/image-adversarial.test.ts index f8d226d59fe2..220e7df1beaf 100644 --- a/test/js/bun/image/image-adversarial.test.ts +++ b/test/js/bun/image/image-adversarial.test.ts @@ -10,7 +10,7 @@ // Kept in its own file so the happy-path image.test.ts stays readable. import { afterEach, describe, expect, test } from "bun:test"; -import { gcTick, isASAN, tempDir } from "harness"; +import { gcTick, isASAN, rss, tempDir } from "harness"; import { join } from "node:path"; import zlib from "node:zlib"; @@ -547,13 +547,13 @@ describe("memory hygiene", () => { if ((i & 127) === 0) gcTick(); } gcTick(); - const before = process.memoryUsage().rss; + const before = rss(); for (let i = 0; i < run; i++) { await body(); if ((i & 127) === 0) gcTick(); } gcTick(); - return process.memoryUsage().rss - before; + return rss() - before; } test("decode/encode cycles plateau (no per-call leak after warmup)", async () => { @@ -571,7 +571,7 @@ describe("memory hygiene", () => { }); test("constructor with throwing getter cleans up under repetition", () => { - const before = process.memoryUsage().rss; + const before = rss(); for (let i = 0; i < 10_000; i++) { try { new Bun.Image(tinyPng, { @@ -583,7 +583,7 @@ describe("memory hygiene", () => { if ((i & 1023) === 0) gcTick(); } gcTick(); - expect(process.memoryUsage().rss - before).toBeLessThan((isASAN ? 256 : 64) * 1024 * 1024); + expect(rss() - before).toBeLessThan((isASAN ? 256 : 64) * 1024 * 1024); }); }); diff --git a/test/js/bun/io/bun-write-leak-fixture.js b/test/js/bun/io/bun-write-leak-fixture.js index bfe9cc510459..f32518807adc 100644 --- a/test/js/bun/io/bun-write-leak-fixture.js +++ b/test/js/bun/io/bun-write-leak-fixture.js @@ -5,15 +5,19 @@ const isASAN = process.execPath.includes("bun-asan"); const MAX_ALLOWED_MEMORY_USAGE = isASAN ? 768 : 256; const dest = process.argv.at(-1); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; async function run(inputType) { for (let i = 0; i < 100; i++) { const largeFile = inputType; await Bun.write(dest, largeFile); Bun.gc(true); - const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; - console.log("Memory usage:", rss, "MB"); - if (rss > MAX_ALLOWED_MEMORY_USAGE) { + const rssMB = (rss() / 1024 / 1024) | 0; + console.log("Memory usage:", rssMB, "MB"); + if (rssMB > MAX_ALLOWED_MEMORY_USAGE) { throw new Error("Memory usage is too high"); } } diff --git a/test/js/bun/json5/json5.test.ts b/test/js/bun/json5/json5.test.ts index 98a4f41b3ebd..f3f4d83d9dd4 100644 --- a/test/js/bun/json5/json5.test.ts +++ b/test/js/bun/json5/json5.test.ts @@ -1671,14 +1671,15 @@ describe("stringify memory", () => { "--smol", "-e", /* js */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const obj = { a: [1, 2, 3], b: { c: 4 }, d: 5 }; const pad = Buffer.alloc(1024 * 1024, " ").toString(); for (let i = 0; i < 20; i++) Bun.JSON5.stringify(obj, null, pad + i); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 200; i++) Bun.JSON5.stringify(obj, null, pad + i); Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; if (growthMB > 64) throw new Error("leaked " + growthMB.toFixed(2) + "MB"); `, ], diff --git a/test/js/bun/resolve/load-file-loader-a-lot.test.ts b/test/js/bun/resolve/load-file-loader-a-lot.test.ts index dba4b93a62c3..6453017d207f 100644 --- a/test/js/bun/resolve/load-file-loader-a-lot.test.ts +++ b/test/js/bun/resolve/load-file-loader-a-lot.test.ts @@ -1,5 +1,5 @@ import { test } from "bun:test"; -import { isASAN } from "harness"; +import { isASAN, rss } from "harness"; // The internal `Loader.registry` JS object was removed when JSC's module loader // became pure C++; there's no public way to wipe the whole ESM registry from JS, @@ -13,14 +13,14 @@ test.skip("a file: loader file can be imported 10,000 times", async () => { for (let i = 0; i < 1000; i++) { await import("./an-empty-file-with-a-strange-extension.weird?j" + i); } - baseline = process.memoryUsage.rss(); + baseline = rss(); Bun.gc(true); for (let i = 0; i < (isASAN ? 25_000 : 100_000); i++) { await import("./an-empty-file-with-a-strange-extension.weird?i" + i); } Bun.gc(true); - const memory = process.memoryUsage.rss(); + const memory = rss(); Bun.unsafe.gcAggressionLevel(prev); // It's pretty hard to test for not leaking specifier strings correctly. We need string stats. diff --git a/test/js/bun/s3/bun-write-leak-fixture.js b/test/js/bun/s3/bun-write-leak-fixture.js index 0a7dabee8186..b36a7c923d11 100644 --- a/test/js/bun/s3/bun-write-leak-fixture.js +++ b/test/js/bun/s3/bun-write-leak-fixture.js @@ -3,6 +3,10 @@ let MAX_ALLOWED_MEMORY_USAGE = 0; let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; const { randomUUID } = require("crypto"); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const payload = new Buffer(1024 * 1024 * 1, "A".charCodeAt(0)).toString("utf-8"); async function writeLargeFile() { @@ -17,14 +21,14 @@ async function run() { await Bun.sleep(10); Bun.gc(true); } - MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + MAX_ALLOWED_MEMORY_USAGE = ((rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; { await Promise.all(new Array(100).fill(writeLargeFile())); Bun.gc(true); } - const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; - if (rss > MAX_ALLOWED_MEMORY_USAGE) { + const rssMB = (rss() / 1024 / 1024) | 0; + if (rssMB > MAX_ALLOWED_MEMORY_USAGE) { throw new Error("Memory usage is too high"); } } diff --git a/test/js/bun/s3/s3-stream-leak-fixture.js b/test/js/bun/s3/s3-stream-leak-fixture.js index b440f8334ba7..2ce770d9eff6 100644 --- a/test/js/bun/s3/s3-stream-leak-fixture.js +++ b/test/js/bun/s3/s3-stream-leak-fixture.js @@ -3,6 +3,10 @@ let MAX_ALLOWED_MEMORY_USAGE = 0; let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; const { randomUUID } = require("crypto"); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const s3Dest = randomUUID() + "-s3-stream-leak-fixture"; @@ -25,15 +29,15 @@ async function run(inputType) { await Bun.sleep(10); Bun.gc(true); } - MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + MAX_ALLOWED_MEMORY_USAGE = ((rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; { await Promise.all(new Array(100).fill(readLargeFile())); Bun.gc(true); } - const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; - if (rss > MAX_ALLOWED_MEMORY_USAGE) { + const rssMB = (rss() / 1024 / 1024) | 0; + if (rssMB > MAX_ALLOWED_MEMORY_USAGE) { await s3file.unlink(); - throw new Error("RSS reached " + rss + "MB"); + throw new Error("RSS reached " + rssMB + "MB"); } } await run(new Buffer(1024 * 1024 * 1, "A".charCodeAt(0)).toString("utf-8")); diff --git a/test/js/bun/s3/s3-text-leak-fixture.js b/test/js/bun/s3/s3-text-leak-fixture.js index 358d8818db9b..0d6dc7983c5b 100644 --- a/test/js/bun/s3/s3-text-leak-fixture.js +++ b/test/js/bun/s3/s3-text-leak-fixture.js @@ -3,6 +3,10 @@ let MAX_ALLOWED_MEMORY_USAGE = 0; let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; const { randomUUID } = require("crypto"); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const s3Dest = randomUUID() + "-s3-stream-leak-fixture"; @@ -20,13 +24,13 @@ async function run(inputType) { await Bun.sleep(10); Bun.gc(true); } - MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + MAX_ALLOWED_MEMORY_USAGE = ((rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; { await Promise.all(new Array(100).fill(readLargeFile())); Bun.gc(true); } - const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; - if (rss > MAX_ALLOWED_MEMORY_USAGE) { + const rssMB = (rss() / 1024 / 1024) | 0; + if (rssMB > MAX_ALLOWED_MEMORY_USAGE) { await s3file.unlink(); throw new Error("Memory usage is too high"); } diff --git a/test/js/bun/s3/s3-write-leak-fixture.js b/test/js/bun/s3/s3-write-leak-fixture.js index 75a8c2d36a55..c0b8384161be 100644 --- a/test/js/bun/s3/s3-write-leak-fixture.js +++ b/test/js/bun/s3/s3-write-leak-fixture.js @@ -4,6 +4,10 @@ let MAX_ALLOWED_MEMORY_USAGE = 0; let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; const dest = process.argv.at(-1); const { randomUUID } = require("crypto"); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const payload = new Buffer(1024 * 1024 + 1, "A".charCodeAt(0)).toString("utf-8"); async function writeLargeFile() { const s3file = Bun.s3.file(randomUUID()); @@ -17,14 +21,14 @@ async function run() { await Bun.sleep(10); Bun.gc(true); } - MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + MAX_ALLOWED_MEMORY_USAGE = ((rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; { await Promise.all(new Array(100).fill(writeLargeFile())); Bun.gc(true); } - const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; - if (rss > MAX_ALLOWED_MEMORY_USAGE) { + const rssMB = (rss() / 1024 / 1024) | 0; + if (rssMB > MAX_ALLOWED_MEMORY_USAGE) { throw new Error("Memory usage is too high"); } } diff --git a/test/js/bun/s3/s3-writer-leak-fixture.js b/test/js/bun/s3/s3-writer-leak-fixture.js index 5fc0e7038696..5b3e7d39bad0 100644 --- a/test/js/bun/s3/s3-writer-leak-fixture.js +++ b/test/js/bun/s3/s3-writer-leak-fixture.js @@ -4,6 +4,10 @@ let MAX_ALLOWED_MEMORY_USAGE = 0; let MAX_ALLOWED_MEMORY_USAGE_INCREMENT = 15; const dest = process.argv.at(-1); const { randomUUID } = require("crypto"); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const payload = new Buffer(1024 * 256, "A".charCodeAt(0)).toString("utf-8"); async function writeLargeFile() { const s3file = Bun.s3.file(randomUUID()); @@ -25,14 +29,14 @@ async function run() { await Bun.sleep(10); Bun.gc(true); } - MAX_ALLOWED_MEMORY_USAGE = ((process.memoryUsage.rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; + MAX_ALLOWED_MEMORY_USAGE = ((rss() / 1024 / 1024) | 0) + MAX_ALLOWED_MEMORY_USAGE_INCREMENT; { await Promise.all(new Array(100).fill(writeLargeFile())); Bun.gc(true); } - const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; - if (rss > MAX_ALLOWED_MEMORY_USAGE) { + const rssMB = (rss() / 1024 / 1024) | 0; + if (rssMB > MAX_ALLOWED_MEMORY_USAGE) { throw new Error("Memory usage is too high"); } } diff --git a/test/js/bun/shell/bunshell.test.ts b/test/js/bun/shell/bunshell.test.ts index b475721d3445..aaae3ce3b7de 100644 --- a/test/js/bun/shell/bunshell.test.ts +++ b/test/js/bun/shell/bunshell.test.ts @@ -8,13 +8,13 @@ import { $ } from "bun"; import { afterAll, beforeAll, describe, expect, it, test } from "bun:test"; import { chmodSync, mkdirSync } from "fs"; import { mkdir, rm, stat } from "fs/promises"; -import { bunExe, isPosix, isWindows, runWithErrorPromise, tempDir, tempDirWithFiles, tmpdirSync } from "harness"; +import { bunExe, isPosix, isWindows, rss, runWithErrorPromise, tempDir, tempDirWithFiles, tmpdirSync } from "harness"; import { join, sep } from "path"; import { createTestBuilder, sortedShellOutput } from "./util"; const TestBuilder = createTestBuilder(import.meta.path); -afterAll(() => console.error("After all RSS", process.memoryUsage.rss() / 1024 / 1024)); -beforeAll(() => console.error("Before all RSS", process.memoryUsage.rss() / 1024 / 1024)); +afterAll(() => console.error("After all RSS", rss() / 1024 / 1024)); +beforeAll(() => console.error("Before all RSS", rss() / 1024 / 1024)); export const bunEnv: NodeJS.ProcessEnv = { ...process.env, diff --git a/test/js/bun/shell/commands/seq.test.ts b/test/js/bun/shell/commands/seq.test.ts index 56e5a19195ba..5ff6b24aefbc 100644 --- a/test/js/bun/shell/commands/seq.test.ts +++ b/test/js/bun/shell/commands/seq.test.ts @@ -150,11 +150,12 @@ test.skipIf(!isASAN)("seq piped to an fd does not clone its output buffer before cmd: [ bunExe(), "-e", - `const sep = Buffer.alloc(100, "x").toString();` + + `const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss;` + + `const sep = Buffer.alloc(100, "x").toString();` + `await Bun.$\`seq 1 10 > /dev/null\`;` + - `const b = process.memoryUsage().rss;` + + `const b = rss();` + `await Bun.$\`seq -s \${sep} 1 300000 > /dev/null\`;` + - `console.log(process.memoryUsage().rss - b);`, + `console.log(rss() - b);`, ], env: bunEnv, stderr: "pipe", diff --git a/test/js/bun/shell/leak.test.ts b/test/js/bun/shell/leak.test.ts index 3426e93c459f..b0aec1637cb7 100644 --- a/test/js/bun/shell/leak.test.ts +++ b/test/js/bun/shell/leak.test.ts @@ -119,6 +119,7 @@ describe.concurrent("fd leak", () => { const impl = /* ts */ ` import { heapStats } from "bun:jsc"; const TestBuilder = createTestBuilder(import.meta.path); + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const threshold = ${threshold} let prev: number | undefined = undefined; @@ -137,7 +138,7 @@ describe.concurrent("fd leak", () => { process.exit(1); } - const val = process.memoryUsage.rss(); + const val = rss(); if (prev === undefined) { prev = val; prevprev = val; diff --git a/test/js/bun/shell/shell-leak-args.test.ts b/test/js/bun/shell/shell-leak-args.test.ts index f546a50868d3..f3334a2c4bd4 100644 --- a/test/js/bun/shell/shell-leak-args.test.ts +++ b/test/js/bun/shell/shell-leak-args.test.ts @@ -1,6 +1,6 @@ import { $ } from "bun"; import { expect, test } from "bun:test"; -import { isASAN } from "harness"; +import { isASAN, rss } from "harness"; test("shell parsing error does not leak emmory", async () => { const buffer = Buffer.alloc(1024 * 1024, "A").toString(); @@ -9,14 +9,14 @@ test("shell parsing error does not leak emmory", async () => { $`${{ raw: buffer }} `; } catch (e) {} } - const rss = process.memoryUsage.rss(); + const rssBefore = rss(); for (let i = 0; i < 200; i++) { try { $`${{ raw: buffer }} `; } catch (e) {} } - const after = process.memoryUsage.rss() / 1024 / 1024; - const before = rss / 1024 / 1024; + const after = rss() / 1024 / 1024; + const before = rssBefore / 1024 / 1024; // In Bun v1.3.0 on macOS arm64: // Expected: < 100 // Received: 524.65625 @@ -39,12 +39,12 @@ test("shell execution doesn't leak argv", async () => { for (let i = 0; i < 5; i++) { await $`${{ raw: cmd }}`.quiet(); } - const rss = process.memoryUsage.rss(); + const rssBefore = rss(); for (let i = 0; i < 200; i++) { await $`${{ raw: cmd }}`.quiet(); } - const after = process.memoryUsage.rss() / 1024 / 1024; - const before = rss / 1024 / 1024; + const after = rss() / 1024 / 1024; + const before = rssBefore / 1024 / 1024; // In Bun v1.3.0 on macOS arm64: // Expected: < 250 // Received: 588.515625 @@ -64,12 +64,12 @@ test("non-awaited shell command does not leak argv", async () => { for (let i = 0; i < 5; i++) { $`${{ raw: cmd }}`.quiet(); } - const rss = process.memoryUsage.rss(); + const rssBefore = rss(); for (let i = 0; i < 200; i++) { $`${{ raw: cmd }}`.quiet(); } - const after = process.memoryUsage.rss() / 1024 / 1024; - const before = rss / 1024 / 1024; + const after = rss() / 1024 / 1024; + const before = rssBefore / 1024 / 1024; // In Bun v1.3.0 on macOS arm64: // Expected: < 250 // Received: 588.515625 diff --git a/test/js/bun/spawn/spawn-noread-leak.test.ts b/test/js/bun/spawn/spawn-noread-leak.test.ts index c3c5e204bdae..68f90d885ca7 100644 --- a/test/js/bun/spawn/spawn-noread-leak.test.ts +++ b/test/js/bun/spawn/spawn-noread-leak.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { isASAN } from "harness"; +import { isASAN, rss } from "harness"; async function spawn() { const proc = Bun.spawn(["cat", import.meta.path], { @@ -13,13 +13,13 @@ async function spawn100() { } test("does not leak", async () => { - const before = process.memoryUsage().rss; + const before = rss(); console.log("before", (before / 1024 / 1024).toFixed(3), "MB"); for (let index = 0; index < 30; index++) { await spawn100(); Bun.gc(true); } - const after = process.memoryUsage().rss; + const after = rss(); console.log("after", (after / 1024 / 1024).toFixed(3), "MB"); // ASAN's quarantine retains freed allocations so RSS grows much more under // bun-asan; widen the multiplier there. diff --git a/test/js/bun/spawn/spawn-pipe-leak.test.ts b/test/js/bun/spawn/spawn-pipe-leak.test.ts index b39ffc5ffc3a..86024690b3ff 100644 --- a/test/js/bun/spawn/spawn-pipe-leak.test.ts +++ b/test/js/bun/spawn/spawn-pipe-leak.test.ts @@ -5,7 +5,7 @@ * and then exits. We only await the `process.exited` promise without reading * any of the output data to test for potential memory leaks. */ -import { bunExe, isASAN, isCI, isWindows } from "harness"; +import { bunExe, isASAN, isCI, isWindows, rss } from "harness"; describe.todoIf( /** @@ -79,7 +79,7 @@ describe.todoIf( */ async function testSpawnMemoryLeak(batchSize: number, totalBatches: number) { log("Starting memory leak test..."); - log(`Initial memory usage: ${Math.round(process.memoryUsage.rss() / MB)} MB`); + log(`Initial memory usage: ${Math.round(rss() / MB)} MB`); let peak = 0; for (let batch = 0; batch < totalBatches; batch++) { @@ -99,12 +99,12 @@ describe.todoIf( // batches and the peak comparison sees a false positive. Bun.gc(true); - const rss = process.memoryUsage.rss(); - peak = Math.max(peak, rss); + const rssNow = rss(); + peak = Math.max(peak, rssNow); // Log progress after each batch log(`Batch ${batch + 1}/${totalBatches} completed (${(batch + 1) * batchSize} processes)`); - log(`Current memory usage: ${Math.round(rss / MB)} MB`); + log(`Current memory usage: ${Math.round(rssNow / MB)} MB`); } return peak; @@ -119,7 +119,7 @@ describe.todoIf( const mainPeak = await testSpawnMemoryLeak(batchSize, 10); log("Memory leak test completed"); - log(`Final memory usage: ${Math.round(process.memoryUsage.rss() / MB)} MB`); + log(`Final memory usage: ${Math.round(rss() / MB)} MB`); // Compare the max RSS seen across batches rather than a single post-run sample. // mimalloc returns pages on its own schedule, so one sample can land anywhere; diff --git a/test/js/bun/spawn/spawn-stdout-iterate-leak.fixture.ts b/test/js/bun/spawn/spawn-stdout-iterate-leak.fixture.ts index 0bd8614fc0fa..01977cd86076 100644 --- a/test/js/bun/spawn/spawn-stdout-iterate-leak.fixture.ts +++ b/test/js/bun/spawn/spawn-stdout-iterate-leak.fixture.ts @@ -18,6 +18,10 @@ import { once } from "events"; const MB = 1024 * 1024; const CHUNK = 32768; +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; async function run(iters: number) { const proc = spawn("cat", [], { stdio: ["pipe", "pipe", "ignore"] }); @@ -76,7 +80,7 @@ const samples: number[] = []; for (let i = 0; i < STEPS; i++) { await run(ITERS); Bun.gc(true); - samples.push(process.memoryUsage.rss()); + samples.push(rss()); } console.log( diff --git a/test/js/bun/transpiler/transpiler-comma-chain-oom.test.ts b/test/js/bun/transpiler/transpiler-comma-chain-oom.test.ts index a89307a2d6ce..3d7884f4f685 100644 --- a/test/js/bun/transpiler/transpiler-comma-chain-oom.test.ts +++ b/test/js/bun/transpiler/transpiler-comma-chain-oom.test.ts @@ -8,12 +8,13 @@ import { bunEnv, bunExe } from "harness"; test("long comma expression does not blow up memory with target: bun", async () => { const fixture = ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const n = 4000; const input = Array(n).fill("a").join(","); const expected = Array(n).fill("a").join(", ") + ";\\n"; - const before = process.memoryUsage().rss; + const before = rss(); const out = new Bun.Transpiler({ target: "bun" }).transformSync(input); - const after = process.memoryUsage().rss; + const after = rss(); if (out !== expected) { throw new Error("wrong output: " + JSON.stringify(out.slice(0, 80))); } diff --git a/test/js/bun/util/filesystem_router.test.ts b/test/js/bun/util/filesystem_router.test.ts index a815be1d9c7d..ffd35ffd529a 100644 --- a/test/js/bun/util/filesystem_router.test.ts +++ b/test/js/bun/util/filesystem_router.test.ts @@ -538,6 +538,7 @@ it("MatchedRoute.params does not leak", async () => { // garbage-collected. Use long segment values so any leak is large enough to // dominate RSS noise. const code = /* ts */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const router = new Bun.FileSystemRouter({ dir: ${JSON.stringify(path.join(String(dir), "pages"))}, style: "nextjs", @@ -549,11 +550,11 @@ it("MatchedRoute.params does not leak", async () => { // warm up for (let i = 0; i < 1000; i++) router.match(url).params; Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 30000; i++) router.match(url).params; Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; console.error("RSS growth: " + growthMB.toFixed(2) + "MB"); // ASAN's quarantine retains freed allocations (default 256 MB) so RSS // deltas run far higher under bun-asan; widen the threshold there. diff --git a/test/js/bun/util/inspect-error-leak.test.js b/test/js/bun/util/inspect-error-leak.test.js index 058aa5edd945..48619ce18f62 100644 --- a/test/js/bun/util/inspect-error-leak.test.js +++ b/test/js/bun/util/inspect-error-leak.test.js @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { isASAN } from "../../../harness"; +import { isASAN, rss } from "../../../harness"; const perBatch = 2000; const repeat = 50; @@ -12,12 +12,12 @@ test("Printing errors does not leak", () => { } batch(); - const baseline = Math.floor(process.memoryUsage.rss() / 1024); + const baseline = Math.floor(rss() / 1024); for (let i = 0; i < repeat; i++) { batch(); } - const after = Math.floor(process.memoryUsage.rss() / 1024); + const after = Math.floor(rss() / 1024); const diff = ((after - baseline) / 1024) | 0; console.log(`RSS increased by ${diff} MB`); // ASAN's free quarantine (default 256 MB) plus redzones and glibc page diff --git a/test/js/bun/util/password.test.ts b/test/js/bun/util/password.test.ts index 0aa020fa71c4..d9693962246b 100644 --- a/test/js/bun/util/password.test.ts +++ b/test/js/bun/util/password.test.ts @@ -24,15 +24,16 @@ describe.skipIf(isDebug)("does not leak", () => { test("hashSync", async () => { await run(/* js */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const opts = { algorithm: "argon2id", memoryCost: 8, timeCost: 1 }; // Large warm-up so the JSC heap and allocator arenas reach steady state // before we start measuring (debug/ASAN builds especially need this). for (let i = 0; i < 60000; i++) Bun.password.hashSync("hey", opts); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 60000; i++) Bun.password.hashSync("hey", opts); Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; // ASAN's free quarantine (default 256 MB) plus redzones and glibc page // retention inflate RSS even when nothing is leaking. const limit = ${isASAN ? 400 : 4}; @@ -42,6 +43,7 @@ describe.skipIf(isDebug)("does not leak", () => { test("hash", async () => { await run(/* js */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const opts = { algorithm: "argon2id", memoryCost: 8, timeCost: 1 }; async function batch(n) { const promises = []; @@ -50,10 +52,10 @@ describe.skipIf(isDebug)("does not leak", () => { } for (let i = 0; i < 500; i++) await batch(100); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 2000; i++) await batch(100); Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; // ASAN's free quarantine (default 256 MB) plus redzones and glibc page // retention inflate RSS even when nothing is leaking. const limit = ${isASAN ? 400 : 20}; diff --git a/test/js/bun/util/zstd.test.ts b/test/js/bun/util/zstd.test.ts index 5a4efb6fec55..9529b63fd99b 100644 --- a/test/js/bun/util/zstd.test.ts +++ b/test/js/bun/util/zstd.test.ts @@ -9,7 +9,7 @@ import { zstdDecompressSync, } from "bun"; import { afterAll, beforeAll, describe, expect, it } from "bun:test"; -import { bunEnv, bunExe } from "harness"; +import { bunEnv, bunExe, rss } from "harness"; import path from "path"; describe("Zstandard compression", async () => { @@ -66,7 +66,7 @@ describe("Zstandard compression", async () => { } catch {} } Bun.gc(true); - return process.memoryUsage.rss(); + return rss(); } // Warm up until RSS stabilizes (allocator / ASAN quarantine reach steady state). diff --git a/test/js/node/buffer-from-encoding-leak.test.ts b/test/js/node/buffer-from-encoding-leak.test.ts index 4c8a55a23707..d75235d785ab 100644 --- a/test/js/node/buffer-from-encoding-leak.test.ts +++ b/test/js/node/buffer-from-encoding-leak.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { isASAN } from "harness"; +import { isASAN, rss } from "harness"; // JSBuffer__bufferFromPointerAndLengthAndDeinit used to skip the deallocator entirely // when the resulting length was 0. Buffer.from(, "hex") would @@ -22,11 +22,11 @@ describe("Buffer.from(string, encoding) does not leak when decoded length is 0", // warm up so any one-time allocations (JIT tiers, caches) don't count for (let i = 0; i < 1000; i++) Buffer.from(str, "hex"); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < iterations; i++) Buffer.from(str, "hex"); Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); const growthMB = (after - before) / 1024 / 1024; // Without the fix this grows by ~200 MB (50k * 4096 bytes). With the fix, diff --git a/test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts b/test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts index abba695ee60f..a71cef7f516e 100644 --- a/test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts +++ b/test/js/node/fs/abort-signal-leak-read-write-file-fixture.ts @@ -1,6 +1,6 @@ import fs from "fs"; import { join } from "path"; -import { isASAN, tmpdirSync } from "harness"; +import { isASAN, rss, tmpdirSync } from "harness"; import { heapStats } from "bun:jsc"; const tmpdir = tmpdirSync(); @@ -35,7 +35,7 @@ if (numAbortSignalObjects > 10) { // ASAN's quarantine retains freed allocations (default 256 MB) and shadow // memory raises the absolute RSS floor; widen the cap to avoid false positives. const limitMB = isASAN ? 700 : 200; -const rss = (process.memoryUsage().rss / 1024 / 1024) | 0; -if (rss > limitMB) { - throw new Error(`Memory leak detected: ${rss} MB, expected < ${limitMB} MB`); +const rssMB = (rss() / 1024 / 1024) | 0; +if (rssMB > limitMB) { + throw new Error(`Memory leak detected: ${rssMB} MB, expected < ${limitMB} MB`); } diff --git a/test/js/node/fs/readdirSync-recursive-error-leak-fixture.js b/test/js/node/fs/readdirSync-recursive-error-leak-fixture.js index 9982ed4d52d8..7fad9e081e67 100644 --- a/test/js/node/fs/readdirSync-recursive-error-leak-fixture.js +++ b/test/js/node/fs/readdirSync-recursive-error-leak-fixture.js @@ -14,6 +14,11 @@ const fs = require("fs"); const path = require("path"); const os = require("os"); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; + const seg = (ch, n = 220) => Buffer.alloc(n, ch).toString(); const base = fs.mkdtempSync(path.join(os.tmpdir(), "readdir-err-leak-")); @@ -55,7 +60,7 @@ for (let i = 0; i < 10000; i++) { } catch {} } Bun.gc(true); -const before = process.memoryUsage.rss(); +const before = rss(); for (let i = 0; i < 20000; i++) { try { @@ -63,7 +68,7 @@ for (let i = 0; i < 20000; i++) { } catch {} } Bun.gc(true); -const after = process.memoryUsage.rss(); +const after = rss(); const deltaMB = Math.round((after - before) / 1024 / 1024); console.log("RSS delta", deltaMB, "MB"); diff --git a/test/js/node/http/fixtures/http.compress.leak.server.ts b/test/js/node/http/fixtures/http.compress.leak.server.ts index 102b332d76bb..ed9d5b887135 100644 --- a/test/js/node/http/fixtures/http.compress.leak.server.ts +++ b/test/js/node/http/fixtures/http.compress.leak.server.ts @@ -20,7 +20,11 @@ function listen(server: Server, protocol: string = "http"): Promise { }); } -const baseline = process.memoryUsage.rss(); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; +const baseline = rss(); let count = 0; var server = createServer(async (req, res) => { @@ -44,11 +48,11 @@ var server = createServer(async (req, res) => { count += 1; if (count % 1000 === 0) { Bun.gc(true); - console.log("count", count, process.memoryUsage.rss()); + console.log("count", count, rss()); } if (count == 10_000) { Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); console.log("heapStats", jsc.heapStats()); process.send({ baseline, after }); } diff --git a/test/js/node/http2/node-http2-memory-leak.js b/test/js/node/http2/node-http2-memory-leak.js index 8d4419b71075..559fa522fb16 100644 --- a/test/js/node/http2/node-http2-memory-leak.js +++ b/test/js/node/http2/node-http2-memory-leak.js @@ -20,6 +20,10 @@ function getHeapStats() { } } const gc = globalThis.gc || globalThis.Bun?.gc || (() => {}); +const rss = + process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const sleep = dur => new Promise(resolve => setTimeout(resolve, dur)); const ASAN_MULTIPLIER = process.env.ASAN_OPTIONS ? 1 / 10 : 1; @@ -97,7 +101,7 @@ async function main() { await sleep(10); gc(true); // take a baseline - const baseline = process.memoryUsage.rss(); + const baseline = rss(); // run requests await runRequests(ITERATIONS); @@ -105,7 +109,7 @@ async function main() { await sleep(10); // take an end snapshot - const end = process.memoryUsage.rss(); + const end = rss(); const delta = end - baseline; const deltaMegaBytes = (delta / 1024 / 1024) | 0; diff --git a/test/js/node/module/node-module-module.test.js b/test/js/node/module/node-module-module.test.js index 2c9fc41ff6c7..bd78e9d04e44 100644 --- a/test/js/node/module/node-module-module.test.js +++ b/test/js/node/module/node-module-module.test.js @@ -158,14 +158,15 @@ describe.concurrent("node-module-module", () => { // dominates RSS noise within a few thousand iterations. const code = /* js */ ` const m = require("module"); + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const comp = Buffer.alloc(30, "a").toString(); const base = "/" + Array(20).fill(comp).join("/"); for (let i = 0; i < 200; i++) m._nodeModulePaths(base + i); Bun.gc(true); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 5000; i++) m._nodeModulePaths(base + i); Bun.gc(true); Bun.gc(true); Bun.gc(true); - process.stdout.write(String((process.memoryUsage.rss() - before) / 1024 / 1024)); + process.stdout.write(String((rss() - before) / 1024 / 1024)); `; await using proc = Bun.spawn({ cmd: [bunExe(), "--smol", "-e", code], diff --git a/test/js/node/net/handle-leak.test.ts b/test/js/node/net/handle-leak.test.ts index a9a2e186498f..c7b45512a893 100644 --- a/test/js/node/net/handle-leak.test.ts +++ b/test/js/node/net/handle-leak.test.ts @@ -1,5 +1,5 @@ import { expect } from "bun:test"; -import { isASAN, isWindows } from "harness"; +import { isASAN, isWindows, rss } from "harness"; import * as net from "node:net"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -45,12 +45,12 @@ while (started < warmup_total) { await Promise.all(promises); await setTimeout(1); if (started % 10_000 === 0) { - console.log(`Completed ${started} connections. RSS: ${(process.memoryUsage.rss() / 1024 / 1024) | 0} MB`); + console.log(`Completed ${started} connections. RSS: ${(rss() / 1024 / 1024) | 0} MB`); } } Bun.gc(true); -const warmup_rss = process.memoryUsage.rss(); +const warmup_rss = rss(); started = 0; while (started < measured_total) { @@ -73,14 +73,14 @@ while (started < measured_total) { await Promise.all(promises); await setTimeout(1); if (started % 10_000 === 0) { - console.log(`Completed ${started} connections. RSS: ${(process.memoryUsage.rss() / 1024 / 1024) | 0} MB`); + console.log(`Completed ${started} connections. RSS: ${(rss() / 1024 / 1024) | 0} MB`); } } // Mirror the warmup sample: collect before measuring so the assertion compares // like-for-like and isn't sensitive to garbage still in flight from the last batch. Bun.gc(true); -const post_rss = process.memoryUsage.rss(); +const post_rss = rss(); server.close(); diff --git a/test/js/node/net/net-mongodb-pattern-leak.test.ts b/test/js/node/net/net-mongodb-pattern-leak.test.ts index 09df0ec8b885..10f1af142b3d 100644 --- a/test/js/node/net/net-mongodb-pattern-leak.test.ts +++ b/test/js/node/net/net-mongodb-pattern-leak.test.ts @@ -21,7 +21,7 @@ import { heapStats } from "bun:jsc"; import { describe, expect, test } from "bun:test"; -import { isASAN, isDebug, tls as tlsCerts } from "harness"; +import { isASAN, isDebug, rss, tls as tlsCerts } from "harness"; import { once } from "node:events"; import net from "node:net"; import { Transform } from "node:stream"; @@ -152,7 +152,7 @@ function snapshot() { Bun.gc(true); const t = heapStats().objectTypeCounts; return { - rss: process.memoryUsage.rss(), + rss: rss(), Timeout: t.Timeout || 0, Promise: t.Promise || 0, AsyncGenerator: t.AsyncGenerator || 0, diff --git a/test/js/node/net/socketaddress.spec.ts b/test/js/node/net/socketaddress.spec.ts index f34b68eb73d8..f330b218f325 100644 --- a/test/js/node/net/socketaddress.spec.ts +++ b/test/js/node/net/socketaddress.spec.ts @@ -1,6 +1,7 @@ /** * @see https://nodejs.org/api/net.html#class-netsocketaddress */ +import { rss } from "harness"; import { SocketAddress, SocketAddressInitOptions } from "node:net"; let v4: SocketAddress; @@ -116,7 +117,7 @@ describe("SocketAddress constructor", () => { sa = undefined; Bun.gc(true); - const before = process.memoryUsage(); + const before = rss(); if (debug) console.log("before", before); // actual test @@ -126,10 +127,10 @@ describe("SocketAddress constructor", () => { sa = undefined; Bun.gc(true); - const after = process.memoryUsage(); + const after = rss(); if (debug) console.log("after", after); - expect(after.rss).toBeLessThanOrEqual(before.rss * growthFactor); + expect(after).toBeLessThanOrEqual(before * growthFactor); }); }); // diff --git a/test/js/node/process/process-stdin.test.ts b/test/js/node/process/process-stdin.test.ts index c3e26ade88ed..3cb6fd76de81 100644 --- a/test/js/node/process/process-stdin.test.ts +++ b/test/js/node/process/process-stdin.test.ts @@ -460,13 +460,14 @@ describe.skipIf(isWindows)("pipe backpressure", () => { test.concurrent("Bun.stdin.stream(): a single read does not ingest the whole pipe", async () => { const { first, deltaMB } = await run(` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const rd = Bun.stdin.stream().getReader(); const c = await rd.read(); - const base = process.memoryUsage().rss; + const base = rss(); // Give the event loop time to (incorrectly) drain the pipe. The loop is // native (no JS on the no-pending path), so debug/ASAN overhead is small. await new Promise(r => setTimeout(r, 1500)); - const deltaMB = Math.round((process.memoryUsage().rss - base) / 1048576); + const deltaMB = Math.round((rss() - base) / 1048576); process.stdout.write(JSON.stringify({ first: c.value?.length ?? 0, deltaMB })); process.exit(0); `); @@ -476,15 +477,16 @@ describe.skipIf(isWindows)("pipe backpressure", () => { test.concurrent("process.stdin.pause() stops the fd from being read", async () => { const { bytesAfter, deltaMB } = await run(` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; let bytes = 0, pausedAt = 0; process.stdin.on("data", chunk => { bytes += chunk.length; if (!pausedAt && bytes >= 1 << 20) { pausedAt = bytes; process.stdin.pause(); - const base = process.memoryUsage().rss; + const base = rss(); setTimeout(() => { - const deltaMB = Math.round((process.memoryUsage().rss - base) / 1048576); + const deltaMB = Math.round((rss() - base) / 1048576); process.stdout.write(JSON.stringify({ bytesAtPause: pausedAt, bytesAfter: bytes, deltaMB })); process.exit(0); }, 1500); diff --git a/test/js/node/tls/node-tls-getpeercert-leak.test.ts b/test/js/node/tls/node-tls-getpeercert-leak.test.ts index 11bbdfda7238..65563f9f50c0 100644 --- a/test/js/node/tls/node-tls-getpeercert-leak.test.ts +++ b/test/js/node/tls/node-tls-getpeercert-leak.test.ts @@ -1,7 +1,7 @@ import { expect, it } from "bun:test"; import { once } from "events"; import { readFileSync } from "fs"; -import { isASAN, isDebug } from "harness"; +import { isASAN, isDebug, rss } from "harness"; import type { AddressInfo } from "node:net"; import type { TLSSocket } from "node:tls"; import { join } from "path"; @@ -73,10 +73,10 @@ it.skipIf(isDebug)( // regardless of leaks, so take the baseline after warmup. const perRound = 5_000; for (let round = 0; round < 8; round++) spin(perRound); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let round = 0; round < 15; round++) spin(perRound); - const after = process.memoryUsage.rss(); + const after = rss(); const growth = after - baseline; // Unpatched, the BIO leak alone is ~800 bytes/call → ~60MB over the diff --git a/test/js/node/tls/node-tls-set-session-leak.fixture.ts b/test/js/node/tls/node-tls-set-session-leak.fixture.ts index 41130fa589d7..e9722f7dc690 100644 --- a/test/js/node/tls/node-tls-set-session-leak.fixture.ts +++ b/test/js/node/tls/node-tls-set-session-leak.fixture.ts @@ -15,6 +15,10 @@ import tls from "node:tls"; const fixturesDir = path.join(import.meta.dirname, "fixtures"); const iterations = parseInt(process.argv[2] || "20000", 10); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const server = tls.createServer( { @@ -74,11 +78,11 @@ function runSetSessionLoop(n: number) { // growth (mimalloc page commit, JIT, etc.). await runSetSessionLoop(500); Bun.gc(true); -const before = process.memoryUsage.rss(); +const before = rss(); const calls = await runSetSessionLoop(iterations); Bun.gc(true); -const after = process.memoryUsage.rss(); +const after = rss(); console.log(JSON.stringify({ calls, growthBytes: after - before })); diff --git a/test/js/node/tls/tls-connect-socket-churn.test.ts b/test/js/node/tls/tls-connect-socket-churn.test.ts index 484f3b46da23..5761b7f4cb74 100644 --- a/test/js/node/tls/tls-connect-socket-churn.test.ts +++ b/test/js/node/tls/tls-connect-socket-churn.test.ts @@ -9,7 +9,7 @@ import { once } from "node:events"; import tls from "node:tls"; // @ts-expect-error - debug-only export import { sslCtxLiveCount } from "bun:internal-for-testing"; -import { isASAN, isDebug, tls as tlsCerts } from "harness"; +import { isASAN, isDebug, rss, tls as tlsCerts } from "harness"; test("tls.connect churn does not leak SSL_CTX or us_socket_context_t", async () => { const server = tls.createServer({ ...tlsCerts, rejectUnauthorized: false }, sock => { @@ -24,7 +24,7 @@ test("tls.connect churn does not leak SSL_CTX or us_socket_context_t", async () await connectOnce(port); Bun.gc(true); const ctxBefore = sslCtxLiveCount(); - const rssBefore = process.memoryUsage.rss(); + const rssBefore = rss(); // 50 is enough to prove O(1): the old code leaked one SSL_CTX per connect, // so the count delta would be ~50 not ≤2. (200 was used originally but @@ -37,7 +37,7 @@ test("tls.connect churn does not leak SSL_CTX or us_socket_context_t", async () Bun.gc(true); const ctxAfter = sslCtxLiveCount(); - const rssAfter = process.memoryUsage.rss(); + const rssAfter = rss(); // The whole point: no per-connection SSL_CTX. Allow a tiny slack for the // close-list / GC race, but 200 connects must not move this by 200. diff --git a/test/js/node/url/pathToFileURL-leak-fixture.js b/test/js/node/url/pathToFileURL-leak-fixture.js index 10c72c8a4970..48ec6497ca5e 100644 --- a/test/js/node/url/pathToFileURL-leak-fixture.js +++ b/test/js/node/url/pathToFileURL-leak-fixture.js @@ -3,15 +3,19 @@ const isDebugBuildOfBun = globalThis?.Bun?.revision?.includes("debug"); // ASAN's quarantine retains freed allocations (default 256 MB) and shadow // memory raises the absolute RSS floor; widen the cap to avoid false positives. const isASAN = process.execPath.includes("bun-asan"); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; import { pathToFileURL } from "url"; for (let i = 0; i < 1024 * (isDebugBuildOfBun ? 32 : 256); i++) { pathToFileURL(longPath); } Bun.gc(true); const limitMB = isASAN ? 700 : 250; -const rss = (process.memoryUsage.rss() / 1024 / 1024) | 0; -console.log("RSS", rss, "MB"); -if (rss > limitMB) { +const rssMB = (rss() / 1024 / 1024) | 0; +console.log("RSS", rssMB, "MB"); +if (rssMB > limitMB) { // On macOS, this was 860 MB. throw new Error("RSS is too high. Must be less than " + limitMB + "MB"); } diff --git a/test/js/node/vm/script-leak.test.ts b/test/js/node/vm/script-leak.test.ts index a53215854701..586ec3d88b99 100644 --- a/test/js/node/vm/script-leak.test.ts +++ b/test/js/node/vm/script-leak.test.ts @@ -1,10 +1,10 @@ const vm = require("vm"); const { describe, it, expect } = require("bun:test"); -const { isASAN } = require("harness"); +const { isASAN, rss } = require("harness"); describe("vm.Script", () => { it("shouldn't leak memory", () => { - const initialUsage = process.memoryUsage.rss(); + const initialUsage = rss(); { const source = `/*\n${Buffer.alloc(10000, " * aaaaa\n").toString("utf8")}\n*/ Buffer.alloc(10, 'hello');`; @@ -21,7 +21,7 @@ describe("vm.Script", () => { Bun.gc(true); - const finalUsage = process.memoryUsage.rss(); + const finalUsage = rss(); const megabytes = Math.round(((finalUsage - initialUsage) / 1024 / 1024) * 100) / 100; // ASAN's quarantine retains freed allocations (default 256 MB) so RSS // deltas run far higher under bun-asan; widen the threshold there. diff --git a/test/js/node/vm/sourcetextmodule-leak.test.ts b/test/js/node/vm/sourcetextmodule-leak.test.ts index e36ed69d899d..eba37752d4d2 100644 --- a/test/js/node/vm/sourcetextmodule-leak.test.ts +++ b/test/js/node/vm/sourcetextmodule-leak.test.ts @@ -1,6 +1,6 @@ const vm = require("vm"); const { describe, it, expect } = require("bun:test"); -const { isASAN, isDebug } = require("harness"); +const { isASAN, isDebug, rss } = require("harness"); // 50k×50KB ≈ 2.5 GB of source text — if module records leak their source we // blow past the threshold. Debug builds parse/link ~50× slower, so scale down. @@ -12,7 +12,7 @@ describe("vm.SourceTextModule", () => { it( "shouldn't leak memory", async () => { - const initialUsage = process.memoryUsage.rss(); + const initialUsage = rss(); { const source = `/*\n${Buffer.alloc(50_000, " * aaaaa\n").toString("utf8")}\n*/ Buffer.alloc(10, 'hello');`; @@ -32,7 +32,7 @@ describe("vm.SourceTextModule", () => { Bun.gc(true); - const finalUsage = process.memoryUsage.rss(); + const finalUsage = rss(); const megabytes = Math.round(((finalUsage - initialUsage) / 1024 / 1024) * 100) / 100; expect(megabytes).toBeLessThan(THRESHOLD_MB); }, diff --git a/test/js/node/watch/fs.watch.test.ts b/test/js/node/watch/fs.watch.test.ts index 36b9528b5f5f..683b3e87496c 100644 --- a/test/js/node/watch/fs.watch.test.ts +++ b/test/js/node/watch/fs.watch.test.ts @@ -1204,6 +1204,7 @@ test.skipIf(!isMacOS)("fs.watch(dir) on macOS does not leak the resolved FSEvent /* ts */ ` const fs = require("fs"); const dir = process.argv[1]; + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; async function cycle(count) { for (let i = 0; i < count; i++) fs.watch(dir, () => {}).close(); @@ -1218,14 +1219,14 @@ test.skipIf(!isMacOS)("fs.watch(dir) on macOS does not leak the resolved FSEvent // sizing, and the PathWatcherManager caches reach steady state // (one-time growth tapers off only after ~10k cycles). for (let i = 0; i < 3; i++) await cycle(5000); - const before = process.memoryUsage.rss(); + const before = rss(); // With a ~700-byte resolved path, 5000 leaked dupeZ buffers is // ~3.5 MB of growth on unpatched builds. Keep the iteration count // low enough that rapid FSEventStream recreate doesn't exhaust the // kernel queue (FSEventStreamCreate -> NULL). await cycle(5000); - const after = process.memoryUsage.rss(); + const after = rss(); const growthMB = (after - before) / 1024 / 1024; console.log("RSS growth: " + growthMB.toFixed(2) + " MB"); diff --git a/test/js/node/worker_threads/eval-source-leak-fixture.js b/test/js/node/worker_threads/eval-source-leak-fixture.js index 63758a66a89d..3e381c7edfab 100644 --- a/test/js/node/worker_threads/eval-source-leak-fixture.js +++ b/test/js/node/worker_threads/eval-source-leak-fixture.js @@ -5,6 +5,10 @@ const { Worker } = require("node:worker_threads"); const eachSizeMiB = 100; const iterations = 5; +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; function test() { const code = " ".repeat(eachSizeMiB * 1024 * 1024); @@ -26,12 +30,12 @@ async function reallyGC() { await test(); await reallyGC(); -const before = process.memoryUsage.rss(); +const before = rss(); for (let i = 0; i < iterations; i++) { await test(); await reallyGC(); } -const after = process.memoryUsage.rss(); +const after = rss(); // The bug is that the source code passed to `new Worker` would never be freed. // If this bug is present, then the memory growth likely won't be much more than the total amount // of source code, but it's impossible for the memory growth to be less than the source code size. diff --git a/test/js/node/worker_threads/worker_thread_check.ts b/test/js/node/worker_threads/worker_thread_check.ts index 02ec174c9bfa..531de8eb26fa 100644 --- a/test/js/node/worker_threads/worker_thread_check.ts +++ b/test/js/node/worker_threads/worker_thread_check.ts @@ -4,6 +4,10 @@ const RUN_COUNT = 5; import { Worker, isMainThread, workerData } from "worker_threads"; const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const actions = { async ["Bun.connect"](port) { @@ -76,7 +80,7 @@ if (isMainThread) { } await Promise.all(promises); - console.log(`Spawned ${CONCURRENCY} workers`, "RSS", (process.memoryUsage().rss / 1024 / 1024) | 0, "MB"); + console.log(`Spawned ${CONCURRENCY} workers`, "RSS", (rss() / 1024 / 1024) | 0, "MB"); Bun.gc(true); } server.stop(true); diff --git a/test/js/node/zlib/leak.test.ts b/test/js/node/zlib/leak.test.ts index 373eb9f268dc..8e255e49c17f 100644 --- a/test/js/node/zlib/leak.test.ts +++ b/test/js/node/zlib/leak.test.ts @@ -1,5 +1,5 @@ import { beforeAll, describe, expect, test } from "bun:test"; -import { isASAN } from "harness"; +import { isASAN, rss } from "harness"; import { promisify } from "node:util"; import zlib from "node:zlib"; @@ -28,13 +28,13 @@ describe("zlib compression does not leak memory", () => { for (let index = 0; index < 10_000; index++) { await promisify(zlib[compress])(input); } - const baseline = process.memoryUsage.rss(); + const baseline = rss(); console.log(baseline); for (let index = 0; index < 10_000; index++) { await promisify(zlib[compress])(input); } Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(after); console.log("-", after - baseline); console.log("-", upper); @@ -51,13 +51,13 @@ describe("zlib compression does not leak memory", () => { for (let index = 0; index < 10_000; index++) { zlib[compress](input); } - const baseline = process.memoryUsage.rss(); + const baseline = rss(); console.log(baseline); for (let index = 0; index < 10_000; index++) { zlib[compress](input); } Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(after); console.log("-", after - baseline); console.log("-", upper); @@ -71,13 +71,13 @@ describe("zlib compression does not leak memory", () => { for (let index = 0; index < 1_000; index++) { await promisify(zlib.brotliCompress)(input, brotliOpts); } - const baseline = process.memoryUsage.rss(); + const baseline = rss(); console.log(baseline); for (let index = 0; index < 1_000; index++) { await promisify(zlib.brotliCompress)(input, brotliOpts); } Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(after); console.log("-", after - baseline); console.log("-", upper); @@ -88,13 +88,13 @@ describe("zlib compression does not leak memory", () => { for (let index = 0; index < 1_000; index++) { zlib.brotliCompressSync(input, brotliOpts); } - const baseline = process.memoryUsage.rss(); + const baseline = rss(); console.log(baseline); for (let index = 0; index < 1_000; index++) { zlib.brotliCompressSync(input, brotliOpts); } Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(after); console.log("-", after - baseline); console.log("-", upper); @@ -105,13 +105,13 @@ describe("zlib compression does not leak memory", () => { for (let index = 0; index < 1_000; index++) { await promisify(zlib.zstdCompress)(input); } - const baseline = process.memoryUsage.rss(); + const baseline = rss(); console.log(baseline); for (let index = 0; index < 1_000; index++) { await promisify(zlib.zstdCompress)(input); } Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(after); console.log("-", after - baseline); console.log("-", upper); @@ -122,13 +122,13 @@ describe("zlib compression does not leak memory", () => { for (let index = 0; index < 1_000; index++) { zlib.zstdCompressSync(input); } - const baseline = process.memoryUsage.rss(); + const baseline = rss(); console.log(baseline); for (let index = 0; index < 1_000; index++) { zlib.zstdCompressSync(input); } Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(after); console.log("-", after - baseline); console.log("-", upper); diff --git a/test/js/sql/sql-mysql-query-string-leak.test.ts b/test/js/sql/sql-mysql-query-string-leak.test.ts index edff1e05fe36..a94cafb871ab 100644 --- a/test/js/sql/sql-mysql-query-string-leak.test.ts +++ b/test/js/sql/sql-mysql-query-string-leak.test.ts @@ -20,6 +20,7 @@ if (isDockerEnabled()) { using dir = tempDir("mysql-query-string-leak", { "fixture.js": /* js */ ` const { SQL } = require("bun"); + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const sql = new SQL({ url: process.env.MYSQL_URL, max: 1 }); @@ -33,7 +34,7 @@ if (isDockerEnabled()) { const CHUNK = 512 * 1024; Bun.gc(true); - const rssBefore = process.memoryUsage.rss(); + const rssBefore = rss(); for (let i = 0; i < ITERATIONS; i++) { const pad = Buffer.alloc(CHUNK, 0x61 + (i % 26)).toString("latin1"); @@ -53,7 +54,7 @@ if (isDockerEnabled()) { Bun.gc(true); } - const rssAfter = process.memoryUsage.rss(); + const rssAfter = rss(); const deltaMiB = (rssAfter - rssBefore) / 1024 / 1024; console.log(JSON.stringify({ rssBefore, rssAfter, deltaMiB })); `, diff --git a/test/js/sql/sql.test.ts b/test/js/sql/sql.test.ts index d057f08bae7e..acc704365ebf 100644 --- a/test/js/sql/sql.test.ts +++ b/test/js/sql/sql.test.ts @@ -1,6 +1,6 @@ import { $, randomUUIDv7, sql, SQL } from "bun"; import { afterAll, describe, expect, mock, test } from "bun:test"; -import { bunEnv, bunExe, isASAN, isCI, isDockerEnabled, tempDir, tempDirWithFiles } from "harness"; +import { bunEnv, bunExe, isASAN, isCI, isDockerEnabled, rss, tempDir, tempDirWithFiles } from "harness"; import path from "path"; const postgres = (...args) => new SQL(...args); @@ -896,7 +896,7 @@ if (isDockerEnabled()) { test("query string memory leak test", async () => { await using sql = postgres(options); Bun.gc(true); - const rss = process.memoryUsage.rss(); + const before = rss(); for (let potato of Array.from({ length: 8 * 1024 }, a => "okkk" + a)) { await sql` select 1 as abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde @@ -905,8 +905,8 @@ if (isDockerEnabled()) { } Bun.gc(true); - const after = process.memoryUsage.rss(); - console.log({ after, rss }); + const after = rss(); + console.log({ after, rss: before }); // Previously: // { // after: 507150336, @@ -915,7 +915,7 @@ if (isDockerEnabled()) { // ~440 MB. // ASAN's quarantine retains freed allocations (default 256 MB) so RSS // deltas run far higher under bun-asan; widen the threshold there. - expect((after - rss) / 1024 / 1024).toBeLessThan(isASAN ? 500 : 200); + expect((after - before) / 1024 / 1024).toBeLessThan(isASAN ? 500 : 200); }); // Last one wins. diff --git a/test/js/third_party/body-parser/express-memory-leak-fixture.mjs b/test/js/third_party/body-parser/express-memory-leak-fixture.mjs index 607f52249790..d3d5a172326e 100644 --- a/test/js/third_party/body-parser/express-memory-leak-fixture.mjs +++ b/test/js/third_party/body-parser/express-memory-leak-fixture.mjs @@ -1,5 +1,10 @@ import express from "express"; +const rss = + process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; + const app = express(); const port = 0; const body = Buffer.alloc(10 * 1024, "X"); @@ -25,7 +30,7 @@ app.get("/response-body", (req, res) => { app.get("/rss", (req, res) => { typeof Bun !== "undefined" && Bun.gc(true); res.json({ - rss: process.memoryUsage.rss(), + rss: rss(), objects: smallAssign(typeof Bun !== "undefined" ? require("bun:jsc").heapStats().objectTypeCounts : {}), }); }); diff --git a/test/js/third_party/prisma/prisma.test.ts b/test/js/third_party/prisma/prisma.test.ts index 20a6f8fe675a..f3c5ff771fcc 100644 --- a/test/js/third_party/prisma/prisma.test.ts +++ b/test/js/third_party/prisma/prisma.test.ts @@ -1,7 +1,7 @@ import { createCanvas } from "@napi-rs/canvas"; import { it as bunIt, test as bunTest, describe, expect } from "bun:test"; import { appendFile } from "fs/promises"; -import { getSecret, isCI } from "harness"; +import { getSecret, isCI, rss } from "harness"; import { generate, generateClient } from "./helper.ts"; import type { PrismaClient } from "./prisma/types.d.ts"; @@ -112,7 +112,7 @@ for (const type of ["sqlite", "postgres" /*"mssql", "mongodb"*/]) { // GC occasionally to make memory usage more deterministic if (totalIters % gcPeriod == gcPeriod - 1) { Bun.gc(true); - const line = `${totalIters * batchSize},${(process.memoryUsage.rss() / 1024 / 1024) | 0}`; + const line = `${totalIters * batchSize},${(rss() / 1024 / 1024) | 0}`; console.log(line); if (!isCI) await appendFile("rss.csv", line + "\n"); } @@ -131,13 +131,13 @@ for (const type of ["sqlite", "postgres" /*"mssql", "mongodb"*/]) { console.time("Test x " + testIters + " x " + batchSize); // measure memory now - const before = process.memoryUsage.rss(); + const before = rss(); // run a bunch more iterations to see if memory usage increases for (let i = 0; i < testIters; i++) { await runQuery(); } console.timeEnd("Test x " + testIters + " x " + batchSize); - const after = process.memoryUsage.rss(); + const after = rss(); const deltaMB = (after - before) / 1024 / 1024; expect(deltaMB).toBeLessThan(10); }, @@ -161,7 +161,7 @@ for (const type of ["sqlite", "postgres" /*"mssql", "mongodb"*/]) { // GC occasionally to make memory usage more deterministic if (totalIters % gcPeriod == gcPeriod - 1) { Bun.gc(true); - const line = `${totalIters},${(process.memoryUsage.rss() / 1024 / 1024) | 0}`; + const line = `${totalIters},${(rss() / 1024 / 1024) | 0}`; // console.log(line); // await appendFile("rss.csv", line + "\n"); } @@ -177,12 +177,12 @@ for (const type of ["sqlite", "postgres" /*"mssql", "mongodb"*/]) { await runQuery(); } // measure memory now - const before = process.memoryUsage.rss(); + const before = rss(); // run a bunch more iterations to see if memory usage increases for (let i = 0; i < testIters; i++) { await runQuery(); } - const after = process.memoryUsage.rss(); + const after = rss(); const deltaMB = (after - before) / 1024 / 1024; expect(deltaMB).toBeLessThan(10); }, diff --git a/test/js/web/encoding/text-decoder.test.js b/test/js/web/encoding/text-decoder.test.js index fab65b7f1b1b..36c3612d9696 100644 --- a/test/js/web/encoding/text-decoder.test.js +++ b/test/js/web/encoding/text-decoder.test.js @@ -1,5 +1,14 @@ import { describe, expect, it } from "bun:test"; -import { bunEnv, bunExe, gc as gcTrace, isASAN, normalizeBunSnapshot, tempDir, withoutAggressiveGC } from "harness"; +import { + bunEnv, + bunExe, + gc as gcTrace, + isASAN, + normalizeBunSnapshot, + rss, + tempDir, + withoutAggressiveGC, +} from "harness"; const getByteLength = str => { // returns the byte length of an utf8 string @@ -924,12 +933,12 @@ it.each(["utf-16le", "utf-16be"])( // Warm up so allocator arenas / JIT reach steady state, then snapshot RSS. run(2); - const before = process.memoryUsage.rss(); + const before = rss(); // Prior to the fix each call leaked ~CODE_UNITS * 2 bytes = 32 KiB, so 3072 // calls leaked ~96 MiB regardless of GC. run(24); - const after = process.memoryUsage.rss(); + const after = rss(); const deltaMiB = (after - before) / 1024 / 1024; // ASAN's quarantine retains freed allocations (default 256 MB) so the delta diff --git a/test/js/web/fetch/abortsignal-leak-fixture.ts b/test/js/web/fetch/abortsignal-leak-fixture.ts index 05e9526776d3..bf37c17265a6 100644 --- a/test/js/web/fetch/abortsignal-leak-fixture.ts +++ b/test/js/web/fetch/abortsignal-leak-fixture.ts @@ -1,5 +1,6 @@ import { heapStats } from "bun:jsc"; import { expect } from "bun:test"; +import { rss } from "harness"; let abortEventCount = 0; let onAbortHandler = () => {}; @@ -185,25 +186,25 @@ export async function testReqSignalAbortEventNeverResolves() { } export async function runAll() { - let initialRSS = (process.memoryUsage.rss() / 1024 / 1024) | 0; + let initialRSS = (rss() / 1024 / 1024) | 0; console.time("testReqSignalGetter"); await testReqSignalGetter(); console.timeEnd("testReqSignalGetter"); - let rssAfterReqSignalGetter = (process.memoryUsage.rss() / 1024 / 1024) | 0; + let rssAfterReqSignalGetter = (rss() / 1024 / 1024) | 0; console.log(`RSS after testReqSignalGetter: ${rssAfterReqSignalGetter}`); console.log(`RSS delta after testReqSignalGetter: ${rssAfterReqSignalGetter - initialRSS}`); console.time("testReqSignalAbortEvent"); await testReqSignalAbortEvent(); console.timeEnd("testReqSignalAbortEvent"); - let rssAfterReqSignalAbortEvent = (process.memoryUsage.rss() / 1024 / 1024) | 0; + let rssAfterReqSignalAbortEvent = (rss() / 1024 / 1024) | 0; console.log(`RSS after testReqSignalAbortEvent: ${rssAfterReqSignalAbortEvent}`); console.log(`RSS delta after testReqSignalAbortEvent: ${rssAfterReqSignalAbortEvent - rssAfterReqSignalGetter}`); console.time("testReqSignalAbortEventNeverResolves"); await testReqSignalAbortEventNeverResolves(); console.timeEnd("testReqSignalAbortEventNeverResolves"); - let rssAfterReqSignalAbortEventNeverResolves = (process.memoryUsage.rss() / 1024 / 1024) | 0; + let rssAfterReqSignalAbortEventNeverResolves = (rss() / 1024 / 1024) | 0; console.log(`RSS after testReqSignalAbortEventNeverResolves: ${rssAfterReqSignalAbortEventNeverResolves}`); console.log( `RSS delta after testReqSignalAbortEventNeverResolves: ${rssAfterReqSignalAbortEventNeverResolves - rssAfterReqSignalAbortEvent}`, diff --git a/test/js/web/fetch/blob.test.ts b/test/js/web/fetch/blob.test.ts index 8aa907c12b92..5007a416f5ea 100644 --- a/test/js/web/fetch/blob.test.ts +++ b/test/js/web/fetch/blob.test.ts @@ -381,15 +381,16 @@ test("Bun.file(path, {type}).text() does not leak the duped content_type", async "data.txt": "hello", }); const script = ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const p = ${JSON.stringify(path.join(String(dir), "data.txt"))}; const type = "application/x-" + Buffer.alloc(64 * 1024, "a").toString(); const file = Bun.file(p, { type }); for (let i = 0; i < 100; i++) await file.text(); Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 1024; i++) await file.text(); Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); console.log(JSON.stringify({ deltaMiB: (after - before) / 1024 / 1024 })); `; await using proc = Bun.spawn({ diff --git a/test/js/web/fetch/body.test.ts b/test/js/web/fetch/body.test.ts index 694f93bf078e..cf80cde9b899 100644 --- a/test/js/web/fetch/body.test.ts +++ b/test/js/web/fetch/body.test.ts @@ -1210,13 +1210,14 @@ describe.concurrent("string body consumption does not leak", () => { : `b => new Response(b)`; const src = ` const SZ = 2_000_000, WARM = 50, BLOCK = 40; - const rss = () => (process.memoryUsage().rss / 1048576) | 0; + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; + const mb = () => (rss() / 1048576) | 0; const body = ${makeBody}; const make = ${make}; const run = async n => { for (let i = 0; i < n; i++) await make(body())[${JSON.stringify(method)}](); }; - await run(WARM); Bun.gc(true); const a = rss(); - await run(BLOCK); Bun.gc(true); const b = rss(); - await run(BLOCK); Bun.gc(true); const c = rss(); + await run(WARM); Bun.gc(true); const a = mb(); + await run(BLOCK); Bun.gc(true); const b = mb(); + await run(BLOCK); Bun.gc(true); const c = mb(); console.log("BLOCK1:" + (b - a) + " BLOCK2:" + (c - b)); `; await using proc = Bun.spawn({ diff --git a/test/js/web/fetch/fetch-abort-stream-leak-fixture.ts b/test/js/web/fetch/fetch-abort-stream-leak-fixture.ts index 6c192a718eba..3baadca8eb18 100644 --- a/test/js/web/fetch/fetch-abort-stream-leak-fixture.ts +++ b/test/js/web/fetch/fetch-abort-stream-leak-fixture.ts @@ -1,6 +1,11 @@ // https://github.com/oven-sh/bun/issues/32659 import { heapStats } from "bun:jsc"; +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; + const ITER = Number(process.env.ITERATIONS ?? "60"); const MAX_GROWTH_MB = Number(process.env.MAX_GROWTH_MB ?? "55"); const CHUNK = new Uint8Array(512 * 1024); @@ -27,7 +32,7 @@ using server = Bun.serve({ const held: unknown[] = []; Bun.gc(true); -const rss0 = process.memoryUsage().rss; +const rss0 = rss(); for (let n = 0; n < ITER; n++) { sent = 0; @@ -53,7 +58,7 @@ Bun.gc(true); await Bun.sleep(1); Bun.gc(true); -const growthMB = (process.memoryUsage().rss - rss0) / 1024 / 1024; +const growthMB = (rss() - rss0) / 1024 / 1024; const heapMB = heapStats().heapSize / 1024 / 1024; console.log(`held=${held.length / 2} growthMB=${growthMB.toFixed(1)} heapMB=${heapMB.toFixed(1)}`); diff --git a/test/js/web/fetch/fetch-backpressure.test.ts b/test/js/web/fetch/fetch-backpressure.test.ts index cabc70188c5f..7576b9e8eb1c 100644 --- a/test/js/web/fetch/fetch-backpressure.test.ts +++ b/test/js/web/fetch/fetch-backpressure.test.ts @@ -140,12 +140,13 @@ async function spawnClient(url: string, kind: Kind, script: string) { } const SETTLE_RSS = /* js */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; async function settleRss() { - const before = process.memoryUsage.rss(); + const before = rss(); let last = before, stable = 0; while (stable < 3) { await Bun.sleep(20); - const now = process.memoryUsage.rss(); + const now = rss(); stable = Math.abs(now - last) < (1 << 20) ? stable + 1 : 0; last = now; } diff --git a/test/js/web/fetch/fetch-http2-adversarial.test.ts b/test/js/web/fetch/fetch-http2-adversarial.test.ts index d66fc0782ac9..16932ec21046 100644 --- a/test/js/web/fetch/fetch-http2-adversarial.test.ts +++ b/test/js/web/fetch/fetch-http2-adversarial.test.ts @@ -139,11 +139,12 @@ describe.concurrent("fetch() HTTP/2 adversarial", () => { }, async url => { await using proc = spawnFetch(` - const baseline = process.memoryUsage().rss; + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; + const baseline = rss(); let peak = baseline; const t = setInterval(() => { - const rss = process.memoryUsage().rss; - if (rss > peak) peak = rss; + const cur = rss(); + if (cur > peak) peak = cur; }, 50); const r = await fetch(${JSON.stringify(url)}, { protocol: "http2", diff --git a/test/js/web/fetch/fetch-leak-test-fixture-2.js b/test/js/web/fetch/fetch-leak-test-fixture-2.js index aa51bb3b2b02..da15bfd089c9 100644 --- a/test/js/web/fetch/fetch-leak-test-fixture-2.js +++ b/test/js/web/fetch/fetch-leak-test-fixture-2.js @@ -1,5 +1,10 @@ import { heapStats } from "bun:jsc"; +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; + const { SERVER } = process.env; if (typeof SERVER === "undefined" || !SERVER?.length) { @@ -35,7 +40,7 @@ async function getBaseline() { Bun.gc(true); - return process.memoryUsage.rss(); + return rss(); } const baseline = await getBaseline(); @@ -63,7 +68,7 @@ if (oks !== COUNT) { await Bun.sleep(10); Bun.gc(true); -const delta = process.memoryUsage.rss() - baseline; +const delta = rss() - baseline; if ((heapStats().objectTypeCounts.Response ?? 0) > 5) { throw new Error("Too many Response objects: " + heapStats().objectTypeCounts.Response); } diff --git a/test/js/web/fetch/fetch-leak-test-fixture-4.js b/test/js/web/fetch/fetch-leak-test-fixture-4.js index c25971c46275..b573968bac40 100644 --- a/test/js/web/fetch/fetch-leak-test-fixture-4.js +++ b/test/js/web/fetch/fetch-leak-test-fixture-4.js @@ -1,5 +1,9 @@ import { heapStats } from "bun:jsc"; import { expect } from "bun:test"; +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; function getHeapStats() { return heapStats().objectTypeCounts; } @@ -27,7 +31,7 @@ try { Response ||= 0; Promise ||= 0; console.log({ - rss: ((process.memoryUsage.rss() / 1024 / 1024) | 0) + " MB", + rss: ((rss() / 1024 / 1024) | 0) + " MB", Response, Promise, }); diff --git a/test/js/web/fetch/fetch-leak-test-fixture-5.js b/test/js/web/fetch/fetch-leak-test-fixture-5.js index 94d464ec6ec1..41293fa722d3 100644 --- a/test/js/web/fetch/fetch-leak-test-fixture-5.js +++ b/test/js/web/fetch/fetch-leak-test-fixture-5.js @@ -1,5 +1,9 @@ import { heapStats } from "bun:jsc"; import { expect } from "bun:test"; +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; function getHeapStats() { return heapStats().objectTypeCounts; } @@ -112,12 +116,12 @@ try { expect(stats.Response || 0).toBeLessThanOrEqual(threshold); expect(stats.Promise || 0).toBeLessThanOrEqual(promiseThreshold); process.send({ - rss: process.memoryUsage.rss(), + rss: rss(), }); } } process.send({ - rss: process.memoryUsage.rss(), + rss: rss(), }); await Bun.sleep(10); process.exit(0); diff --git a/test/js/web/fetch/fetch-leak-test-fixture-6.js b/test/js/web/fetch/fetch-leak-test-fixture-6.js index 06e3cec79bef..3c3816791966 100644 --- a/test/js/web/fetch/fetch-leak-test-fixture-6.js +++ b/test/js/web/fetch/fetch-leak-test-fixture-6.js @@ -1,4 +1,8 @@ import { expect } from "bun:test"; +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; let rssSample = 0; const url = process.env.SERVER_URL; const maxMemoryIncrease = parseInt(process.env.MAX_MEMORY_INCREASE || "0", 10); @@ -13,12 +17,12 @@ for (let i = 0; i < 500; i++) { await Bun.sleep(1); } await Bun.sleep(1); - const memoryUsage = process.memoryUsage().rss / 1024 / 1024; + const memoryUsage = rss() / 1024 / 1024; // memory should be stable after X iterations if (i == 250) rssSample = memoryUsage; } await Bun.sleep(1); Bun.gc(true); -const memoryUsage = process.memoryUsage().rss / 1024 / 1024; +const memoryUsage = rss() / 1024 / 1024; expect(rssSample).toBeGreaterThanOrEqual(memoryUsage - maxMemoryIncrease); console.log("done"); diff --git a/test/js/web/fetch/fetch-leak.test.ts b/test/js/web/fetch/fetch-leak.test.ts index 6ac6922f6b6b..0f903061168c 100644 --- a/test/js/web/fetch/fetch-leak.test.ts +++ b/test/js/web/fetch/fetch-leak.test.ts @@ -205,6 +205,7 @@ test("fetch(data:) with percent-encoding does not leak", async () => { // base64 output buffer on decode error). Each fetch of a percent-encoded // data: URL leaked ~len(url.data) bytes from bun.default_allocator. const script = ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; // ~240KB of percent-encoded payload; the intermediate percent-decoded // buffer is allocated at url.data.len bytes and was previously leaked. const plain = "data:text/plain," + Buffer.alloc(240000, "%41").toString(); @@ -223,11 +224,11 @@ test("fetch(data:) with percent-encoding does not leak", async () => { for (let i = 0; i < 40; i++) await hit(); Bun.gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let i = 0; i < 200; i++) await hit(); Bun.gc(true); - const final = process.memoryUsage.rss(); + const final = rss(); const deltaMB = (final - baseline) / 1024 / 1024; console.log(JSON.stringify({ baselineMB: (baseline / 1024 / 1024) | 0, finalMB: (final / 1024 / 1024) | 0, deltaMB: Math.round(deltaMB) })); @@ -275,6 +276,7 @@ test("fetch() compress option does not leak bodies or compressor state", async ( // > 512 KiB shared buffer → slow path; large enough that the compressed // output also spans multiple socket writes. const big = Buffer.alloc(700 * 1024, "abcdefghij"); + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const opts = [ { compress: "gzip" }, { compress: "deflate" }, @@ -297,11 +299,11 @@ test("fetch() compress option does not leak bodies or compressor state", async ( // shared_buffer) is allocated once here and stays for the process. for (let i = 0; i < 10; i++) await round(); Bun.gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let i = 0; i < 80; i++) await round(); Bun.gc(true); - const final = process.memoryUsage.rss(); + const final = rss(); const deltaMB = (final - baseline) / 1024 / 1024; console.log(JSON.stringify({ baselineMB: (baseline / 1024 / 1024) | 0, finalMB: (final / 1024 / 1024) | 0, deltaMB: Math.round(deltaMB) })); @@ -334,6 +336,7 @@ test("fetch() does not leak streaming decompressor state across fragmented compr const script = /* js */ ` import { createServer } from "node:net"; import { gzipSync, brotliCompressSync, zstdCompressSync } from "node:zlib"; + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const plain = Buffer.alloc(64 * 1024, "abcdefghij"); const bodies = { @@ -404,11 +407,11 @@ test("fetch() does not leak streaming decompressor state across fragmented compr for (let i = 0; i < 10; i++) await round(); Bun.gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let i = 0; i < 60; i++) await round(); Bun.gc(true); - const final = process.memoryUsage.rss(); + const final = rss(); server.close(); const deltaMB = (final - baseline) / 1024 / 1024; @@ -451,6 +454,7 @@ describe.each(["string", "object"])("fetch({proxy}) %s form does not leak the pr // JS string would make href_from_js return the same StringImpl every // call (only the refcount grows, RSS stays flat) and hide the leak. const pad = Buffer.alloc(256 * 1024, "a").toString(); + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; async function hit(i) { const proxyUrl = "http://127.0.0.1:1/" + i + "/" + pad; @@ -463,12 +467,12 @@ describe.each(["string", "object"])("fetch({proxy}) %s form does not leak the pr // Warm up (JIT, allocator page commit). for (let i = 0; i < 20; i++) await hit(-i); Bun.gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); const ITERS = 150; for (let i = 0; i < ITERS; i++) await hit(i); Bun.gc(true); - const final = process.memoryUsage.rss(); + const final = rss(); const deltaMB = (final - baseline) / 1024 / 1024; console.log(JSON.stringify({ @@ -529,6 +533,7 @@ test.concurrent( // Windows strips the leading "/" then asserts is_absolute_windows() in // PosixToWinNormalizer under debug_assertions, which needs a drive letter. const prefix = process.platform === "win32" ? "file:///C:/" : "file:///"; + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; async function hit(i) { // Fresh path per iteration so each leaked ref pins a distinct impl. // The file does not exist; the Response is created (with url_string set) @@ -537,7 +542,7 @@ test.concurrent( } for (let i = 0; i < 200; i++) { try { await hit(-i); } catch {} } Bun.gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); const ITERS = 20000; for (let i = 0; i < ITERS; i++) { @@ -545,7 +550,7 @@ test.concurrent( if ((i & 1023) === 0) Bun.gc(true); } Bun.gc(true); - const final = process.memoryUsage.rss(); + const final = rss(); const deltaMB = (final - baseline) / 1024 / 1024; console.log(JSON.stringify({ @@ -670,6 +675,7 @@ describe("Request body HiveRef pool returns slot via Body.Value.deinit (does not const payload = Buffer.alloc(128 * 1024, 0x61); // 128 KiB of 'a' const str = payload.toString("latin1"); const sharedBlob = new Blob([payload]); + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; function makeBody() { ${ @@ -690,11 +696,11 @@ describe("Request body HiveRef pool returns slot via Body.Value.deinit (does not for (let i = 0; i < 8; i++) cycle(); Bun.gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let i = 0; i < 32; i++) cycle(); Bun.gc(true); - const final = process.memoryUsage.rss(); + const final = rss(); const deltaMB = (final - baseline) / 1024 / 1024; console.log(JSON.stringify({ kind: "${kind}", baselineMB: (baseline / 1024 / 1024) | 0, finalMB: (final / 1024 / 1024) | 0, deltaMB: Math.round(deltaMB) })); diff --git a/test/js/web/fetch/fetch-redirect.test.ts b/test/js/web/fetch/fetch-redirect.test.ts index 73ea51d509ea..edbeed676f22 100644 --- a/test/js/web/fetch/fetch-redirect.test.ts +++ b/test/js/web/fetch/fetch-redirect.test.ts @@ -246,12 +246,13 @@ it("fetch() does not leak intermediate redirect URLs in multi-hop chains", async // pollute the RSS we measure. The child samples RSS after warmup and // again after two equal batches so we can assert on steady-state growth. const script = ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const url = "${server.url.origin}/hop/0"; async function once() { const res = await fetch(url, { redirect: "follow" }); if (await res.text() !== "ok") throw new Error("unexpected body: " + res.status); } - function sample() { Bun.gc(true); return process.memoryUsage.rss(); } + function sample() { Bun.gc(true); return rss(); } for (let i = 0; i < 15; i++) await once(); const rss0 = sample(); for (let i = 0; i < 25; i++) await once(); diff --git a/test/js/web/fetch/fetch.test.ts b/test/js/web/fetch/fetch.test.ts index d4bd8359e8dc..a70b69a0b0a2 100644 --- a/test/js/web/fetch/fetch.test.ts +++ b/test/js/web/fetch/fetch.test.ts @@ -13,6 +13,7 @@ import { isFlaky, isMacOS, isWindows, + rss, tls, tmpdirSync, withoutAggressiveGC, @@ -2925,11 +2926,11 @@ it("releases interim 1xx response bytes as they are parsed while waiting for the try { Bun.gc(true); - const rssBefore = process.memoryUsage.rss(); + const rssBefore = rss(); const responsePromise = fetch(`http://localhost:${port}/`); await floodDone; Bun.gc(true); - const rssDuringFlood = process.memoryUsage.rss(); + const rssDuringFlood = rss(); // Complete the partially written interim response, then send the real response. const socket = sockets[0]; diff --git a/test/js/web/html/FormData-file-error-leak-fixture.ts b/test/js/web/html/FormData-file-error-leak-fixture.ts index 10c3c85c7f58..0ab1c6d28919 100644 --- a/test/js/web/html/FormData-file-error-leak-fixture.ts +++ b/test/js/web/html/FormData-file-error-leak-fixture.ts @@ -15,6 +15,10 @@ // `using tempDir(...)` so cleanup is guaranteed even if this process is // killed by signal. +const rss = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const iterations = parseInt(process.env.ITERATIONS || "100", 10); const warmup = parseInt(process.env.WARMUP || "10", 10); const realPath = process.env.REAL_PATH; @@ -44,11 +48,11 @@ function iterate() { for (let i = 0; i < warmup; i++) iterate(); Bun.gc(true); -const baselineRss = process.memoryUsage.rss(); +const baselineRss = rss(); for (let i = 0; i < iterations; i++) iterate(); Bun.gc(true); -const finalRss = process.memoryUsage.rss(); +const finalRss = rss(); const growthMB = (finalRss - baselineRss) / (1024 * 1024); diff --git a/test/js/web/request/request-clone-leak.test.ts b/test/js/web/request/request-clone-leak.test.ts index 2e1d6b2f823d..9230144e2409 100644 --- a/test/js/web/request/request-clone-leak.test.ts +++ b/test/js/web/request/request-clone-leak.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { isASAN } from "harness"; +import { isASAN, rss } from "harness"; const ASAN_MULTIPLIER = isASAN ? 1 / 10 : 1; @@ -64,7 +64,7 @@ for (let i = 0; i < constructorArgs.length; i++) { } Bun.gc(true); - const baseline = (process.memoryUsage.rss() / 1024 / 1024) | 0; + const baseline = (rss() / 1024 / 1024) | 0; for (let i = 0; i < 2000 * ASAN_MULTIPLIER; i++) { for (let j = 0; j < 500; j++) { new Request(...args); @@ -73,7 +73,7 @@ for (let i = 0; i < constructorArgs.length; i++) { } Bun.gc(true); - const memory = (process.memoryUsage.rss() / 1024 / 1024) | 0; + const memory = (rss() / 1024 / 1024) | 0; const delta = Math.max(memory, baseline) - Math.min(baseline, memory); console.log("RSS delta: ", delta, "MB"); // ASAN's quarantine and redzones retain freed pages so RSS over-reports @@ -92,7 +92,7 @@ for (let i = 0; i < constructorArgs.length; i++) { } Bun.gc(true); - const baseline = (process.memoryUsage.rss() / 1024 / 1024) | 0; + const baseline = (rss() / 1024 / 1024) | 0; for (let i = 0; i < 2000 * ASAN_MULTIPLIER; i++) { for (let j = 0; j < 500 * ASAN_MULTIPLIER; j++) { const request = new Request(...args); @@ -102,7 +102,7 @@ for (let i = 0; i < constructorArgs.length; i++) { } Bun.gc(true); - const memory = (process.memoryUsage.rss() / 1024 / 1024) | 0; + const memory = (rss() / 1024 / 1024) | 0; const delta = Math.max(memory, baseline) - Math.min(baseline, memory); console.log("RSS delta: ", delta, "MB"); // ASAN's quarantine and redzones retain freed pages so RSS over-reports diff --git a/test/js/web/streams/streams-leak.test.ts b/test/js/web/streams/streams-leak.test.ts index 12158559e6fb..0a2862ade9c1 100644 --- a/test/js/web/streams/streams-leak.test.ts +++ b/test/js/web/streams/streams-leak.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isASAN, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, isASAN, isWindows, rss, tempDir } from "harness"; test("native ReadableStream reuses the pull buffer across small reads", async () => { // #getInternalBuffer used to rotate to a fresh autoAllocateChunkSize @@ -154,19 +154,19 @@ test.skipIf(isWindows)( await readAndWrite(BYTES_TO_WRITE); } Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < rounds; i++) { await readAndWrite(); } Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); for (let i = 0; i < rounds; i++) { await readAndWrite(); } Bun.gc(true); - const after2 = process.memoryUsage.rss(); + const after2 = rss(); console.log({ after, after2 }); console.log(require("bun:jsc").heapStats()); console.log("RSS delta", ((after - before) | 0) / 1024 / 1024); diff --git a/test/js/web/structured-clone-blob-file.test.ts b/test/js/web/structured-clone-blob-file.test.ts index 3a4cd9d8ff99..fe50739b5ed6 100644 --- a/test/js/web/structured-clone-blob-file.test.ts +++ b/test/js/web/structured-clone-blob-file.test.ts @@ -1,6 +1,6 @@ import { deserialize, serialize } from "bun:jsc"; import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isASAN, isWindows } from "harness"; +import { bunEnv, bunExe, isASAN, isWindows, rss } from "harness"; import v8 from "node:v8"; describe("structuredClone with Blob and File", () => { @@ -767,10 +767,10 @@ describe("structuredClone with Blob and File", () => { // with it the window is flat modulo a few MiB of noise. for (let i = 0; i < 1000; i++) attempt(); Bun.gc(true); - const rssBefore = process.memoryUsage.rss(); + const rssBefore = rss(); for (let i = 0; i < 1500; i++) attempt(); Bun.gc(true); - const rssAfter = process.memoryUsage.rss(); + const rssAfter = rss(); const deltaMiB = (rssAfter - rssBefore) / 1024 / 1024; // ASAN's quarantine retains freed allocations (default 256 MB) so the diff --git a/test/js/web/structured-clone-fastpath.test.ts b/test/js/web/structured-clone-fastpath.test.ts index 6ad4ec6a2ee4..f6279c458fcb 100644 --- a/test/js/web/structured-clone-fastpath.test.ts +++ b/test/js/web/structured-clone-fastpath.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { isASAN } from "harness"; +import { isASAN, rss } from "harness"; describe("Structured Clone Fast Path", () => { test("structuredClone should work with empty object", () => { @@ -63,13 +63,13 @@ describe("Structured Clone Fast Path", () => { clones.push(structuredClone(largeString)); } Bun.gc(true); - const rss = process.memoryUsage.rss(); + const rss1 = rss(); for (let i = 0; i < 10000; i++) { clones.push(structuredClone(largeString)); } Bun.gc(true); - const rss2 = process.memoryUsage.rss(); - const delta = rss2 - rss; + const rss2 = rss(); + const delta = rss2 - rss1; // ASAN's free quarantine (default 256 MB) plus redzones and glibc page // retention inflate RSS even when nothing is leaking. expect(delta).toBeLessThan(isASAN ? 1024 * 1024 * 400 : 1024 * 1024 * 8); @@ -83,13 +83,13 @@ describe("Structured Clone Fast Path", () => { structuredClone(largeValue); } Bun.gc(true); - const rss = process.memoryUsage.rss(); + const rss1 = rss(); for (let i = 0; i < 10000; i++) { structuredClone(largeValue); } Bun.gc(true); - const rss2 = process.memoryUsage.rss(); - const delta = rss2 - rss; + const rss2 = rss(); + const delta = rss2 - rss1; // ASAN's free quarantine (default 256 MB) plus redzones and glibc page // retention inflate RSS even when nothing is leaking. expect(delta).toBeLessThan(isASAN ? 1024 * 1024 * 400 : 1024 * 1024); diff --git a/test/js/web/timers/setInterval-leak-fixture.js b/test/js/web/timers/setInterval-leak-fixture.js index d0b383523866..b23780a5fb1f 100644 --- a/test/js/web/timers/setInterval-leak-fixture.js +++ b/test/js/web/timers/setInterval-leak-fixture.js @@ -5,9 +5,10 @@ let runs = initialRuns; // run far higher under bun-asan; widen the threshold to avoid false positives. const isASAN = process.execPath.includes("bun-asan"); -function usage() { - return process.memoryUsage.rss(); -} +const usage = + process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; Promise.withResolvers ??= () => { let promise, resolve, reject; diff --git a/test/js/web/timers/setTimeout-clear-in-callback-leak-fixture.js b/test/js/web/timers/setTimeout-clear-in-callback-leak-fixture.js index f3f7d0ae67df..53ba75418e39 100644 --- a/test/js/web/timers/setTimeout-clear-in-callback-leak-fixture.js +++ b/test/js/web/timers/setTimeout-clear-in-callback-leak-fixture.js @@ -12,6 +12,10 @@ if (mode !== "clear" && mode !== "refresh" && mode !== "repeat") { // ASAN's quarantine retains freed allocations (default 256 MB) so RSS deltas // run far higher under bun-asan; widen the threshold to avoid false positives. const isASAN = process.execPath.includes("bun-asan"); +const rss = + process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; const BATCH = 2_000; @@ -60,11 +64,11 @@ async function runBatch() { // warmup for (let i = 0; i < 15; i++) await runBatch(); gc(); -const initial = process.memoryUsage.rss(); +const initial = rss(); for (let i = 0; i < 100; i++) await runBatch(); gc(); -const final = process.memoryUsage.rss(); +const final = rss(); const deltaMB = (final - initial) / 1024 / 1024; console.log("mode:", mode); console.log("initial RSS:", (initial / 1024 / 1024) | 0, "MB"); diff --git a/test/js/web/timers/setinterval-cancel-fixture.js b/test/js/web/timers/setinterval-cancel-fixture.js index 4c5d58939ed3..7cffd8642085 100644 --- a/test/js/web/timers/setinterval-cancel-fixture.js +++ b/test/js/web/timers/setinterval-cancel-fixture.js @@ -2,6 +2,10 @@ const huge = Array.from({ length: 1000000 }, () => 0); huge.fill(0); let hasRun = false; const gc = typeof Bun !== "undefined" ? Bun.gc : typeof globalThis.gc !== "undefined" ? globalThis.gc : () => {}; +const rss = + process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; var timers = new Array(50_000); @@ -18,7 +22,7 @@ function fn(huge) { gc(true); setTimeout(() => { - console.log("RSS:", (process.memoryUsage.rss() / 1024 / 1024) | 0, "MB"); + console.log("RSS:", (rss() / 1024 / 1024) | 0, "MB"); process.exit(0); }, 10); } diff --git a/test/js/web/websocket/websocket.test.js b/test/js/web/websocket/websocket.test.js index 7e69ca8077c7..334c9a46ab4e 100644 --- a/test/js/web/websocket/websocket.test.js +++ b/test/js/web/websocket/websocket.test.js @@ -878,6 +878,7 @@ describe("WebSocket tls option does not leak SSLConfig on error paths", () => { // 256 KiB duped per SSLConfig -> ~128 MiB per path if every iteration leaks. const bigCA = Buffer.alloc(256 * 1024, "A").toString(); const tls = { ca: bigCA, rejectUnauthorized: false }; + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; function hit() { // Path 1: getter on a later option throws after the SSLConfig has @@ -906,11 +907,11 @@ describe("WebSocket tls option does not leak SSLConfig on error paths", () => { // Warm up so one-off allocations (ICU, resolver caches, JIT) settle. for (let i = 0; i < 100; i++) hit(); Bun.gc(true); - const baseline = process.memoryUsage.rss(); + const baseline = rss(); for (let i = 0; i < iterations; i++) hit(); Bun.gc(true); - const after = process.memoryUsage.rss(); + const after = rss(); const growthMiB = (after - baseline) / (1024 * 1024); console.log(JSON.stringify({ baseline, after, growthMiB })); diff --git a/test/js/web/workers/message-port-closed-leak.test.ts b/test/js/web/workers/message-port-closed-leak.test.ts index 13e043333e6a..b9426df4d994 100644 --- a/test/js/web/workers/message-port-closed-leak.test.ts +++ b/test/js/web/workers/message-port-closed-leak.test.ts @@ -10,6 +10,7 @@ describe("MessagePortChannel closed port", () => { bunExe(), "-e", ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const { port1, port2 } = new MessageChannel(); port2.close(); @@ -22,13 +23,13 @@ describe("MessagePortChannel closed port", () => { // Measure: second batch should reuse freed memory if messages // are dropped (not queued) for closed ports. - const rssBefore = process.memoryUsage().rss; + const rssBefore = rss(); for (let i = 0; i < 5000; i++) { port1.postMessage(Buffer.alloc(64 * 1024).toString()); } Bun.gc(true); Bun.gc(true); - const rssAfter = process.memoryUsage().rss; + const rssAfter = rss(); const deltaMB = (rssAfter - rssBefore) / 1024 / 1024; // Without fix: ~300+ MB growth (5000 * 64KB queued) @@ -64,6 +65,7 @@ describe("MessagePortChannel closed port", () => { bunExe(), "-e", ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const closeBeforePost = ${closeBeforePost}; const ITERATIONS = 1000; const PAYLOAD_SIZE = 128 * 1024; @@ -100,9 +102,9 @@ describe("MessagePortChannel closed port", () => { // Warm up to establish allocator high-water mark. round(); - const rssBefore = process.memoryUsage().rss; + const rssBefore = rss(); round(); - const rssAfter = process.memoryUsage().rss; + const rssAfter = rss(); const deltaMB = (rssAfter - rssBefore) / 1024 / 1024; // Without fix: ~130+ MB growth (1000 leaked channels each holding a 128KB string). diff --git a/test/js/web/workers/message-port-context-destroy-leak.test.ts b/test/js/web/workers/message-port-context-destroy-leak.test.ts index 1eff0816d06f..270d44248a85 100644 --- a/test/js/web/workers/message-port-context-destroy-leak.test.ts +++ b/test/js/web/workers/message-port-context-destroy-leak.test.ts @@ -20,6 +20,7 @@ test.skipIf(isWindows)( bunExe(), "-e", ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const workerBody = ${JSON.stringify(` const keep = []; for (let i = 0; i < 8000; i++) { @@ -47,11 +48,11 @@ test.skipIf(isWindows)( Bun.gc(true); Bun.gc(true); - const rssBefore = process.memoryUsage().rss; + const rssBefore = rss(); for (let i = 0; i < 8; i++) await runWorker(); Bun.gc(true); Bun.gc(true); - const rssAfter = process.memoryUsage().rss; + const rssAfter = rss(); const deltaMB = (rssAfter - rssBefore) / 1024 / 1024; // 8 workers × 8000 ports: when leaking, each MessagePort plus its diff --git a/test/js/web/workers/performance-observer-leak.test.ts b/test/js/web/workers/performance-observer-leak.test.ts index 63109aa0e87b..ee25e0a8d3de 100644 --- a/test/js/web/workers/performance-observer-leak.test.ts +++ b/test/js/web/workers/performance-observer-leak.test.ts @@ -24,6 +24,7 @@ test("PerformanceObserver without disconnect() does not leak Performance when Wo // Intentionally never call observer.disconnect(). `, "main.js": ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const workerPath = new URL("./worker.js", import.meta.url).href; async function runOne() { @@ -43,12 +44,12 @@ test("PerformanceObserver without disconnect() does not leak Performance when Wo // Warm up: establish allocator / JIT high-water mark. await runBatch(8); - const rssBefore = process.memoryUsage.rss(); + const rssBefore = rss(); // Measured batch: if each worker leaks ~4 MB via the Performance <-> // PerformanceObserver cycle, 20 iterations leak ~80 MB over baseline. await runBatch(20); - const rssAfter = process.memoryUsage.rss(); + const rssAfter = rss(); const deltaMB = (rssAfter - rssBefore) / 1024 / 1024; // Without fix: ~110+ MB (baseline ~30 MB + 20 * 4 MB leaked). diff --git a/test/js/workerd/html-rewriter-leak.test.ts b/test/js/workerd/html-rewriter-leak.test.ts index c51e13d93983..d90a8f7b5d42 100644 --- a/test/js/workerd/html-rewriter-leak.test.ts +++ b/test/js/workerd/html-rewriter-leak.test.ts @@ -60,6 +60,7 @@ test.skipIf(isDebug)( "HTMLRewriter does not leak element/document handler allocations", async () => { const code = /* js */ ` + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const noop = { element() {}, comments() {}, text() {} }; const docNoop = { doctype() {}, comments() {}, text() {}, end() {} }; @@ -73,7 +74,7 @@ test.skipIf(isDebug)( function pass() { for (let i = 0; i < N; i++) once(); Bun.gc(true); - return process.memoryUsage.rss(); + return rss(); } pass(); pass(); diff --git a/test/napi/napi-app/leak-fixture.js b/test/napi/napi-app/leak-fixture.js index 9c7891daffc7..cecc7074b84d 100644 --- a/test/napi/napi-app/leak-fixture.js +++ b/test/napi/napi-app/leak-fixture.js @@ -1,8 +1,13 @@ // UNUSED as of #14501 const nativeTests = require("./build/Debug/napitests.node"); +const rss = + process.platform === "darwin" && typeof Bun !== "undefined" && typeof Bun.unsafe.memoryFootprint === "function" + ? Bun.unsafe.memoryFootprint + : process.memoryUsage.rss; + function usage() { - return process.memoryUsage.rss(); + return rss(); } function gc() { diff --git a/test/napi/napi.test.ts b/test/napi/napi.test.ts index 651e7749806b..5c34d3b024dd 100644 --- a/test/napi/napi.test.ts +++ b/test/napi/napi.test.ts @@ -1231,19 +1231,20 @@ describe.skipIf(!canBuildNodeAddons())("napi_create_string_latin1", () => { it("does not leak the WTFStringImpl", async () => { const fixture = /* js */ ` const nativeTests = require(${JSON.stringify(join(__dirname, "napi-app/build/Debug/napitests.node"))}); + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; const size = 256 * 1024; for (let i = 0; i < 20; i++) { const s = nativeTests.create_latin1_string(size); if (s.length !== size) throw new Error("wrong length: " + s.length); } Bun.gc(true); - const before = process.memoryUsage.rss(); + const before = rss(); for (let i = 0; i < 300; i++) { const s = nativeTests.create_latin1_string(size); if (s.length !== size) throw new Error("wrong length: " + s.length); } Bun.gc(true); - const growthMB = (process.memoryUsage.rss() - before) / 1024 / 1024; + const growthMB = (rss() - before) / 1024 / 1024; console.error("RSS growth: " + growthMB.toFixed(1) + " MB"); process.exit(growthMB > Number(process.env.THRESHOLD_MB) ? 1 : 0); `; diff --git a/test/regression/brotli-reset-leak.test.ts b/test/regression/brotli-reset-leak.test.ts index 54aab52d941d..bb2a9093b276 100644 --- a/test/regression/brotli-reset-leak.test.ts +++ b/test/regression/brotli-reset-leak.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { isASAN } from "harness"; +import { isASAN, rss } from "harness"; import { createBrotliCompress, createBrotliDecompress } from "zlib"; // ASAN's quarantine retains freed allocations (default 256 MB) so RSS deltas @@ -16,7 +16,7 @@ test("Brotli reset() should not leak memory", { timeout: 30_000 }, async () => { // Get baseline memory Bun.gc(true); await Bun.sleep(10); - const baselineMemory = process.memoryUsage.rss(); + const baselineMemory = rss(); const compressor = createBrotliCompress(); @@ -30,7 +30,7 @@ test("Brotli reset() should not leak memory", { timeout: 30_000 }, async () => { // Force GC and measure Bun.gc(true); await Bun.sleep(10); - const finalMemory = process.memoryUsage.rss(); + const finalMemory = rss(); const memoryGrowth = finalMemory - baselineMemory; const memoryGrowthMB = memoryGrowth / 1024 / 1024; @@ -47,7 +47,7 @@ test("BrotliDecompress reset() should not leak memory", { timeout: 30_000 }, asy Bun.gc(true); await Bun.sleep(10); - const baselineMemory = process.memoryUsage.rss(); + const baselineMemory = rss(); const decompressor = createBrotliDecompress(); @@ -59,7 +59,7 @@ test("BrotliDecompress reset() should not leak memory", { timeout: 30_000 }, asy Bun.gc(true); await Bun.sleep(10); - const finalMemory = process.memoryUsage.rss(); + const finalMemory = rss(); const memoryGrowth = finalMemory - baselineMemory; const memoryGrowthMB = memoryGrowth / 1024 / 1024; diff --git a/test/regression/issue/26088.test.ts b/test/regression/issue/26088.test.ts index 1a0d3ada2128..93d260f39f1c 100644 --- a/test/regression/issue/26088.test.ts +++ b/test/regression/issue/26088.test.ts @@ -1,6 +1,6 @@ import { YAML } from "bun"; import { expect, test } from "bun:test"; -import { isASAN } from "harness"; +import { isASAN, rss } from "harness"; // https://github.com/oven-sh/bun/issues/26088 // YAML parser was leaking memory on each parse call because AST nodes were @@ -12,7 +12,7 @@ test("YAML.parse shouldn't leak memory", () => { const yaml = `list:\n${items}`; Bun.gc(true); - const initialMemory = process.memoryUsage.rss(); + const initialMemory = rss(); // Parse 100 times - each creates 10000 AST string nodes for (let i = 0; i < 100; i++) { @@ -20,7 +20,7 @@ test("YAML.parse shouldn't leak memory", () => { } Bun.gc(true); - const finalMemory = process.memoryUsage.rss(); + const finalMemory = rss(); // Memory increase should be less than 50MB if AST nodes are freed properly const memoryIncreaseMB = (finalMemory - initialMemory) / 1024 / 1024; diff --git a/test/regression/issue/28632.test.ts b/test/regression/issue/28632.test.ts index b7c5e0eb8ba9..96d7423f6b85 100644 --- a/test/regression/issue/28632.test.ts +++ b/test/regression/issue/28632.test.ts @@ -1,7 +1,7 @@ // https://github.com/oven-sh/bun/issues/28632 import { SQL } from "bun"; import { beforeAll, expect, test } from "bun:test"; -import { describeWithContainer, isASAN, isDebug, isDockerEnabled } from "harness"; +import { describeWithContainer, isASAN, isDebug, isDockerEnabled, rss } from "harness"; if (isDockerEnabled()) { describeWithContainer( @@ -52,7 +52,7 @@ if (isDockerEnabled()) { } Bun.gc(true); await Bun.sleep(50); - const rssAfterWarmup = process.memoryUsage.rss(); + const rssAfterWarmup = rss(); // Run queries — each re-decodes 50 column definitions. Collect every 500 // so the RSS delta reflects the name_or_index leak rather than the @@ -66,7 +66,7 @@ if (isDockerEnabled()) { } Bun.gc(true); await Bun.sleep(50); - const rssAfterQueries = process.memoryUsage.rss(); + const rssAfterQueries = rss(); const growthMB = (rssAfterQueries - rssAfterWarmup) / 1024 / 1024; diff --git a/test/regression/issue/28756.test.ts b/test/regression/issue/28756.test.ts index fcc997ec64c3..ba59cd1311c1 100644 --- a/test/regression/issue/28756.test.ts +++ b/test/regression/issue/28756.test.ts @@ -12,6 +12,7 @@ test("AbortSignal.timeout + util.aborted does not leak memory", async () => { "-e", ` const { aborted } = require("util"); + const rss = process.platform === "darwin" && typeof Bun.unsafe.memoryFootprint === "function" ? Bun.unsafe.memoryFootprint : process.memoryUsage.rss; // 10 batches * 8000 = 80k signals. // Without fix: leaks ~0.8 KB/signal => ~60 MB growth. @@ -30,7 +31,7 @@ test("AbortSignal.timeout + util.aborted does not leak memory", async () => { Bun.gc(true); await new Promise(r => setTimeout(r, 50)); Bun.gc(true); - const baselineRSS = process.memoryUsage().rss; + const baselineRSS = rss(); for (let iter = 0; iter < iterations; iter++) { for (let i = 0; i < batchSize; i++) { @@ -49,7 +50,7 @@ test("AbortSignal.timeout + util.aborted does not leak memory", async () => { await new Promise(r => setTimeout(r, 100)); Bun.gc(true); - const finalRSS = process.memoryUsage().rss; + const finalRSS = rss(); const growth = finalRSS - baselineRSS; const growthMB = (growth / 1024 / 1024).toFixed(1); console.log(JSON.stringify({ baselineMB: (baselineRSS/1024/1024).toFixed(1), finalMB: (finalRSS/1024/1024).toFixed(1), growthMB }));