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
}
|