passenger 4.0.42 → 4.0.43

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (67) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +13 -0
  5. data/CONTRIBUTING.md +2 -19
  6. data/build/agents.rb +4 -1
  7. data/build/cxx_tests.rb +7 -2
  8. data/build/debian.rb +1 -1
  9. data/debian.template/control.template +0 -2
  10. data/doc/CodingTipsAndPitfalls.md +56 -0
  11. data/doc/Users guide Apache.idmap.txt +16 -14
  12. data/doc/Users guide Nginx.idmap.txt +8 -6
  13. data/doc/Users guide Standalone.idmap.txt +3 -1
  14. data/doc/Users guide Standalone.txt +1 -1
  15. data/doc/users_guide_snippets/environment_variables.txt +1 -0
  16. data/doc/users_guide_snippets/installation.txt +5 -5
  17. data/doc/users_guide_snippets/support_information.txt +42 -9
  18. data/doc/users_guide_snippets/troubleshooting/default.txt +42 -0
  19. data/ext/common/ApplicationPool2/Common.h +1 -0
  20. data/ext/common/ApplicationPool2/DirectSpawner.h +2 -7
  21. data/ext/common/ApplicationPool2/DummySpawner.h +1 -1
  22. data/ext/common/ApplicationPool2/Group.h +4 -2
  23. data/ext/common/ApplicationPool2/Options.h +9 -7
  24. data/ext/common/ApplicationPool2/Pool.h +83 -40
  25. data/ext/common/ApplicationPool2/Process.h +2 -6
  26. data/ext/common/ApplicationPool2/README.md +0 -40
  27. data/ext/common/ApplicationPool2/SmartSpawner.h +2 -9
  28. data/ext/common/ApplicationPool2/Spawner.h +1 -4
  29. data/ext/common/ApplicationPool2/SpawnerFactory.h +6 -9
  30. data/ext/common/ApplicationPool2/SuperGroup.h +3 -3
  31. data/ext/common/Constants.h +1 -1
  32. data/ext/common/UnionStation/Connection.h +227 -0
  33. data/ext/common/UnionStation/Core.h +497 -0
  34. data/ext/common/UnionStation/ScopeLog.h +172 -0
  35. data/ext/common/UnionStation/Transaction.h +276 -0
  36. data/ext/common/Utils.cpp +83 -8
  37. data/ext/common/Utils.h +25 -4
  38. data/ext/common/Utils/AnsiColorConstants.h +1 -0
  39. data/ext/common/Utils/ProcessMetricsCollector.h +6 -170
  40. data/ext/common/Utils/SpeedMeter.h +258 -0
  41. data/ext/common/Utils/StrIntUtils.cpp +6 -0
  42. data/ext/common/Utils/StringScanning.h +277 -0
  43. data/ext/common/Utils/SystemMetricsCollector.h +1460 -0
  44. data/ext/common/agents/Base.cpp +8 -8
  45. data/ext/common/agents/HelperAgent/Main.cpp +12 -6
  46. data/ext/common/agents/HelperAgent/RequestHandler.h +15 -16
  47. data/ext/common/agents/HelperAgent/SystemMetricsTool.cpp +199 -0
  48. data/ext/common/agents/LoggingAgent/LoggingServer.h +2 -1
  49. data/ext/common/agents/SpawnPreparer.cpp +20 -32
  50. data/lib/phusion_passenger.rb +1 -1
  51. data/lib/phusion_passenger/config/list_instances_command.rb +118 -0
  52. data/lib/phusion_passenger/config/main.rb +22 -4
  53. data/lib/phusion_passenger/config/system_metrics_command.rb +37 -0
  54. data/lib/phusion_passenger/config/utils.rb +1 -1
  55. data/lib/phusion_passenger/loader_shared_helpers.rb +8 -5
  56. data/lib/phusion_passenger/platform_info/compiler.rb +1 -1
  57. data/resources/templates/error_layout.html.template +3 -3
  58. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +3 -5
  59. data/test/cxx/ApplicationPool2/PoolTest.cpp +1 -3
  60. data/test/cxx/ApplicationPool2/ProcessTest.cpp +4 -4
  61. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +5 -7
  62. data/test/cxx/RequestHandlerTest.cpp +9 -3
  63. data/test/cxx/UnionStationTest.cpp +61 -64
  64. metadata +13 -4
  65. metadata.gz.asc +7 -7
  66. data/ext/common/UnionStation.h +0 -968
  67. data/helper-scripts/system-memory-stats.py +0 -207
