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 /// Address of the pointer returned by malloc.
128 /// Memory requested by the malloc() call (in bytes).
130 /// Memory attributes as BlkAttr flags.
132 /// True if this malloc() triggered a collection.
133 bool collected = false;
134 /// Address of the pointer map bitmask.
135 size_t* ptrmap = null;
142 * Control and store the GC statistics.
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.
149 * All the data is logged in memory and printed to the standard output when
150 * requested (usually at the end of the program).
152 * See_Also: CollectionInfo and MallocInfo structs.
159 /// The GC instance we are collecting stats from.
162 /// True if statistics should be collected.
165 /// Current collection information (printed when the malloc finishes).
166 MallocInfo malloc_info;
168 /// File where to write the malloc information to.
169 cstdio.FILE* mallocs_file;
171 /// Current collection information (printed when the collection finishes).
172 CollectionInfo collection_info;
174 /// File where to write the collections information to.
175 cstdio.FILE* collections_file;
177 /// Time when the program started.
178 double program_start = -1.0;
180 /// Return the current time as seconds since the epoch.
184 ctime.gettimeofday(&tv, null);
185 return cast(double) tv.tv_sec + cast(double) tv.tv_usec / 1_000_000.0;
188 /// Fill a MemoryInfo struct with the current state of the GC heap.
189 void fill_memory_info(MemoryInfo* mem_info)
191 mem_info.overhead += .gc.GC.sizeof + gc.pools.elements_sizeof +
192 gc.roots.elements_sizeof + gc.ranges.elements_sizeof;
195 for (size_t i = 0; i < gc.pools.length; i++)
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);
203 for (size_t pn = 0; pn < pool.npages; pn++)
205 auto bin = cast(.gc.Bins) pool.pagetable[pn];
206 if (bin < .gc.B_PAGE)
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)
214 if (pool.freebits.test(attri))
215 mem_info.free += size;
217 mem_info.used += size; // TODO: wasted
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
228 cstdio.FILE* start_file(char* filename, char* header)
230 cstdio.FILE* file = cstdio.fopen(filename, "w");
231 assert (file !is null);
232 cstdio.fputs(header, file);
238 if (this.mallocs_file is null)
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
254 ptrmap[0] * size_t.sizeof, // 9
255 size_t.sizeof, // fill length
257 size_t.sizeof, // fill length
258 ptrmap[1 + ptrmask_offset]); // 11
259 // TODO: make it an option
260 cstdio.fflush(this.mallocs_file);
263 void print_collection()
265 if (this.collections_file is null)
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);
290 * Construct a Stats object (useful for easy initialization).
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.
296 static Stats opCall(.gc.GC* gc)
300 if (options.malloc_stats_file[0] != '\0') {
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");
310 if (options.collect_stats_file[0] != '\0') {
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");
318 this_.program_start = this_.now();
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);
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)
336 auto now = this.now();
337 auto relative_now = now - this.program_start;
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()
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
352 /// Inform the end of an allocation.
353 void malloc_finished(void* ptr)
357 auto now = this.now();
358 auto collected = !(this.collection_info.time.collection < 0.0);
360 this.malloc_info.time = now - this.malloc_info.time;
362 this.malloc_info.collected = true;
363 this.malloc_info.ptr = ptr;
368 this.collection_info.time.malloc = this.malloc_info.time;
369 this.print_collection();
372 /// Inform that all threads (the world) have been stopped.
377 this.collection_info.time.stop_the_world = this.now();
380 /// Inform that all threads (the world) have been resumed.
385 this.collection_info.time.stop_the_world =
386 this.now() - this.collection_info.time.stop_the_world;
389 /// Inform the start of a collection.
390 void collection_started()
394 this.fill_memory_info(&this.collection_info.before);
395 this.collection_info.time.collection = this.now();
398 /// Inform the end of a collection.
399 void collection_finished()
403 this.collection_info.time.collection =
404 this.now() - this.collection_info.time.collection;
405 this.fill_memory_info(&this.collection_info.after);
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
424 // vim: set et sw=4 sts=4 :