shithub: mc

Download patch

ref: 2216e4b75b74784ba691af9492db3d449b41c361
parent: b2ca8481f37bccdd0488cf7de05ea79cd17ae5ed
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Aug 28 11:21:06 EDT 2016

Add support for leak tracing.

--- a/bld.proj
+++ b/bld.proj
@@ -3,5 +3,6 @@
 	lib
 	test
 	bench
+	support
 ;;
 
--- 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)
--- a/lib/std/bytealloc.myr
+++ b/lib/std/bytealloc.myr
@@ -5,8 +5,14 @@
 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 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 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 @@
 	-> 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 @@
 			die("could not get memory\n")
 		;;
 	;;
+	if trace
+		lock(memlck)
+		tracealloc(p, sz)
+		unlock(memlck)
+	;;
 	-> p
 }
 
@@ -83,6 +137,11 @@
 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)]
--- 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 . 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 . 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 . 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 	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 . 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  &&\
--- /dev/null
+++ b/support/dumpleak.myr
@@ -1,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
+}