]>
Commit | Line | Data |
---|---|---|
7fa270a1 FF |
1 | /** |
2 | * collectd - src/memory.c | |
71b4a17a | 3 | * Copyright (C) 2005-2020 Florian octo Forster |
eeb8f7f4 | 4 | * Copyright (C) 2009 Simon Kuhnle |
f1b812b3 | 5 | * Copyright (C) 2009 Manuel Sanmartin |
7fa270a1 FF |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
293819d0 | 9 | * Free Software Foundation; only version 2 of the License is applicable. |
7fa270a1 FF |
10 | * |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; if not, write to the Free Software Foundation, Inc., | |
18 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | * | |
20 | * Authors: | |
633c3966 | 21 | * Florian octo Forster <octo at collectd.org> |
eeb8f7f4 | 22 | * Simon Kuhnle <simon at blarzwurst.de> |
f1b812b3 | 23 | * Manuel Sanmartin |
7fa270a1 FF |
24 | **/ |
25 | ||
73681054 | 26 | #include "collectd.h" |
a5377cf9 | 27 | |
73681054 | 28 | #include "plugin.h" |
6378ec28 | 29 | #include "utils/common/common.h" |
7fa270a1 | 30 | |
f22b9ecc ZS |
31 | #if (defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME)) || \ |
32 | defined(__OpenBSD__) | |
a937f260 | 33 | /* Implies BSD variant */ |
936c450a | 34 | #include <sys/sysctl.h> |
b8248eb1 | 35 | #endif |
8fce2979 | 36 | #ifdef HAVE_SYS_VMMETER_H |
936c450a | 37 | #include <sys/vmmeter.h> |
8fce2979 | 38 | #endif |
7fa270a1 | 39 | |
4d25c0e0 | 40 | #ifdef HAVE_MACH_KERN_RETURN_H |
936c450a | 41 | #include <mach/kern_return.h> |
4d25c0e0 FF |
42 | #endif |
43 | #ifdef HAVE_MACH_MACH_INIT_H | |
936c450a | 44 | #include <mach/mach_init.h> |
4d25c0e0 | 45 | #endif |
984e46be | 46 | #ifdef HAVE_MACH_MACH_HOST_H |
936c450a | 47 | #include <mach/mach_host.h> |
984e46be | 48 | #endif |
4d25c0e0 | 49 | #ifdef HAVE_MACH_HOST_PRIV_H |
936c450a | 50 | #include <mach/host_priv.h> |
4d25c0e0 | 51 | #endif |
f5adb596 | 52 | #ifdef HAVE_MACH_VM_STATISTICS_H |
936c450a | 53 | #include <mach/vm_statistics.h> |
984e46be | 54 | #endif |
4d25c0e0 | 55 | |
f05e9721 | 56 | #if HAVE_STATGRAB_H |
936c450a | 57 | #include <statgrab.h> |
f05e9721 FF |
58 | #endif |
59 | ||
f1b812b3 | 60 | #if HAVE_PERFSTAT |
936c450a FF |
61 | #include <libperfstat.h> |
62 | #include <sys/protosw.h> | |
f1b812b3 MS |
63 | #endif /* HAVE_PERFSTAT */ |
64 | ||
2a056a37 FF |
65 | static char const *const label_state = "system.memory.state"; |
66 | ||
71b4a17a | 67 | typedef enum { |
8ec9a545 FF |
68 | STATE_USED, |
69 | STATE_FREE, | |
70 | STATE_BUFFERS, | |
71 | STATE_CACHED, | |
8ec9a545 FF |
72 | STATE_WIRED, |
73 | STATE_ACTIVE, | |
74 | STATE_INACTIVE, | |
75 | STATE_KERNEL, | |
76 | STATE_LOCKED, | |
77 | STATE_ARC, | |
78 | STATE_UNUSED, | |
8ec9a545 FF |
79 | STATE_USER_WIRE, |
80 | STATE_LAUNDRY, | |
81 | STATE_MAX, /* #states */ | |
71b4a17a FF |
82 | } memory_type_t; |
83 | ||
8ec9a545 | 84 | static char const *memory_type_names[STATE_MAX] = { |
c575d3e8 FF |
85 | "used", "free", "buffers", "cached", "wired", |
86 | "active", "inactive", "kernel", "locked", "arc", | |
87 | "unusable", "user_wire", "laundry", | |
71b4a17a FF |
88 | }; |
89 | ||
4d25c0e0 | 90 | /* vm_statistics_data_t */ |
e2df2097 | 91 | #if HAVE_HOST_STATISTICS |
4d25c0e0 FF |
92 | static mach_port_t port_host; |
93 | static vm_size_t pagesize; | |
e2df2097 | 94 | /* #endif HAVE_HOST_STATISTICS */ |
4d25c0e0 | 95 | |
74da5795 | 96 | #elif HAVE_SYSCTLBYNAME |
5eebeafe EF |
97 | #if HAVE_SYSCTL && defined(KERNEL_NETBSD) |
98 | static int pagesize; | |
99 | #include <unistd.h> /* getpagesize() */ | |
100 | #else | |
b8248eb1 | 101 | /* no global variables */ |
5eebeafe | 102 | #endif |
74da5795 | 103 | /* #endif HAVE_SYSCTLBYNAME */ |
b8248eb1 FF |
104 | |
105 | #elif KERNEL_LINUX | |
4d25c0e0 FF |
106 | /* no global variables */ |
107 | /* #endif KERNEL_LINUX */ | |
108 | ||
b8248eb1 | 109 | #elif HAVE_LIBKSTAT |
7fa270a1 FF |
110 | static int pagesize; |
111 | static kstat_t *ksp; | |
d13ba64e | 112 | static kstat_t *ksz; |
e4b274ed FF |
113 | /* #endif HAVE_LIBKSTAT */ |
114 | ||
4a2ec3e0 | 115 | #elif HAVE_SYSCTL && __OpenBSD__ |
f22b9ecc | 116 | /* OpenBSD variant does not have sysctlbyname */ |
38f27d54 | 117 | static int pagesize; |
4a2ec3e0 | 118 | /* #endif HAVE_SYSCTL && __OpenBSD__ */ |
38f27d54 | 119 | |
f05e9721 FF |
120 | #elif HAVE_LIBSTATGRAB |
121 | /* no global variables */ | |
122 | /* endif HAVE_LIBSTATGRAB */ | |
f1b812b3 MS |
123 | #elif HAVE_PERFSTAT |
124 | static int pagesize; | |
f1b812b3 | 125 | /* endif HAVE_PERFSTAT */ |
e4b274ed | 126 | #else |
936c450a | 127 | #error "No applicable input method." |
e4b274ed | 128 | #endif |
7fa270a1 | 129 | |
c920cda8 | 130 | #if KERNEL_NETBSD |
30318d13 | 131 | #include <uvm/uvm_extern.h> |
c920cda8 EF |
132 | #endif |
133 | ||
67285dfc | 134 | static bool report_usage = true; |
ede88c1b | 135 | static bool report_utilization = true; |
9f1a78d5 | 136 | static bool report_limit; |
1aa8032d | 137 | |
936c450a | 138 | static int memory_config(oconfig_item_t *ci) /* {{{ */ |
1aa8032d | 139 | { |
936c450a FF |
140 | for (int i = 0; i < ci->children_num; i++) { |
141 | oconfig_item_t *child = ci->children + i; | |
67285dfc FF |
142 | if (strcasecmp("ReportUsage", child->key) == 0 || |
143 | strcasecmp("ValuesAbsolute", child->key) == 0) | |
144 | cf_util_get_boolean(child, &report_usage); | |
5877b8d0 FF |
145 | else if (strcasecmp("ReportUtilization", child->key) == 0 || |
146 | strcasecmp("ValuesPercentage", child->key) == 0) | |
147 | cf_util_get_boolean(child, &report_utilization); | |
9f1a78d5 FF |
148 | else if (strcasecmp("ReportLimit", child->key) == 0) |
149 | cf_util_get_boolean(child, &report_limit); | |
936c450a | 150 | else |
71b4a17a | 151 | ERROR("memory plugin: Invalid configuration option: \"%s\".", child->key); |
936c450a FF |
152 | } |
153 | ||
307c875e | 154 | return 0; |
1aa8032d JK |
155 | } /* }}} int memory_config */ |
156 | ||
8ec9a545 | 157 | static int memory_dispatch(gauge_t values[STATE_MAX]) { |
67285dfc | 158 | metric_family_t fam_usage = { |
eb249dc2 | 159 | .name = "system.memory.usage", |
4616f834 FF |
160 | .help = "Reports memory in use by state", |
161 | .unit = "By", | |
71b4a17a FF |
162 | .type = METRIC_TYPE_GAUGE, |
163 | }; | |
164 | gauge_t total = 0; | |
165 | ||
8ec9a545 | 166 | for (size_t i = 0; i < STATE_MAX; i++) { |
71b4a17a FF |
167 | if (isnan(values[i])) { |
168 | continue; | |
169 | } | |
170 | ||
171 | total += values[i]; | |
172 | ||
67285dfc FF |
173 | if (report_usage) { |
174 | metric_family_append(&fam_usage, label_state, memory_type_names[i], | |
2a056a37 | 175 | (value_t){.gauge = values[i]}, NULL); |
71b4a17a FF |
176 | } |
177 | } | |
178 | ||
179 | int ret = 0; | |
67285dfc FF |
180 | if (report_usage) { |
181 | int status = plugin_dispatch_metric_family(&fam_usage); | |
71b4a17a FF |
182 | if (status != 0) { |
183 | ERROR("memory plugin: plugin_dispatch_metric_family failed: %s", | |
184 | STRERROR(status)); | |
185 | } | |
186 | ret = status; | |
187 | } | |
67285dfc | 188 | metric_family_metric_reset(&fam_usage); |
71b4a17a | 189 | |
71b4a17a FF |
190 | if (total == 0) { |
191 | return EINVAL; | |
192 | } | |
193 | ||
9f1a78d5 FF |
194 | if (report_limit) { |
195 | metric_family_t fam_limit = { | |
196 | .name = "system.memory.limit", | |
197 | .help = "Total memory available in the system.", | |
198 | .unit = "By", | |
199 | .type = METRIC_TYPE_COUNTER, // [sic] should be UpDownCounter | |
200 | }; | |
201 | metric_t m = { | |
202 | .value = (value_t){.derive = (derive_t)total}, | |
203 | }; | |
204 | metric_family_metric_append(&fam_limit, m); | |
205 | ||
206 | int status = plugin_dispatch_metric_family(&fam_limit); | |
207 | if (status != 0) { | |
208 | ERROR("memory plugin: plugin_dispatch_metric_family failed: %s", | |
209 | STRERROR(status)); | |
210 | } | |
211 | ret = ret ? ret : status; | |
212 | } | |
213 | ||
214 | if (!report_utilization) { | |
215 | return ret; | |
216 | } | |
217 | ||
81425990 | 218 | metric_family_t fam_util = { |
eb249dc2 | 219 | .name = "system.memory.utilization", |
4616f834 FF |
220 | .help = "Reports memory in use by state", |
221 | .unit = "1", | |
71b4a17a FF |
222 | .type = METRIC_TYPE_GAUGE, |
223 | }; | |
8ec9a545 | 224 | for (size_t i = 0; i < STATE_MAX; i++) { |
71b4a17a FF |
225 | if (isnan(values[i])) { |
226 | continue; | |
227 | } | |
228 | ||
81425990 FF |
229 | metric_family_append(&fam_util, label_state, memory_type_names[i], |
230 | (value_t){.gauge = values[i] / total}, NULL); | |
71b4a17a FF |
231 | } |
232 | ||
81425990 | 233 | int status = plugin_dispatch_metric_family(&fam_util); |
71b4a17a FF |
234 | if (status != 0) { |
235 | ERROR("memory plugin: plugin_dispatch_metric_family failed: %s", | |
236 | STRERROR(status)); | |
237 | ret = ret ? ret : status; | |
238 | } | |
81425990 | 239 | metric_family_metric_reset(&fam_util); |
71b4a17a FF |
240 | |
241 | return ret; | |
242 | } | |
243 | ||
936c450a | 244 | static int memory_init(void) { |
e2df2097 | 245 | #if HAVE_HOST_STATISTICS |
936c450a FF |
246 | port_host = mach_host_self(); |
247 | host_page_size(port_host, &pagesize); | |
ec9234cd | 248 | /* #endif HAVE_HOST_STATISTICS */ |
4d25c0e0 | 249 | |
74da5795 | 250 | #elif HAVE_SYSCTLBYNAME |
5eebeafe EF |
251 | #if HAVE_SYSCTL && defined(KERNEL_NETBSD) |
252 | pagesize = getpagesize(); | |
253 | #else | |
254 | /* no init stuff */ | |
255 | #endif /* HAVE_SYSCTL && defined(KERNEL_NETBSD) */ | |
60c914f1 | 256 | /* #endif HAVE_SYSCTLBYNAME */ |
b8248eb1 | 257 | |
4d25c0e0 | 258 | #elif defined(KERNEL_LINUX) |
60c914f1 FF |
259 | /* no init stuff */ |
260 | /* #endif KERNEL_LINUX */ | |
4d25c0e0 FF |
261 | |
262 | #elif defined(HAVE_LIBKSTAT) | |
936c450a FF |
263 | /* getpagesize(3C) tells me this does not fail.. */ |
264 | pagesize = getpagesize(); | |
265 | if (get_kstat(&ksp, "unix", 0, "system_pages") != 0) { | |
266 | ksp = NULL; | |
307c875e | 267 | return -1; |
936c450a FF |
268 | } |
269 | if (get_kstat(&ksz, "zfs", 0, "arcstats") != 0) { | |
270 | ksz = NULL; | |
307c875e | 271 | return -1; |
936c450a | 272 | } |
d13ba64e | 273 | |
60c914f1 | 274 | /* #endif HAVE_LIBKSTAT */ |
38f27d54 | 275 | |
4a2ec3e0 | 276 | #elif HAVE_SYSCTL && __OpenBSD__ |
f22b9ecc | 277 | /* OpenBSD variant does not have sysctlbyname */ |
936c450a FF |
278 | pagesize = getpagesize(); |
279 | if (pagesize <= 0) { | |
280 | ERROR("memory plugin: Invalid pagesize: %i", pagesize); | |
307c875e | 281 | return -1; |
936c450a | 282 | } |
60c914f1 | 283 | /* #endif HAVE_SYSCTL && __OpenBSD__ */ |
38f27d54 FF |
284 | |
285 | #elif HAVE_LIBSTATGRAB | |
60c914f1 FF |
286 | /* no init stuff */ |
287 | /* #endif HAVE_LIBSTATGRAB */ | |
7fa270a1 | 288 | |
f1b812b3 | 289 | #elif HAVE_PERFSTAT |
936c450a | 290 | pagesize = getpagesize(); |
f1b812b3 | 291 | #endif /* HAVE_PERFSTAT */ |
307c875e | 292 | return 0; |
293819d0 | 293 | } /* int memory_init */ |
7fa270a1 | 294 | |
8ec9a545 | 295 | static int memory_read_internal(gauge_t values[STATE_MAX]) { |
e2df2097 | 296 | #if HAVE_HOST_STATISTICS |
71b4a17a FF |
297 | if (!port_host || !pagesize) { |
298 | return EINVAL; | |
299 | } | |
936c450a | 300 | |
71b4a17a | 301 | vm_statistics_data_t vm_data = {0}; |
60c914f1 FF |
302 | mach_msg_type_number_t vm_data_len = sizeof(vm_data) / sizeof(natural_t); |
303 | kern_return_t status = host_statistics(port_host, HOST_VM_INFO, | |
304 | (host_info_t)&vm_data, &vm_data_len); | |
305 | if (status != KERN_SUCCESS) { | |
306 | ERROR("memory-plugin: host_statistics failed and returned the value %d", | |
936c450a | 307 | (int)status); |
71b4a17a | 308 | return (int)status; |
936c450a FF |
309 | } |
310 | ||
311 | /* | |
312 | * From <http://docs.info.apple.com/article.html?artnum=107918>: | |
313 | * | |
314 | * Wired memory | |
315 | * This information can't be cached to disk, so it must stay in RAM. | |
316 | * The amount depends on what applications you are using. | |
317 | * | |
318 | * Active memory | |
319 | * This information is currently in RAM and actively being used. | |
320 | * | |
321 | * Inactive memory | |
322 | * This information is no longer being used and has been cached to | |
323 | * disk, but it will remain in RAM until another application needs | |
324 | * the space. Leaving this information in RAM is to your advantage if | |
325 | * you (or a client of your computer) come back to it later. | |
326 | * | |
327 | * Free memory | |
328 | * This memory is not being used. | |
329 | */ | |
330 | ||
8ec9a545 FF |
331 | values[STATE_WIRED] = (gauge_t)(vm_data.wire_count * pagesize); |
332 | values[STATE_ACTIVE] = (gauge_t)(vm_data.active_count * pagesize); | |
333 | values[STATE_INACTIVE] = (gauge_t)(vm_data.inactive_count * pagesize); | |
334 | values[STATE_FREE] = (gauge_t)(vm_data.free_count * pagesize); | |
ec9234cd | 335 | /* #endif HAVE_HOST_STATISTICS */ |
4d25c0e0 | 336 | |
74da5795 | 337 | #elif HAVE_SYSCTLBYNAME |
c920cda8 EF |
338 | |
339 | #if HAVE_SYSCTL && defined(KERNEL_NETBSD) | |
71b4a17a FF |
340 | if (pagesize == 0) { |
341 | return EINVAL; | |
342 | } | |
343 | ||
c920cda8 | 344 | int mib[] = {CTL_VM, VM_UVMEXP2}; |
71b4a17a FF |
345 | struct uvmexp_sysctl uvmexp = {0}; |
346 | size_t size = sizeof(uvmexp); | |
347 | if (sysctl(mib, STATIC_ARRAY_SIZE(mib), &uvmexp, &size, NULL, 0) < 0) { | |
348 | WARNING("memory plugin: sysctl failed: %s", STRERRNO); | |
349 | return errno; | |
c920cda8 EF |
350 | } |
351 | ||
8ec9a545 FF |
352 | values[STATE_WIRED] = (gauge_t)(uvmexp.wired * pagesize); |
353 | values[STATE_ACTIVE] = (gauge_t)(uvmexp.active * pagesize); | |
354 | values[STATE_INACTIVE] = (gauge_t)(uvmexp.inactive * pagesize); | |
355 | values[STATE_FREE] = (gauge_t)(uvmexp.free * pagesize); | |
71b4a17a FF |
356 | |
357 | int64_t accounted = | |
358 | uvmexp.wired + uvmexp.active + uvmexp.inactive + uvmexp.free; | |
359 | if (uvmexp.npages > accounted) { | |
8ec9a545 | 360 | values[STATE_KERNEL] = (gauge_t)((uvmexp.npages - accounted) * pagesize); |
71b4a17a | 361 | } |
30318d13 | 362 | /* #endif HAVE_SYSCTL && defined(KERNEL_NETBSD) */ |
c920cda8 | 363 | |
71b4a17a | 364 | #else /* Other HAVE_SYSCTLBYNAME providers */ |
936c450a FF |
365 | /* |
366 | * vm.stats.vm.v_page_size: 4096 | |
367 | * vm.stats.vm.v_page_count: 246178 | |
368 | * vm.stats.vm.v_free_count: 28760 | |
369 | * vm.stats.vm.v_wire_count: 37526 | |
370 | * vm.stats.vm.v_active_count: 55239 | |
371 | * vm.stats.vm.v_inactive_count: 113730 | |
372 | * vm.stats.vm.v_cache_count: 10809 | |
8dc63496 FC |
373 | * vm.stats.vm.v_user_wire_count: 0 |
374 | * vm.stats.vm.v_laundry_count: 40394 | |
936c450a | 375 | */ |
60c914f1 FF |
376 | struct { |
377 | char const *sysctl_key; | |
71b4a17a | 378 | memory_type_t type; |
60c914f1 | 379 | } metrics[] = { |
71b4a17a | 380 | {"vm.stats.vm.v_page_size"}, |
8ec9a545 FF |
381 | {"vm.stats.vm.v_free_count", STATE_FREE}, |
382 | {"vm.stats.vm.v_wire_count", STATE_WIRED}, | |
383 | {"vm.stats.vm.v_active_count", STATE_ACTIVE}, | |
384 | {"vm.stats.vm.v_inactive_count", STATE_INACTIVE}, | |
385 | {"vm.stats.vm.v_cache_count", STATE_CACHED}, | |
386 | {"vm.stats.vm.v_user_wire_count", STATE_USER_WIRE}, | |
387 | {"vm.stats.vm.v_laundry_count", STATE_LAUNDRY}, | |
60c914f1 FF |
388 | }; |
389 | ||
71b4a17a | 390 | gauge_t pagesize = 0; |
60c914f1 | 391 | for (size_t i = 0; i < STATIC_ARRAY_SIZE(metrics); i++) { |
71b4a17a | 392 | long value = 0; |
936c450a FF |
393 | size_t value_len = sizeof(value); |
394 | ||
60c914f1 FF |
395 | int status = sysctlbyname(metrics[i].sysctl_key, (void *)&value, &value_len, |
396 | NULL, 0); | |
397 | if (status != 0) { | |
398 | WARNING("sysctlbyname(\"%s\") failed: %s", metrics[i].sysctl_key, | |
399 | STRERROR(status)); | |
400 | continue; | |
936c450a | 401 | } |
936c450a | 402 | |
60c914f1 | 403 | if (i == 0) { |
71b4a17a | 404 | pagesize = (gauge_t)value; |
60c914f1 FF |
405 | continue; |
406 | } | |
936c450a | 407 | |
71b4a17a | 408 | values[metrics[i].type] = ((gauge_t)value) * pagesize; |
60c914f1 | 409 | } /* for (sysctl_keys) */ |
c920cda8 | 410 | #endif /* HAVE_SYSCTL && KERNEL_NETBSD */ |
ec9234cd | 411 | /* #endif HAVE_SYSCTLBYNAME */ |
b8248eb1 | 412 | |
f05e9721 | 413 | #elif KERNEL_LINUX |
60c914f1 FF |
414 | FILE *fh = fopen("/proc/meminfo", "r"); |
415 | if (fh == NULL) { | |
416 | int status = errno; | |
417 | ERROR("memory plugin: fopen(\"/proc/meminfo\") failed: %s", STRERRNO); | |
418 | return status; | |
419 | } | |
936c450a | 420 | |
71b4a17a | 421 | gauge_t mem_total = 0; |
60c914f1 | 422 | gauge_t mem_not_used = 0; |
936c450a | 423 | |
60c914f1 | 424 | char buffer[256]; |
936c450a | 425 | while (fgets(buffer, sizeof(buffer), fh) != NULL) { |
71b4a17a | 426 | char *fields[4] = {NULL}; |
60c914f1 FF |
427 | int fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields)); |
428 | if ((fields_num != 3) || (strcmp("kB", fields[2]) != 0)) { | |
429 | continue; | |
430 | } | |
431 | ||
71b4a17a FF |
432 | gauge_t v = 1024.0 * atof(fields[1]); |
433 | if (!isfinite(v)) { | |
434 | continue; | |
435 | } | |
60c914f1 FF |
436 | |
437 | if (strcmp(fields[0], "MemTotal:") == 0) { | |
71b4a17a | 438 | mem_total = v; |
60c914f1 | 439 | } else if (strcmp(fields[0], "MemFree:") == 0) { |
8ec9a545 | 440 | values[STATE_FREE] = v; |
71b4a17a | 441 | mem_not_used += v; |
60c914f1 | 442 | } else if (strcmp(fields[0], "Buffers:") == 0) { |
8ec9a545 | 443 | values[STATE_BUFFERS] = v; |
71b4a17a | 444 | mem_not_used += v; |
60c914f1 | 445 | } else if (strcmp(fields[0], "Cached:") == 0) { |
8ec9a545 | 446 | values[STATE_CACHED] = v; |
71b4a17a | 447 | mem_not_used += v; |
60c914f1 | 448 | } |
936c450a FF |
449 | } |
450 | ||
451 | if (fclose(fh)) { | |
60c914f1 | 452 | WARNING("memory plugin: fclose failed: %s", STRERRNO); |
936c450a FF |
453 | } |
454 | ||
71b4a17a | 455 | if (isnan(mem_total) || (mem_total == 0) || (mem_total < mem_not_used)) { |
60c914f1 FF |
456 | return EINVAL; |
457 | } | |
936c450a | 458 | |
71b4a17a FF |
459 | /* "used" is not explicitly reported. It is calculated as everything that is |
460 | * not "not used", e.g. cached, buffers, ... */ | |
8ec9a545 | 461 | values[STATE_USED] = mem_total - mem_not_used; |
60c914f1 | 462 | /* #endif KERNEL_LINUX */ |
7fa270a1 | 463 | |
f05e9721 | 464 | #elif HAVE_LIBKSTAT |
936c450a FF |
465 | /* Most of the additions here were taken as-is from the k9toolkit from |
466 | * Brendan Gregg and are subject to change I guess */ | |
71b4a17a FF |
467 | if ((ksp == NULL) || (ksz == NULL)) { |
468 | return EINVAL; | |
469 | } | |
936c450a | 470 | |
71b4a17a FF |
471 | long long mem_used = get_kstat_value(ksp, "pagestotal"); |
472 | long long mem_free = get_kstat_value(ksp, "pagesfree"); | |
473 | long long mem_lock = get_kstat_value(ksp, "pageslocked"); | |
474 | long long mem_kern = 0; | |
475 | long long mem_unus = 0; | |
476 | long long arcsize = get_kstat_value(ksz, "size"); | |
936c450a | 477 | |
71b4a17a FF |
478 | long long pp_kernel = get_kstat_value(ksp, "pp_kernel"); |
479 | long long physmem = get_kstat_value(ksp, "physmem"); | |
480 | long long availrmem = get_kstat_value(ksp, "availrmem"); | |
936c450a FF |
481 | |
482 | if ((mem_used < 0LL) || (mem_free < 0LL) || (mem_lock < 0LL)) { | |
71b4a17a | 483 | ERROR("memory plugin: one of used, free or locked is negative."); |
307c875e | 484 | return -1; |
936c450a FF |
485 | } |
486 | ||
487 | mem_unus = physmem - mem_used; | |
488 | ||
489 | if (mem_used < (mem_free + mem_lock)) { | |
490 | /* source: http://wesunsolve.net/bugid/id/4909199 | |
491 | * this seems to happen when swap space is small, e.g. 2G on a 32G system | |
492 | * we will make some assumptions here | |
493 | * educated solaris internals help welcome here */ | |
494 | DEBUG("memory plugin: pages total is smaller than \"free\" " | |
495 | "+ \"locked\". This is probably due to small " | |
496 | "swap space"); | |
497 | mem_free = availrmem; | |
498 | mem_used = 0; | |
499 | } else { | |
500 | mem_used -= mem_free + mem_lock; | |
501 | } | |
502 | ||
503 | /* mem_kern is accounted for in mem_lock */ | |
504 | if (pp_kernel < mem_lock) { | |
505 | mem_kern = pp_kernel; | |
506 | mem_lock -= pp_kernel; | |
507 | } else { | |
508 | mem_kern = mem_lock; | |
509 | mem_lock = 0; | |
510 | } | |
511 | ||
8ec9a545 FF |
512 | values[STATE_USED] = (gauge_t)(mem_used * pagesize); |
513 | values[STATE_FREE] = (gauge_t)(mem_free * pagesize); | |
514 | values[STATE_LOCKED] = (gauge_t)(mem_lock * pagesize); | |
515 | values[STATE_KERNEL] = (gauge_t)((mem_kern * pagesize) - arcsize); | |
516 | values[STATE_UNUSED] = (gauge_t)(mem_unus * pagesize); | |
517 | values[STATE_ARC] = (gauge_t)arcsize; | |
ec9234cd | 518 | /* #endif HAVE_LIBKSTAT */ |
7fa270a1 | 519 | |
4a2ec3e0 | 520 | #elif HAVE_SYSCTL && __OpenBSD__ |
f22b9ecc | 521 | /* OpenBSD variant does not have HAVE_SYSCTLBYNAME */ |
71b4a17a FF |
522 | if (pagesize == 0) { |
523 | return EINVAL; | |
524 | } | |
525 | ||
936c450a FF |
526 | int mib[] = {CTL_VM, VM_METER}; |
527 | struct vmtotal vmtotal = {0}; | |
71b4a17a FF |
528 | size_t size = sizeof(vmtotal); |
529 | if (sysctl(mib, STATIC_ARRAY_SIZE(mib), &vmtotal, &size, NULL, 0) < 0) { | |
530 | ERROR("memory plugin: sysctl failed: %s", STRERRNO); | |
531 | return errno; | |
936c450a FF |
532 | } |
533 | ||
8ec9a545 FF |
534 | values[STATE_ACTIVE] = (gauge_t)(vmtotal.t_arm * pagesize); |
535 | values[STATE_INACTIVE] = (gauge_t)((vmtotal.t_rm - vmtotal.t_arm) * pagesize); | |
536 | values[STATE_FREE] = (gauge_t)(vmtotal.t_free * pagesize); | |
4a2ec3e0 | 537 | /* #endif HAVE_SYSCTL && __OpenBSD__ */ |
38f27d54 | 538 | |
f05e9721 | 539 | #elif HAVE_LIBSTATGRAB |
71b4a17a FF |
540 | sg_mem_stats *ios = sg_get_mem_stats(); |
541 | if (ios == NULL) { | |
307c875e | 542 | return -1; |
71b4a17a | 543 | } |
c791ba09 | 544 | |
8ec9a545 FF |
545 | values[STATE_USED] = (gauge_t)ios->used; |
546 | values[STATE_CACHED] = (gauge_t)ios->cache; | |
547 | values[STATE_FREE] = (gauge_t)ios->free; | |
ec9234cd | 548 | /* #endif HAVE_LIBSTATGRAB */ |
f1b812b3 MS |
549 | |
550 | #elif HAVE_PERFSTAT | |
936c450a | 551 | perfstat_memory_total_t pmemory = {0}; |
936c450a | 552 | if (perfstat_memory_total(NULL, &pmemory, sizeof(pmemory), 1) < 0) { |
71b4a17a FF |
553 | ERROR("memory plugin: perfstat_memory_total failed: %s", STRERRNO); |
554 | return errno; | |
936c450a FF |
555 | } |
556 | ||
557 | /* Unfortunately, the AIX documentation is not very clear on how these | |
71b4a17a | 558 | * numbers relate to one another. The only thing is states explicitly |
936c450a FF |
559 | * is: |
560 | * real_total = real_process + real_free + numperm + real_system | |
561 | * | |
562 | * Another segmentation, which would be closer to the numbers reported | |
563 | * by the "svmon" utility, would be: | |
564 | * real_total = real_free + real_inuse | |
565 | * real_inuse = "active" + real_pinned + numperm | |
566 | */ | |
8ec9a545 FF |
567 | values[STATE_FREE] = (gauge_t)(pmemory.real_free * pagesize); |
568 | values[STATE_CACHED] = (gauge_t)(pmemory.numperm * pagesize); | |
569 | values[STATE_KERNEL] = (gauge_t)(pmemory.real_system * pagesize); | |
570 | values[STATE_USED] = (gauge_t)(pmemory.real_process * pagesize); | |
f1b812b3 | 571 | #endif /* HAVE_PERFSTAT */ |
293819d0 | 572 | |
307c875e | 573 | return 0; |
577e34aa FF |
574 | } /* }}} int memory_read_internal */ |
575 | ||
936c450a | 576 | static int memory_read(void) /* {{{ */ |
577e34aa | 577 | { |
8ec9a545 FF |
578 | gauge_t values[STATE_MAX] = {0}; |
579 | for (size_t i = 0; i < STATE_MAX; i++) { | |
71b4a17a FF |
580 | values[i] = NAN; |
581 | } | |
577e34aa | 582 | |
71b4a17a FF |
583 | int status = memory_read_internal(values); |
584 | if (status != 0) { | |
585 | return status; | |
586 | } | |
577e34aa | 587 | |
71b4a17a | 588 | return memory_dispatch(values); |
577e34aa | 589 | } /* }}} int memory_read */ |
7fa270a1 | 590 | |
936c450a FF |
591 | void module_register(void) { |
592 | plugin_register_complex_config("memory", memory_config); | |
593 | plugin_register_init("memory", memory_init); | |
594 | plugin_register_read("memory", memory_read); | |
2f0dfdda | 595 | } /* void module_register */ |