summaryrefslogtreecommitdiff
path: root/support/dumpleak.myr
blob: 350298aef706013d09605549eb4697fbcc0da340 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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 : 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 : 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)) : 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)) : 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 : sl
		h ^= std.inthash(i)
	;;
	-> h
}