Merge branch 'collectd-5.5'
[collectd.git] / src / memory.c
1 /**
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
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
9  * Free Software Foundation; only version 2 of the License is applicable.
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:
21  *   Florian octo Forster <octo at collectd.org>
22  *   Simon Kuhnle <simon at blarzwurst.de>
23  *   Manuel Sanmartin
24  **/
25
26 #include "collectd.h"
27
28 #include "common.h"
29 #include "plugin.h"
30
31 #ifdef HAVE_SYS_SYSCTL_H
32 # include <sys/sysctl.h>
33 #endif
34 #ifdef HAVE_SYS_VMMETER_H
35 # include <sys/vmmeter.h>
36 #endif
37
38 #ifdef HAVE_MACH_KERN_RETURN_H
39 # include <mach/kern_return.h>
40 #endif
41 #ifdef HAVE_MACH_MACH_INIT_H
42 # include <mach/mach_init.h>
43 #endif
44 #ifdef HAVE_MACH_MACH_HOST_H
45 # include <mach/mach_host.h>
46 #endif
47 #ifdef HAVE_MACH_HOST_PRIV_H
48 # include <mach/host_priv.h>
49 #endif
50 #ifdef HAVE_MACH_VM_STATISTICS_H
51 # include <mach/vm_statistics.h>
52 #endif
53
54 #if HAVE_STATGRAB_H
55 # include <statgrab.h>
56 #endif
57
58 #if HAVE_PERFSTAT
59 # include <sys/protosw.h>
60 # include <libperfstat.h>
61 #endif /* HAVE_PERFSTAT */
62
63 /* vm_statistics_data_t */
64 #if HAVE_HOST_STATISTICS
65 static mach_port_t port_host;
66 static vm_size_t pagesize;
67 /* #endif HAVE_HOST_STATISTICS */
68
69 #elif HAVE_SYSCTLBYNAME
70 /* no global variables */
71 /* #endif HAVE_SYSCTLBYNAME */
72
73 #elif KERNEL_LINUX
74 /* no global variables */
75 /* #endif KERNEL_LINUX */
76
77 #elif HAVE_LIBKSTAT
78 static int pagesize;
79 static kstat_t *ksp;
80 static kstat_t *ksz;
81 /* #endif HAVE_LIBKSTAT */
82
83 #elif HAVE_SYSCTL
84 static int pagesize;
85 /* #endif HAVE_SYSCTL */
86
87 #elif HAVE_LIBSTATGRAB
88 /* no global variables */
89 /* endif HAVE_LIBSTATGRAB */
90 #elif HAVE_PERFSTAT
91 static int pagesize;
92 /* endif HAVE_PERFSTAT */
93 #else
94 # error "No applicable input method."
95 #endif
96
97 static _Bool values_absolute = 1;
98 static _Bool values_percentage = 0;
99
100 static int memory_config (oconfig_item_t *ci) /* {{{ */
101 {
102         for (int i = 0; i < ci->children_num; i++)
103         {
104                 oconfig_item_t *child = ci->children + i;
105                 if (strcasecmp ("ValuesAbsolute", child->key) == 0)
106                         cf_util_get_boolean (child, &values_absolute);
107                 else if (strcasecmp ("ValuesPercentage", child->key) == 0)
108                         cf_util_get_boolean (child, &values_percentage);
109                 else
110                         ERROR ("memory plugin: Invalid configuration option: "
111                                         "\"%s\".", child->key);
112         }
113
114         return (0);
115 } /* }}} int memory_config */
116
117 static int memory_init (void)
118 {
119 #if HAVE_HOST_STATISTICS
120         port_host = mach_host_self ();
121         host_page_size (port_host, &pagesize);
122 /* #endif HAVE_HOST_STATISTICS */
123
124 #elif HAVE_SYSCTLBYNAME
125 /* no init stuff */
126 /* #endif HAVE_SYSCTLBYNAME */
127
128 #elif defined(KERNEL_LINUX)
129 /* no init stuff */
130 /* #endif KERNEL_LINUX */
131
132 #elif defined(HAVE_LIBKSTAT)
133         /* getpagesize(3C) tells me this does not fail.. */
134         pagesize = getpagesize ();
135         if (get_kstat (&ksp, "unix", 0, "system_pages") != 0)
136         {
137                 ksp = NULL;
138                 return (-1);
139         }
140         if (get_kstat (&ksz, "zfs", 0, "arcstats") != 0)
141         {
142                 ksz = NULL;
143                 return (-1);
144         }
145
146 /* #endif HAVE_LIBKSTAT */
147
148 #elif HAVE_SYSCTL
149         pagesize = getpagesize ();
150         if (pagesize <= 0)
151         {
152                 ERROR ("memory plugin: Invalid pagesize: %i", pagesize);
153                 return (-1);
154         }
155 /* #endif HAVE_SYSCTL */
156
157 #elif HAVE_LIBSTATGRAB
158 /* no init stuff */
159 /* #endif HAVE_LIBSTATGRAB */
160
161 #elif HAVE_PERFSTAT
162         pagesize = getpagesize ();
163 #endif /* HAVE_PERFSTAT */
164         return (0);
165 } /* int memory_init */
166
167 #define MEMORY_SUBMIT(...) do { \
168         if (values_absolute) \
169                 plugin_dispatch_multivalue (vl, 0, DS_TYPE_GAUGE, __VA_ARGS__, NULL); \
170         if (values_percentage) \
171                 plugin_dispatch_multivalue (vl, 1, DS_TYPE_GAUGE, __VA_ARGS__, NULL); \
172 } while (0)
173
174 static int memory_read_internal (value_list_t *vl)
175 {
176 #if HAVE_HOST_STATISTICS
177         kern_return_t status;
178         vm_statistics_data_t   vm_data;
179         mach_msg_type_number_t vm_data_len;
180
181         gauge_t wired;
182         gauge_t active;
183         gauge_t inactive;
184         gauge_t free;
185
186         if (!port_host || !pagesize)
187                 return (-1);
188
189         vm_data_len = sizeof (vm_data) / sizeof (natural_t);
190         if ((status = host_statistics (port_host, HOST_VM_INFO,
191                                         (host_info_t) &vm_data,
192                                         &vm_data_len)) != KERN_SUCCESS)
193         {
194                 ERROR ("memory-plugin: host_statistics failed and returned the value %i", (int) status);
195                 return (-1);
196         }
197
198         /*
199          * From <http://docs.info.apple.com/article.html?artnum=107918>:
200          *
201          * Wired memory
202          *   This information can't be cached to disk, so it must stay in RAM.
203          *   The amount depends on what applications you are using.
204          *
205          * Active memory
206          *   This information is currently in RAM and actively being used.
207          *
208          * Inactive memory
209          *   This information is no longer being used and has been cached to
210          *   disk, but it will remain in RAM until another application needs
211          *   the space. Leaving this information in RAM is to your advantage if
212          *   you (or a client of your computer) come back to it later.
213          *
214          * Free memory
215          *   This memory is not being used.
216          */
217
218         wired    = (gauge_t) (((uint64_t) vm_data.wire_count)     * ((uint64_t) pagesize));
219         active   = (gauge_t) (((uint64_t) vm_data.active_count)   * ((uint64_t) pagesize));
220         inactive = (gauge_t) (((uint64_t) vm_data.inactive_count) * ((uint64_t) pagesize));
221         free     = (gauge_t) (((uint64_t) vm_data.free_count)     * ((uint64_t) pagesize));
222
223         MEMORY_SUBMIT ("wired",    wired,
224                        "active",   active,
225                        "inactive", inactive,
226                        "free",     free);
227 /* #endif HAVE_HOST_STATISTICS */
228
229 #elif HAVE_SYSCTLBYNAME
230         /*
231          * vm.stats.vm.v_page_size: 4096
232          * vm.stats.vm.v_page_count: 246178
233          * vm.stats.vm.v_free_count: 28760
234          * vm.stats.vm.v_wire_count: 37526
235          * vm.stats.vm.v_active_count: 55239
236          * vm.stats.vm.v_inactive_count: 113730
237          * vm.stats.vm.v_cache_count: 10809
238          */
239         const char *sysctl_keys[8] =
240         {
241                 "vm.stats.vm.v_page_size",
242                 "vm.stats.vm.v_page_count",
243                 "vm.stats.vm.v_free_count",
244                 "vm.stats.vm.v_wire_count",
245                 "vm.stats.vm.v_active_count",
246                 "vm.stats.vm.v_inactive_count",
247                 "vm.stats.vm.v_cache_count",
248                 NULL
249         };
250         double sysctl_vals[8];
251
252         for (int i = 0; sysctl_keys[i] != NULL; i++)
253         {
254                 int value;
255                 size_t value_len = sizeof (value);
256
257                 if (sysctlbyname (sysctl_keys[i], (void *) &value, &value_len,
258                                         NULL, 0) == 0)
259                 {
260                         sysctl_vals[i] = value;
261                         DEBUG ("memory plugin: %26s: %g", sysctl_keys[i], sysctl_vals[i]);
262                 }
263                 else
264                 {
265                         sysctl_vals[i] = NAN;
266                 }
267         } /* for (sysctl_keys) */
268
269         /* multiply all all page counts with the pagesize */
270         for (int i = 1; sysctl_keys[i] != NULL; i++)
271                 if (!isnan (sysctl_vals[i]))
272                         sysctl_vals[i] *= sysctl_vals[0];
273
274         MEMORY_SUBMIT ("free",     (gauge_t) sysctl_vals[2],
275                        "wired",    (gauge_t) sysctl_vals[3],
276                        "active",   (gauge_t) sysctl_vals[4],
277                        "inactive", (gauge_t) sysctl_vals[5],
278                        "cache",    (gauge_t) sysctl_vals[6]);
279 /* #endif HAVE_SYSCTLBYNAME */
280
281 #elif KERNEL_LINUX
282         FILE *fh;
283         char buffer[1024];
284
285         char *fields[8];
286         int numfields;
287
288         _Bool detailed_slab_info = 0;
289
290         gauge_t mem_total = 0;
291         gauge_t mem_used = 0;
292         gauge_t mem_buffered = 0;
293         gauge_t mem_cached = 0;
294         gauge_t mem_free = 0;
295         gauge_t mem_slab_total = 0;
296         gauge_t mem_slab_reclaimable = 0;
297         gauge_t mem_slab_unreclaimable = 0;
298
299         if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
300         {
301                 char errbuf[1024];
302                 WARNING ("memory: fopen: %s",
303                                 sstrerror (errno, errbuf, sizeof (errbuf)));
304                 return (-1);
305         }
306
307         while (fgets (buffer, sizeof (buffer), fh) != NULL)
308         {
309                 gauge_t *val = NULL;
310
311                 if (strncasecmp (buffer, "MemTotal:", 9) == 0)
312                         val = &mem_total;
313                 else if (strncasecmp (buffer, "MemFree:", 8) == 0)
314                         val = &mem_free;
315                 else if (strncasecmp (buffer, "Buffers:", 8) == 0)
316                         val = &mem_buffered;
317                 else if (strncasecmp (buffer, "Cached:", 7) == 0)
318                         val = &mem_cached;
319                 else if (strncasecmp (buffer, "Slab:", 5) == 0)
320                         val = &mem_slab_total;
321                 else if (strncasecmp (buffer, "SReclaimable:", 13) == 0) {
322                         val = &mem_slab_reclaimable;
323                         detailed_slab_info = 1;
324                 }
325                 else if (strncasecmp (buffer, "SUnreclaim:", 11) == 0) {
326                         val = &mem_slab_unreclaimable;
327                         detailed_slab_info = 1;
328                 }
329                 else
330                         continue;
331
332                 numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
333                 if (numfields < 2)
334                         continue;
335
336                 *val = 1024.0 * atof (fields[1]);
337         }
338
339         if (fclose (fh))
340         {
341                 char errbuf[1024];
342                 WARNING ("memory: fclose: %s",
343                                 sstrerror (errno, errbuf, sizeof (errbuf)));
344         }
345
346         if (mem_total < (mem_free + mem_buffered + mem_cached + mem_slab_total))
347                 return (-1);
348
349         mem_used = mem_total - (mem_free + mem_buffered + mem_cached + mem_slab_total);
350
351         /* SReclaimable and SUnreclaim were introduced in kernel 2.6.19
352          * They sum up to the value of Slab, which is available on older & newer
353          * kernels. So SReclaimable/SUnreclaim are submitted if available, and Slab
354          * if not. */
355         if (detailed_slab_info)
356                 MEMORY_SUBMIT ("used",        mem_used,
357                                "buffered",    mem_buffered,
358                                "cached",      mem_cached,
359                                "free",        mem_free,
360                                "slab_unrecl", mem_slab_unreclaimable,
361                                "slab_recl",   mem_slab_reclaimable);
362         else
363                 MEMORY_SUBMIT ("used",     mem_used,
364                                "buffered", mem_buffered,
365                                "cached",   mem_cached,
366                                "free",     mem_free,
367                                "slab",     mem_slab_total);
368 /* #endif KERNEL_LINUX */
369
370 #elif HAVE_LIBKSTAT
371         /* Most of the additions here were taken as-is from the k9toolkit from
372          * Brendan Gregg and are subject to change I guess */
373         long long mem_used;
374         long long mem_free;
375         long long mem_lock;
376         long long mem_kern;
377         long long mem_unus;
378         long long arcsize;
379
380
381         long long pp_kernel;
382         long long physmem;
383         long long availrmem;
384
385         if (ksp == NULL)
386                 return (-1);
387         if (ksz == NULL)
388                 return (-1);
389
390         mem_used = get_kstat_value (ksp, "pagestotal");
391         mem_free = get_kstat_value (ksp, "pagesfree");
392         mem_lock = get_kstat_value (ksp, "pageslocked");
393         arcsize = get_kstat_value (ksz, "size");
394         pp_kernel = get_kstat_value (ksp, "pp_kernel");
395         physmem = get_kstat_value (ksp, "physmem");
396         availrmem = get_kstat_value (ksp, "availrmem");
397
398         mem_kern = 0;
399         mem_unus = 0;
400
401         if ((mem_used < 0LL) || (mem_free < 0LL) || (mem_lock < 0LL))
402         {
403                 WARNING ("memory plugin: one of used, free or locked is negative.");
404                 return (-1);
405         }
406
407         mem_unus = physmem - mem_used;
408
409         if (mem_used < (mem_free + mem_lock))
410         {
411                 /* source: http://wesunsolve.net/bugid/id/4909199
412                  * this seems to happen when swap space is small, e.g. 2G on a 32G system
413                  * we will make some assumptions here
414                  * educated solaris internals help welcome here */
415                 DEBUG ("memory plugin: pages total is smaller than \"free\" "
416                                 "+ \"locked\". This is probably due to small "
417                                 "swap space");
418                 mem_free = availrmem;
419                 mem_used = 0;
420         }
421         else
422         {
423                 mem_used -= mem_free + mem_lock;
424         }
425
426         /* mem_kern is accounted for in mem_lock */
427         if (pp_kernel < mem_lock)
428         {
429                 mem_kern = pp_kernel;
430                 mem_lock -= pp_kernel;
431         }
432         else
433         {
434                 mem_kern = mem_lock;
435                 mem_lock = 0;
436         }
437
438         mem_used *= pagesize; /* If this overflows you have some serious */
439         mem_free *= pagesize; /* memory.. Why not call me up and give me */
440         mem_lock *= pagesize; /* some? ;) */
441         mem_kern *= pagesize; /* it's 2011 RAM is cheap */
442         mem_unus *= pagesize;
443         mem_kern -= arcsize;
444
445
446         MEMORY_SUBMIT ("used",     (gauge_t) mem_used,
447                        "free",     (gauge_t) mem_free,
448                        "locked",   (gauge_t) mem_lock,
449                        "kernel",   (gauge_t) mem_kern,
450                        "arc",      (gauge_t) arcsize,
451                        "unusable", (gauge_t) mem_unus);
452 /* #endif HAVE_LIBKSTAT */
453
454 #elif HAVE_SYSCTL
455         int mib[] = {CTL_VM, VM_METER};
456         struct vmtotal vmtotal = { 0 };
457         gauge_t mem_active;
458         gauge_t mem_inactive;
459         gauge_t mem_free;
460         size_t size;
461
462         size = sizeof (vmtotal);
463
464         if (sysctl (mib, 2, &vmtotal, &size, NULL, 0) < 0) {
465                 char errbuf[1024];
466                 WARNING ("memory plugin: sysctl failed: %s",
467                         sstrerror (errno, errbuf, sizeof (errbuf)));
468                 return (-1);
469         }
470
471         assert (pagesize > 0);
472         mem_active   = (gauge_t) (vmtotal.t_arm * pagesize);
473         mem_inactive = (gauge_t) ((vmtotal.t_rm - vmtotal.t_arm) * pagesize);
474         mem_free     = (gauge_t) (vmtotal.t_free * pagesize);
475
476         MEMORY_SUBMIT ("active",   mem_active,
477                        "inactive", mem_inactive,
478                        "free",     mem_free);
479 /* #endif HAVE_SYSCTL */
480
481 #elif HAVE_LIBSTATGRAB
482         sg_mem_stats *ios;
483
484         ios = sg_get_mem_stats ();
485         if (ios == NULL)
486                 return (-1);
487
488         MEMORY_SUBMIT ("used",   (gauge_t) ios->used,
489                        "cached", (gauge_t) ios->cache,
490                        "free",   (gauge_t) ios->free);
491 /* #endif HAVE_LIBSTATGRAB */
492
493 #elif HAVE_PERFSTAT
494         perfstat_memory_total_t pmemory = { 0 };
495
496         if (perfstat_memory_total(NULL, &pmemory, sizeof(pmemory), 1) < 0)
497         {
498                 char errbuf[1024];
499                 WARNING ("memory plugin: perfstat_memory_total failed: %s",
500                         sstrerror (errno, errbuf, sizeof (errbuf)));
501                 return (-1);
502         }
503
504         /* Unfortunately, the AIX documentation is not very clear on how these
505          * numbers relate to one another. The only thing is states explcitly
506          * is:
507          *   real_total = real_process + real_free + numperm + real_system
508          *
509          * Another segmentation, which would be closer to the numbers reported
510          * by the "svmon" utility, would be:
511          *   real_total = real_free + real_inuse
512          *   real_inuse = "active" + real_pinned + numperm
513          */
514         MEMORY_SUBMIT ("free",   (gauge_t) (pmemory.real_free    * pagesize),
515                        "cached", (gauge_t) (pmemory.numperm      * pagesize),
516                        "system", (gauge_t) (pmemory.real_system  * pagesize),
517                        "user",   (gauge_t) (pmemory.real_process * pagesize));
518 #endif /* HAVE_PERFSTAT */
519
520         return (0);
521 } /* }}} int memory_read_internal */
522
523 static int memory_read (void) /* {{{ */
524 {
525         value_t v[1];
526         value_list_t vl = VALUE_LIST_INIT;
527
528         vl.values = v;
529         vl.values_len = STATIC_ARRAY_SIZE (v);
530         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
531         sstrncpy (vl.plugin, "memory", sizeof (vl.plugin));
532         sstrncpy (vl.type, "memory", sizeof (vl.type));
533         vl.time = cdtime ();
534
535         return (memory_read_internal (&vl));
536 } /* }}} int memory_read */
537
538 void module_register (void)
539 {
540         plugin_register_complex_config ("memory", memory_config);
541         plugin_register_init ("memory", memory_init);
542         plugin_register_read ("memory", memory_read);
543 } /* void module_register */