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 }