profilegc.d revision 1.1 1 1.1 mrg /*
2 1.1 mrg * Data collection and report generation for
3 1.1 mrg * -profile=gc
4 1.1 mrg * switch
5 1.1 mrg *
6 1.1 mrg * Copyright: Copyright Digital Mars 2015 - 2015.
7 1.1 mrg * License: Distributed under the
8 1.1 mrg * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
9 1.1 mrg * (See accompanying file LICENSE)
10 1.1 mrg * Authors: Andrei Alexandrescu and Walter Bright
11 1.1 mrg * Source: $(DRUNTIMESRC rt/_profilegc.d)
12 1.1 mrg */
13 1.1 mrg
14 1.1 mrg module rt.profilegc;
15 1.1 mrg
16 1.1 mrg private:
17 1.1 mrg
18 1.1 mrg import core.stdc.stdio;
19 1.1 mrg import core.stdc.stdlib;
20 1.1 mrg import core.stdc.string;
21 1.1 mrg
22 1.1 mrg import core.exception : onOutOfMemoryError;
23 1.1 mrg import core.internal.container.hashtab;
24 1.1 mrg
25 1.1 mrg struct Entry { ulong count, size; }
26 1.1 mrg
27 1.1 mrg char[] buffer;
28 1.1 mrg HashTab!(const(char)[], Entry) newCounts;
29 1.1 mrg
30 1.1 mrg __gshared
31 1.1 mrg {
32 1.1 mrg HashTab!(const(char)[], Entry) globalNewCounts;
33 1.1 mrg string logfilename = "profilegc.log";
34 1.1 mrg }
35 1.1 mrg
36 1.1 mrg /****
37 1.1 mrg * Set file name for output.
38 1.1 mrg * A file name of "" means write results to stdout.
39 1.1 mrg * Params:
40 1.1 mrg * name = file name
41 1.1 mrg */
42 1.1 mrg
43 1.1 mrg extern (C) void profilegc_setlogfilename(string name)
44 1.1 mrg {
45 1.1 mrg logfilename = name ~ "\0";
46 1.1 mrg }
47 1.1 mrg
48 1.1 mrg public void accumulate(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow
49 1.1 mrg {
50 1.1 mrg if (sz == 0)
51 1.1 mrg return;
52 1.1 mrg
53 1.1 mrg char[3 * line.sizeof + 1] buf = void;
54 1.1 mrg auto buflen = snprintf(buf.ptr, buf.length, "%u", line);
55 1.1 mrg
56 1.1 mrg auto length = type.length + 1 + funcname.length + 1 + file.length + 1 + buflen;
57 1.1 mrg if (length > buffer.length)
58 1.1 mrg {
59 1.1 mrg // Enlarge buffer[] so it is big enough
60 1.1 mrg assert(buffer.length > 0 || buffer.ptr is null);
61 1.1 mrg auto p = cast(char*)realloc(buffer.ptr, length);
62 1.1 mrg if (!p)
63 1.1 mrg onOutOfMemoryError();
64 1.1 mrg buffer = p[0 .. length];
65 1.1 mrg }
66 1.1 mrg
67 1.1 mrg // "type funcname file:line"
68 1.1 mrg buffer[0 .. type.length] = type[];
69 1.1 mrg buffer[type.length] = ' ';
70 1.1 mrg buffer[type.length + 1 ..
71 1.1 mrg type.length + 1 + funcname.length] = funcname[];
72 1.1 mrg buffer[type.length + 1 + funcname.length] = ' ';
73 1.1 mrg buffer[type.length + 1 + funcname.length + 1 ..
74 1.1 mrg type.length + 1 + funcname.length + 1 + file.length] = file[];
75 1.1 mrg buffer[type.length + 1 + funcname.length + 1 + file.length] = ':';
76 1.1 mrg buffer[type.length + 1 + funcname.length + 1 + file.length + 1 ..
77 1.1 mrg type.length + 1 + funcname.length + 1 + file.length + 1 + buflen] = buf[0 .. buflen];
78 1.1 mrg
79 1.1 mrg if (auto pcount = cast(string)buffer[0 .. length] in newCounts)
80 1.1 mrg { // existing entry
81 1.1 mrg pcount.count++;
82 1.1 mrg pcount.size += sz;
83 1.1 mrg }
84 1.1 mrg else
85 1.1 mrg {
86 1.1 mrg auto key = (cast(char*) malloc(char.sizeof * length))[0 .. length];
87 1.1 mrg key[] = buffer[0..length];
88 1.1 mrg newCounts[key] = Entry(1, sz); // new entry
89 1.1 mrg }
90 1.1 mrg }
91 1.1 mrg
92 1.1 mrg // Merge thread local newCounts into globalNewCounts
93 1.1 mrg static ~this()
94 1.1 mrg {
95 1.1 mrg if (newCounts.length)
96 1.1 mrg {
97 1.1 mrg synchronized
98 1.1 mrg {
99 1.1 mrg foreach (name, entry; newCounts)
100 1.1 mrg {
101 1.1 mrg if (!(name in globalNewCounts))
102 1.1 mrg globalNewCounts[name] = Entry.init;
103 1.1 mrg
104 1.1 mrg globalNewCounts[name].count += entry.count;
105 1.1 mrg globalNewCounts[name].size += entry.size;
106 1.1 mrg }
107 1.1 mrg }
108 1.1 mrg newCounts.reset();
109 1.1 mrg }
110 1.1 mrg free(buffer.ptr);
111 1.1 mrg buffer = null;
112 1.1 mrg }
113 1.1 mrg
114 1.1 mrg // Write report to stderr
115 1.1 mrg shared static ~this()
116 1.1 mrg {
117 1.1 mrg static struct Result
118 1.1 mrg {
119 1.1 mrg const(char)[] name;
120 1.1 mrg Entry entry;
121 1.1 mrg
122 1.1 mrg // qsort() comparator to sort by count field
123 1.1 mrg extern (C) static int qsort_cmp(scope const void *r1, scope const void *r2) @nogc nothrow
124 1.1 mrg {
125 1.1 mrg auto result1 = cast(Result*)r1;
126 1.1 mrg auto result2 = cast(Result*)r2;
127 1.1 mrg long cmp = result2.entry.size - result1.entry.size;
128 1.1 mrg if (cmp) return cmp < 0 ? -1 : 1;
129 1.1 mrg cmp = result2.entry.count - result1.entry.count;
130 1.1 mrg if (cmp) return cmp < 0 ? -1 : 1;
131 1.1 mrg if (result2.name == result1.name) return 0;
132 1.1 mrg // ascending order for names reads better
133 1.1 mrg return result2.name > result1.name ? -1 : 1;
134 1.1 mrg }
135 1.1 mrg }
136 1.1 mrg
137 1.1 mrg size_t size = globalNewCounts.length;
138 1.1 mrg Result[] counts = (cast(Result*) malloc(size * Result.sizeof))[0 .. size];
139 1.1 mrg scope(exit)
140 1.1 mrg free(counts.ptr);
141 1.1 mrg
142 1.1 mrg size_t i;
143 1.1 mrg foreach (name, entry; globalNewCounts)
144 1.1 mrg {
145 1.1 mrg counts[i].name = name;
146 1.1 mrg counts[i].entry = entry;
147 1.1 mrg ++i;
148 1.1 mrg }
149 1.1 mrg
150 1.1 mrg if (counts.length)
151 1.1 mrg {
152 1.1 mrg qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp);
153 1.1 mrg
154 1.1 mrg FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename).ptr, "w");
155 1.1 mrg if (fp)
156 1.1 mrg {
157 1.1 mrg fprintf(fp, "bytes allocated, allocations, type, function, file:line\n");
158 1.1 mrg foreach (ref c; counts)
159 1.1 mrg {
160 1.1 mrg fprintf(fp, "%15llu\t%15llu\t%8.*s\n",
161 1.1 mrg cast(ulong)c.entry.size, cast(ulong)c.entry.count,
162 1.1 mrg cast(int) c.name.length, c.name.ptr);
163 1.1 mrg }
164 1.1 mrg if (logfilename.length)
165 1.1 mrg fclose(fp);
166 1.1 mrg }
167 1.1 mrg else
168 1.1 mrg fprintf(stderr, "cannot write profilegc log file '%.*s'", cast(int) logfilename.length, logfilename.ptr);
169 1.1 mrg }
170 1.1 mrg }
171