]> git.llucax.com Git - software/dgc/cdgc.git/blob - rt/gc/cdgc/stats.d
Make heap scanning precise
[software/dgc/cdgc.git] / rt / gc / cdgc / stats.d
1 /**
2  * This module contains garbage collector statistics functionality.
3  *
4  * Copyright: Copyright (C) 2005-2006 Digital Mars, www.digitalmars.com.
5  *            All rights reserved.
6  * License:
7  *  This software is provided 'as-is', without any express or implied
8  *  warranty. In no event will the authors be held liable for any damages
9  *  arising from the use of this software.
10  *
11  *  Permission is granted to anyone to use this software for any purpose,
12  *  including commercial applications, and to alter it and redistribute it
13  *  freely, in both source and binary form, subject to the following
14  *  restrictions:
15  *
16  *  o  The origin of this software must not be misrepresented; you must not
17  *     claim that you wrote the original software. If you use this software
18  *     in a product, an acknowledgment in the product documentation would be
19  *     appreciated but is not required.
20  *  o  Altered source versions must be plainly marked as such, and must not
21  *     be misrepresented as being the original software.
22  *  o  This notice may not be removed or altered from any source
23  *     distribution.
24  * Authors:   Walter Bright, Sean Kelly, Leandro Lucarella
25  */
26
27 module rt.gc.cdgc.stats;
28
29 import gc = rt.gc.cdgc.gc;
30 import rt.gc.cdgc.bits: GCBits;
31 import rt.gc.cdgc.opts: options;
32
33 import cstdio = tango.stdc.stdio;
34 import ctime = tango.stdc.posix.sys.time;
35
36
37 private:
38
39
40 /**
41  * Time information for a collection.
42  *
43  * This struct groups all the timing information for a particular collection.
44  * It stores how much time it took the different parts of a collection, splat
45  * in: allocation time (the time the mutator spent in the malloc() call that
46  * triggered the collection), time the world was stopped because of the
47  * collection and the time spent in the collection itself. The time the world
48  * was stopped includes the time spent in the collection (in this
49  * implementation, which is not concurrent) and the time spent in the
50  * allocation includes the time the world was stopped (this is probably true
51  * for any implementation).
52  */
53 struct TimeInfo
54 {
55     /// Collection time (in seconds).
56     double collection = -1.0;
57     /// Stop-the-world time (in seconds).
58     double stop_the_world = -1.0;
59     /// Time spent in the malloc that triggered the collection (in seconds).
60     double malloc = -1.0;
61 }
62
63
64 /**
65  * Memory (space) information for a collection.
66  *
67  * This struct groups all the space information for a particular collection.
68  * The space is partitioned in four: used, free, wasted and overhead. "used" is
69  * the net space needed by the mutator; "free" is the space the GC has ready to
70  * be given to the mutator when needed; "wasted" is memory that can't be used
71  * by neither the collector nor the mutator (usually because of fragmentation)
72  * and "overhead" is the space needed by the GC itself.
73  */
74 struct MemoryInfo
75 {
76     /// Heap memory used by the program (in bytes).
77     size_t used = 0;
78     /// Free heap memory (in bytes).
79     size_t free = 0;
80     /// Memory that can't be used at all (in bytes).
81     size_t wasted = 0;
82     /// Memory used by the GC for bookkeeping (in bytes).
83     size_t overhead = 0;
84 }
85
86
87 /**
88  * Information about a particular collection.
89  *
90  * This struct groups all information related to a particular collection. The
91  * timings for the collection as stored and the space requirements are both,
92  * before and after that collection, logged to easily measure how effective
93  * the collection was in terms of space. The time when this collection was
94  * triggered is logged too (relative to the program start, in seconds).
95  *
96  * See_Also: TimeInfo and MemoryInfo structs.
97  */
98 struct CollectionInfo
99 {
100     /// When this information were taken (seconds since the program started).
101     double timestamp = -1.0;
102     /// Time statistics for this collection.
103     TimeInfo time;
104     /// Memory statistics before the collection.
105     MemoryInfo before;
106     /// Memory statistics after the collection.
107     MemoryInfo after;
108 }
109
110
111 /**
112  * Information about a particular allocation.
113  *
114  * This struct groups all the information about a particular allocation. The
115  * size requested in that allocation is logged, as well as the attributes
116  * assigned to that cell, the time malloc() took to complete and if
117  * a collection was triggered. The time when this allocation was requested is
118  * logged too (relative to the program start, in seconds).
119  */
120 struct MallocInfo
121 {
122     /// When this information were taken (seconds since the program started).
123     double timestamp = -1.0;
124     /// Time spent in the malloc() call (in seconds).
125     double time = -1.0;
126     /// Memory requested by the malloc() call (in bytes).
127     size_t size = 0;
128     /// Memory attributes as BlkAttr flags.
129     uint attr = 0;
130     /// True if this malloc() triggered a collection.
131     bool collected = false;
132     /// Address of the pointer map bitmask.
133     size_t* ptrmap = null;
134 }
135
136
137 package:
138
139 /**
140  * Control and store the GC statistics.
141  *
142  * This is the interface to this module, it has methods for the GC to inform
143  * when a relevant event has happened. The events are the start and finish of
144  * an allocation, when the world is stopped and restarted and when
145  * a collection is triggered and done.
146  *
147  * All the data is logged in memory and printed to the standard output when
148  * requested (usually at the end of the program).
149  *
150  * See_Also: CollectionInfo and MallocInfo structs.
151  */
152 struct Stats
153 {
154
155 private:
156
157     /// The GC instance we are collecting stats from.
158     .gc.GC gc = null;
159
160     /// True if statistics should be collected.
161     bool active = false;
162
163     /// Current collection information (printed when the malloc finishes).
164     MallocInfo malloc_info;
165
166     /// File where to write the malloc information to.
167     cstdio.FILE* mallocs_file;
168
169     /// Current collection information (printed when the collection finishes).
170     CollectionInfo collection_info;
171
172     /// File where to write the collections information to.
173     cstdio.FILE* collections_file;
174
175     /// Time when the program started.
176     double program_start = -1.0;
177
178     /// Return the current time as seconds since the epoch.
179     static double now()
180     {
181         ctime.timeval tv;
182         ctime.gettimeofday(&tv, null);
183         return cast(double) tv.tv_sec + cast(double) tv.tv_usec / 1_000_000.0;
184     }
185
186     /// Fill a MemoryInfo struct with the current state of the GC heap.
187     void fill_memory_info(MemoryInfo* mem_info)
188     {
189         mem_info.overhead += .gc.GC.classinfo.init.length + .gc.Gcx.sizeof
190                 + .gc.pools.size_of + .gc.roots.size_of + .gc.ranges.size_of;
191
192         // pools
193         for (size_t i = 0; i < .gc.pools.length; i++)
194         {
195             auto pool = .gc.pools[i];
196             mem_info.overhead += pool.npages * ubyte.sizeof;
197             // the 5 bitmaps (mark, scan, free, final, noscan)
198             mem_info.overhead += 5 * (GCBits.sizeof
199                     + (pool.mark.nwords + 2) * uint.sizeof);
200
201             for (size_t pn = 0; pn < pool.npages; pn++)
202             {
203                 auto bin = cast(.gc.Bins) pool.pagetable[pn];
204                 if (bin < .gc.B_PAGE)
205                 {
206                     size_t size = .gc.binsize[bin];
207                     size_t attrstride = size / 16;
208                     size_t attrbase = pn * (.gc.PAGESIZE / 16);
209                     size_t attrtop = attrbase + (.gc.PAGESIZE / 16);
210                     for (auto attri = attrbase; attri < attrtop; attri += attrstride)
211                     {
212                         if (pool.freebits.test(attri))
213                             mem_info.free += size;
214                         else
215                             mem_info.used += size; // TODO: wasted
216                     }
217                 }
218                 else if (bin == .gc.B_FREE)
219                     mem_info.free += .gc.PAGESIZE;
220                 else // B_PAGE / B_PAGEPLUS
221                     mem_info.used += .gc.PAGESIZE; // TODO: wasted
222             }
223         }
224     }
225
226     cstdio.FILE* start_file(char* filename, char* header)
227     {
228         cstdio.FILE* file = cstdio.fopen(filename, "w");
229         assert (file !is null);
230         cstdio.fputs(header, file);
231         return file;
232     }
233
234     void print_malloc()
235     {
236         if (this.mallocs_file is null)
237             return;
238         cstdio.fprintf(this.mallocs_file,
239                 "%f,%f,%zu,%zu,%zu,%zu,%zu,%p\n",
240                 //0  1   2   3   4   5   6  7
241                 this.malloc_info.timestamp,                   // 0
242                 this.malloc_info.time,                        // 1
243                 this.malloc_info.size,                        // 2
244                 this.malloc_info.collected ? 1u : 0u,         // 3
245                 this.malloc_info.attr & .gc.BlkAttr.FINALIZE, // 4
246                 this.malloc_info.attr & .gc.BlkAttr.NO_SCAN,  // 5
247                 this.malloc_info.attr & .gc.BlkAttr.NO_MOVE,  // 6
248                 this.malloc_info.ptrmap);                     // 7
249         // TODO: make it an option
250         cstdio.fflush(this.mallocs_file);
251     }
252
253     void print_collection()
254     {
255         if (this.collections_file is null)
256             return;
257         cstdio.fprintf(this.collections_file,
258                 "%f,%f,%f,%f,%zu,%zu,%zu,%zu,%zu,%zu,%zu,%zu\n",
259                 //0  1  2  3   4   5   6   7   8   9  10  11
260                 this.collection_info.timestamp,           // 0
261                 this.collection_info.time.malloc,         // 1
262                 this.collection_info.time.collection,     // 2
263                 this.collection_info.time.stop_the_world, // 3
264                 this.collection_info.before.used,         // 4
265                 this.collection_info.before.free,         // 5
266                 this.collection_info.before.wasted,       // 6
267                 this.collection_info.before.overhead,     // 7
268                 this.collection_info.after.used,          // 8
269                 this.collection_info.after.free,          // 9
270                 this.collection_info.after.wasted,        // 10
271                 this.collection_info.after.overhead);     // 11
272         // TODO: make it an option
273         cstdio.fflush(this.collections_file);
274     }
275
276
277 public:
278
279     /**
280      * Construct a Stats object (useful for easy initialization).
281      *
282      * This function should be always used to initialize a Stats object because
283      * the program start time (in seconds since the epoch) needs to be taken to
284      * properly add timestamps to allocations and collections.
285      */
286     static Stats opCall(.gc.GC gc)
287     {
288         Stats this_;
289         this_.gc = gc;
290         if (options.malloc_stats_file[0] != '\0') {
291             this_.active = true;
292             this_.mallocs_file = this_.start_file(
293                     options.malloc_stats_file.ptr,
294                     "Timestamp,Time,Size,Collection triggered,"
295                     "Finalize,No scan,No move,Pointer map\n");
296         }
297         // collection
298         if (options.collect_stats_file[0] != '\0') {
299             this_.active = true;
300             this_.collections_file = this_.start_file(
301                     options.collect_stats_file.ptr,
302                     "Timestamp,Malloc time,Collection time,Stop-the-world time,"
303                     "Used before,Free before,Wasted before,Overhead before,"
304                     "Used after,Free after,Wasted after,Overhead after\n");
305         }
306         this_.program_start = this_.now();
307         return this_;
308     }
309
310     void finalize()
311     {
312         if (this.mallocs_file !is null)
313             cstdio.fclose(this.mallocs_file);
314         if (this.collections_file !is null)
315             cstdio.fclose(this.collections_file);
316     }
317
318     /// Inform the start of an allocation.
319     // TODO: store/use type information
320     void malloc_started(size_t size, uint attr, size_t* ptrmap_bitmask)
321     {
322         if (!this.active)
323             return;
324         auto now = this.now();
325         auto relative_now = now - this.program_start;
326         // malloc
327         this.malloc_info = this.malloc_info.init;
328         this.malloc_info.timestamp = relative_now;
329         this.malloc_info.time = now;
330         this.malloc_info.size = size;
331         this.malloc_info.attr = attr;
332         this.malloc_info.ptrmap = ptrmap_bitmask;
333         // this.malloc_info.collected is filled in malloc_finished()
334         // collection
335         this.collection_info = this.collection_info.init;
336         this.collection_info.timestamp = relative_now;
337         // this.collection_info.time.malloc is the same as malloc_info.time
338     }
339
340     /// Inform the end of an allocation.
341     void malloc_finished()
342     {
343         if (!this.active)
344             return;
345         auto now = this.now();
346         auto collected = !(this.collection_info.time.collection < 0.0);
347         // malloc
348         this.malloc_info.time = now - this.malloc_info.time;
349         if (collected)
350             this.malloc_info.collected = true;
351         this.print_malloc();
352         if (!collected)
353             return;
354         // collection
355         this.collection_info.time.malloc = this.malloc_info.time;
356         this.print_collection();
357     }
358
359     /// Inform that all threads (the world) have been stopped.
360     void world_stopped()
361     {
362         if (!this.active)
363             return;
364         this.collection_info.time.stop_the_world = this.now();
365     }
366
367     /// Inform that all threads (the world) have been resumed.
368     void world_started()
369     {
370         if (!this.active)
371             return;
372         this.collection_info.time.stop_the_world =
373                 this.now() - this.collection_info.time.stop_the_world;
374     }
375
376     /// Inform the start of a collection.
377     void collection_started()
378     {
379         if (!this.active)
380             return;
381         this.fill_memory_info(&this.collection_info.before);
382         this.collection_info.time.collection = this.now();
383     }
384
385     /// Inform the end of a collection.
386     void collection_finished()
387     {
388         if (!this.active)
389             return;
390         this.collection_info.time.collection =
391                 this.now() - this.collection_info.time.collection;
392         this.fill_memory_info(&this.collection_info.after);
393     }
394
395 }
396
397
398 /**
399  *
400  */
401 struct GCStats
402 {
403     size_t poolsize;        // total size of pool
404     size_t usedsize;        // bytes allocated
405     size_t freeblocks;      // number of blocks marked FREE
406     size_t freelistsize;    // total of memory on free lists
407     size_t pageblocks;      // number of blocks marked PAGE
408 }
409
410
411 // vim: set et sw=4 sts=4 :