Re-enable heap breakdown

This commit is contained in:
Jarred Sumner
2025-12-17 18:38:26 -08:00
parent ffd2240c31
commit eb7db6255e
9 changed files with 50 additions and 17 deletions

View File

@@ -34,6 +34,7 @@ const BunBuildOptions = struct {
enable_asan: bool,
enable_fuzzilli: bool,
enable_valgrind: bool,
enable_heap_breakdown: bool,
use_mimalloc: bool,
tracy_callstack_depth: u16,
reported_nodejs_version: Version,
@@ -84,6 +85,7 @@ const BunBuildOptions = struct {
opts.addOption(bool, "enable_asan", this.enable_asan);
opts.addOption(bool, "enable_fuzzilli", this.enable_fuzzilli);
opts.addOption(bool, "enable_valgrind", this.enable_valgrind);
opts.addOption(bool, "enable_heap_breakdown", this.enable_heap_breakdown);
opts.addOption(bool, "use_mimalloc", this.use_mimalloc);
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{f}", .{this.reported_nodejs_version}));
opts.addOption(bool, "zig_self_hosted_backend", this.no_llvm);
@@ -259,6 +261,7 @@ pub fn build(b: *Build) !void {
.enable_asan = b.option(bool, "enable_asan", "Enable asan") orelse false,
.enable_fuzzilli = b.option(bool, "enable_fuzzilli", "Enable fuzzilli instrumentation") orelse false,
.enable_valgrind = b.option(bool, "enable_valgrind", "Enable valgrind") orelse false,
.enable_heap_breakdown = b.option(bool, "enable_heap_breakdown", "Enable malloc heap breakdown (macOS only)") orelse false,
.use_mimalloc = b.option(bool, "use_mimalloc", "Use mimalloc as default allocator") orelse false,
.llvm_codegen_threads = b.option(u32, "llvm_codegen_threads", "Number of threads to use for LLVM codegen") orelse 1,
};
@@ -494,6 +497,7 @@ fn addMultiCheck(
.enable_asan = root_build_options.enable_asan,
.enable_valgrind = root_build_options.enable_valgrind,
.enable_fuzzilli = root_build_options.enable_fuzzilli,
.enable_heap_breakdown = root_build_options.enable_heap_breakdown,
.use_mimalloc = root_build_options.use_mimalloc,
.override_no_export_cpp_apis = root_build_options.override_no_export_cpp_apis,
};
@@ -609,7 +613,6 @@ fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void {
obj.no_link_obj = opts.os != .windows and !opts.no_llvm;
if (opts.enable_asan and !enableFastBuild(b)) {
if (@hasField(Build.Module, "sanitize_address")) {
if (opts.enable_fuzzilli) {

View File

@@ -129,6 +129,13 @@ endif()
optionx(ENABLE_FUZZILLI BOOL "If fuzzilli support should be enabled" DEFAULT OFF)
optionx(ENABLE_HEAP_BREAKDOWN BOOL "If malloc heap breakdown support should be enabled (macOS only)" DEFAULT OFF)
if(ENABLE_HEAP_BREAKDOWN AND NOT APPLE)
message(WARNING "ENABLE_HEAP_BREAKDOWN is only supported on macOS, disabling")
setx(ENABLE_HEAP_BREAKDOWN OFF)
endif()
if(RELEASE AND LINUX AND CI AND NOT ENABLE_ASSERTIONS AND NOT ENABLE_ASAN)
set(DEFAULT_LTO ON)
else()

View File

@@ -697,6 +697,7 @@ register_command(
-Denable_asan=$<IF:$<BOOL:${ENABLE_ZIG_ASAN}>,true,false>
-Denable_fuzzilli=$<IF:$<BOOL:${ENABLE_FUZZILLI}>,true,false>
-Denable_valgrind=$<IF:$<BOOL:${ENABLE_VALGRIND}>,true,false>
-Denable_heap_breakdown=$<IF:$<BOOL:${ENABLE_HEAP_BREAKDOWN}>,true,false>
-Duse_mimalloc=$<IF:$<BOOL:${USE_MIMALLOC_AS_DEFAULT_ALLOCATOR}>,true,false>
-Dllvm_codegen_threads=${LLVM_ZIG_CODEGEN_THREADS}
-Dversion=${VERSION}

View File

@@ -29,11 +29,11 @@ if(WEBKIT_LOCAL)
include_directories(
${WEBKIT_PATH}
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
${WEBKIT_PATH}/JavaScriptCore/DerivedSources/inspector
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
${WEBKIT_PATH}/bmalloc/Headers
${WEBKIT_PATH}/WTF/Headers
${WEBKIT_PATH}/JavaScriptCore/DerivedSources/inspector
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
)
endif()

View File

@@ -45,6 +45,7 @@
"build:smol": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -B build/release-smol",
"build:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DWEBKIT_LOCAL=ON -B build/debug-local",
"build:release:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DWEBKIT_LOCAL=ON -B build/release-local",
"build:release:local:heap-breakdown": "NO_SCCACHE=1 bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DWEBKIT_LOCAL=ON -DWEBKIT_PATH=$PWD/../WebKit/WebKitBuild/ReleaseHeapBreakdown -DENABLE_HEAP_BREAKDOWN=ON -DENABLE_ASAN=OFF -B build/release-heap-breakdown",
"build:release:with_logs": "cmake . -DCMAKE_BUILD_TYPE=Release -DENABLE_LOGS=true -GNinja -Bbuild-release && ninja -Cbuild-release",
"build:debug-zig-release": "cmake . -DCMAKE_BUILD_TYPE=Release -DZIG_OPTIMIZE=Debug -GNinja -Bbuild-debug-zig-release && ninja -Cbuild-debug-zig-release",
"run:linux": "docker run --rm -v \"$PWD:/root/bun/\" -w /root/bun ghcr.io/oven-sh/bun-development-docker-image",
@@ -54,6 +55,7 @@
"jsc:build": "bun ./scripts/build-jsc.ts release",
"jsc:build:debug": "bun ./scripts/build-jsc.ts debug",
"jsc:build:lto": "bun ./scripts/build-jsc.ts lto",
"jsc:build:heap-breakdown": "bun ./scripts/build-jsc.ts heap-breakdown",
"typecheck": "tsc --noEmit && cd test && bun run typecheck",
"fmt": "bun run prettier",
"fmt:cpp": "bun run clang-format",

View File

@@ -5,12 +5,12 @@ import { arch, platform } from "os";
import { join, resolve } from "path";
// Build configurations
type BuildConfig = "debug" | "release" | "lto";
type BuildConfig = "debug" | "release" | "lto" | "heap-breakdown";
// Parse command line arguments
const args = process.argv.slice(2);
const buildConfig: BuildConfig = (args[0] as BuildConfig) || "debug";
const validConfigs = ["debug", "release", "lto"];
const validConfigs = ["debug", "release", "lto", "heap-breakdown"];
if (!validConfigs.includes(buildConfig)) {
console.error(`Invalid build configuration: ${buildConfig}`);
@@ -32,6 +32,7 @@ const WEBKIT_BUILD_DIR = join(WEBKIT_DIR, "WebKitBuild");
const WEBKIT_RELEASE_DIR = join(WEBKIT_BUILD_DIR, "Release");
const WEBKIT_DEBUG_DIR = join(WEBKIT_BUILD_DIR, "Debug");
const WEBKIT_RELEASE_DIR_LTO = join(WEBKIT_BUILD_DIR, "ReleaseLTO");
const WEBKIT_HEAP_BREAKDOWN_DIR = join(WEBKIT_BUILD_DIR, "ReleaseHeapBreakdown");
// Homebrew prefix detection
const HOMEBREW_PREFIX = IS_ARM64 ? "/opt/homebrew/" : "/usr/local/";
@@ -57,6 +58,8 @@ const getBuildDir = (config: BuildConfig) => {
return WEBKIT_DEBUG_DIR;
case "lto":
return WEBKIT_RELEASE_DIR_LTO;
case "heap-breakdown":
return WEBKIT_HEAP_BREAKDOWN_DIR;
default:
return WEBKIT_RELEASE_DIR;
}
@@ -122,6 +125,10 @@ const getBuildFlags = (config: BuildConfig) => {
flags.push("-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_C_FLAGS=-flto=full", "-DCMAKE_CXX_FLAGS=-flto=full");
break;
case "heap-breakdown":
flags.push("-DCMAKE_BUILD_TYPE=RelWithDebInfo", "-DENABLE_MALLOC_HEAP_BREAKDOWN=ON");
break;
default: // release
flags.push("-DCMAKE_BUILD_TYPE=RelWithDebInfo");
break;
@@ -172,7 +179,7 @@ function runCommand(command: string, args: string[], options: any = {}) {
}
// Main build function
function buildJSC() {
async function buildJSC() {
const buildDir = getBuildDir(buildConfig);
const cmakeFlags = getBuildFlags(buildConfig);
const env = getBuildEnv();
@@ -198,18 +205,27 @@ function buildJSC() {
// Build with CMake
console.log("\n🔨 Building JSC...");
const buildType = buildConfig === "debug" ? "Debug" : buildConfig === "lto" ? "Release" : "RelWithDebInfo";
const buildType = buildConfig === "debug" ? "Debug" : buildConfig === "lto" ? "Release" : "RelWithDebInfo"; // heap-breakdown uses RelWithDebInfo
runCommand("cmake", ["--build", buildDir, "--config", buildType, "--target", "jsc"], {
cwd: buildDir,
env,
});
// Remove duplicate InspectorProtocolObjects.h to prevent redefinition errors when building Bun
// The file exists in both DerivedSources/inspector and PrivateHeaders/JavaScriptCore,
// and #pragma once doesn't prevent double-inclusion from different paths
const duplicateHeader = join(buildDir, "JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h");
if (await Bun.file(duplicateHeader).exists()) {
console.log("\n🧹 Removing duplicate InspectorProtocolObjects.h...");
await Bun.$`rm ${duplicateHeader}`;
}
console.log(`\n✅ JSC build completed successfully!`);
console.log(`Build output: ${buildDir}`);
}
// Entry point
if (import.meta.main) {
buildJSC();
await buildJSC();
}

View File

@@ -50,7 +50,7 @@
#include <JavaScriptCore/ControlFlowProfiler.h>
#if OS(DARWIN)
#if ASSERT_ENABLED
#if (ASSERT_ENABLED || ENABLE(MALLOC_HEAP_BREAKDOWN))
#if !__has_feature(address_sanitizer)
#include <malloc/malloc.h>
#define IS_MALLOC_DEBUGGING_ENABLED 1
@@ -310,13 +310,16 @@ JSC_DEFINE_HOST_FUNCTION(functionMemoryUsageStatistics,
Vector<std::pair<Identifier, size_t>> zoneSizes;
zoneSizes.reserveInitialCapacity(count);
for (unsigned i = 0; i < count; i++) {
auto zone = reinterpret_cast<malloc_zone_t*>(zones[i]);
if (const char* name = malloc_get_zone_name(zone)) {
malloc_zone_statistics(reinterpret_cast<malloc_zone_t*>(zones[i]),
&zone_stats);
zoneSizes.append(
std::make_pair(Identifier::fromString(vm, String::fromUTF8(name)),
zone_stats.size_in_use));
auto* zone = reinterpret_cast<malloc_zone_t*>(zones[i]);
if (zone) {
if (const char* name = malloc_get_zone_name(zone)) {
auto nameString = String::fromUTF8(name);
malloc_zone_statistics(reinterpret_cast<malloc_zone_t*>(zones[i]),
&zone_stats);
zoneSizes.append(
std::make_pair(Identifier::fromString(vm, nameString),
zone_stats.size_in_use));
}
}
}

View File

@@ -51,6 +51,7 @@ pub const dump_source = isDebug and !isTest;
pub const base_path = build_options.base_path;
pub const enable_logs = build_options.enable_logs;
pub const enable_asan = build_options.enable_asan;
pub const enable_heap_breakdown = build_options.enable_heap_breakdown;
pub const enable_fuzzilli = build_options.enable_fuzzilli;
pub const codegen_path = build_options.codegen_path;
pub const codegen_embed = build_options.codegen_embed;

View File

@@ -1,6 +1,6 @@
const vm_size_t = usize;
pub const enabled = Environment.allow_assert and Environment.isMac and !Environment.enable_asan;
pub const enabled = Environment.enable_heap_breakdown and Environment.isMac;
fn heapLabel(comptime T: type) [:0]const u8 {
const base_name = if (comptime bun.meta.hasDecl(T, "heap_label"))