fence (25.1c): the census reads the heap outside Ludic's blocks; blocks counted at malloc's own size
The census's native line is malloc's live bytes over every zone since judging began less what Ludic's tracked blocks kept - the libraries' and drivers' growth, read before the census file is opened. A tracked block counts malloc_size(), not the size asked for, so kept is what the heap pays and the residual carries no rounding. @malloc_zone_statistics is declared once, by the fence or by Os.heap_bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a8d54e9878
commit
691964877b
7 changed files with 47519 additions and 47279 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
# alloc_fence_leak.ludic - frame 50 keeps one record in a list that outlives it. The fence fails
|
# alloc_fence_leak.ludic - frame 50 keeps one record in a list that outlives it. The fence fails
|
||||||
# the run there (exit 86) and names this file's `new Box` line; under R3D_ALLOC_FENCE=count it
|
# the run there (exit 86) and names this file's `new Box` line; under R3D_ALLOC_FENCE=count it
|
||||||
# runs on and prints `kept 72 bad 1` (the record and the list's first growth, one frame).
|
# runs on and prints `kept 80 bad 1` (the record and the list's growth, at malloc's own sizes, in one frame).
|
||||||
program AllocFenceLeak {
|
program AllocFenceLeak {
|
||||||
property Box { a: int = 0, b: int = 0 }
|
property Box { a: int = 0, b: int = 0 }
|
||||||
entry {
|
entry {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ var g_site_kinds: []pointer = new []pointer # kinds seen, and their constants
|
||||||
var g_site_kindc: []pointer = new []pointer
|
var g_site_kindc: []pointer = new []pointer
|
||||||
var g_site_next: pointer = null # a kind the next allocation takes (`new Foo`)
|
var g_site_next: pointer = null # a kind the next allocation takes (`new Foo`)
|
||||||
var g_atexit_declared: bool = false # @atexit is declared once: by the fence or by --coverage
|
var g_atexit_declared: bool = false # @atexit is declared once: by the fence or by --coverage
|
||||||
|
var g_mzs_declared: bool = false # @malloc_zone_statistics: by the fence or by Os.heap_bytes
|
||||||
# the build's defaults (--fence=, --fence-warm=, --fence-census=, else a `fence` line in the nearest
|
# the build's defaults (--fence=, --fence-warm=, --fence-census=, else a `fence` line in the nearest
|
||||||
# package.ludic); the environment overrides each at run time (R3D_ALLOC_FENCE, _WARM, _CENSUS)
|
# package.ludic); the environment overrides each at run time (R3D_ALLOC_FENCE, _WARM, _CENSUS)
|
||||||
var g_fence_mode: int = 0 # 0: headless fails, windowed fails only under R3D_DEV
|
var g_fence_mode: int = 0 # 0: headless fails, windowed fails only under R3D_DEV
|
||||||
|
|
@ -30,6 +31,7 @@ function fence_reset() -> void {
|
||||||
g_site_fn = new []pointer; g_site_file = new []pointer; g_site_line = new []int; g_site_kind = new []pointer
|
g_site_fn = new []pointer; g_site_file = new []pointer; g_site_line = new []int; g_site_kind = new []pointer
|
||||||
g_site_kinds = new []pointer; g_site_kindc = new []pointer
|
g_site_kinds = new []pointer; g_site_kindc = new []pointer
|
||||||
g_atexit_declared = false
|
g_atexit_declared = false
|
||||||
|
g_mzs_declared = false
|
||||||
g_cur_fn_name = "?"; g_site_fnc = null; g_site_filen = null; g_site_filec = null; g_site_next = null
|
g_cur_fn_name = "?"; g_site_fnc = null; g_site_filen = null; g_site_filec = null; g_site_next = null
|
||||||
push(g_site_fn, null); push(g_site_file, null); push(g_site_line, 0); push(g_site_kind, null) # site 0, named at the end
|
push(g_site_fn, null); push(g_site_file, null); push(g_site_line, 0); push(g_site_kind, null) # site 0, named at the end
|
||||||
}
|
}
|
||||||
|
|
@ -168,6 +170,7 @@ function emit_fence_runtime() -> void {
|
||||||
if g_target_win { emit_fence_win(); return }
|
if g_target_win { emit_fence_win(); return }
|
||||||
if not g_fprintf_declared { emith("declare i32 @fprintf(ptr, ptr, ...)\n"); g_fprintf_declared = true }
|
if not g_fprintf_declared { emith("declare i32 @fprintf(ptr, ptr, ...)\n"); g_fprintf_declared = true }
|
||||||
if not g_atexit_declared { emith("declare i32 @atexit(ptr)\n"); g_atexit_declared = true }
|
if not g_atexit_declared { emith("declare i32 @atexit(ptr)\n"); g_atexit_declared = true }
|
||||||
|
if not g_mzs_declared { emith("declare extern_weak void @malloc_zone_statistics(ptr, ptr)\n"); g_mzs_declared = true }
|
||||||
emit_fence_globals()
|
emit_fence_globals()
|
||||||
emit_fence_entry()
|
emit_fence_entry()
|
||||||
emit_fence_table()
|
emit_fence_table()
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ function emit_fence_globals() -> void {
|
||||||
emith("@lp_fkept = global i64 0\n")
|
emith("@lp_fkept = global i64 0\n")
|
||||||
emith("@lp_fbad = global i64 0\n")
|
emith("@lp_fbad = global i64 0\n")
|
||||||
emith("@lp_fjudged = global i64 0\n")
|
emith("@lp_fjudged = global i64 0\n")
|
||||||
|
emith("@lp_zset = global i64 0\n")
|
||||||
emith("@lp_census = global ptr null\n")
|
emith("@lp_census = global ptr null\n")
|
||||||
emith("declare extern_weak i64 @malloc_size(ptr)\n")
|
emith("declare extern_weak i64 @malloc_size(ptr)\n")
|
||||||
emith("declare i32 @dprintf(i32, ptr, ...)\n")
|
emith("declare i32 @dprintf(i32, ptr, ...)\n")
|
||||||
|
|
@ -58,6 +59,7 @@ function emit_fence_globals() -> void {
|
||||||
emith("@.lpf_auto = private unnamed_addr constant [15 x i8] c\"R3D_ALLOC_AUTO\\00\"\n")
|
emith("@.lpf_auto = private unnamed_addr constant [15 x i8] c\"R3D_ALLOC_AUTO\\00\"\n")
|
||||||
emith("@.lpf_w = private unnamed_addr constant [2 x i8] c\"w\\00\"\n")
|
emith("@.lpf_w = private unnamed_addr constant [2 x i8] c\"w\\00\"\n")
|
||||||
emith("@.lpf_chdr = private unnamed_addr constant [76 x i8] c\"alloc-census frames %lld bad %lld kept %lld made %lld freed %lld over %lld\\0A\\00\"\n")
|
emith("@.lpf_chdr = private unnamed_addr constant [76 x i8] c\"alloc-census frames %lld bad %lld kept %lld made %lld freed %lld over %lld\\0A\\00\"\n")
|
||||||
|
emith("@.lpf_cnat = private unnamed_addr constant [91 x i8] c\"native %lld (the heap outside Ludic's blocks, since judging began: libraries and drivers)\\0A\\00\"\n")
|
||||||
emith("@.lpf_crow = private unnamed_addr constant [23 x i8] c\"site %lld %s %s:%d %s\\0A\\00\"\n")
|
emith("@.lpf_crow = private unnamed_addr constant [23 x i8] c\"site %lld %s %s:%d %s\\0A\\00\"\n")
|
||||||
emith("@.lpf_from = private unnamed_addr constant [15 x i8] c\" from %s\\0A\\00\"\n")
|
emith("@.lpf_from = private unnamed_addr constant [15 x i8] c\" from %s\\0A\\00\"\n")
|
||||||
emith("@.lpf_cfrom = private unnamed_addr constant [11 x i8] c\" from %s\\0A\\00\"\n")
|
emith("@.lpf_cfrom = private unnamed_addr constant [11 x i8] c\" from %s\\0A\\00\"\n")
|
||||||
|
|
@ -277,11 +279,18 @@ function emit_fence_table() -> void {
|
||||||
emith("out:\n")
|
emith("out:\n")
|
||||||
emith(" ret void\n")
|
emith(" ret void\n")
|
||||||
emith("}\n")
|
emith("}\n")
|
||||||
emith("define internal void @lp_fadd(ptr %p, i64 %n) {\n")
|
emith("define internal void @lp_fadd(ptr %p, i64 %asked) {\n")
|
||||||
emith("entry:\n")
|
emith("entry:\n")
|
||||||
emith(" %isnull = icmp eq ptr %p, null\n")
|
emith(" %isnull = icmp eq ptr %p, null\n")
|
||||||
emith(" br i1 %isnull, label %out, label %go\n")
|
emith(" br i1 %isnull, label %out, label %size\n")
|
||||||
|
emith("size:\n")
|
||||||
|
emith(" %hasms = icmp ne ptr @malloc_size, null\n")
|
||||||
|
emith(" br i1 %hasms, label %real, label %go\n")
|
||||||
|
emith("real:\n")
|
||||||
|
emith(" %ms = call i64 @malloc_size(ptr %p)\n")
|
||||||
|
emith(" br label %go\n")
|
||||||
emith("go:\n")
|
emith("go:\n")
|
||||||
|
emith(" %n = phi i64 [ %asked, %size ], [ %ms, %real ]\n")
|
||||||
emith(" call void @lp_flock()\n")
|
emith(" call void @lp_flock()\n")
|
||||||
emith(" %cnt = load i64, ptr @lp_tabn\n")
|
emith(" %cnt = load i64, ptr @lp_tabn\n")
|
||||||
emith(" %max = load i64, ptr @lp_tabmax\n")
|
emith(" %max = load i64, ptr @lp_tabmax\n")
|
||||||
|
|
@ -749,6 +758,7 @@ function emit_fence_report() -> void {
|
||||||
emith(" %none = icmp eq ptr %path, null\n")
|
emith(" %none = icmp eq ptr %path, null\n")
|
||||||
emith(" br i1 %none, label %out, label %open\n")
|
emith(" br i1 %none, label %out, label %open\n")
|
||||||
emith("open:\n")
|
emith("open:\n")
|
||||||
|
emith(" %zn = call i64 @lp_fzone()\n")
|
||||||
emith(" %fp = call ptr @fopen(ptr %path, ptr @.lpf_w)\n")
|
emith(" %fp = call ptr @fopen(ptr %path, ptr @.lpf_w)\n")
|
||||||
emith(" %bad = icmp eq ptr %fp, null\n")
|
emith(" %bad = icmp eq ptr %fp, null\n")
|
||||||
emith(" br i1 %bad, label %out, label %write\n")
|
emith(" br i1 %bad, label %out, label %write\n")
|
||||||
|
|
@ -761,6 +771,12 @@ function emit_fence_report() -> void {
|
||||||
emith(" %fr = load i64, ptr @lp_freed\n")
|
emith(" %fr = load i64, ptr @lp_freed\n")
|
||||||
emith(" %ov = load i64, ptr @lp_over\n")
|
emith(" %ov = load i64, ptr @lp_over\n")
|
||||||
emith(" call i32 (ptr, ptr, ...) @fprintf(ptr %fp, ptr @.lpf_chdr, i64 %j, i64 %b, i64 %k, i64 %md, i64 %fr, i64 %ov)\n")
|
emith(" call i32 (ptr, ptr, ...) @fprintf(ptr %fp, ptr @.lpf_chdr, i64 %j, i64 %b, i64 %k, i64 %md, i64 %fr, i64 %ov)\n")
|
||||||
|
emith(" %jq = icmp ne i64 %j, 0\n")
|
||||||
|
emith(" %zs = load i64, ptr @lp_zset\n")
|
||||||
|
emith(" %zd = sub i64 %zn, %zs\n")
|
||||||
|
emith(" %nat = sub i64 %zd, %k\n")
|
||||||
|
emith(" %nat1 = select i1 %jq, i64 %nat, i64 0\n")
|
||||||
|
emith(" call i32 (ptr, ptr, ...) @fprintf(ptr %fp, ptr @.lpf_cnat, i64 %nat1)\n")
|
||||||
emith(" %sn = load i32, ptr @lp_site_n\n")
|
emith(" %sn = load i32, ptr @lp_site_n\n")
|
||||||
emith(" call void @lp_franked(ptr %fp, ptr @lp_site_base, i1 1, i32 %sn, i32 40)\n")
|
emith(" call void @lp_franked(ptr %fp, ptr @lp_site_base, i1 1, i32 %sn, i32 40)\n")
|
||||||
emith(" call i32 @fclose(ptr %fp)\n")
|
emith(" call i32 @fclose(ptr %fp)\n")
|
||||||
|
|
@ -797,6 +813,20 @@ function emit_fence_frame() -> void {
|
||||||
emith(" call void @lp_fsnap()\n")
|
emith(" call void @lp_fsnap()\n")
|
||||||
emith(" ret void\n")
|
emith(" ret void\n")
|
||||||
emith("}\n")
|
emith("}\n")
|
||||||
|
emith("; malloc's live bytes over every zone (0 where there is no malloc_zone_statistics)\n")
|
||||||
|
emith("define internal i64 @lp_fzone() {\n")
|
||||||
|
emith("entry:\n")
|
||||||
|
emith(" %has = icmp ne ptr @malloc_zone_statistics, null\n")
|
||||||
|
emith(" br i1 %has, label %read, label %none\n")
|
||||||
|
emith("none:\n")
|
||||||
|
emith(" ret i64 0\n")
|
||||||
|
emith("read:\n")
|
||||||
|
emith(" %st = alloca [4 x i64], align 8\n")
|
||||||
|
emith(" call void @malloc_zone_statistics(ptr null, ptr %st)\n")
|
||||||
|
emith(" %p = getelementptr [4 x i64], ptr %st, i32 0, i32 1\n")
|
||||||
|
emith(" %n = load i64, ptr %p\n")
|
||||||
|
emith(" ret i64 %n\n")
|
||||||
|
emith("}\n")
|
||||||
emith("define internal void @lp_fsettle_locked() {\n")
|
emith("define internal void @lp_fsettle_locked() {\n")
|
||||||
emith("entry:\n")
|
emith("entry:\n")
|
||||||
emith(" %n = load i32, ptr @lp_site_n\n")
|
emith(" %n = load i32, ptr @lp_site_n\n")
|
||||||
|
|
@ -807,6 +837,8 @@ function emit_fence_frame() -> void {
|
||||||
emith(" store i64 %lv, ptr @lp_slive\n")
|
emith(" store i64 %lv, ptr @lp_slive\n")
|
||||||
emith(" %pr = load i64, ptr @lp_pre\n")
|
emith(" %pr = load i64, ptr @lp_pre\n")
|
||||||
emith(" store i64 %pr, ptr @lp_spre\n")
|
emith(" store i64 %pr, ptr @lp_spre\n")
|
||||||
|
emith(" %z = call i64 @lp_fzone()\n")
|
||||||
|
emith(" store i64 %z, ptr @lp_zset\n")
|
||||||
emith(" store i8 1, ptr @lp_fj\n")
|
emith(" store i8 1, ptr @lp_fj\n")
|
||||||
emith(" ret void\n")
|
emith(" ret void\n")
|
||||||
emith("}\n")
|
emith("}\n")
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ function emit_os_prelude() -> void {
|
||||||
# Os.heap_bytes: malloc_zone_statistics' in-use bytes, all zones. The symbol is weak, so a libc
|
# Os.heap_bytes: malloc_zone_statistics' in-use bytes, all zones. The symbol is weak, so a libc
|
||||||
# without it links and reads 0.
|
# without it links and reads 0.
|
||||||
function emit_os_heap() -> void {
|
function emit_os_heap() -> void {
|
||||||
emith("declare extern_weak void @malloc_zone_statistics(ptr, ptr)\n")
|
if not g_mzs_declared { emith("declare extern_weak void @malloc_zone_statistics(ptr, ptr)\n"); g_mzs_declared = true }
|
||||||
emith("define i64 @lp_os_heap_bytes() {\n")
|
emith("define i64 @lp_os_heap_bytes() {\n")
|
||||||
emith("entry:\n %has = icmp ne ptr @malloc_zone_statistics, null\n br i1 %has, label %read, label %none\n")
|
emith("entry:\n %has = icmp ne ptr @malloc_zone_statistics, null\n br i1 %has, label %read, label %none\n")
|
||||||
emith("none:\n ret i64 0\n")
|
emith("none:\n ret i64 0\n")
|
||||||
|
|
|
||||||
47399
selfhost/ludicc.seed.ll
47399
selfhost/ludicc.seed.ll
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -797,7 +797,7 @@ function alloc_fence_leak_case() -> void {
|
||||||
let head = capture_line(`grep 'alloc-fence: frame' {tmp_dir()}/afl.err | head -1`)
|
let head = capture_line(`grep 'alloc-fence: frame' {tmp_dir()}/afl.err | head -1`)
|
||||||
let named = shq(`grep -q 'alloc_fence_leak.ludic:11 new Box' {tmp_dir()}/afl.err`)
|
let named = shq(`grep -q 'alloc_fence_leak.ludic:11 new Box' {tmp_dir()}/afl.err`)
|
||||||
let counted = capture_line(`R3D_ALLOC_FENCE=count {b} < /dev/null`)
|
let counted = capture_line(`R3D_ALLOC_FENCE=count {b} < /dev/null`)
|
||||||
if (rc == 86) and (head == "alloc-fence: frame 50 kept +72 B (2 made, 0 freed)") and named and (counted == "kept 72 bad 1") { ok(lbl) }
|
if (rc == 86) and (head == "alloc-fence: frame 50 kept +80 B (2 made, 0 freed)") and named and (counted == "kept 80 bad 1") { ok(lbl) }
|
||||||
else { bad2(lbl, `rc={string(rc)} head=[{head}] named={string(named)} count=[{counted}]`) }
|
else { bad2(lbl, `rc={string(rc)} head=[{head}] named={string(named)} count=[{counted}]`) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -813,7 +813,7 @@ function alloc_fence_auto_case() -> void {
|
||||||
let bc = `{tmp_dir()}/e_alloc_fence_auto_c`
|
let bc = `{tmp_dir()}/e_alloc_fence_auto_c`
|
||||||
var through = ""
|
var through = ""
|
||||||
if shq(`bin/ludicc --fence=count examples/lang/alloc_fence_auto.ludic -o {bc} 2>/dev/null`) { through = capture_line(`{bc} < /dev/null`) }
|
if shq(`bin/ludicc --fence=count examples/lang/alloc_fence_auto.ludic -o {bc} 2>/dev/null`) { through = capture_line(`{bc} < /dev/null`) }
|
||||||
if (rc == 86) and (head == "alloc-fence: frame 1500 kept +4 B (1 made, 0 freed)") and (through == "through") { ok(lbl) }
|
if (rc == 86) and (head == "alloc-fence: frame 1500 kept +16 B (1 made, 0 freed)") and (through == "through") { ok(lbl) }
|
||||||
else { bad2(lbl, `rc={string(rc)} head=[{head}] count=[{through}]`) }
|
else { bad2(lbl, `rc={string(rc)} head=[{head}] count=[{through}]`) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue