2 * collectd - src/memory.c
3 * Copyright (C) 2005-2014 Florian octo Forster
4 * Copyright (C) 2009 Simon Kuhnle
5 * Copyright (C) 2009 Manuel Sanmartin
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
9 * Free Software Foundation; only version 2 of the License is applicable.
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.
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
21 * Florian octo Forster <octo at collectd.org>
22 * Simon Kuhnle <simon at blarzwurst.de>
29 #include "utils/common/common.h"
31 #if (defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME)) || \
33 /* Implies BSD variant */
34 #include <sys/sysctl.h>
36 #ifdef HAVE_SYS_VMMETER_H
37 #include <sys/vmmeter.h>
40 #ifdef HAVE_MACH_KERN_RETURN_H
41 #include <mach/kern_return.h>
43 #ifdef HAVE_MACH_MACH_INIT_H
44 #include <mach/mach_init.h>
46 #ifdef HAVE_MACH_MACH_HOST_H
47 #include <mach/mach_host.h>
49 #ifdef HAVE_MACH_HOST_PRIV_H
50 #include <mach/host_priv.h>
52 #ifdef HAVE_MACH_VM_STATISTICS_H
53 #include <mach/vm_statistics.h>
61 #include <libperfstat.h>
62 #include <sys/protosw.h>
63 #endif /* HAVE_PERFSTAT */
65 /* vm_statistics_data_t */
66 #if HAVE_HOST_STATISTICS
67 static mach_port_t port_host
;
68 static vm_size_t pagesize
;
69 /* #endif HAVE_HOST_STATISTICS */
71 #elif HAVE_SYSCTLBYNAME
72 #if HAVE_SYSCTL && defined(KERNEL_NETBSD)
74 #include <unistd.h> /* getpagesize() */
76 /* no global variables */
78 /* #endif HAVE_SYSCTLBYNAME */
81 /* no global variables */
82 /* #endif KERNEL_LINUX */
88 /* #endif HAVE_LIBKSTAT */
90 #elif HAVE_SYSCTL && __OpenBSD__
91 /* OpenBSD variant does not have sysctlbyname */
93 /* #endif HAVE_SYSCTL && __OpenBSD__ */
95 #elif HAVE_LIBSTATGRAB
96 /* no global variables */
97 /* endif HAVE_LIBSTATGRAB */
100 /* endif HAVE_PERFSTAT */
102 #error "No applicable input method."
106 #include <uvm/uvm_extern.h>
109 static bool values_absolute
= true;
110 static bool values_percentage
;
112 static int memory_config(oconfig_item_t
*ci
) /* {{{ */
114 for (int i
= 0; i
< ci
->children_num
; i
++) {
115 oconfig_item_t
*child
= ci
->children
+ i
;
116 if (strcasecmp("ValuesAbsolute", child
->key
) == 0)
117 cf_util_get_boolean(child
, &values_absolute
);
118 else if (strcasecmp("ValuesPercentage", child
->key
) == 0)
119 cf_util_get_boolean(child
, &values_percentage
);
121 ERROR("memory plugin: Invalid configuration option: "
127 } /* }}} int memory_config */
129 static int memory_init(void) {
130 #if HAVE_HOST_STATISTICS
131 port_host
= mach_host_self();
132 host_page_size(port_host
, &pagesize
);
133 /* #endif HAVE_HOST_STATISTICS */
135 #elif HAVE_SYSCTLBYNAME
136 #if HAVE_SYSCTL && defined(KERNEL_NETBSD)
137 pagesize
= getpagesize();
140 #endif /* HAVE_SYSCTL && defined(KERNEL_NETBSD) */
141 /* #endif HAVE_SYSCTLBYNAME */
143 #elif defined(KERNEL_LINUX)
145 /* #endif KERNEL_LINUX */
147 #elif defined(HAVE_LIBKSTAT)
148 /* getpagesize(3C) tells me this does not fail.. */
149 pagesize
= getpagesize();
150 if (get_kstat(&ksp
, "unix", 0, "system_pages") != 0) {
154 if (get_kstat(&ksz
, "zfs", 0, "arcstats") != 0) {
159 /* #endif HAVE_LIBKSTAT */
161 #elif HAVE_SYSCTL && __OpenBSD__
162 /* OpenBSD variant does not have sysctlbyname */
163 pagesize
= getpagesize();
165 ERROR("memory plugin: Invalid pagesize: %i", pagesize
);
168 /* #endif HAVE_SYSCTL && __OpenBSD__ */
170 #elif HAVE_LIBSTATGRAB
172 /* #endif HAVE_LIBSTATGRAB */
175 pagesize
= getpagesize();
176 #endif /* HAVE_PERFSTAT */
178 } /* int memory_init */
180 #define MEMORY_SUBMIT(...) \
182 if (values_absolute) \
183 plugin_dispatch_multivalue(vl, false, DS_TYPE_GAUGE, __VA_ARGS__, NULL); \
184 if (values_percentage) \
185 plugin_dispatch_multivalue(vl, true, DS_TYPE_GAUGE, __VA_ARGS__, NULL); \
189 static void memory_submit_available(gauge_t value
) {
190 value_list_t vl
= VALUE_LIST_INIT
;
192 vl
.values
= &(value_t
){.gauge
= value
};
195 sstrncpy(vl
.plugin
, "memory", sizeof(vl
.plugin
));
196 sstrncpy(vl
.type
, "memory", sizeof(vl
.type
));
197 sstrncpy(vl
.type_instance
, "available", sizeof(vl
.type_instance
));
199 plugin_dispatch_values(&vl
);
203 static int memory_read_internal(value_list_t
*vl
) {
204 #if HAVE_HOST_STATISTICS
205 kern_return_t status
;
206 vm_statistics_data_t vm_data
;
207 mach_msg_type_number_t vm_data_len
;
214 if (!port_host
|| !pagesize
)
217 vm_data_len
= sizeof(vm_data
) / sizeof(natural_t
);
218 if ((status
= host_statistics(port_host
, HOST_VM_INFO
, (host_info_t
)&vm_data
,
219 &vm_data_len
)) != KERN_SUCCESS
) {
220 ERROR("memory-plugin: host_statistics failed and returned the value %i",
226 * From <http://docs.info.apple.com/article.html?artnum=107918>:
229 * This information can't be cached to disk, so it must stay in RAM.
230 * The amount depends on what applications you are using.
233 * This information is currently in RAM and actively being used.
236 * This information is no longer being used and has been cached to
237 * disk, but it will remain in RAM until another application needs
238 * the space. Leaving this information in RAM is to your advantage if
239 * you (or a client of your computer) come back to it later.
242 * This memory is not being used.
245 wired
= (gauge_t
)(((uint64_t)vm_data
.wire_count
) * ((uint64_t)pagesize
));
246 active
= (gauge_t
)(((uint64_t)vm_data
.active_count
) * ((uint64_t)pagesize
));
248 (gauge_t
)(((uint64_t)vm_data
.inactive_count
) * ((uint64_t)pagesize
));
249 free
= (gauge_t
)(((uint64_t)vm_data
.free_count
) * ((uint64_t)pagesize
));
251 MEMORY_SUBMIT("wired", wired
, "active", active
, "inactive", inactive
, "free",
253 /* #endif HAVE_HOST_STATISTICS */
255 #elif HAVE_SYSCTLBYNAME
257 #if HAVE_SYSCTL && defined(KERNEL_NETBSD)
258 int mib
[] = {CTL_VM
, VM_UVMEXP2
};
259 struct uvmexp_sysctl uvmexp
;
261 gauge_t mem_inactive
;
267 memset(&uvmexp
, 0, sizeof(uvmexp
));
268 size
= sizeof(uvmexp
);
270 if (sysctl(mib
, 2, &uvmexp
, &size
, NULL
, 0) < 0) {
272 WARNING("memory plugin: sysctl failed: %s",
273 sstrerror(errno
, errbuf
, sizeof(errbuf
)));
277 assert(pagesize
> 0);
278 mem_active
= (gauge_t
)(uvmexp
.active
* pagesize
);
279 mem_inactive
= (gauge_t
)(uvmexp
.inactive
* pagesize
);
280 mem_free
= (gauge_t
)(uvmexp
.free
* pagesize
);
281 mem_wired
= (gauge_t
)(uvmexp
.wired
* pagesize
);
282 mem_kernel
= (gauge_t
)((uvmexp
.npages
- (uvmexp
.active
+ uvmexp
.inactive
+
283 uvmexp
.free
+ uvmexp
.wired
)) *
286 MEMORY_SUBMIT("active", mem_active
, "inactive", mem_inactive
, "free",
287 mem_free
, "wired", mem_wired
, "kernel", mem_kernel
);
288 /* #endif HAVE_SYSCTL && defined(KERNEL_NETBSD) */
290 #else /* Other HAVE_SYSCTLBYNAME providers */
292 * vm.stats.vm.v_page_size: 4096
293 * vm.stats.vm.v_page_count: 246178
294 * vm.stats.vm.v_free_count: 28760
295 * vm.stats.vm.v_wire_count: 37526
296 * vm.stats.vm.v_active_count: 55239
297 * vm.stats.vm.v_inactive_count: 113730
298 * vm.stats.vm.v_cache_count: 10809
299 * vm.stats.vm.v_user_wire_count: 0
300 * vm.stats.vm.v_laundry_count: 40394
302 const char *sysctl_keys
[10] = {
303 "vm.stats.vm.v_page_size", "vm.stats.vm.v_page_count",
304 "vm.stats.vm.v_free_count", "vm.stats.vm.v_wire_count",
305 "vm.stats.vm.v_active_count", "vm.stats.vm.v_inactive_count",
306 "vm.stats.vm.v_cache_count", "vm.stats.vm.v_user_wire_count",
307 "vm.stats.vm.v_laundry_count", NULL
};
308 double sysctl_vals
[10];
310 for (int i
= 0; sysctl_keys
[i
] != NULL
; i
++) {
312 size_t value_len
= sizeof(value
);
314 if (sysctlbyname(sysctl_keys
[i
], (void *)&value
, &value_len
, NULL
, 0) ==
316 sysctl_vals
[i
] = value
;
317 DEBUG("memory plugin: %26s: %g", sysctl_keys
[i
], sysctl_vals
[i
]);
319 sysctl_vals
[i
] = NAN
;
321 } /* for (sysctl_keys) */
323 /* multiply all all page counts with the pagesize */
324 for (int i
= 1; sysctl_keys
[i
] != NULL
; i
++)
325 if (!isnan(sysctl_vals
[i
]))
326 sysctl_vals
[i
] *= sysctl_vals
[0];
328 MEMORY_SUBMIT("free", (gauge_t
)sysctl_vals
[2], "wired",
329 (gauge_t
)sysctl_vals
[3], "active", (gauge_t
)sysctl_vals
[4],
330 "inactive", (gauge_t
)sysctl_vals
[5], "cache",
331 (gauge_t
)sysctl_vals
[6], "user_wire", (gauge_t
)sysctl_vals
[7],
332 "laundry", (gauge_t
)sysctl_vals
[8]);
334 #endif /* HAVE_SYSCTL && KERNEL_NETBSD */
335 /* #endif HAVE_SYSCTLBYNAME */
344 bool mem_available_info
= false;
345 bool detailed_slab_info
= false;
347 gauge_t mem_total
= 0;
348 gauge_t mem_used
= 0;
349 gauge_t mem_buffered
= 0;
350 gauge_t mem_cached
= 0;
351 gauge_t mem_free
= 0;
352 gauge_t mem_available
= 0;
353 gauge_t mem_slab_total
= 0;
354 gauge_t mem_slab_reclaimable
= 0;
355 gauge_t mem_slab_unreclaimable
= 0;
357 if ((fh
= fopen("/proc/meminfo", "r")) == NULL
) {
358 WARNING("memory: fopen: %s", STRERRNO
);
362 while (fgets(buffer
, sizeof(buffer
), fh
) != NULL
) {
365 if (strncasecmp(buffer
, "MemTotal:", 9) == 0)
367 else if (strncasecmp(buffer
, "MemFree:", 8) == 0)
369 else if (strncasecmp(buffer
, "Buffers:", 8) == 0)
371 else if (strncasecmp(buffer
, "Cached:", 7) == 0)
373 else if (strncasecmp(buffer
, "Slab:", 5) == 0)
374 val
= &mem_slab_total
;
375 else if (strncasecmp(buffer
, "SReclaimable:", 13) == 0) {
376 val
= &mem_slab_reclaimable
;
377 detailed_slab_info
= true;
378 } else if (strncasecmp(buffer
, "SUnreclaim:", 11) == 0) {
379 val
= &mem_slab_unreclaimable
;
380 detailed_slab_info
= true;
381 } else if (strncasecmp(buffer
, "MemAvailable:", 13) == 0) {
382 val
= &mem_available
;
383 mem_available_info
= true;
387 numfields
= strsplit(buffer
, fields
, STATIC_ARRAY_SIZE(fields
));
391 *val
= 1024.0 * atof(fields
[1]);
395 WARNING("memory: fclose: %s", STRERRNO
);
398 if (mem_total
< (mem_free
+ mem_buffered
+ mem_cached
+ mem_slab_total
))
401 if (detailed_slab_info
)
402 mem_used
= mem_total
-
403 (mem_free
+ mem_buffered
+ mem_cached
+ mem_slab_reclaimable
);
406 mem_total
- (mem_free
+ mem_buffered
+ mem_cached
+ mem_slab_total
);
408 /* SReclaimable and SUnreclaim were introduced in kernel 2.6.19
409 * They sum up to the value of Slab, which is available on older & newer
410 * kernels. So SReclaimable/SUnreclaim are submitted if available, and Slab
412 if (detailed_slab_info
)
413 MEMORY_SUBMIT("used", mem_used
, "buffered", mem_buffered
, "cached",
414 mem_cached
, "free", mem_free
, "slab_unrecl",
415 mem_slab_unreclaimable
, "slab_recl", mem_slab_reclaimable
);
417 MEMORY_SUBMIT("used", mem_used
, "buffered", mem_buffered
, "cached",
418 mem_cached
, "free", mem_free
, "slab", mem_slab_total
);
420 if (mem_available_info
)
421 memory_submit_available(mem_available
);
422 /* #endif KERNEL_LINUX */
425 /* Most of the additions here were taken as-is from the k9toolkit from
426 * Brendan Gregg and are subject to change I guess */
443 mem_used
= get_kstat_value(ksp
, "pagestotal");
444 mem_free
= get_kstat_value(ksp
, "pagesfree");
445 mem_lock
= get_kstat_value(ksp
, "pageslocked");
446 arcsize
= get_kstat_value(ksz
, "size");
447 pp_kernel
= get_kstat_value(ksp
, "pp_kernel");
448 physmem
= get_kstat_value(ksp
, "physmem");
449 availrmem
= get_kstat_value(ksp
, "availrmem");
454 if ((mem_used
< 0LL) || (mem_free
< 0LL) || (mem_lock
< 0LL)) {
455 WARNING("memory plugin: one of used, free or locked is negative.");
459 mem_unus
= physmem
- mem_used
;
461 if (mem_used
< (mem_free
+ mem_lock
)) {
462 /* source: http://wesunsolve.net/bugid/id/4909199
463 * this seems to happen when swap space is small, e.g. 2G on a 32G system
464 * we will make some assumptions here
465 * educated solaris internals help welcome here */
466 DEBUG("memory plugin: pages total is smaller than \"free\" "
467 "+ \"locked\". This is probably due to small "
469 mem_free
= availrmem
;
472 mem_used
-= mem_free
+ mem_lock
;
475 /* mem_kern is accounted for in mem_lock */
476 if (pp_kernel
< mem_lock
) {
477 mem_kern
= pp_kernel
;
478 mem_lock
-= pp_kernel
;
484 mem_used
*= pagesize
; /* If this overflows you have some serious */
485 mem_free
*= pagesize
; /* memory.. Why not call me up and give me */
486 mem_lock
*= pagesize
; /* some? ;) */
487 mem_kern
*= pagesize
; /* it's 2011 RAM is cheap */
488 mem_unus
*= pagesize
;
491 MEMORY_SUBMIT("used", (gauge_t
)mem_used
, "free", (gauge_t
)mem_free
, "locked",
492 (gauge_t
)mem_lock
, "kernel", (gauge_t
)mem_kern
, "arc",
493 (gauge_t
)arcsize
, "unusable", (gauge_t
)mem_unus
);
494 /* #endif HAVE_LIBKSTAT */
496 #elif HAVE_SYSCTL && __OpenBSD__
497 /* OpenBSD variant does not have HAVE_SYSCTLBYNAME */
498 int mib
[] = {CTL_VM
, VM_METER
};
499 struct vmtotal vmtotal
= {0};
501 gauge_t mem_inactive
;
505 size
= sizeof(vmtotal
);
507 if (sysctl(mib
, 2, &vmtotal
, &size
, NULL
, 0) < 0) {
508 WARNING("memory plugin: sysctl failed: %s", STRERRNO
);
512 assert(pagesize
> 0);
513 mem_active
= (gauge_t
)(vmtotal
.t_arm
* pagesize
);
514 mem_inactive
= (gauge_t
)((vmtotal
.t_rm
- vmtotal
.t_arm
) * pagesize
);
515 mem_free
= (gauge_t
)(vmtotal
.t_free
* pagesize
);
517 MEMORY_SUBMIT("active", mem_active
, "inactive", mem_inactive
, "free",
519 /* #endif HAVE_SYSCTL && __OpenBSD__ */
521 #elif HAVE_LIBSTATGRAB
524 ios
= sg_get_mem_stats();
528 MEMORY_SUBMIT("used", (gauge_t
)ios
->used
, "cached", (gauge_t
)ios
->cache
,
529 "free", (gauge_t
)ios
->free
);
530 /* #endif HAVE_LIBSTATGRAB */
533 perfstat_memory_total_t pmemory
= {0};
535 if (perfstat_memory_total(NULL
, &pmemory
, sizeof(pmemory
), 1) < 0) {
536 WARNING("memory plugin: perfstat_memory_total failed: %s", STRERRNO
);
540 /* Unfortunately, the AIX documentation is not very clear on how these
541 * numbers relate to one another. The only thing is states explcitly
543 * real_total = real_process + real_free + numperm + real_system
545 * Another segmentation, which would be closer to the numbers reported
546 * by the "svmon" utility, would be:
547 * real_total = real_free + real_inuse
548 * real_inuse = "active" + real_pinned + numperm
550 MEMORY_SUBMIT("free", (gauge_t
)(pmemory
.real_free
* pagesize
), "cached",
551 (gauge_t
)(pmemory
.numperm
* pagesize
), "system",
552 (gauge_t
)(pmemory
.real_system
* pagesize
), "user",
553 (gauge_t
)(pmemory
.real_process
* pagesize
));
554 #endif /* HAVE_PERFSTAT */
557 } /* }}} int memory_read_internal */
559 static int memory_read(void) /* {{{ */
562 value_list_t vl
= VALUE_LIST_INIT
;
565 vl
.values_len
= STATIC_ARRAY_SIZE(v
);
566 sstrncpy(vl
.plugin
, "memory", sizeof(vl
.plugin
));
567 sstrncpy(vl
.type
, "memory", sizeof(vl
.type
));
570 return memory_read_internal(&vl
);
571 } /* }}} int memory_read */
573 void module_register(void) {
574 plugin_register_complex_config("memory", memory_config
);
575 plugin_register_init("memory", memory_init
);
576 plugin_register_read("memory", memory_read
);
577 } /* void module_register */