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 }