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