shithub: mc

ref: 186c4e4026e1d9db729153e185bd717377060926
dir: /support/dumpleak.myr/

View raw version
use std
use bio

var stackaggr = 10
var summary

type memstats = struct
	allocs	: uint64
	allocsz	: uint64
	frees	: uint64
	freesz	: uint64
	tab	: std.htab(uint64, (uint64, uint64[:]))#
;;


const main = {args
	var cmd
	var stats : memstats

	cmd = std.optparse(args, &[
		.argdesc="dumps...",
		.opts=[
			[.opt='d', .arg="depth", .desc="aggregate by at most `depth` stack elements"],
			[.opt='s', .arg="", .desc="only show a summary of memory activity"],
		][:]
	])

	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)
			;;
		| ('s', ""):
			summary = true
		| _:	std.die("unreachable")
		;;
	;;

	stats.tab = std.mkht(std.inthash, std.inteq)
	for d in cmd.args
		match bio.open(d, bio.Rd)
		| `std.Ok f:	dump(d, f, &stats)
		| `std.Err e:	std.fatal("could not open {}: {}\n", d, e)
		;;
	;;
}

const dump = {path, f, stats
	while true
		match bio.getle64(f)
		| `bio.Ok 0:	tracealloc(path, f, stats)
		| `bio.Ok 1:	tracefree(path, f, stats)
		| `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)
		;;
	;;
	if !summary
		dumptrace(stats.tab)
	;;
	dumpsummary(stats)
}

const dumpsummary = {stats
	std.put("allocs:\t{}\n", stats.allocs)
	std.put("allocsz:\t{}\n", stats.allocsz)
	std.put("frees:\t{}\n", stats.frees)
	std.put("freesz:\t{}\n", stats.freesz)
	std.put("livesz:\t{}\n", stats.allocsz - stats.freesz)
}


const tracealloc = {path, f, stats
	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))
	;;
	stats.allocs++
	stats.allocsz += sz
	std.htput(stats.tab, ptr, (sz, stk))
}

const tracefree = {path, f, stats
	var ptr, sz

	ptr = get64(path, f)
	sz = get64(path, f)
	stats.allocs++
	stats.allocsz += sz
	std.htdel(stats.tab, ptr)
}

const dumptrace = {tab
	var aggr

	aggr = std.mkht(hashintsl, std.sleq)
	for (k, (sz, stk)) in std.byhtkeyvals(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.byhtkeyvals(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
}