2 * This module contains garbage collector statistics functionality.
4 * Copyright: Copyright (C) 2005-2006 Digital Mars, www.digitalmars.com.
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.
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
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
24 * Authors: Walter Bright, Sean Kelly, Leandro Lucarella
27 module rt.gc.cdgc.stats;
29 import gc = rt.gc.cdgc.gc;
30 import rt.gc.cdgc.bits: GCBits;
31 import rt.gc.cdgc.opts: options;
33 import cstdio = tango.stdc.stdio;
34 import ctime = tango.stdc.posix.sys.time;
41 * Time information for a collection.
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).
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).
65 * Memory (space) information for a collection.
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.
76 /// Heap memory used by the program (in bytes).
78 /// Free heap memory (in bytes).
80 /// Memory that can't be used at all (in bytes).
82 /// Memory used by the GC for bookkeeping (in bytes).
88 * Information about a particular collection.
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).
96 * See_Also: TimeInfo and MemoryInfo structs.
100 /// When this information were taken (seconds since the program started).
101 double timestamp = -1.0;
102 /// Time statistics for this collection.
104 /// Memory statistics before the collection.
106 /// Memory statistics after the collection.
112 * Information about a particular allocation.
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).
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).
126 /// Memory requested by the malloc() call (in bytes).
128 /// Memory attributes as BlkAttr flags.
130 /// True if this malloc() triggered a collection.
131 bool collected = false;
138 * Control and store the GC statistics.
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.
145 * All the data is logged in memory and printed to the standard output when
146 * requested (usually at the end of the program).
148 * See_Also: CollectionInfo and MallocInfo structs.
155 /// The GC instance we are collecting stats from.
158 /// True if statistics should be collected.
161 /// Current collection information (printed when the malloc finishes).
162 MallocInfo malloc_info;
164 /// File where to write the malloc information to.
165 cstdio.FILE* mallocs_file;
167 /// Current collection information (printed when the collection finishes).
168 CollectionInfo collection_info;
170 /// File where to write the collections information to.
171 cstdio.FILE* collections_file;
173 /// Time when the program started.
174 double program_start = -1.0;
176 /// Return the current time as seconds since the epoch.
180 ctime.gettimeofday(&tv, null);
181 return cast(double) tv.tv_sec + cast(double) tv.tv_usec / 1_000_000.0;
184 /// Fill a MemoryInfo struct with the current state of the GC heap.
185 void fill_memory_info(MemoryInfo* mem_info)
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;
191 for (size_t i = 0; i < .gc.pools.length; i++)
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);
199 for (size_t pn = 0; pn < pool.npages; pn++)
201 auto bin = cast(.gc.Bins) pool.pagetable[pn];
202 if (bin < .gc.B_PAGE)
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)
210 if (pool.freebits.test(biti))
211 mem_info.free += size;
213 mem_info.used += size; // TODO: wasted
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
224 cstdio.FILE* start_file(char* filename, char* header)
226 cstdio.FILE* file = cstdio.fopen(filename, "w");
227 assert (file !is null);
228 cstdio.fputs(header, file);
234 if (this.mallocs_file is null)
236 cstdio.fprintf(this.mallocs_file,
237 "%f,%f,%zu,%zu,%zu,%zu,%zu\n",
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
248 void print_collection()
250 if (this.collections_file is null)
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
273 * Construct a Stats object (useful for easy initialization).
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.
279 static Stats opCall(.gc.GC gc)
283 if (options.malloc_stats_file[0] != '\0') {
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");
291 if (options.collect_stats_file[0] != '\0') {
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");
299 this_.program_start = this_.now();
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);
311 /// Inform the start of an allocation.
312 void malloc_started(size_t size, uint attr)
316 auto now = this.now();
317 auto relative_now = now - this.program_start;
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()
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
331 /// Inform the end of an allocation.
332 void malloc_finished()
336 auto now = this.now();
337 auto collected = !(this.collection_info.time.collection < 0.0);
339 this.malloc_info.time = now - this.malloc_info.time;
341 this.malloc_info.collected = true;
346 this.collection_info.time.malloc = this.malloc_info.time;
347 this.print_collection();
350 /// Inform that all threads (the world) have been stopped.
355 this.collection_info.time.stop_the_world = this.now();
358 /// Inform that all threads (the world) have been resumed.
363 this.collection_info.time.stop_the_world =
364 this.now() - this.collection_info.time.stop_the_world;
367 /// Inform the start of a collection.
368 void collection_started()
372 this.fill_memory_info(&this.collection_info.before);
373 this.collection_info.time.collection = this.now();
376 /// Inform the end of a collection.
377 void collection_finished()
381 this.collection_info.time.collection =
382 this.now() - this.collection_info.time.collection;
383 this.fill_memory_info(&this.collection_info.after);
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
402 // vim: set et sw=4 sts=4 :