summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOri Bernstein <ori@eigenstate.org>2016-08-28 15:21:06 -0700
committerOri Bernstein <ori@eigenstate.org>2016-08-28 15:21:06 -0700
commit2216e4b75b74784ba691af9492db3d449b41c361 (patch)
tree9bdd9ffd8e93199c77237d544745aadfe17b96f7
parentb2ca8481f37bccdd0488cf7de05ea79cd17ae5ed (diff)
downloadmc-2216e4b75b74784ba691af9492db3d449b41c361.tar.gz
Add support for leak tracing.
-rw-r--r--bld.proj1
-rw-r--r--lib/std/backtrace+x64.myr1
-rw-r--r--lib/std/bytealloc.myr59
-rwxr-xr-xmk/bootstrap/bootstrap+FreeBSD-amd64.sh9
-rw-r--r--support/dumpleak.myr103
5 files changed, 169 insertions, 4 deletions
diff --git a/bld.proj b/bld.proj
index bbcd931..4fcc171 100644
--- a/bld.proj
+++ b/bld.proj
@@ -3,5 +3,6 @@ sub =
lib
test
bench
+ support
;;
diff --git a/lib/std/backtrace+x64.myr b/lib/std/backtrace+x64.myr
index 61d6625..27d88bc 100644
--- a/lib/std/backtrace+x64.myr
+++ b/lib/std/backtrace+x64.myr
@@ -1,5 +1,4 @@
use "types"
-use "fmt"
pkg std =
const backtrace : (pc : void#[:] -> std.size)
diff --git a/lib/std/bytealloc.myr b/lib/std/bytealloc.myr
index 6ae2af2..3eda8aa 100644
--- a/lib/std/bytealloc.myr
+++ b/lib/std/bytealloc.myr
@@ -5,8 +5,14 @@ use "syswrap"
use "threadhooks"
use "types"
use "units"
+use "result"
+use "slfill"
+use "backtrace"
pkg std =
+ const startalloctrace : (f : byte[:] -> void)
+ const endalloctrace : (-> void)
+
/* null pointers. only used internally. */
pkglocal const Zsliceptr = (0 : byte#)
pkglocal const Align = 16 /* minimum allocation alignment */
@@ -27,6 +33,8 @@ const Bktmax = 32*KiB /* Slabsz / 8; a balance. */
const Pagesz = 4*KiB
var buckets : bucket[32] /* excessive */
+var trace : bool
+var tracefd : std.fd
type bucket = struct
sz : size /* aligned size */
@@ -53,6 +61,19 @@ const __init__ = {
;;
}
+const startalloctrace = {path
+ match openmode(path, Owronly | Ocreat, 0o644)
+ | `Ok fd: tracefd = fd
+ | `Err e: -> void
+ ;;
+ trace = true
+}
+
+const endalloctrace = {
+ std.close(tracefd)
+ trace = false
+}
+
const zbytealloc = {sz
var p
@@ -61,6 +82,34 @@ const zbytealloc = {sz
-> p
}
+const tracealloc = {p, sz
+ var stk : void#[13] /* [type, addr, sz, 10 stack slots] */
+
+ slfill(stk[:], (0 : void#))
+ stk[0] = (0 : void#)
+ stk[1] = (p : void#)
+ stk[2] = (sz : void#)
+ backtrace(stk[3:])
+ writealloctrace(stk[:])
+}
+
+const tracefree = {p, sz
+ var stk : void#[3]
+
+ stk[0] = (1 : void#)
+ stk[1] = (p : void#)
+ stk[2] = (sz : void#)
+ writealloctrace(stk[:])
+}
+
+const writealloctrace = {sl
+ var len, p
+
+ len = sl.len * sizeof(void#)
+ p = (sl : byte#)
+ write(tracefd, p[:len])
+}
+
/* Allocates a blob that is 'sz' bytes long. Dies if the allocation fails */
const bytealloc = {sz
var bkt, p
@@ -76,6 +125,11 @@ const bytealloc = {sz
die("could not get memory\n")
;;
;;
+ if trace
+ lock(memlck)
+ tracealloc(p, sz)
+ unlock(memlck)
+ ;;
-> p
}
@@ -83,6 +137,11 @@ const bytealloc = {sz
const bytefree = {p, sz
var bkt
+ if trace
+ lock(memlck)
+ tracefree(p, sz)
+ unlock(memlck)
+ ;;
memfill(p, 0xa8, sz)
if (sz < Bktmax)
bkt = &buckets[bktnum(sz)]
diff --git a/mk/bootstrap/bootstrap+FreeBSD-amd64.sh b/mk/bootstrap/bootstrap+FreeBSD-amd64.sh
index 6580087..0902a30 100755
--- a/mk/bootstrap/bootstrap+FreeBSD-amd64.sh
+++ b/mk/bootstrap/bootstrap+FreeBSD-amd64.sh
@@ -25,6 +25,8 @@ echo $pwd/6/6m -I ../sys -I . extremum.myr && $pwd/6/6m -I ../sys -I . extrem
echo $pwd/6/6m -I ../sys -I . memops.myr && $pwd/6/6m -I ../sys -I . memops.myr &&\
echo $pwd/6/6m -I ../sys -I . threadhooks.myr && $pwd/6/6m -I ../sys -I . threadhooks.myr &&\
echo $pwd/6/6m -I ../sys -I . units.myr && $pwd/6/6m -I ../sys -I . units.myr &&\
+echo $pwd/6/6m -I ../sys -I . slfill.myr && $pwd/6/6m -I ../sys -I . slfill.myr &&\
+echo $pwd/6/6m -I ../sys -I . backtrace+x64.myr && $pwd/6/6m -I ../sys -I . backtrace+x64.myr &&\
echo $pwd/6/6m -I ../sys -I . bytealloc.myr && $pwd/6/6m -I ../sys -I . bytealloc.myr &&\
echo $pwd/6/6m -I ../sys -I . alloc.myr && $pwd/6/6m -I ../sys -I . alloc.myr &&\
echo $pwd/6/6m -I ../sys -I . consts.myr && $pwd/6/6m -I ../sys -I . consts.myr &&\
@@ -34,7 +36,6 @@ echo $pwd/6/6m -I ../sys -I . cmp.myr && $pwd/6/6m -I ../sys -I . cmp.myr &&
echo $pwd/6/6m -I ../sys -I . hasprefix.myr && $pwd/6/6m -I ../sys -I . hasprefix.myr &&\
echo $pwd/6/6m -I ../sys -I . slcp.myr && $pwd/6/6m -I ../sys -I . slcp.myr &&\
echo $pwd/6/6m -I ../sys -I . sldup.myr && $pwd/6/6m -I ../sys -I . sldup.myr &&\
-echo $pwd/6/6m -I ../sys -I . slfill.myr && $pwd/6/6m -I ../sys -I . slfill.myr &&\
echo $pwd/6/6m -I ../sys -I . slpush.myr && $pwd/6/6m -I ../sys -I . slpush.myr &&\
echo $pwd/6/6m -I ../sys -I . strfind.myr && $pwd/6/6m -I ../sys -I . strfind.myr &&\
echo $pwd/6/6m -I ../sys -I . striter.myr && $pwd/6/6m -I ../sys -I . striter.myr &&\
@@ -54,6 +55,7 @@ echo $pwd/6/6m -I ../sys -I . fmt.myr && $pwd/6/6m -I ../sys -I . fmt.myr &&
echo $pwd/6/6m -I ../sys -I . assert.myr && $pwd/6/6m -I ../sys -I . assert.myr &&\
echo $pwd/6/6m -I ../sys -I . now.myr && $pwd/6/6m -I ../sys -I . now.myr &&\
echo $pwd/6/6m -I ../sys -I . rand.myr && $pwd/6/6m -I ../sys -I . rand.myr &&\
+echo as -g -o getbp.o getbp+posixy-x64.s && as -g -o getbp.o getbp+posixy-x64.s &&\
echo $pwd/6/6m -I ../sys -I . sljoin.myr && $pwd/6/6m -I ../sys -I . sljoin.myr &&\
echo $pwd/6/6m -I ../sys -I . readall.myr && $pwd/6/6m -I ../sys -I . readall.myr &&\
echo $pwd/6/6m -I ../sys -I . slurp.myr && $pwd/6/6m -I ../sys -I . slurp.myr &&\
@@ -89,6 +91,7 @@ echo $pwd/6/6m -I ../sys -I . slpop.myr && $pwd/6/6m -I ../sys -I . slpop.myr
echo as -g -o sjlj-impl.o sjlj-impl+posixy-x64.s && as -g -o sjlj-impl.o sjlj-impl+posixy-x64.s &&\
echo $pwd/6/6m -I ../sys -I . bitset.myr && $pwd/6/6m -I ../sys -I . bitset.myr &&\
echo $pwd/6/6m -I ../sys -I . fmtfuncs.myr && $pwd/6/6m -I ../sys -I . fmtfuncs.myr &&\
+echo $pwd/6/6m -I ../sys -I . sleep.myr && $pwd/6/6m -I ../sys -I . sleep.myr &&\
echo $pwd/6/6m -I ../sys -I . try.myr && $pwd/6/6m -I ../sys -I . try.myr &&\
echo $pwd/6/6m -I ../sys -I . sort.myr && $pwd/6/6m -I ../sys -I . sort.myr &&\
echo $pwd/6/6m -I ../sys -I . search.myr && $pwd/6/6m -I ../sys -I . search.myr &&\
@@ -96,8 +99,8 @@ echo $pwd/6/6m -I ../sys -I . getcwd.myr && $pwd/6/6m -I ../sys -I . getcwd.m
echo $pwd/6/6m -I ../sys -I . swap.myr && $pwd/6/6m -I ../sys -I . swap.myr &&\
echo $pwd/6/6m -I ../sys -I . sjlj.myr && $pwd/6/6m -I ../sys -I . sjlj.myr &&\
echo $pwd/6/6m -I ../sys -I . dial+posixy.myr && $pwd/6/6m -I ../sys -I . dial+posixy.myr &&\
-echo $pwd/muse/muse -o libstd.use -p std dial.use fmtfuncs.use fmt.use try.use pathjoin.use strjoin.use sljoin.use slpush.use strstrip.use htab.use now.use getcwd.use rand.use slurp.use varargs.use listen.use strbuf.use clear.use slput.use strsplit.use introspect.use mktemp.use alloc.use optparse.use memops.use bytealloc.use fltbits.use striter.use sldup.use fltfmt.use extremum.use option.use errno.use wait.use slcp.use writeall.use putint.use consts.use syswrap.use readall.use sort.use dir.use blat.use diriter.use sjlj.use mk.use swap.use hassuffix.use execvp.use ipparse.use types.use slpop.use strfind.use utf.use dialparse.use cstrconv.use search.use die.use units.use result.use bitset.use env.use resolve.use intparse.use hasprefix.use mkpath.use getint.use dirname.use sleq.use endian.use iterutil.use spork.use assert.use cmp.use syswrap-ss.use chartype.use bigint.use threadhooks.use slfill.use hashfuncs.use fndup.use chomp.use && $pwd/muse/muse -o libstd.use -p std dial.use fmtfuncs.use fmt.use try.use pathjoin.use strjoin.use sljoin.use slpush.use strstrip.use htab.use now.use getcwd.use rand.use slurp.use varargs.use listen.use strbuf.use clear.use slput.use strsplit.use introspect.use mktemp.use alloc.use optparse.use memops.use bytealloc.use fltbits.use striter.use sldup.use fltfmt.use extremum.use option.use errno.use wait.use slcp.use writeall.use putint.use consts.use syswrap.use readall.use sort.use dir.use blat.use diriter.use sjlj.use mk.use swap.use hassuffix.use execvp.use ipparse.use types.use slpop.use strfind.use utf.use dialparse.use cstrconv.use search.use die.use units.use result.use bitset.use env.use resolve.use intparse.use hasprefix.use mkpath.use getint.use dirname.use sleq.use endian.use iterutil.use spork.use assert.use cmp.use syswrap-ss.use chartype.use bigint.use threadhooks.use slfill.use hashfuncs.use fndup.use chomp.use &&\
-echo ar -rcs libstd.a dial.o fmtfuncs.o fmt.o try.o pathjoin.o strjoin.o memops-impl.o sljoin.o slpush.o strstrip.o htab.o now.o getcwd.o rand.o slurp.o varargs.o listen.o strbuf.o clear.o slput.o strsplit.o introspect.o mktemp.o alloc.o optparse.o memops.o bytealloc.o fltbits.o striter.o sldup.o fltfmt.o extremum.o option.o errno.o wait.o slcp.o writeall.o putint.o consts.o syswrap.o readall.o sort.o dir.o blat.o diriter.o sjlj.o mk.o swap.o hassuffix.o execvp.o ipparse.o types.o slpop.o strfind.o utf.o dialparse.o cstrconv.o search.o die.o units.o result.o bitset.o env.o resolve.o intparse.o hasprefix.o mkpath.o getint.o dirname.o sleq.o endian.o iterutil.o spork.o assert.o cmp.o syswrap-ss.o sjlj-impl.o chartype.o bigint.o threadhooks.o slfill.o hashfuncs.o fndup.o chomp.o && ar -rcs libstd.a dial.o fmtfuncs.o fmt.o try.o pathjoin.o strjoin.o memops-impl.o sljoin.o slpush.o strstrip.o htab.o now.o getcwd.o rand.o slurp.o varargs.o listen.o strbuf.o clear.o slput.o strsplit.o introspect.o mktemp.o alloc.o optparse.o memops.o bytealloc.o fltbits.o striter.o sldup.o fltfmt.o extremum.o option.o errno.o wait.o slcp.o writeall.o putint.o consts.o syswrap.o readall.o sort.o dir.o blat.o diriter.o sjlj.o mk.o swap.o hassuffix.o execvp.o ipparse.o types.o slpop.o strfind.o utf.o dialparse.o cstrconv.o search.o die.o units.o result.o bitset.o env.o resolve.o intparse.o hasprefix.o mkpath.o getint.o dirname.o sleq.o endian.o iterutil.o spork.o assert.o cmp.o syswrap-ss.o sjlj-impl.o chartype.o bigint.o threadhooks.o slfill.o hashfuncs.o fndup.o chomp.o &&\
+echo $pwd/muse/muse -o libstd.use -p std dial.use fmtfuncs.use fmt.use try.use pathjoin.use strjoin.use sljoin.use slpush.use strstrip.use htab.use now.use getcwd.use rand.use slurp.use varargs.use listen.use strbuf.use clear.use slput.use strsplit.use introspect.use mktemp.use alloc.use optparse.use memops.use bytealloc.use fltbits.use striter.use sldup.use fltfmt.use extremum.use option.use errno.use wait.use slcp.use writeall.use putint.use consts.use syswrap.use sleep.use readall.use sort.use dir.use blat.use diriter.use sjlj.use backtrace.use mk.use swap.use hassuffix.use execvp.use ipparse.use types.use slpop.use strfind.use utf.use dialparse.use cstrconv.use search.use die.use units.use result.use bitset.use env.use resolve.use intparse.use hasprefix.use mkpath.use getint.use dirname.use sleq.use endian.use iterutil.use spork.use assert.use cmp.use syswrap-ss.use chartype.use bigint.use hashfuncs.use slfill.use threadhooks.use fndup.use chomp.use && $pwd/muse/muse -o libstd.use -p std dial.use fmtfuncs.use fmt.use try.use pathjoin.use strjoin.use sljoin.use slpush.use strstrip.use htab.use now.use getcwd.use rand.use slurp.use varargs.use listen.use strbuf.use clear.use slput.use strsplit.use introspect.use mktemp.use alloc.use optparse.use memops.use bytealloc.use fltbits.use striter.use sldup.use fltfmt.use extremum.use option.use errno.use wait.use slcp.use writeall.use putint.use consts.use syswrap.use sleep.use readall.use sort.use dir.use blat.use diriter.use sjlj.use backtrace.use mk.use swap.use hassuffix.use execvp.use ipparse.use types.use slpop.use strfind.use utf.use dialparse.use cstrconv.use search.use die.use units.use result.use bitset.use env.use resolve.use intparse.use hasprefix.use mkpath.use getint.use dirname.use sleq.use endian.use iterutil.use spork.use assert.use cmp.use syswrap-ss.use chartype.use bigint.use hashfuncs.use slfill.use threadhooks.use fndup.use chomp.use &&\
+echo ar -rcs libstd.a dial.o fmtfuncs.o fmt.o try.o pathjoin.o strjoin.o memops-impl.o sljoin.o slpush.o strstrip.o htab.o now.o getbp.o getcwd.o rand.o slurp.o varargs.o listen.o strbuf.o clear.o slput.o strsplit.o introspect.o mktemp.o alloc.o optparse.o memops.o bytealloc.o fltbits.o striter.o sldup.o fltfmt.o extremum.o option.o errno.o wait.o slcp.o writeall.o putint.o consts.o syswrap.o sleep.o readall.o sort.o dir.o blat.o diriter.o sjlj.o backtrace.o mk.o swap.o hassuffix.o execvp.o ipparse.o types.o slpop.o strfind.o utf.o dialparse.o cstrconv.o search.o die.o units.o result.o bitset.o env.o resolve.o intparse.o hasprefix.o mkpath.o getint.o dirname.o sleq.o endian.o iterutil.o spork.o assert.o cmp.o syswrap-ss.o sjlj-impl.o chartype.o bigint.o hashfuncs.o slfill.o threadhooks.o fndup.o chomp.o && ar -rcs libstd.a dial.o fmtfuncs.o fmt.o try.o pathjoin.o strjoin.o memops-impl.o sljoin.o slpush.o strstrip.o htab.o now.o getbp.o getcwd.o rand.o slurp.o varargs.o listen.o strbuf.o clear.o slput.o strsplit.o introspect.o mktemp.o alloc.o optparse.o memops.o bytealloc.o fltbits.o striter.o sldup.o fltfmt.o extremum.o option.o errno.o wait.o slcp.o writeall.o putint.o consts.o syswrap.o sleep.o readall.o sort.o dir.o blat.o diriter.o sjlj.o backtrace.o mk.o swap.o hassuffix.o execvp.o ipparse.o types.o slpop.o strfind.o utf.o dialparse.o cstrconv.o search.o die.o units.o result.o bitset.o env.o resolve.o intparse.o hasprefix.o mkpath.o getint.o dirname.o sleq.o endian.o iterutil.o spork.o assert.o cmp.o syswrap-ss.o sjlj-impl.o chartype.o bigint.o hashfuncs.o slfill.o threadhooks.o fndup.o chomp.o &&\
echo cd $pwd/lib/regex && cd $pwd/lib/regex &&\
echo $pwd/6/6m -I ../std -I ../sys types.myr && $pwd/6/6m -I ../std -I ../sys types.myr &&\
echo $pwd/6/6m -I ../std -I ../sys interp.myr && $pwd/6/6m -I ../std -I ../sys interp.myr &&\
diff --git a/support/dumpleak.myr b/support/dumpleak.myr
new file mode 100644
index 0000000..06a4953
--- /dev/null
+++ b/support/dumpleak.myr
@@ -0,0 +1,103 @@
+use std
+use bio
+
+var stackaggr = 10
+
+const main = {args
+ var tab : std.htab(int64, (int64, int64[:]))#
+ var cmd
+
+ cmd = std.optparse(args, &[
+ .argdesc="dumps...",
+ .opts=[
+ [.opt='d', .arg="depth", .desc="aggregate by at most `depth` stack elements"],
+ ][:]
+ ])
+
+ for opt in cmd.opts
+ match opt
+ | ('d', depth):
+ match std.intparse(depth)
+ | `std.Some d: stackaggr = d
+ | `std.None: std.fatal("could not parse stack depth {}\n", depth)
+ ;;
+ | _: std.die("unreachable")
+ ;;
+ ;;
+
+ tab = std.mkht(std.inthash, std.inteq)
+ for d in cmd.args
+ match bio.open(d, bio.Rd)
+ | `std.Ok f: dump(d, f, tab)
+ | `std.Err e: std.fatal("could not open {}: {}\n", d, e)
+ ;;
+ ;;
+}
+
+const dump = {path, f, tab
+ while true
+ match bio.getle64(f)
+ | `bio.Ok 0: tracealloc(path, f, tab)
+ | `bio.Ok 1: tracefree(path, f, tab)
+ | `bio.Eof: break
+ | `bio.Ok wat: std.fatal("unknown action type {x}\n", wat)
+ | `bio.Err e: std.fatal("failed to read {}: {}\n", path, e)
+ ;;
+ ;;
+ dumptrace(tab)
+}
+
+const tracealloc = {path, f, tab
+ var ptr, sz, stk
+
+ ptr = get64(path, f)
+ sz = get64(path, f)
+ stk = [][:]
+ for var i = 0; i < 10; i++
+ std.slpush(&stk, get64(path, f))
+ ;;
+ std.htput(tab, ptr, (sz, stk))
+}
+
+const tracefree = {path, f, tab
+ var ptr, sz
+
+ ptr = get64(path, f)
+ sz = get64(path, f)
+ std.htdel(tab, ptr)
+}
+
+const dumptrace = {tab
+ var aggr
+
+ aggr = std.mkht(hashintsl, std.sleq)
+ for (k, (sz, stk)) in std.htbykeyvals(tab)
+ match std.htget(aggr, stk[:stackaggr])
+ | `std.Some (count, total):
+ std.htput(aggr, stk[:stackaggr], (count + 1, sz + total))
+ | `std.None:
+ std.htput(aggr, stk[:stackaggr], (1, sz))
+ ;;
+ ;;
+
+ for (stk, (n, sz)) in std.htbykeyvals(aggr)
+ std.put("unfreed: {} (size: {}): {}\n", n, sz, stk)
+ ;;
+}
+
+const get64 = {path, f
+ match bio.getle64(f)
+ | `bio.Ok v: -> v
+ | res: std.fatal("failed to read {}: {}\n", path, res)
+ ;;
+}
+
+const hashintsl = {sl
+ var h
+
+ h = 0
+ for i in sl
+ h ^= std.inthash(i)
+ ;;
+ -> h
+}