@@ -439,6 +439,12 @@ distanceOfTimeInWords(time_t fromTime, time_t toTime) {
439
439
  time_t minutes = seconds / 60;
440
440
  if (minutes >= 60) {
441
441
  time_t hours = minutes / 60;
442
+ if (hours >= 24) {
443
+ time_t days = hours / 24;
444
+ hours = hours % 24;
445
+ result << days << "d ";
446
+ }
447
+
442
448
  minutes = minutes % 60;
443
449
  result << hours << "h ";
444
450
  }
@@ -0,0 +1,277 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2013-2014 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_STRING_SCANNING_H_
26
+ #define _PASSENGER_STRING_SCANNING_H_
27
+
28
+ #include <cstring>
29
+ #include <cstdlib>
30
+ #include <string>
31
+ #include <StaticString.h>
32
+
33
+
34
+ /**
35
+ * Utility functions for scanning strings. Given a pointer to string data,
36
+ * these functions can read or skip parts of it and advance the pointer.
37
+ * Especially useful for parsing the output of command line tools.
38
+ *
39
+ * const char *data = "hello world 1234";
40
+ *
41
+ * readNextWord(&data); // => "hello"
42
+ * printf("%s\n", data); // => " world 1234"
43
+ *
44
+ * readNextWord(&data); // => "world"
45
+ * printf("%s\n", data); // => " 1234"
46
+ *
47
+ * readNextWordAsInt(&data); // => 1234
48
+ * printf("%s\n", data); // => ""
49
+ *
50
+ * readNextWord(&data); // => ParseException,
51
+ * // because end has been reached
52
+ */
53
+
54
+
55
+ namespace Passenger {
56
+
57
+ using namespace std;
58
+
59
+ struct ParseException {};
60
+
61
+ inline void
62
+ _skipLeadingWhitespaces(const char **data) {
63
+ while (**data == ' ') {
64
+ (*data)++;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Scan the given data for the first word that appears on the first line.
70
+ * Leading whitespaces (but not newlines) are ignored. If a word is found
71
+ * then the word is returned and the data pointer is moved to the end of
72
+ * the word.
73
+ *
74
+ * If the first line only contains whitespaces, or if the first line is empty,
75
+ * then a ParseException is thrown.
76
+ *
77
+ * @post result.size() > 0
78
+ */
79
+ inline StaticString
80
+ readNextWord(const char **data) {
81
+ _skipLeadingWhitespaces(data);
82
+ if (**data == '\n' || **data == '\0') {
83
+ throw ParseException();
84
+ }
85
+
86
+ // Find end of word and extract the word.
87
+ const char *endOfWord = *data;
88
+ while (*endOfWord != ' ' && *endOfWord != '\n' && *endOfWord != '\0') {
89
+ endOfWord++;
90
+ }
91
+ StaticString result(*data, endOfWord - *data);
92
+
93
+ // Move data pointer to the end of this word.
94
+ *data = endOfWord;
95
+ return result;
96
+ }
97
+
98
+ inline long long
99
+ _processNextWordAsLongLong(const StaticString &word, char *nullTerminatedWord) {
100
+ memcpy(nullTerminatedWord, word.c_str(), word.size());
101
+ nullTerminatedWord[word.size()] = '\0';
102
+ if (*nullTerminatedWord == '\0') {
103
+ throw ParseException();
104
+ } else {
105
+ return atoll(nullTerminatedWord);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Scan the given data for the first word that appears on the first line.
111
+ * Leading whitespaces (but not newlines) are ignored. If a word is found
112
+ * then the word is parsed as a 'long long' number and returned, and the
113
+ * data pointer is moved to the end of the word.
114
+ *
115
+ * If the first line only contains whitespaces, or if the first line is empty,
116
+ * then a ParseException is thrown.
117
+ */
118
+ inline long long
119
+ readNextWordAsLongLong(const char **data) {
120
+ StaticString word = readNextWord(data);
121
+ if (word.size() < 50) {
122
+ char nullTerminatedWord[50];
123
+ return _processNextWordAsLongLong(word, nullTerminatedWord);
124
+ } else {
125
+ string nullTerminatedWord(word.size() + 1, '\0');
126
+ return _processNextWordAsLongLong(word, &nullTerminatedWord[0]);
127
+ }
128
+ }
129
+
130
+ inline int
131
+ _processNextWordAsInt(const StaticString &word, char *nullTerminatedWord) {
132
+ memcpy(nullTerminatedWord, word.c_str(), word.size());
133
+ nullTerminatedWord[word.size()] = '\0';
134
+ if (*nullTerminatedWord == '\0') {
135
+ throw ParseException();
136
+ } else {
137
+ return atoi(nullTerminatedWord);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Scan the given data for the first word that appears on the first line.
143
+ * Leading whitespaces (but not newlines) are ignored. If a word is found
144
+ * then the word is parsed as an 'int' number and returned, and the
145
+ * data pointer is moved to the end of the word.
146
+ *
147
+ * If the first line only contains whitespaces, or if the first line is empty,
148
+ * then a ParseException is thrown.
149
+ */
150
+ inline int
151
+ readNextWordAsInt(const char **data) {
152
+ StaticString word = readNextWord(data);
153
+ if (word.size() < 50) {
154
+ char nullTerminatedWord[50];
155
+ return _processNextWordAsInt(word, nullTerminatedWord);
156
+ } else {
157
+ string nullTerminatedWord(word.size() + 1, '\0');
158
+ return _processNextWordAsInt(word, &nullTerminatedWord[0]);
159
+ }
160
+ }
161
+
162
+ inline double
163
+ _processNextWordAsDouble(const StaticString &word, char *nullTerminatedWord) {
164
+ memcpy(nullTerminatedWord, word.c_str(), word.size());
165
+ nullTerminatedWord[word.size()] = '\0';
166
+ if (*nullTerminatedWord == '\0') {
167
+ throw ParseException();
168
+ } else {
169
+ return atof(nullTerminatedWord);
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Scan the given data for the first word that appears on the first line.
175
+ * Leading whitespaces (but not newlines) are ignored. If a word is found
176
+ * then the word is parsed as an 'double' number and returned, and the
177
+ * data pointer is moved to the end of the word.
178
+ *
179
+ * If the first line only contains whitespaces, or if the first line is empty,
180
+ * then a ParseException is thrown.
181
+ */
182
+ inline double
183
+ readNextWordAsDouble(const char **data) {
184
+ StaticString word = readNextWord(data);
185
+ if (word.size() < 50) {
186
+ char nullTerminatedWord[50];
187
+ return _processNextWordAsDouble(word, nullTerminatedWord);
188
+ } else {
189
+ string nullTerminatedWord(word.size() + 1, '\0');
190
+ return _processNextWordAsDouble(word, &nullTerminatedWord[0]);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Return the first line in the given data, excluding leading and trailing
196
+ * whitespaces, and excluding newline. If the first line only contains
197
+ * whitespaces or if the first line is empty, then the empty string is
198
+ * returned.
199
+ *
200
+ * If the data does not contain a newline, then a ParseException is thrown.
201
+ */
202
+ inline string
203
+ readRestOfLine(const char *data) {
204
+ _skipLeadingWhitespaces(&data);
205
+ // Rest of line is allowed to be empty.
206
+ if (*data == '\n' || *data == '\0') {
207
+ return "";
208
+ }
209
+
210
+ // Look for newline character. From there, scan back until we've
211
+ // found a non-whitespace character.
212
+ const char *endOfLine = strchr(data, '\n');
213
+ if (endOfLine == NULL) {
214
+ throw ParseException();
215
+ }
216
+ while (*(endOfLine - 1) == ' ') {
217
+ endOfLine--;
218
+ }
219
+
220
+ return string(data, endOfLine - data);
221
+ }
222
+
223
+ inline bool
224
+ skipToNextLine(const char **data) {
225
+ const char *pos = strchr(*data, '\n');
226
+ if (pos != NULL) {
227
+ pos++;
228
+ *data = pos;
229
+ return true;
230
+ } else {
231
+ return false;
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Extract the first sentence from the first line in the data, where the end
237
+ * of the sentence is dictated by the `terminator` argument.
238
+ * Leading whitespaces (but not newlines) are ignored. The sentence is returned
239
+ * and the data pointer is moved to 1 character past the terminator.
240
+ *
241
+ * The terminator may not be '\n' or '\0'.
242
+ *
243
+ * A ParseException is thrown if one of the following conditions are encountered:
244
+ *
245
+ * * The first line only contains whitespaces.
246
+ * * The first line is empty.
247
+ * * The read sentence is empty.
248
+ * * The terminator character is not found.
249
+ *
250
+ * @post result.size() > 0
251
+ */
252
+ inline StaticString
253
+ readNextSentence(const char **data, char terminator) {
254
+ _skipLeadingWhitespaces(data);
255
+ if (**data == '\n' || **data == '\0' || **data == terminator) {
256
+ throw ParseException();
257
+ }
258
+
259
+ // Find end of sentence and extract the sentence.
260
+ const char *endOfSentence = *data;
261
+ do {
262
+ endOfSentence++;
263
+ } while (*endOfSentence != terminator && *endOfSentence != '\n' && *endOfSentence != '\0');
264
+
265
+ if (*endOfSentence == terminator) {
266
+ StaticString result(*data, endOfSentence - *data);
267
+ // Move data pointer 1 character past the terminator.
268
+ *data = endOfSentence + 1;
269
+ return result;
270
+ } else {
271
+ throw ParseException();
272
+ }
273
+ }
274
+
275
+ } // namespace Passenger
276
+
277
+ #endif /* _PASSENGER_STRING_SCANNING_H_ */
@@ -0,0 +1,1460 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2014 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_SYSTEM_METRICS_COLLECTOR_H_
26
+ #define _PASSENGER_SYSTEM_METRICS_COLLECTOR_H_
27
+
28
+ #include <boost/cstdint.hpp>
29
+ #include <boost/thread.hpp>
30
+ #include <boost/typeof/typeof.hpp>
31
+ #include <ostream>
32
+ #include <iomanip>
33
+ #include <algorithm>
34
+ #include <limits>
35
+ #include <vector>
36
+ #include <string>
37
+ #include <cstdio>
38
+ #include <cstdlib>
39
+ #include <climits>
40
+ #include <cmath>
41
+ #include <ctime>
42
+ #include <sys/types.h>
43
+ #include <sys/utsname.h>
44
+ #ifdef __linux__
45
+ #include <sys/sysinfo.h>
46
+ #include <Exceptions.h>
47
+ #include <Utils/StringScanning.h>
48
+ #include <Utils/SpeedMeter.h>
49
+ #include <Utils/IOUtils.h>
50
+ #endif
51
+ #ifdef __APPLE__
52
+ #include <mach/mach.h>
53
+ #include <sys/sysctl.h>
54
+ #include <sys/time.h>
55
+ #endif
56
+ #ifdef __FreeBSD__
57
+ #include <sys/param.h>
58
+ #include <sys/pcpu.h>
59
+ #include <sys/sysctl.h>
60
+ #include <sys/resource.h>
61
+ #include <vm/vm_param.h>
62
+ #endif
63
+ #include <unistd.h>
64
+ #include <stdlib.h>
65
+ #include <Constants.h>
66
+ #include <StaticString.h>
67
+ #include <Utils.h>
68
+ #include <Utils/StrIntUtils.h>
69
+ #include <Utils/SystemTime.h>
70
+ #include <Utils/AnsiColorConstants.h>
71
+
72
+ /*
73
+ * Useful resources
74
+ *
75
+ * OS X:
76
+ * http://www.opensource.apple.com/source/system_cmds/system_cmds-496/iostat.tproj/iostat.c
77
+ * https://github.com/max-horvath/htop-osx
78
+ * https://github.com/malkia/busybox-osx/blob/master/procps/iostat.c
79
+ *
80
+ * Linux:
81
+ * http://procps.cvs.sourceforge.net/viewvc/procps/procps/
82
+ * https://github.com/sysstat/sysstat/blob/master/mpstat.c
83
+ * http://www.thomas-krenn.com/en/wiki/Linux_Performance_Measurements_using_vmstat
84
+ * http://man7.org/linux/man-pages/man5/proc.5.html
85
+ *
86
+ * FreeBSD:
87
+ * https://github.com/freebsd/freebsd/blob/master/usr.bin/vmstat/vmstat.c
88
+ * https://github.com/freebsd/freebsd/blob/master/sbin/swapon/swapon.c
89
+ * http://stuff.mit.edu/afs/sipb/project/freebsd/head/contrib/top/machine.h
90
+ */
91
+
92
+ namespace Passenger {
93
+
94
+ using namespace boost;
95
+ using namespace std;
96
+
97
+ class SystemMetricsCollector;
98
+
99
+ /** All memory sizes are in KB. */
100
+ class SystemMetrics {
101
+ public:
102
+ struct DescriptionOptions {
103
+ bool general;
104
+ bool cpu;
105
+ bool memory;
106
+ bool colors;
107
+
108
+ DescriptionOptions()
109
+ : general(true),
110
+ cpu(true),
111
+ memory(true),
112
+ colors(false)
113
+ { }
114
+ };
115
+
116
+ struct XmlOptions {
117
+ bool general;
118
+ bool cpu;
119
+ bool memory;
120
+
121
+ XmlOptions()
122
+ : general(true),
123
+ cpu(true),
124
+ memory(true)
125
+ { }
126
+ };
127
+
128
+ private:
129
+ friend class SystemMetricsCollector;
130
+
131
+ #ifdef __linux__
132
+ SpeedMeter<unsigned long long, 8, 1000000, 60 * 1000000, 1000000> forkRateSpeedMeter;
133
+ SpeedMeter<size_t, 8, 1000000, 60 * 1000000, 1000000> swapInSpeedMeter, swapOutSpeedMeter;
134
+ #endif
135
+
136
+ double divideTotalCpuUsageByNCpus(double total) const {
137
+ if (ncpus() == 0) {
138
+ return -1;
139
+ } else {
140
+ total /= ncpus();
141
+ if (total > 100) {
142
+ return 100;
143
+ } else {
144
+ return total;
145
+ }
146
+ }
147
+ }
148
+
149
+ void outputHeader(ostream &stream, const DescriptionOptions &options,
150
+ const char *label) const
151
+ {
152
+ if (options.colors) {
153
+ stream << ANSI_COLOR_BLUE_BG ANSI_COLOR_BOLD ANSI_COLOR_YELLOW;
154
+ }
155
+ stream << "------------- " << label << " -------------";
156
+ if (options.colors) {
157
+ stream << ANSI_COLOR_RESET;
158
+ }
159
+ stream << endl;
160
+ }
161
+
162
+ string formatWidth(const string &str, int width) const {
163
+ char buf[128];
164
+ snprintf(buf, sizeof(buf), "%*s", width, str.c_str());
165
+ return buf;
166
+ }
167
+
168
+ string maybeColorAfterThreshold(const DescriptionOptions &options, const string &str,
169
+ double value, double threshold) const
170
+ {
171
+ if (value >= threshold && options.colors) {
172
+ return string(ANSI_COLOR_BOLD) + str + ANSI_COLOR_RESET;
173
+ } else {
174
+ return str;
175
+ }
176
+ }
177
+
178
+ string formatPercent(const DescriptionOptions &options, double percent,
179
+ int precision, int width, double threshold) const
180
+ {
181
+ string result;
182
+
183
+ if (percent == -2) {
184
+ if (options.colors) {
185
+ return string(ANSI_COLOR_DGRAY)
186
+ + formatWidth("unsupported by OS", width)
187
+ + ANSI_COLOR_RESET;
188
+ } else {
189
+ return formatWidth("unsupported by OS", width);
190
+ }
191
+ } else if (percent == -1) {
192
+ if (options.colors) {
193
+ return string(ANSI_COLOR_RED)
194
+ + formatWidth("?", width)
195
+ + ANSI_COLOR_RESET;
196
+ } else {
197
+ return formatWidth("?", width);
198
+ }
199
+ } else {
200
+ char buf[64];
201
+ snprintf(buf, sizeof(buf), "%.*f%%", precision, percent);
202
+ return maybeColorAfterThreshold(options, formatWidth(buf, width),
203
+ percent, threshold);
204
+ }
205
+ }
206
+
207
+ string formatPercent0(const DescriptionOptions &options, double percent,
208
+ int width = -1, double threshold = numeric_limits<double>::infinity()) const
209
+ {
210
+ return formatPercent(options, percent, 0, width, threshold);
211
+ }
212
+
213
+ string formatPercent2(const DescriptionOptions &options, double percent,
214
+ int width = -1, double threshold = -10) const
215
+ {
216
+ return formatPercent(options, percent, 2, width, threshold);
217
+ }
218
+
219
+ string kbToMb(ssize_t size) const {
220
+ if (size < 0) {
221
+ return "?";
222
+ } else {
223
+ char buf[32];
224
+ snprintf(buf, sizeof(buf), "%lld", (long long) size / 1024);
225
+ return buf;
226
+ }
227
+ }
228
+
229
+ public:
230
+ class CpuUsage {
231
+ private:
232
+ friend class SystemMetricsCollector;
233
+
234
+ unsigned long long lastUserTicks;
235
+ unsigned long long lastNiceTicks;
236
+ unsigned long long lastSystemTicks;
237
+ unsigned long long lastIoWaitTicks;
238
+ unsigned long long lastIdleTicks;
239
+ unsigned long long lastStealTicks;
240
+
241
+ /** Current usage statistics for this CPU.
242
+ *
243
+ * userUsage, niceUsage, systemUsage and idleUsage are fractions
244
+ * of user + nice + system + idle.
245
+ *
246
+ * ioWaitUsage is a fraction of user + nice + system + idle + iowait.
247
+ *
248
+ * stealUsage is a fraction of user + nice + system + idle + steal.
249
+ *
250
+ * All fractions range from 0 (unutilized) to SHRT_MAX (fully utilized).
251
+ * Use the *Pct() methods to convert them to percentages.
252
+ *
253
+ * Each statistic can individually be -1 if an error occurred while querying
254
+ * it, or -2 if the OS doesn't support it.
255
+ */
256
+ short userUsage, niceUsage, systemUsage;
257
+ short ioWaitUsage, idleUsage, stealUsage;
258
+
259
+ double fracToPercentage(short usage) const {
260
+ if (usage < 0) {
261
+ return usage;
262
+ } else {
263
+ return (double) usage / SHRT_MAX * 100.0;
264
+ }
265
+ }
266
+
267
+ public:
268
+ CpuUsage()
269
+ : lastUserTicks(0),
270
+ lastNiceTicks(0),
271
+ lastSystemTicks(0),
272
+ lastIoWaitTicks(0),
273
+ lastIdleTicks(0),
274
+ lastStealTicks(0),
275
+ userUsage(-1),
276
+ niceUsage(-1),
277
+ systemUsage(-1),
278
+ ioWaitUsage(-1),
279
+ idleUsage(-1),
280
+ stealUsage(-1)
281
+ { }
282
+
283
+ /** These methods return the usage statistics as percentages (0..100) */
284
+
285
+ double userPct() const {
286
+ return fracToPercentage(userUsage);
287
+ }
288
+
289
+ double nicePct() const {
290
+ return fracToPercentage(niceUsage);
291
+ }
292
+
293
+ double systemPct() const {
294
+ return fracToPercentage(systemUsage);
295
+ }
296
+
297
+ double ioWaitPct() const {
298
+ return fracToPercentage(ioWaitUsage);
299
+ }
300
+
301
+ double idlePct() const {
302
+ return fracToPercentage(idleUsage);
303
+ }
304
+
305
+ double stealPct() const {
306
+ return fracToPercentage(stealUsage);
307
+ }
308
+
309
+ /** Returns this CPU's usage as a percentage (0..100),
310
+ * or -1 if it cannot be determined.
311
+ */
312
+ double usage() const {
313
+ if (userUsage < 0 || niceUsage < 0 || systemUsage < 0) {
314
+ return -1;
315
+ } else {
316
+ return userPct() + nicePct() + systemPct();
317
+ }
318
+ }
319
+ };
320
+
321
+ /** Per-core CPU usage. This collection is empty if the number of cores
322
+ * cannot be queried.
323
+ */
324
+ vector<CpuUsage> cpuUsages;
325
+
326
+ /** Total system physical RAM. -1 if this information cannot be queried. */
327
+ ssize_t ramTotal;
328
+ /** Amount of RAM used. Does not include kernel caches and buffers. -1
329
+ * if this information cannot be queried.
330
+ */
331
+ ssize_t ramUsed;
332
+ /** Total system swap space, or -1 if this information cannot be queried. */
333
+ ssize_t swapTotal;
334
+ /** Amount of swap space used, or -1 if this information cannot be queried. */
335
+ ssize_t swapUsed;
336
+
337
+ /** Load averages for the past 1, 5 and 15 minutes. Can each individually be -1
338
+ * if that particular statistic cannot be queried.
339
+ */
340
+ double loadAverage1;
341
+ double loadAverage5;
342
+ double loadAverage15;
343
+
344
+ /** Time at which the system booted. -1 if this information cannot be queried. */
345
+ time_t boottime;
346
+
347
+ /** Speed at which processes are created per second.
348
+ * NaN if it's not yet known (because too few samples have been taken so far).
349
+ * -1 if there was an error querying this information.
350
+ * -2 if the OS does not support this metric.
351
+ */
352
+ double forkRate;
353
+ /** Speed at which the OS swaps in and swaps out data, in KB/sec.
354
+ * NaN if it's not yet known (because too few samples have been taken so far).
355
+ * -1 if there was an error querying this information.
356
+ * -2 if the OS does not support this metric.
357
+ */
358
+ double swapInRate, swapOutRate;
359
+
360
+ /** Kernel version number, or the empty string if this information cannot be queried. */
361
+ string kernelVersion;
362
+
363
+ SystemMetrics()
364
+ :
365
+ #ifdef __linux__
366
+ forkRateSpeedMeter(),
367
+ swapInSpeedMeter(),
368
+ swapOutSpeedMeter(),
369
+ #endif
370
+ ramTotal(-1),
371
+ ramUsed(-1),
372
+ swapTotal(-1),
373
+ swapUsed(-1),
374
+ loadAverage1(-1),
375
+ loadAverage5(-1),
376
+ loadAverage15(-1),
377
+ boottime(-1),
378
+ forkRate(-2),
379
+ swapInRate(-2),
380
+ swapOutRate(-2)
381
+ { }
382
+
383
+ unsigned int ncpus() const {
384
+ return cpuUsages.size();
385
+ }
386
+
387
+ /** The following methods calculate the current average system CPU usage
388
+ * statistics. Ranges from 0 (no cores are being used) to 100 (all cores
389
+ * at full utilization). Return -1 if the information cannot be queried.
390
+ */
391
+
392
+ double avgUserCpuUsage() const {
393
+ BOOST_AUTO(it, cpuUsages.begin());
394
+ BOOST_AUTO(end, cpuUsages.end());
395
+ double total = 0;
396
+
397
+ for (; it != end; it++) {
398
+ double val = it->userPct();
399
+ if (val < 0) {
400
+ return val;
401
+ } else {
402
+ total += val;
403
+ }
404
+ }
405
+ return divideTotalCpuUsageByNCpus(total);
406
+ }
407
+
408
+ double avgNiceCpuUsage() const {
409
+ BOOST_AUTO(it, cpuUsages.begin());
410
+ BOOST_AUTO(end, cpuUsages.end());
411
+ double total = 0;
412
+
413
+ for (; it != end; it++) {
414
+ double val = it->nicePct();
415
+ if (val < 0) {
416
+ return val;
417
+ } else {
418
+ total += val;
419
+ }
420
+ }
421
+ return divideTotalCpuUsageByNCpus(total);
422
+ }
423
+
424
+ double avgSystemCpuUsage() const {
425
+ BOOST_AUTO(it, cpuUsages.begin());
426
+ BOOST_AUTO(end, cpuUsages.end());
427
+ double total = 0;
428
+
429
+ for (; it != end; it++) {
430
+ double val = it->systemPct();
431
+ if (val < 0) {
432
+ return val;
433
+ } else {
434
+ total += val;
435
+ }
436
+ }
437
+ return divideTotalCpuUsageByNCpus(total);
438
+ }
439
+
440
+ double avgIoWaitCpuUsage() const {
441
+ BOOST_AUTO(it, cpuUsages.begin());
442
+ BOOST_AUTO(end, cpuUsages.end());
443
+ double total = 0;
444
+
445
+ for (; it != end; it++) {
446
+ double val = it->ioWaitPct();
447
+ if (val < 0) {
448
+ return val;
449
+ } else {
450
+ total += val;
451
+ }
452
+ }
453
+ return divideTotalCpuUsageByNCpus(total);
454
+ }
455
+
456
+ double avgIdleCpuUsage() const {
457
+ BOOST_AUTO(it, cpuUsages.begin());
458
+ BOOST_AUTO(end, cpuUsages.end());
459
+ double total = 0;
460
+
461
+ for (; it != end; it++) {
462
+ double val = it->idlePct();
463
+ if (val < 0) {
464
+ return val;
465
+ } else {
466
+ total += val;
467
+ }
468
+ }
469
+ return divideTotalCpuUsageByNCpus(total);
470
+ }
471
+
472
+ double avgStealCpuUsage() const {
473
+ BOOST_AUTO(it, cpuUsages.begin());
474
+ BOOST_AUTO(end, cpuUsages.end());
475
+ double total = 0;
476
+
477
+ for (; it != end; it++) {
478
+ double val = it->stealPct();
479
+ if (val < 0) {
480
+ return val;
481
+ } else {
482
+ total += val;
483
+ }
484
+ }
485
+ return divideTotalCpuUsageByNCpus(total);
486
+ }
487
+
488
+ double avgCpuUsage() const {
489
+ BOOST_AUTO(it, cpuUsages.begin());
490
+ BOOST_AUTO(end, cpuUsages.end());
491
+ double total = 0;
492
+
493
+ for (; it != end; it++) {
494
+ double val = it->usage();
495
+ if (val < 0) {
496
+ return val;
497
+ } else {
498
+ total += val;
499
+ }
500
+ }
501
+ return divideTotalCpuUsageByNCpus(total);
502
+ }
503
+
504
+ ssize_t ramFree() const {
505
+ if (ramTotal == -1 || ramUsed == -1) {
506
+ return -1;
507
+ } else {
508
+ return ramTotal - ramUsed;
509
+ }
510
+ }
511
+
512
+ ssize_t swapFree() const {
513
+ if (swapTotal == -1 || swapUsed == -1) {
514
+ return -1;
515
+ } else {
516
+ return swapTotal - swapUsed;
517
+ }
518
+ }
519
+
520
+ void toDescription(ostream &stream, const DescriptionOptions &options = DescriptionOptions()) const {
521
+ char buf[1024];
522
+ stream << std::right << std::setfill(' ');
523
+
524
+ if (options.general) {
525
+ outputHeader(stream, options, "General");
526
+ stream << "Kernel version : " << kernelVersion << endl;
527
+ stream << "Uptime : " << distanceOfTimeInWords(boottime) << endl;
528
+
529
+ stream << "Load averages : ";
530
+ stream << formatPercent2(options, loadAverage1, 5, 2);
531
+ stream << ", ";
532
+ stream << formatPercent2(options, loadAverage5, 5, 2);
533
+ stream << ", ";
534
+ stream << formatPercent2(options, loadAverage15, 5, 2);
535
+ stream << endl;
536
+
537
+ if (forkRate != -2) {
538
+ stream << "Fork rate : ";
539
+ if (std::isnan(forkRate) || forkRate < 0) {
540
+ if (options.colors) {
541
+ stream << ANSI_COLOR_DGRAY;
542
+ }
543
+ stream << "unknown";
544
+ if (options.colors) {
545
+ stream << ANSI_COLOR_RESET;
546
+ }
547
+ } else {
548
+ char buf[32];
549
+
550
+ snprintf(buf, sizeof(buf), "%.1f", forkRate);
551
+ stream << buf << "/sec";
552
+ }
553
+ stream << endl;
554
+ }
555
+
556
+ stream << endl;
557
+ }
558
+
559
+ if (options.cpu) {
560
+ double tmp;
561
+
562
+ outputHeader(stream, options, "CPU");
563
+ if (ncpus() == 0) {
564
+ stream << "Number of CPUs : unknown" << endl;
565
+ } else {
566
+ snprintf(buf, sizeof(buf), "%4u", ncpus());
567
+ stream << "Number of CPUs : " << buf << endl;
568
+ stream << "Average CPU usage : ";
569
+ stream << formatPercent0(options, avgCpuUsage(), 4, 95);
570
+ stream << " -- ";
571
+ stream << formatPercent0(options, avgUserCpuUsage(), 4, 95);
572
+ stream << " user, ";
573
+ stream << formatPercent0(options, avgNiceCpuUsage(), 4, 95);
574
+ stream << " nice, ";
575
+ stream << formatPercent0(options, avgSystemCpuUsage(), 4, 95);
576
+ stream << " system, ";
577
+ stream << formatPercent0(options, avgIdleCpuUsage(), 4);
578
+ stream << " idle" << endl;
579
+ }
580
+
581
+ for (unsigned i = 0; i < ncpus(); i++) {
582
+ snprintf(buf, sizeof(buf),
583
+ " CPU %-2u : ",
584
+ i + 1);
585
+ stream << buf;
586
+ stream << formatPercent0(options, cpuUsages[i].usage(), 4, 95);
587
+ stream << " -- ";
588
+ stream << formatPercent0(options, cpuUsages[i].userPct(), 4, 95);
589
+ stream << " user, ";
590
+ stream << formatPercent0(options, cpuUsages[i].nicePct(), 4, 95);
591
+ stream << " nice, ";
592
+ stream << formatPercent0(options, cpuUsages[i].systemPct(), 4, 95);
593
+ stream << " system, ";
594
+ stream << formatPercent0(options, cpuUsages[i].idlePct(), 4);
595
+ stream << " idle" << endl;
596
+ }
597
+
598
+ // For the two average metrics below, if a metric is unsupported by the OS (-2)
599
+ // then that implies that it's unsupported for all individual CPUs, so we
600
+ // don't bother printing CPU-specific metrics.
601
+ // But if an average metric is merely errored (-1), then it's still
602
+ // possible that we succeeded in querying the metric for a specific CPU.
603
+
604
+ tmp = avgIoWaitCpuUsage();
605
+ if (tmp != -2) {
606
+ stream << "I/O pressure : ";
607
+ stream << formatPercent0(options, avgIoWaitCpuUsage(), 4, 95);
608
+ stream << endl;
609
+ for (unsigned i = 0; i < ncpus(); i++) {
610
+ snprintf(buf, sizeof(buf),
611
+ " CPU %-2u : %s",
612
+ i + 1,
613
+ formatPercent0(options, cpuUsages[i].ioWaitPct(), 4, 95).c_str());
614
+ stream << buf << endl;
615
+ }
616
+ }
617
+
618
+ tmp = avgStealCpuUsage();
619
+ if (tmp != -2) {
620
+ stream << "Interference from other VMs: ";
621
+ stream << formatPercent0(options, tmp, 4, 20);
622
+ stream << endl;
623
+ for (unsigned i = 0; i < ncpus(); i++) {
624
+ snprintf(buf, sizeof(buf),
625
+ " CPU %-2u : %s",
626
+ i + 1,
627
+ formatPercent0(options, cpuUsages[i].stealPct(), 4, 35).c_str());
628
+ stream << buf << endl;
629
+ }
630
+ }
631
+
632
+ stream << endl;
633
+ }
634
+
635
+ if (options.memory) {
636
+ double ramUsedPct = ramUsed / (double) ramTotal * 100;
637
+ double swapUsedPct = swapUsed / (double) swapTotal * 100;
638
+ outputHeader(stream, options, "Memory");
639
+ stream << "RAM total : " << formatWidth(kbToMb(ramTotal), 6) << " MB" << endl;
640
+ stream << "RAM used : " << formatWidth(kbToMb(ramUsed), 6) << " MB ("
641
+ << formatPercent0(options, ramUsedPct, 1, 90) << ")" << endl;
642
+ stream << "RAM free : " << formatWidth(kbToMb(ramFree()), 6) << " MB" << endl;
643
+ stream << "Swap total : " << formatWidth(kbToMb(swapTotal), 6) << " MB" << endl;
644
+ stream << "Swap used : " << formatWidth(kbToMb(swapUsed), 6) << " MB ("
645
+ << formatPercent0(options, swapUsedPct, 1, 90) << ")" << endl;
646
+ stream << "Swap free : " << formatWidth(kbToMb(swapFree()), 6) << " MB" << endl;
647
+
648
+ if (swapInRate != -2) {
649
+ stream << "Swap in : ";
650
+ if (std::isnan(swapInRate) || swapInRate < 0) {
651
+ if (options.colors) {
652
+ stream << ANSI_COLOR_DGRAY;
653
+ }
654
+ stream << "unknown";
655
+ if (options.colors) {
656
+ stream << ANSI_COLOR_RESET;
657
+ }
658
+ } else {
659
+ char buf[32];
660
+
661
+ snprintf(buf, sizeof(buf), "%.1f", swapInRate / 1024);
662
+ stream << maybeColorAfterThreshold(options, buf, swapInRate / 1024, 2);
663
+ stream << " MB/sec";
664
+ }
665
+ stream << endl;
666
+ }
667
+
668
+ if (swapOutRate != -2) {
669
+ stream << "Swap out : ";
670
+ if (std::isnan(swapOutRate) || swapOutRate < 0) {
671
+ if (options.colors) {
672
+ stream << ANSI_COLOR_DGRAY;
673
+ }
674
+ stream << "unknown";
675
+ if (options.colors) {
676
+ stream << ANSI_COLOR_RESET;
677
+ }
678
+ } else {
679
+ char buf[32];
680
+
681
+ snprintf(buf, sizeof(buf), "%.1f", swapOutRate / 1024);
682
+ stream << maybeColorAfterThreshold(options, buf, swapOutRate / 1024, 2);
683
+ stream << " MB/sec";
684
+ }
685
+ stream << endl;
686
+ }
687
+
688
+ stream << endl;
689
+ }
690
+ }
691
+
692
+ void toXml(ostream &stream, const XmlOptions &options = XmlOptions()) const {
693
+ time_t timestamp = SystemTime::get();
694
+ stream << std::fixed << std::setprecision(2);
695
+ stream << "<system_metrics version=\"1.0\">";
696
+
697
+ if (options.general) {
698
+ stream << "<general>";
699
+ stream << "<current_time>";
700
+ stream << "<localtime>" << strip(std::ctime(&timestamp)) << "</localtime>";
701
+ stream << "<timestamp>" << timestamp << "</timestamp>";
702
+ stream << "</current_time>";
703
+ stream << "<passenger_version>" PASSENGER_VERSION "</passenger_version>";
704
+ stream << "<kernel_version>" << kernelVersion << "</kernel_version>";
705
+ stream << "<boottime>";
706
+ stream << "<localtime>" << strip(std::ctime(&boottime)) << "</localtime>";
707
+ stream << "<timestamp>" << boottime << "</timestamp>";
708
+ stream << "</boottime>";
709
+ stream << "<uptime>";
710
+ stream << "<seconds>" << timestamp - boottime << "</seconds>";
711
+ stream << "<description>" << distanceOfTimeInWords(boottime) << "</description>";
712
+ stream << "</uptime>";
713
+ stream << "<load_averages>";
714
+ stream << "<one>" << loadAverage1 << "</one>";
715
+ stream << "<five>" << loadAverage5 << "</five>";
716
+ stream << "<fifteen>" << loadAverage15 << "</fifteen>";
717
+ stream << "</load_averages>";
718
+ stream << "<fork_rate>" << forkRate << "</fork_rate>";
719
+ stream << "</general>";
720
+ }
721
+
722
+ if (options.cpu) {
723
+ stream << "<cpu_metrics>";
724
+ stream << "<ncpus>" << (int) ncpus() << "</ncpus>";
725
+ if (ncpus() != 0) {
726
+ stream << "<average>";
727
+ stream << "<usage>" << avgCpuUsage() << "</usage>";
728
+ stream << "<user>" << avgUserCpuUsage() << "</user>";
729
+ stream << "<nice>" << avgNiceCpuUsage() << "</nice>";
730
+ stream << "<system>" << avgSystemCpuUsage() << "</system>";
731
+ stream << "<iowait>" << avgIoWaitCpuUsage() << "</iowait>";
732
+ stream << "<idle>" << avgIdleCpuUsage() << "</idle>";
733
+ stream << "<steal>" << avgStealCpuUsage() << "</steal>";
734
+ stream << "</average>";
735
+ }
736
+ stream << "<cpus>";
737
+ for (unsigned i = 0; i < ncpus(); i++) {
738
+ const CpuUsage &cpuUsage = cpuUsages[i];
739
+ stream << "<cpu>";
740
+ stream << "<number>" << i + 1 << "</number>";
741
+ stream << "<usage>" << cpuUsage.usage() << "</usage>";
742
+ stream << "<user>" << cpuUsage.userPct() << "</user>";
743
+ stream << "<nice>" << cpuUsage.nicePct() << "</nice>";
744
+ stream << "<system>" << cpuUsage.systemPct() << "</system>";
745
+ stream << "<io_wait>" << cpuUsage.ioWaitPct() << "</io_wait>";
746
+ stream << "<idle>" << cpuUsage.idlePct() << "</idle>";
747
+ stream << "<steal>" << cpuUsage.stealPct() << "</steal>";
748
+ stream << "</cpu>";
749
+ }
750
+ stream << "</cpus>";
751
+ stream << "</cpu_metrics>";
752
+ }
753
+
754
+ if (options.memory) {
755
+ stream << "<memory_metrics>";
756
+ stream << "<ram_total>" << ramTotal << "</ram_total>";
757
+ stream << "<ram_used>" << ramUsed << "</ram_used>";
758
+ stream << "<ram_free>" << ramFree() << "</ram_free>";
759
+ stream << "<swap_total>" << swapTotal << "</swap_total>";
760
+ stream << "<swap_used>" << swapUsed << "</swap_used>";
761
+ stream << "<swap_free>" << swapFree() << "</swap_free>";
762
+ stream << "<swap_in_rate>" << swapInRate << "</swap_in_rate>";
763
+ stream << "<swap_out_rate>" << swapOutRate << "</swap_out_rate>";
764
+ stream << "</memory_metrics>";
765
+ }
766
+
767
+ stream << "</system_metrics>";
768
+ }
769
+ };
770
+
771
+ /**
772
+ * Utility class for collection system metrics, such as system CPU usage,
773
+ * amount of memory available and free, etc.
774
+ *
775
+ * SystemMetrics metrics;
776
+ * SystemMetricsCollector collector;
777
+ *
778
+ * collector.collect(metrics); // => metrics are now available
779
+ * sleep(1);
780
+ * collector.collect(metrics); // => metrics have been updated
781
+ *
782
+ * Note that to measure the CPU usage, you must collect metrics at least
783
+ * twice, using the same metrics object, within a time interval that's
784
+ * longer than 10 ms. That's because on most systems, the CPU usage is
785
+ * measured by comparing the number of CPU ticks that have passed at the
786
+ * beginning and end of a time interval. The metrics object remembers the
787
+ * number of CPU ticks that was queried last time.
788
+ */
789
+ class SystemMetricsCollector {
790
+ private:
791
+ #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
792
+ int pageSize;
793
+
794
+ void queryLoadAvg(SystemMetrics &metrics) const {
795
+ double avg[3];
796
+ int ret;
797
+
798
+ ret = getloadavg(avg, 3);
799
+ if (ret >= 1) {
800
+ metrics.loadAverage1 = avg[0];
801
+ }
802
+ if (ret >= 2) {
803
+ metrics.loadAverage5 = avg[1];
804
+ }
805
+ if (ret >= 3) {
806
+ metrics.loadAverage15 = avg[2];
807
+ }
808
+ }
809
+
810
+ void failReadingCpuUsages(SystemMetrics &metrics) const {
811
+ vector<SystemMetrics::CpuUsage>::iterator it, end = metrics.cpuUsages.end();
812
+
813
+ for (it = metrics.cpuUsages.begin(); it != end; it++) {
814
+ it->userUsage = -1;
815
+ it->niceUsage = -1;
816
+ it->systemUsage = -1;
817
+ it->idleUsage = -1;
818
+ #if defined(__linux__)
819
+ it->ioWaitUsage = -1;
820
+ it->stealUsage = -1;
821
+ #else
822
+ it->ioWaitUsage = -2;
823
+ it->stealUsage = -2;
824
+ #endif
825
+ }
826
+ }
827
+
828
+ short fracToShort(double x) const {
829
+ return x * SHRT_MAX;
830
+ }
831
+
832
+ void updateCpuMetrics(SystemMetrics::CpuUsage &cpuUsage, long long user, long long nice,
833
+ long long sys, long long iowait, long long idle, long long steal) const
834
+ {
835
+ unsigned long long userDiff, niceDiff, systemDiff, ioWaitDiff,
836
+ idleDiff, stealDiff;
837
+ double totalCalculationTicks, totalTicks;
838
+
839
+ userDiff = (unsigned long long) user - cpuUsage.lastUserTicks;
840
+ niceDiff = (unsigned long long) nice - cpuUsage.lastNiceTicks;
841
+ systemDiff = (unsigned long long) sys - cpuUsage.lastSystemTicks;
842
+ if (iowait >= 0) {
843
+ ioWaitDiff = (unsigned long long) iowait - cpuUsage.lastIoWaitTicks;
844
+ }
845
+ idleDiff = (unsigned long long) idle - cpuUsage.lastIdleTicks;
846
+ if (steal >= 0) {
847
+ stealDiff = (unsigned long long) steal - cpuUsage.lastStealTicks;
848
+ }
849
+
850
+ totalCalculationTicks = userDiff;
851
+ totalCalculationTicks += niceDiff;
852
+ totalCalculationTicks += systemDiff;
853
+ totalCalculationTicks += idleDiff;
854
+ if (totalCalculationTicks == 0) {
855
+ // If the CPU didn't tick, treat it as 100% idle.
856
+ cpuUsage.userUsage = 0;
857
+ cpuUsage.niceUsage = 0;
858
+ cpuUsage.systemUsage = 0;
859
+ cpuUsage.idleUsage = fracToShort(1);
860
+ } else {
861
+ cpuUsage.userUsage = fracToShort(userDiff / totalCalculationTicks);
862
+ cpuUsage.niceUsage = fracToShort(niceDiff / totalCalculationTicks);
863
+ cpuUsage.systemUsage = fracToShort(systemDiff / totalCalculationTicks);
864
+ cpuUsage.idleUsage = fracToShort(idleDiff / totalCalculationTicks);
865
+ }
866
+
867
+ if (iowait >= 0) {
868
+ totalTicks = totalCalculationTicks + ioWaitDiff;
869
+ if (totalTicks == 0) {
870
+ cpuUsage.ioWaitUsage = 0;
871
+ } else {
872
+ cpuUsage.ioWaitUsage = fracToShort(ioWaitDiff / totalTicks);
873
+ }
874
+ } else {
875
+ cpuUsage.ioWaitUsage = iowait; // Assign error code.
876
+ }
877
+ if (steal >= 0) {
878
+ totalTicks = totalCalculationTicks + stealDiff;
879
+ if (totalTicks == 0) {
880
+ cpuUsage.stealUsage = 0;
881
+ } else {
882
+ cpuUsage.stealUsage = fracToShort(stealDiff / totalTicks);
883
+ }
884
+ } else {
885
+ cpuUsage.stealUsage = steal; // Assign error code.
886
+ }
887
+
888
+ cpuUsage.lastUserTicks = user;
889
+ cpuUsage.lastNiceTicks = nice;
890
+ cpuUsage.lastSystemTicks = sys;
891
+ if (iowait >= 0) {
892
+ cpuUsage.lastIoWaitTicks = iowait;
893
+ }
894
+ cpuUsage.lastIdleTicks = idle;
895
+ if (steal >= 0) {
896
+ cpuUsage.lastStealTicks = steal;
897
+ }
898
+ }
899
+ #endif
900
+
901
+ #ifdef __linux__
902
+ void readNextWordAndAssertEqual(const char **data, const StaticString &expected) const {
903
+ if (readNextWord(data) != expected) {
904
+ throw ParseException();
905
+ }
906
+ }
907
+
908
+ void queryMemInfo(SystemMetrics &metrics) const {
909
+ string contents;
910
+ bool hasContents = false;
911
+ try {
912
+ contents = readAll("/proc/meminfo");
913
+ hasContents = true;
914
+ } catch (const SystemException &) {
915
+ }
916
+ if (hasContents) {
917
+ try {
918
+ parseMemInfo(metrics, contents);
919
+ } catch (const ParseException &) {
920
+ throw RuntimeException("Cannot parse information in /proc/meminfo");
921
+ }
922
+ } else {
923
+ metrics.ramTotal = metrics.ramUsed = -1;
924
+ metrics.swapTotal = metrics.swapUsed = -1;
925
+ }
926
+ }
927
+
928
+ void parseMemInfo(SystemMetrics &metrics, const string &data) const {
929
+ const char *start = data.c_str();
930
+ long long memTotal = -1, memFree = -1, buffers = -1, cached = -1;
931
+ long long swapTotal = -1, swapFree = -1;
932
+
933
+ while (start != NULL) {
934
+ StaticString name = readNextWord(&start);
935
+ long long value = readNextWordAsLongLong(&start);
936
+ if (!skipToNextLine(&start) || *start == '\0') {
937
+ start = NULL;
938
+ }
939
+
940
+ if (name == "MemTotal:") {
941
+ memTotal = value;
942
+ } else if (name == "MemFree:") {
943
+ memFree = value;
944
+ } else if (name == "Buffers:") {
945
+ buffers = value;
946
+ } else if (name == "Cached:") {
947
+ cached = value;
948
+ } else if (name == "SwapTotal:") {
949
+ swapTotal = value;
950
+ } else if (name == "SwapFree:") {
951
+ swapFree = value;
952
+ }
953
+ }
954
+
955
+ if (memTotal != -1) {
956
+ metrics.ramTotal = memTotal;
957
+ if (memFree != -1) {
958
+ metrics.ramUsed = memTotal - memFree;
959
+ if (buffers != -1) {
960
+ metrics.ramUsed -= buffers;
961
+ }
962
+ if (cached != -1) {
963
+ metrics.ramUsed -= cached;
964
+ }
965
+ } else {
966
+ metrics.ramUsed = -1;
967
+ }
968
+ } else {
969
+ metrics.ramTotal = metrics.ramUsed = -1;
970
+ }
971
+ if (swapTotal != -1) {
972
+ metrics.swapTotal = swapTotal;
973
+ if (swapFree != -1) {
974
+ metrics.swapUsed = swapTotal - swapFree;
975
+ } else {
976
+ metrics.swapUsed = -1;
977
+ }
978
+ } else {
979
+ metrics.swapTotal = metrics.swapUsed = -1;
980
+ }
981
+ }
982
+
983
+ void queryProcStat(SystemMetrics &metrics) const {
984
+ string contents;
985
+ bool hasContents = false;
986
+ try {
987
+ contents = readAll("/proc/stat");
988
+ hasContents = true;
989
+ } catch (const SystemException &) {
990
+ }
991
+ if (hasContents) {
992
+ try {
993
+ parseProcStat(metrics, contents);
994
+ } catch (const ParseException &) {
995
+ throw RuntimeException("Cannot parse information in /proc/stat");
996
+ }
997
+ } else {
998
+ failReadingCpuUsages(metrics);
999
+ }
1000
+ }
1001
+
1002
+ void parseProcStat(SystemMetrics &metrics, const string &data) const {
1003
+ const char *start = data.c_str();
1004
+ unsigned long long forkCount = 0;
1005
+
1006
+ while (start != NULL) {
1007
+ if (*start == '\n') {
1008
+ // Empty line. Skip to next line.
1009
+ start++;
1010
+ continue;
1011
+ }
1012
+
1013
+ StaticString name = readNextWord(&start);
1014
+
1015
+ if (name.size() > 3 && startsWith(name, "cpu")) {
1016
+ const char *numStart = name.data() + 3;
1017
+ unsigned long num = strtoul(numStart, NULL, 10);
1018
+
1019
+ long long user = readNextWordAsLongLong(&start);
1020
+ long long nice = readNextWordAsLongLong(&start);
1021
+ long long sys = readNextWordAsLongLong(&start);
1022
+ long long idle = readNextWordAsLongLong(&start);
1023
+ long long iowait = readNextWordAsLongLong(&start);
1024
+ readNextWordAsLongLong(&start); // irq
1025
+ readNextWordAsLongLong(&start); // softirq
1026
+ long long steal;
1027
+ try {
1028
+ steal = readNextWordAsLongLong(&start);
1029
+ } catch (const ParseException &) {
1030
+ // Not supported on Linux < 2.6.11
1031
+ steal = -2;
1032
+ }
1033
+
1034
+ if (num + 1 > metrics.cpuUsages.size()) {
1035
+ metrics.cpuUsages.resize(num + 1);
1036
+ }
1037
+ updateCpuMetrics(
1038
+ metrics.cpuUsages[num],
1039
+ user,
1040
+ nice,
1041
+ sys,
1042
+ iowait,
1043
+ idle,
1044
+ steal);
1045
+ } else if (name == "processes") {
1046
+ forkCount = (long long) readNextWordAsLongLong(&start);
1047
+ }
1048
+
1049
+ if (!skipToNextLine(&start) || *start == '\0') {
1050
+ start = NULL;
1051
+ }
1052
+ }
1053
+
1054
+ if (forkCount == 0) {
1055
+ metrics.forkRate = -1;
1056
+ } else {
1057
+ metrics.forkRateSpeedMeter.addSample(forkCount);
1058
+ metrics.forkRate = metrics.forkRateSpeedMeter.currentSpeed();
1059
+ }
1060
+ }
1061
+
1062
+ void queryProcVmstat(SystemMetrics &metrics) const {
1063
+ string contents;
1064
+ bool hasContents = false;
1065
+ try {
1066
+ contents = readAll("/proc/vmstat");
1067
+ hasContents = true;
1068
+ } catch (const SystemException &) {
1069
+ }
1070
+ if (hasContents) {
1071
+ try {
1072
+ parseProcVmstat(metrics, contents);
1073
+ } catch (const ParseException &) {
1074
+ throw RuntimeException("Cannot parse information in /proc/vmstat");
1075
+ }
1076
+ } else {
1077
+ failReadingCpuUsages(metrics);
1078
+ }
1079
+ }
1080
+
1081
+ void parseProcVmstat(SystemMetrics &metrics, const string &data) const {
1082
+ const char *start = data.c_str();
1083
+ long long pswpin = -1, pswpout = -1;
1084
+
1085
+ while (start != NULL) {
1086
+ StaticString name = readNextWord(&start);
1087
+ long long value = readNextWordAsLongLong(&start);
1088
+
1089
+ if (!skipToNextLine(&start) || *start == '\0') {
1090
+ start = NULL;
1091
+ }
1092
+
1093
+ if (name == "pswpin") {
1094
+ pswpin = value;
1095
+ } else if (name == "pswpout") {
1096
+ pswpout = value;
1097
+ }
1098
+ }
1099
+
1100
+ if (pswpin == -1 || pswpout == -1) {
1101
+ metrics.swapInRate = -1;
1102
+ metrics.swapOutRate = -1;
1103
+ } else {
1104
+ metrics.swapInSpeedMeter.addSample(pswpin * pageSize / 1024);
1105
+ metrics.swapOutSpeedMeter.addSample(pswpout * pageSize / 1024);
1106
+ metrics.swapInRate = metrics.swapInSpeedMeter.currentSpeed();
1107
+ metrics.swapOutRate = metrics.swapOutSpeedMeter.currentSpeed();
1108
+ }
1109
+ }
1110
+
1111
+ void queryBoottimeFromSysinfo(SystemMetrics &metrics) const {
1112
+ if (metrics.boottime == -1) {
1113
+ struct sysinfo info;
1114
+
1115
+ if (sysinfo(&info) != -1) {
1116
+ metrics.boottime = (long long) SystemTime::get() - (long long) info.uptime;
1117
+ }
1118
+ }
1119
+ }
1120
+ #endif
1121
+
1122
+ #ifdef __APPLE__
1123
+ mach_port_t hostPort;
1124
+
1125
+ void collectOSX(SystemMetrics &metrics) const {
1126
+ kern_return_t status;
1127
+ mach_msg_type_number_t count;
1128
+ host_basic_info_data_t hostInfo;
1129
+ vm_statistics64_data_t vmStat;
1130
+ struct xsw_usage swap;
1131
+ size_t bufSize = sizeof(swap);
1132
+ int mib[2] = { CTL_VM, VM_SWAPUSAGE };
1133
+ unsigned int cpuCount;
1134
+ processor_cpu_load_info_t cpuLoads;
1135
+
1136
+ // Query total RAM.
1137
+ count = HOST_BASIC_INFO_COUNT;
1138
+ status = host_info(hostPort, HOST_BASIC_INFO, (host_info_t) &hostInfo,
1139
+ &count);
1140
+ if (status == KERN_SUCCESS) {
1141
+ metrics.ramTotal = hostInfo.max_mem / 1024;
1142
+ } else {
1143
+ metrics.ramTotal = -1;
1144
+ }
1145
+
1146
+ // Query system memory usage.
1147
+ // We regard memory usage as the sum of active, wired and compressed memory.
1148
+ // Active + wired is shown as "App memory" in Activity Monitor.
1149
+ count = HOST_VM_INFO64_COUNT;
1150
+ status = host_statistics64(hostPort, HOST_VM_INFO64, (host_info64_t) &vmStat,
1151
+ &count);
1152
+ if (status == KERN_SUCCESS) {
1153
+ metrics.ramUsed = ((ssize_t) vmStat.active_count + vmStat.wire_count +
1154
+ vmStat.compressor_page_count) * pageSize / 1024;
1155
+ } else {
1156
+ metrics.ramUsed = -1;
1157
+ }
1158
+
1159
+ // Query swap.
1160
+ if (sysctl(mib, 2, &swap, &bufSize, NULL, 0) == 0) {
1161
+ metrics.swapTotal = swap.xsu_total / 1024;
1162
+ metrics.swapUsed = swap.xsu_used / 1024;
1163
+ } else {
1164
+ metrics.swapTotal = metrics.swapUsed = -1;
1165
+ }
1166
+
1167
+ // Query CPU usages.
1168
+ status = host_processor_info(hostPort, PROCESSOR_CPU_LOAD_INFO,
1169
+ &cpuCount, (processor_info_array_t *) &cpuLoads, &count);
1170
+ if (status == KERN_SUCCESS) {
1171
+ if ((unsigned int) metrics.cpuUsages.size() != cpuCount) {
1172
+ metrics.cpuUsages.resize(cpuCount);
1173
+ }
1174
+ for (unsigned int i = 0; i < cpuCount; i++) {
1175
+ unsigned int *cpuTicks = cpuLoads[i].cpu_ticks;
1176
+ updateCpuMetrics(
1177
+ metrics.cpuUsages[i],
1178
+ cpuTicks[CPU_STATE_USER],
1179
+ cpuTicks[CPU_STATE_NICE],
1180
+ cpuTicks[CPU_STATE_SYSTEM],
1181
+ -2, /* OS X does not support iowait */
1182
+ cpuTicks[CPU_STATE_IDLE],
1183
+ -2 /* OS X does not support steal */);
1184
+ }
1185
+ } else {
1186
+ failReadingCpuUsages(metrics);
1187
+ }
1188
+ }
1189
+ #endif
1190
+
1191
+ #ifdef __FreeBSD__
1192
+ int kern_smp_maxcpus[3];
1193
+ int kern_cp_times[2];
1194
+ int vm_active_count[4];
1195
+ int vm_wire_count[4];
1196
+
1197
+ template<typename IntegerType>
1198
+ bool querySysctl(int mib1, int mib2, IntegerType &result) const {
1199
+ int mib[2] = { mib1, mib2 };
1200
+ IntegerType val;
1201
+ size_t len = sizeof(IntegerType);
1202
+ if (sysctl(mib, 2, &val, &len, NULL, 0) == 0) {
1203
+ if (len == sizeof(IntegerType)) {
1204
+ result = val;
1205
+ return true;
1206
+ } else {
1207
+ return false;
1208
+ }
1209
+ } else {
1210
+ return false;
1211
+ }
1212
+ }
1213
+
1214
+ template<typename IntegerType>
1215
+ bool querySysctlMib(int mib[], size_t mibsize, IntegerType &result) const {
1216
+ IntegerType val;
1217
+ size_t len = sizeof(IntegerType);
1218
+ if (mib[0] == -1) {
1219
+ return false;
1220
+ } else if (sysctl(mib, mibsize / sizeof(int), &val, &len, NULL, 0) == 0) {
1221
+ if (len == sizeof(IntegerType)) {
1222
+ result = val;
1223
+ return true;
1224
+ } else {
1225
+ return false;
1226
+ }
1227
+ } else {
1228
+ return false;
1229
+ }
1230
+ }
1231
+
1232
+ void collectFreeBSD(SystemMetrics &metrics) const {
1233
+ size_t len;
1234
+ size_t val_size_t;
1235
+ unsigned int val1_uint, val2_uint;
1236
+ int vm_active_count[sizeof(this->vm_active_count) / sizeof(int)];
1237
+ int vm_wire_count[sizeof(this->vm_wire_count) / sizeof(int)];
1238
+
1239
+ // Query active CPU count.
1240
+ if (!queryCpuUsage(metrics)) {
1241
+ failReadingCpuUsages(metrics);
1242
+ }
1243
+
1244
+ // Query memory.
1245
+ memcpy(vm_active_count, this->vm_active_count, sizeof(vm_active_count));
1246
+ memcpy(vm_wire_count, this->vm_wire_count, sizeof(vm_wire_count));
1247
+ if (metrics.ramTotal < 0) {
1248
+ if (querySysctl<size_t>(CTL_HW, HW_PHYSMEM, val_size_t)) {
1249
+ metrics.ramTotal = val_size_t / 1024;
1250
+ } else {
1251
+ metrics.ramTotal = -1;
1252
+ }
1253
+ }
1254
+ if (metrics.ramTotal >= 0
1255
+ && querySysctlMib<unsigned int>(vm_active_count, sizeof(vm_active_count), val1_uint)
1256
+ && querySysctlMib<unsigned int>(vm_wire_count, sizeof(vm_wire_count), val2_uint))
1257
+ {
1258
+ metrics.ramUsed = ((long long) val1_uint + val2_uint) * pageSize / 1024;
1259
+ } else {
1260
+ metrics.ramUsed = -1;
1261
+ }
1262
+
1263
+ // Query swap.
1264
+ int mib[17];
1265
+ size_t mibsize = 16;
1266
+ if (sysctlnametomib("vm.swap_info", mib, &mibsize) == 0) {
1267
+ long long total = 0, used = 0;
1268
+
1269
+ for (int n = 0; ; n++) {
1270
+ struct xswdev xsw;
1271
+
1272
+ mib[mibsize] = n;
1273
+ len = sizeof(xsw);
1274
+ if (sysctl(mib, mibsize + 1, &xsw, &len, NULL, 0) == -1) {
1275
+ break;
1276
+ }
1277
+ if (xsw.xsw_version != XSWDEV_VERSION) {
1278
+ metrics.swapTotal = -1;
1279
+ metrics.swapUsed = -1;
1280
+ break;
1281
+ }
1282
+
1283
+ total += (long long) xsw.xsw_nblks * pageSize;
1284
+ used += (long long) xsw.xsw_used * pageSize;
1285
+ }
1286
+
1287
+ metrics.swapTotal = total / 1024;
1288
+ metrics.swapUsed = used / 1024;
1289
+ } else {
1290
+ metrics.swapTotal = -1;
1291
+ metrics.swapUsed = -1;
1292
+ }
1293
+ }
1294
+
1295
+ bool cpuStatesAreEmpty(const long *states) const {
1296
+ for (int i = 0; i < CPUSTATES; i++) {
1297
+ if (states[i] != 0) {
1298
+ return false;
1299
+ }
1300
+ }
1301
+ return true;
1302
+ }
1303
+
1304
+ bool queryCpuUsage(SystemMetrics &metrics) const {
1305
+ int kern_smp_maxcpus[sizeof(this->kern_smp_maxcpus) / sizeof(int)];
1306
+ int kern_cp_times[sizeof(this->kern_cp_times) / sizeof(int)];
1307
+ long *times = NULL;
1308
+ int maxcpus;
1309
+ size_t size;
1310
+ unsigned int i, j;
1311
+
1312
+ // Preparation.
1313
+ memcpy(kern_smp_maxcpus, this->kern_smp_maxcpus, sizeof(kern_smp_maxcpus));
1314
+ memcpy(kern_cp_times, this->kern_cp_times, sizeof(kern_cp_times));
1315
+ if (kern_smp_maxcpus[0] == -1 || kern_cp_times[0] == -1) {
1316
+ goto error;
1317
+ }
1318
+
1319
+ // Query maximum number of supported CPUs.
1320
+ if (!querySysctlMib<int>(kern_smp_maxcpus, sizeof(kern_smp_maxcpus),
1321
+ maxcpus))
1322
+ {
1323
+ goto error;
1324
+ }
1325
+
1326
+ // Query CPU times.
1327
+ size = sizeof(long) * maxcpus * CPUSTATES;
1328
+ times = (long *) malloc(size);
1329
+ if (times == NULL) {
1330
+ goto error;
1331
+ }
1332
+ if (sysctl(kern_cp_times, sizeof(kern_cp_times) / sizeof(int),
1333
+ times, &size, NULL, 0) == -1)
1334
+ {
1335
+ goto error;
1336
+ }
1337
+
1338
+ i = j = 0;
1339
+ while (i < size / CPUSTATES / sizeof(long)) {
1340
+ if (!cpuStatesAreEmpty(&times[i * CPUSTATES])) {
1341
+ if (metrics.cpuUsages.size() < j + 1) {
1342
+ metrics.cpuUsages.resize(j + 1);
1343
+ }
1344
+ updateCpuMetrics(
1345
+ metrics.cpuUsages[j],
1346
+ times[i * CPUSTATES + CP_USER],
1347
+ times[i * CPUSTATES + CP_NICE],
1348
+ times[i * CPUSTATES + CP_SYS],
1349
+ -2, /* FreeBSD does not support iowait */
1350
+ times[i * CPUSTATES + CP_IDLE],
1351
+ -2 /* FreeBSD does not support steal */);
1352
+ j++;
1353
+ }
1354
+ i++;
1355
+ }
1356
+
1357
+ if (metrics.cpuUsages.size() != j) {
1358
+ metrics.cpuUsages.resize(j);
1359
+ }
1360
+
1361
+ return true;
1362
+
1363
+ error:
1364
+ if (times != NULL) {
1365
+ free(times);
1366
+ }
1367
+ return false;
1368
+ }
1369
+ #endif
1370
+
1371
+ #if defined(__APPLE__) || defined(__FreeBSD__)
1372
+ void queryBoottimeFromSysctl(SystemMetrics &metrics) const {
1373
+ if (metrics.boottime == -1) {
1374
+ struct timeval boottime;
1375
+ size_t len = sizeof(boottime);
1376
+ int mib[2] = { CTL_KERN, KERN_BOOTTIME };
1377
+
1378
+ if (sysctl(mib, 2, &boottime, &len, NULL, 0) == 0) {
1379
+ metrics.boottime = boottime.tv_sec;
1380
+ } else {
1381
+ metrics.boottime = -1;
1382
+ }
1383
+ }
1384
+ }
1385
+ #endif
1386
+
1387
+ void queryOsRelease(SystemMetrics &metrics) const {
1388
+ struct utsname name;
1389
+
1390
+ if (metrics.kernelVersion.empty() && uname(&name) == 0) {
1391
+ metrics.kernelVersion = name.release;
1392
+ }
1393
+ }
1394
+
1395
+ public:
1396
+ SystemMetricsCollector() {
1397
+ #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
1398
+ pageSize = getpagesize();
1399
+ #endif
1400
+ #if defined(__APPLE__)
1401
+ hostPort = mach_host_self();
1402
+ #endif
1403
+ #if defined(__FreeBSD__)
1404
+ size_t len;
1405
+
1406
+ len = sizeof(kern_smp_maxcpus) / sizeof(int);
1407
+ if (sysctlnametomib("kern.smp.maxcpus", kern_smp_maxcpus, &len) == -1) {
1408
+ kern_smp_maxcpus[0] = -1;
1409
+ }
1410
+
1411
+ len = sizeof(kern_cp_times) / sizeof(int);
1412
+ if (sysctlnametomib("kern.cp_times", kern_cp_times, &len) == -1) {
1413
+ kern_cp_times[0] = -1;
1414
+ }
1415
+
1416
+ len = sizeof(vm_active_count) / sizeof(int);
1417
+ if (sysctlnametomib("vm.stats.vm.v_active_count", vm_active_count, &len) == -1) {
1418
+ vm_active_count[0] = -1;
1419
+ }
1420
+
1421
+ len = sizeof(vm_wire_count) / sizeof(int);
1422
+ if (sysctlnametomib("vm.stats.vm.v_wire_count", vm_wire_count, &len) == -1) {
1423
+ vm_wire_count[0] = -1;
1424
+ }
1425
+ #endif
1426
+ }
1427
+
1428
+ /**
1429
+ * If some information cannot be queried, then this method does not
1430
+ * throw an exception. Instead, that particular metric in the metrics
1431
+ * object is just not updated. However if something really unexpected
1432
+ * goes wrong (such as when a command did not return the output it's
1433
+ * supposed to return, so that we're unable to parse the output) then
1434
+ * a RuntimeException is thrown.
1435
+ *
1436
+ * @throws RuntimeException
1437
+ */
1438
+ void collect(SystemMetrics &metrics) const {
1439
+ #if defined(__linux__)
1440
+ queryMemInfo(metrics);
1441
+ queryProcStat(metrics);
1442
+ queryProcVmstat(metrics);
1443
+ queryBoottimeFromSysinfo(metrics);
1444
+ queryLoadAvg(metrics);
1445
+ #elif defined(__APPLE__)
1446
+ collectOSX(metrics);
1447
+ queryBoottimeFromSysctl(metrics);
1448
+ queryLoadAvg(metrics);
1449
+ #elif defined(__FreeBSD__)
1450
+ collectFreeBSD(metrics);
1451
+ queryBoottimeFromSysctl(metrics);
1452
+ queryLoadAvg(metrics);
1453
+ #endif
1454
+ queryOsRelease(metrics);
1455
+ }
1456
+ };
1457
+
1458
+ } // namespace Passenger
1459
+
1460
+ #endif /* _PASSENGER_SYSTEM_METRICS_COLLECTOR_H_ */