1 /*******************************************************************************
2 
3     Contains API to obtain garbage collector stats.
4 
5     Copyright:
6         Copyright (c) 2009-2018 dunnhumby Germany GmbH.
7         All rights reserved.
8 
9     License:
10         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
11         Alternatively, this file may be distributed under the terms of the Tango
12         3-Clause BSD License (see LICENSE_BSD.txt for details).
13 
14 *******************************************************************************/
15 
16 module ocean.application.components.GCStats;
17 
18 import core.memory;
19 
20 import ocean.meta.traits.Aggregates : hasMember;
21 import ocean.meta.types.Function : ParametersOf;
22 import ocean.time.MicrosecondsClock;
23 
24 /*******************************************************************************
25 
26     Class used for getting used GC stats for the current process. This will
27     work only with dmd-transitional.
28 
29 *******************************************************************************/
30 
31 public class GCStats
32 {
33     static if ( hasMember!(GC, "monitor") )
34     {
35         /// The type of the gcEnd delegate
36         private alias ParametersOf!(GC.monitor)[1] MonitorEndParams;
37 
38         /// Integers used by the gcEnd delegate
39         private alias ParametersOf!(MonitorEndParams)[0] MonitorInt;
40     }
41     else
42     {
43         pragma(msg, "There will be no gc stats! You need to use dmd transitional:");
44         pragma(msg, "https://github.com/sociomantic-tsunami/dmd-transitional");
45 
46         /// Default type for integers used by monitor
47         private alias long MonitorInt;
48     }
49 
50     /**********************************************************************
51 
52         Structure representing recorded GC stats.
53 
54     **********************************************************************/
55 
56     public struct Stats
57     {
58         /**********************************************************************
59 
60             The number of microseconds the garbage collector ran for in the
61             last stats collection.
62 
63         **********************************************************************/
64 
65         public size_t gc_run_duration;
66 
67         /**********************************************************************
68 
69             The percentage of time that was spent by the garbage collector in
70             the last stats collection.
71 
72         **********************************************************************/
73 
74         public float gc_run_percentage = 0;
75     }
76 
77     /// ditto
78     private Stats stats;
79 
80     /// Timestamp for when the last garbage collection started
81     private ulong gc_start_us;
82 
83     /**********************************************************************
84 
85         Timestamp for when the last stats were collected used to
86         calculate the percentage of the time spent in GC between stats
87         cycles.
88 
89     **********************************************************************/
90 
91     private ulong last_collected_timestamp;
92 
93     /**********************************************************************
94 
95         Reset statistics.
96 
97     **********************************************************************/
98 
99     private void reset ( )
100     {
101         this.last_collected_timestamp = MicrosecondsClock.now_us();
102         this.stats.gc_run_duration = 0;
103     }
104 
105     /**********************************************************************
106 
107         Start stats reporting
108 
109     **********************************************************************/
110 
111     public void start ( )
112     {
113         static if ( hasMember!(GC, "monitor") )
114         {
115             GC.monitor(&this.gcBegin, &this.gcEnd);
116         }
117     }
118 
119     /**********************************************************************
120 
121         Stop stats reporting
122 
123     **********************************************************************/
124 
125     public void stop ( )
126     {
127         static if ( hasMember!(GC, "monitor") )
128         {
129             GC.monitor(null, null);
130         }
131     }
132 
133     /***************************************************************************
134 
135         Get's the GC stats for the current process.
136 
137         Returns:
138             Stats instance recording the current stats.
139 
140     ***************************************************************************/
141 
142     public Stats collect ()
143     {
144         auto result = this.stats;
145         this.reset();
146 
147         return result;
148     }
149 
150     /**********************************************************************
151 
152         Called each time the GC starts a collection
153 
154     **********************************************************************/
155 
156     private void gcBegin ( )
157     {
158         this.gc_start_us = MicrosecondsClock.now_us();
159     }
160 
161     /***************************************************************************
162 
163         Called when the GC completes a collection.
164 
165         Params:
166             freed = the number of bytes freed overall
167             pagebytes = the number of bytes freed within full pages.
168 
169     ***************************************************************************/
170 
171     private void gcEnd ( MonitorInt freed, MonitorInt pagebytes )
172     {
173         auto now = MicrosecondsClock.now_us();
174 
175         this.stats.gc_run_duration += now - this.gc_start_us;
176         this.stats.gc_run_percentage = cast(float)this.stats.gc_run_duration /
177             cast(float)(now - last_collected_timestamp);
178     }
179 }