]> git.llucax.com Git - software/dgc/cdgc.git/blob - rt/gc/cdgc/stats.d
Add eager allocation support when fork()ing
[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     /// Address of the pointer returned by malloc.
127     void* ptr = null;
128     /// Memory requested by the malloc() call (in bytes).
129     size_t size = 0;
130     /// Memory attributes as BlkAttr flags.
131     uint attr = 0;
132     /// True if this malloc() triggered a collection.
133     bool collected = false;
134     /// Address of the pointer map bitmask.
135     size_t* ptrmap = null;
136 }
137
138
139 package:
140
141 /**
142  * Control and store the GC statistics.
143  *
144  * This is the interface to this module, it has methods for the GC to inform
145  * when a relevant event has happened. The events are the start and finish of
146  * an allocation, when the world is stopped and restarted and when
147  * a collection is triggered and done.
148  *
149  * All the data is logged in memory and printed to the standard output when
150  * requested (usually at the end of the program).
151  *
152  * See_Also: CollectionInfo and MallocInfo structs.
153  */
154 struct Stats
155 {
156
157 private:
158
159     /// The GC instance we are collecting stats from.
160     .gc.GC* gc = null;
161
162     /// True if statistics should be collected.
163     bool active = false;
164
165     /// Current collection information (printed when the malloc finishes).
166     MallocInfo malloc_info;
167
168     /// File where to write the malloc information to.
169     cstdio.FILE* mallocs_file;
170
171     /// Current collection information (printed when the collection finishes).
172     CollectionInfo collection_info;
173
174     /// File where to write the collections information to.
175     cstdio.FILE* collections_file;
176
177     /// Time when the program started.
178     double program_start = -1.0;
179
180     /// Return the current time as seconds since the epoch.
181     static double now()
182     {
183         ctime.timeval tv;
184         ctime.gettimeofday(&tv, null);
185         return cast(double) tv.tv_sec + cast(double) tv.tv_usec / 1_000_000.0;
186     }
187
188     /// Fill a MemoryInfo struct with the current state of the GC heap.
189     void fill_memory_info(MemoryInfo* mem_info)
190     {
191         mem_info.overhead += .gc.GC.sizeof + gc.pools.elements_sizeof +
192                 gc.roots.elements_sizeof + gc.ranges.elements_sizeof;
193
194         // pools
195         for (size_t i = 0; i < gc.pools.length; i++)
196         {
197             auto pool = gc.pools[i];
198             mem_info.overhead += pool.npages * ubyte.sizeof;
199             // the 5 bitmaps (mark, scan, free, final, noscan)
200             mem_info.overhead += 5 * (GCBits.sizeof
201                     + (pool.mark.nwords + 2) * uint.sizeof);
202
203             for (size_t pn = 0; pn < pool.npages; pn++)
204             {
205                 auto bin = cast(.gc.Bins) pool.pagetable[pn];
206                 if (bin < .gc.B_PAGE)
207                 {
208                     size_t size = .gc.binsize[bin];
209                     size_t attrstride = size / 16;
210                     size_t attrbase = pn * (.gc.PAGESIZE / 16);
211                     size_t attrtop = attrbase + (.gc.PAGESIZE / 16);
212                     for (auto attri = attrbase; attri < attrtop; attri += attrstride)
213                     {
214                         if (pool.freebits.test(attri))
215                             mem_info.free += size;
216                         else
217                             mem_info.used += size; // TODO: wasted
218                     }
219                 }
220                 else if (bin == .gc.B_FREE)
221                     mem_info.free += .gc.PAGESIZE;
222                 else // B_PAGE / B_PAGEPLUS
223                     mem_info.used += .gc.PAGESIZE; // TODO: wasted
224             }
225         }
226     }
227
228     cstdio.FILE* start_file(char* filename, char* header)
229     {
230         cstdio.FILE* file = cstdio.fopen(filename, "w");
231         assert (file !is null);
232         cstdio.fputs(header, file);
233         return file;
234     }
235
236     void print_malloc()
237     {
238         if (this.mallocs_file is null)
239             return;
240         auto ptrmap = this.malloc_info.ptrmap;
241         auto ptrmask_offset = (ptrmap[0] - 1) / (size_t.sizeof * 8) + 1;
242         cstdio.fprintf(this.mallocs_file,
243                 "%f,%f,%p,%zu,%zu,%zu,%zu,%zu,%p,%zu,%0*zX,%0*zX\n",
244                 //0  1   2   3   4   5   6  7  8   9    10    11
245                 this.malloc_info.timestamp,                   // 0
246                 this.malloc_info.time,                        // 1
247                 this.malloc_info.ptr,                         // 2
248                 this.malloc_info.size,                        // 3
249                 this.malloc_info.collected ? 1u : 0u,         // 4
250                 this.malloc_info.attr & .gc.BlkAttr.FINALIZE, // 5
251                 this.malloc_info.attr & .gc.BlkAttr.NO_SCAN,  // 6
252                 this.malloc_info.attr & .gc.BlkAttr.NO_MOVE,  // 7
253                 ptrmap,                                       // 8
254                 ptrmap[0] * size_t.sizeof,                    // 9
255                 size_t.sizeof,                                // fill length
256                 ptrmap[1],                                    // 10
257                 size_t.sizeof,                                // fill length
258                 ptrmap[1 + ptrmask_offset]);                  // 11
259         // TODO: make it an option
260         cstdio.fflush(this.mallocs_file);
261     }
262
263     void print_collection()
264     {
265         if (this.collections_file is null)
266             return;
267         cstdio.fprintf(this.collections_file,
268                 "%f,%f,%f,%f,%zu,%zu,%zu,%zu,%zu,%zu,%zu,%zu\n",
269                 //0  1  2  3   4   5   6   7   8   9  10  11
270                 this.collection_info.timestamp,           // 0
271                 this.collection_info.time.malloc,         // 1
272                 this.collection_info.time.collection,     // 2
273                 this.collection_info.time.stop_the_world, // 3
274                 this.collection_info.before.used,         // 4
275                 this.collection_info.before.free,         // 5
276                 this.collection_info.before.wasted,       // 6
277                 this.collection_info.before.overhead,     // 7
278                 this.collection_info.after.used,          // 8
279                 this.collection_info.after.free,          // 9
280                 this.collection_info.after.wasted,        // 10
281                 this.collection_info.after.overhead);     // 11
282         // TODO: make it an option
283         cstdio.fflush(this.collections_file);
284     }
285
286
287 public:
288
289     /**
290      * Construct a Stats object (useful for easy initialization).
291      *
292      * This function should be always used to initialize a Stats object because
293      * the program start time (in seconds since the epoch) needs to be taken to
294      * properly add timestamps to allocations and collections.
295      */
296     static Stats opCall(.gc.GC* gc)
297     {
298         Stats this_;
299         this_.gc = gc;
300         if (options.malloc_stats_file[0] != '\0') {
301             this_.active = true;
302             this_.mallocs_file = this_.start_file(
303                     options.malloc_stats_file.ptr,
304                     "Timestamp,Time,Pointer,Size,Collection triggered,"
305                     "Finalize,No scan,No move,Pointer map,Type size,"
306                     "Pointer map scan bitmask (first word, hexa),"
307                     "Pointer map pointer bitmask (first word, hexa)\n");
308         }
309         // collection
310         if (options.collect_stats_file[0] != '\0') {
311             this_.active = true;
312             this_.collections_file = this_.start_file(
313                     options.collect_stats_file.ptr,
314                     "Timestamp,Malloc time,Collection time,Stop-the-world time,"
315                     "Used before,Free before,Wasted before,Overhead before,"
316                     "Used after,Free after,Wasted after,Overhead after\n");
317         }
318         this_.program_start = this_.now();
319         return this_;
320     }
321
322     void finalize()
323     {
324         if (this.mallocs_file !is null)
325             cstdio.fclose(this.mallocs_file);
326         if (this.collections_file !is null)
327             cstdio.fclose(this.collections_file);
328     }
329
330     /// Inform the start of an allocation.
331     // TODO: store/use type information
332     void malloc_started(size_t size, uint attr, size_t* ptrmap_bitmask)
333     {
334         if (!this.active)
335             return;
336         auto now = this.now();
337         auto relative_now = now - this.program_start;
338         // malloc
339         this.malloc_info = this.malloc_info.init;
340         this.malloc_info.timestamp = relative_now;
341         this.malloc_info.time = now;
342         this.malloc_info.size = size;
343         this.malloc_info.attr = attr;
344         this.malloc_info.ptrmap = ptrmap_bitmask;
345         // this.malloc_info.collected is filled in malloc_finished()
346         // collection
347         this.collection_info = this.collection_info.init;
348         this.collection_info.timestamp = relative_now;
349         // this.collection_info.time.malloc is the same as malloc_info.time
350     }
351
352     /// Inform the end of an allocation.
353     void malloc_finished(void* ptr)
354     {
355         if (!this.active)
356             return;
357         auto now = this.now();
358         auto collected = !(this.collection_info.time.collection < 0.0);
359         // malloc
360         this.malloc_info.time = now - this.malloc_info.time;
361         if (collected)
362             this.malloc_info.collected = true;
363         this.malloc_info.ptr = ptr;
364         this.print_malloc();
365         if (!collected)
366             return;
367         // collection
368         this.collection_info.time.malloc = this.malloc_info.time;
369         this.print_collection();
370     }
371
372     /// Inform that all threads (the world) have been stopped.
373     void world_stopped()
374     {
375         if (!this.active)
376             return;
377         this.collection_info.time.stop_the_world = this.now();
378     }
379
380     /// Inform that all threads (the world) have been resumed.
381     void world_started()
382     {
383         if (!this.active)
384             return;
385         this.collection_info.time.stop_the_world =
386                 this.now() - this.collection_info.time.stop_the_world;
387     }
388
389     /// Inform the start of a collection.
390     void collection_started()
391     {
392         if (!this.active)
393             return;
394         this.fill_memory_info(&this.collection_info.before);
395         this.collection_info.time.collection = this.now();
396     }
397
398     /// Inform the end of a collection.
399     void collection_finished()
400     {
401         if (!this.active)
402             return;
403         this.collection_info.time.collection =
404                 this.now() - this.collection_info.time.collection;
405         this.fill_memory_info(&this.collection_info.after);
406     }
407
408 }
409
410
411 /**
412  *
413  */
414 struct GCStats
415 {
416     size_t poolsize;        // total size of pool
417     size_t usedsize;        // bytes allocated
418     size_t freeblocks;      // number of blocks marked FREE
419     size_t freelistsize;    // total of memory on free lists
420     size_t pageblocks;      // number of blocks marked PAGE
421 }
422
423
424 // vim: set et sw=4 sts=4 :