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;
132 /// Address of the pointer map bitmask.
133 size_t* ptrmap = null;
140 * Control and store the GC statistics.
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.
147 * All the data is logged in memory and printed to the standard output when
148 * requested (usually at the end of the program).
150 * See_Also: CollectionInfo and MallocInfo structs.
157 /// The GC instance we are collecting stats from.
160 /// True if statistics should be collected.
163 /// Current collection information (printed when the malloc finishes).
164 MallocInfo malloc_info;
166 /// File where to write the malloc information to.
167 cstdio.FILE* mallocs_file;
169 /// Current collection information (printed when the collection finishes).
170 CollectionInfo collection_info;
172 /// File where to write the collections information to.
173 cstdio.FILE* collections_file;
175 /// Time when the program started.
176 double program_start = -1.0;
178 /// Return the current time as seconds since the epoch.
182 ctime.gettimeofday(&tv, null);
183 return cast(double) tv.tv_sec + cast(double) tv.tv_usec / 1_000_000.0;
186 /// Fill a MemoryInfo struct with the current state of the GC heap.
187 void fill_memory_info(MemoryInfo* mem_info)
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;
193 for (size_t i = 0; i < .gc.pools.length; i++)
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);
201 for (size_t pn = 0; pn < pool.npages; pn++)
203 auto bin = cast(.gc.Bins) pool.pagetable[pn];
204 if (bin < .gc.B_PAGE)
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)
212 if (pool.freebits.test(attri))
213 mem_info.free += size;
215 mem_info.used += size; // TODO: wasted
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
226 cstdio.FILE* start_file(char* filename, char* header)
228 cstdio.FILE* file = cstdio.fopen(filename, "w");
229 assert (file !is null);
230 cstdio.fputs(header, file);
236 if (this.mallocs_file is null)
238 cstdio.fprintf(this.mallocs_file,
239 "%f,%f,%zu,%zu,%zu,%zu,%zu,%p\n",
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);
253 void print_collection()
255 if (this.collections_file is null)
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);
280 * Construct a Stats object (useful for easy initialization).
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.
286 static Stats opCall(.gc.GC gc)
290 if (options.malloc_stats_file[0] != '\0') {
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");
298 if (options.collect_stats_file[0] != '\0') {
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");
306 this_.program_start = this_.now();
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);
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)
324 auto now = this.now();
325 auto relative_now = now - this.program_start;
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()
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
340 /// Inform the end of an allocation.
341 void malloc_finished()
345 auto now = this.now();
346 auto collected = !(this.collection_info.time.collection < 0.0);
348 this.malloc_info.time = now - this.malloc_info.time;
350 this.malloc_info.collected = true;
355 this.collection_info.time.malloc = this.malloc_info.time;
356 this.print_collection();
359 /// Inform that all threads (the world) have been stopped.
364 this.collection_info.time.stop_the_world = this.now();
367 /// Inform that all threads (the world) have been resumed.
372 this.collection_info.time.stop_the_world =
373 this.now() - this.collection_info.time.stop_the_world;
376 /// Inform the start of a collection.
377 void collection_started()
381 this.fill_memory_info(&this.collection_info.before);
382 this.collection_info.time.collection = this.now();
385 /// Inform the end of a collection.
386 void collection_finished()
390 this.collection_info.time.collection =
391 this.now() - this.collection_info.time.collection;
392 this.fill_memory_info(&this.collection_info.after);
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
411 // vim: set et sw=4 sts=4 :