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