1 /*******************************************************************************
2 
3     Contains API to obtain various information about the running application.
4 
5     Copyright:
6         Copyright (c) 2009-2017 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.sys.Stats;
17 
18 import ocean.meta.types.Qualifiers;
19 import ocean.core.Enforce;
20 import ocean.core.array.Mutation;
21 import core.sys.posix.sys.resource;
22 import ProcVFS = ocean.sys.stats.linux.ProcVFS;
23 import ocean.sys.stats.linux.Queriable;
24 import ocean.util.log.Logger;
25 import ocean.meta.traits.Indirections;
26 
27 /// Logger for logging errors
28 private Logger stats_module_logger;
29 static this ()
30 {
31     stats_module_logger = Log.lookup("ocean.sys.Stats");
32 }
33 
34 /***************************************************************************
35 
36     Convenience wrapper around stats about open/max file descriptors,
37     useful for logging in stats log.
38 
39 ***************************************************************************/
40 
41 public struct OpenFileStats
42 {
43     /// Limit for the number of open fds.
44     long open_fds_limit;
45     /// Number of currently open fds in a process
46     int open_fds_currently;
47 }
48 
49 /***************************************************************************
50 
51     Gets the file descriptor stats for the process.
52 
53     Returns:
54         instance of filled OpenFileStats
55 
56     Note:
57         In order to get the number of open files, this method iterates
58         through the directory entires in /proc VFS. This implies that it should
59         not be called multiple times a second, as there might be performance
60         implications. Ideally, it's called every 30 seconds, or so, just
61         to generate the stats.log as needed.
62 
63     Throws:
64         ErrnoException if the underlying system calls fail.
65 
66 ***************************************************************************/
67 
68 public OpenFileStats getNumFilesStats ()
69 {
70     OpenFileStats stats;
71     stats.open_fds_limit = maximumProcessNumFiles().rlim_cur;
72     stats.open_fds_currently = ProcVFS.getOpenFdCount();
73     return stats;
74 }
75 
76 /*******************************************************************************
77 
78     Class used for getting used CPU percentage and used memory for the
79     current process.
80 
81 *******************************************************************************/
82 
83 public class CpuMemoryStats
84 {
85     /***************************************************************************
86 
87         Structure representing recorded stats.
88 
89     ***************************************************************************/
90 
91     public struct Stats
92     {
93         /// Consumed CPU percentage in user mode
94         public float cpu_user;
95 
96         /// Consumed CPU percentage in system mode
97         public float cpu_system;
98 
99         /// Total consumed CPU percentage
100         public float cpu_total;
101 
102         /// Virtual memory size
103         public ulong vsz;
104 
105         /// Resident set size
106         public ulong rss;
107 
108         /// Total memory usage in percents
109         public float mem_percentage;
110     }
111 
112     /// Previous recorded uptime, used for generating stats
113     private ProcVFS.ProcUptime previous_uptime;
114 
115     /// Previous recorded /proc/self/stat, used for generating stats
116     private ProcVFS.ProcStat previous_stat;
117 
118     /// Curent recorded uptime, used for generating stats, updated each cycle
119     ProcVFS.ProcUptime current_uptime;
120 
121     /// Curent recorded /proc/self/stat, used for generating stats, updated each
122     /// cycle
123     ProcVFS.ProcStat current_stat;
124 
125     /// System configuration facts used for calculating stats
126     private static long clock_ticks_per_second;
127 
128     /// System configuration facts used for calculating stats
129     private static long page_size;
130 
131     /// System configuration facts used for calculating stats
132     private static ulong total_memory;
133 
134     /***************************************************************************
135 
136         Constructor.
137 
138         Prepares CpuMemoryStats for recording stats.
139 
140     ***************************************************************************/
141 
142     public this ( )
143     {
144         this.clock_ticks_per_second = getClockTicksPerSecond();
145         this.page_size = getPageSize();
146         this.total_memory = ProcVFS.getTotalMemoryInBytes();
147 
148         this.previous_uptime = ProcVFS.getProcUptime();
149         enforce(ProcVFS.getProcSelfStat(this.previous_stat));
150     }
151 
152     /***************************************************************************
153 
154         Get's the cpu and memory stats for the current process.
155 
156         Returns:
157             Stats instance recording the current stats.
158 
159     ***************************************************************************/
160 
161     public Stats collect ()
162     {
163         Stats stats;
164 
165         try
166         {
167             this.current_uptime = ProcVFS.getProcUptime();
168             enforce(ProcVFS.getProcSelfStat(this.current_stat));
169         }
170         catch (Exception e)
171         {
172             .stats_module_logger.error("Couldn't get stats for the process: {}@{}:{}",
173                 e.message(), e.file, e.line);
174 
175             return stats;
176         }
177 
178         auto uptime_diff = this.current_uptime.uptime - this.previous_uptime.uptime;
179 
180         // Safety check for divide by zero
181         if (uptime_diff.seconds == 0 && uptime_diff.cents == 0)
182         {
183             stats.cpu_user = float.nan;
184             stats.cpu_system = float.nan;
185             stats.cpu_total = float.nan;
186         }
187         else
188         {
189             auto ticks_diff = convertToTicks(uptime_diff);
190 
191             stats.cpu_user = (this.current_stat.utime <= this.previous_stat.utime) ? 0 :
192                 ((this.current_stat.utime - this.previous_stat.utime) / ticks_diff * 100);
193 
194             stats.cpu_system = (this.current_stat.stime <= this.previous_stat.stime) ? 0 :
195                 ((this.current_stat.stime - this.previous_stat.stime) / ticks_diff * 100);
196 
197             auto previous_total = this.previous_stat.utime + this.previous_stat.stime;
198             auto current_total = this.current_stat.utime + this.current_stat.stime;
199 
200             stats.cpu_total = (current_total <= previous_total) ? 0 :
201                 ((current_total - previous_total) / ticks_diff * 100);
202         }
203 
204         stats.vsz = this.current_stat.vsize;
205         stats.rss = this.current_stat.rss * this.page_size;
206         stats.mem_percentage = cast(float)stats.rss / this.total_memory * 100;
207 
208         this.previous_uptime = current_uptime;
209         foreach (idx, ref field; this.current_stat.tupleof)
210         {
211             static if (!hasIndirections!(typeof(field)))
212                 this.previous_stat.tupleof[idx] = field;
213             else
214                 copy(this.previous_stat.tupleof[idx], field);
215         }
216 
217         return stats;
218     }
219 
220     /***************************************************************************
221 
222         Converts the Uptime type to number of ticks.
223 
224         Params:
225             time = time to convert
226 
227         Returns:
228             time represented in ticks
229 
230     ***************************************************************************/
231 
232     private float convertToTicks (ProcVFS.ProcUptime.Time time)
233     {
234         return time.seconds * this.clock_ticks_per_second +
235             time.cents * this.clock_ticks_per_second / 100.0f;
236     }
237 
238 }