Merge branch 'collectd-4.10'
[collectd.git] / src / swap.c
1 /**
2  * collectd - src/swap.c
3  * Copyright (C) 2005-2009  Florian octo Forster
4  * Copyright (C) 2009       Stefan Völkel
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 verplant.org>
22  *   Manuel Sanmartin
23  **/
24
25 #if HAVE_CONFIG_H
26 # include "config.h"
27 # undef HAVE_CONFIG_H
28 #endif
29 /* avoid swap.h error "Cannot use swapctl in the large files compilation environment" */
30 #if HAVE_SYS_SWAP_H && !defined(_LP64) && _FILE_OFFSET_BITS == 64
31 #  undef _FILE_OFFSET_BITS
32 #  undef _LARGEFILE64_SOURCE
33 #endif
34
35 #include "collectd.h"
36 #include "common.h"
37 #include "plugin.h"
38
39 #if HAVE_SYS_SWAP_H
40 # include <sys/swap.h>
41 #endif
42 #if HAVE_VM_ANON_H
43 # include <vm/anon.h>
44 #endif
45 #if HAVE_SYS_PARAM_H
46 #  include <sys/param.h>
47 #endif
48 #if HAVE_SYS_SYSCTL_H
49 #  include <sys/sysctl.h>
50 #endif
51 #if HAVE_SYS_DKSTAT_H
52 #  include <sys/dkstat.h>
53 #endif
54 #if HAVE_KVM_H
55 #  include <kvm.h>
56 #endif
57
58 #if HAVE_STATGRAB_H
59 # include <statgrab.h>
60 #endif
61
62 #if HAVE_PERFSTAT
63 # include <sys/protosw.h>
64 # include <libperfstat.h>
65 #endif
66
67 #undef  MAX
68 #define MAX(x,y) ((x) > (y) ? (x) : (y))
69
70 #if KERNEL_LINUX
71 /* No global variables */
72 /* #endif KERNEL_LINUX */
73
74 #elif HAVE_LIBKSTAT
75 static derive_t pagesize;
76 static kstat_t *ksp;
77 /* #endif HAVE_LIBKSTAT */
78
79 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
80 static derive_t pagesize;
81 /* #endif HAVE_SWAPCTL */
82
83 #elif defined(VM_SWAPUSAGE)
84 /* No global variables */
85 /* #endif defined(VM_SWAPUSAGE) */
86
87 #elif HAVE_LIBKVM_GETSWAPINFO
88 static kvm_t *kvm_obj = NULL;
89 int kvm_pagesize;
90 /* #endif HAVE_LIBKVM_GETSWAPINFO */
91
92 #elif HAVE_LIBSTATGRAB
93 /* No global variables */
94 /* #endif HAVE_LIBSTATGRAB */
95
96 #elif HAVE_PERFSTAT
97 static int pagesize;
98 static perfstat_memory_total_t pmemory;
99 /*# endif HAVE_PERFSTAT */
100
101 #else
102 # error "No applicable input method."
103 #endif /* HAVE_LIBSTATGRAB */
104
105 static int swap_init (void)
106 {
107 #if KERNEL_LINUX
108         /* No init stuff */
109 /* #endif KERNEL_LINUX */
110
111 #elif HAVE_LIBKSTAT
112         /* getpagesize(3C) tells me this does not fail.. */
113         pagesize = (derive_t) getpagesize ();
114         if (get_kstat (&ksp, "unix", 0, "system_pages"))
115                 ksp = NULL;
116 /* #endif HAVE_LIBKSTAT */
117
118 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
119         /* getpagesize(3C) tells me this does not fail.. */
120         pagesize = (derive_t) getpagesize ();
121 /* #endif HAVE_SWAPCTL */
122
123 #elif defined(VM_SWAPUSAGE)
124         /* No init stuff */
125 /* #endif defined(VM_SWAPUSAGE) */
126
127 #elif HAVE_LIBKVM_GETSWAPINFO
128         if (kvm_obj != NULL)
129         {
130                 kvm_close (kvm_obj);
131                 kvm_obj = NULL;
132         }
133
134         kvm_pagesize = getpagesize ();
135
136         if ((kvm_obj = kvm_open (NULL, /* execfile */
137                                         NULL, /* corefile */
138                                         NULL, /* swapfile */
139                                         O_RDONLY, /* flags */
140                                         NULL)) /* errstr */
141                         == NULL)
142         {
143                 ERROR ("swap plugin: kvm_open failed.");
144                 return (-1);
145         }
146 /* #endif HAVE_LIBKVM_GETSWAPINFO */
147
148 #elif HAVE_LIBSTATGRAB
149         /* No init stuff */
150 /* #endif HAVE_LIBSTATGRAB */
151
152 #elif HAVE_PERFSTAT
153         pagesize = getpagesize();
154 #endif /* HAVE_PERFSTAT */
155
156         return (0);
157 }
158
159 static void swap_submit (const char *type_instance, derive_t value, unsigned type)
160 {
161         value_t values[1];
162         value_list_t vl = VALUE_LIST_INIT;
163
164         switch (type)
165         {
166                 case DS_TYPE_GAUGE:
167                         values[0].gauge = (gauge_t) value;
168                         sstrncpy (vl.type, "swap", sizeof (vl.type));
169                         break;
170                 case DS_TYPE_DERIVE:
171                         values[0].derive = value;
172                         sstrncpy (vl.type, "swap_io", sizeof (vl.type));
173                         break;
174                 default:
175                         ERROR ("swap plugin: swap_submit called with wrong"
176                                 " type");
177         }
178
179         vl.values = values;
180         vl.values_len = 1;
181         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
182         sstrncpy (vl.plugin, "swap", sizeof (vl.plugin));
183         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
184
185         plugin_dispatch_values (&vl);
186 } /* void swap_submit */
187
188 static int swap_read (void)
189 {
190 #if KERNEL_LINUX
191         FILE *fh;
192         char buffer[1024];
193
194         char *fields[8];
195         int numfields;
196
197         _Bool old_kernel=0;
198
199         derive_t swap_used   = 0;
200         derive_t swap_cached = 0;
201         derive_t swap_free   = 0;
202         derive_t swap_total  = 0;
203         derive_t swap_in     = 0;
204         derive_t swap_out    = 0;
205
206         if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
207         {
208                 char errbuf[1024];
209                 WARNING ("memory: fopen: %s",
210                                 sstrerror (errno, errbuf, sizeof (errbuf)));
211                 return (-1);
212         }
213
214         while (fgets (buffer, sizeof (buffer), fh) != NULL)
215         {
216                 numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
217                 if (numfields < 2)
218                         continue;
219
220                 if (strcasecmp (fields[0], "SwapTotal:") == 0)
221                         strtoderive (fields[1], &swap_total);
222                 else if (strcasecmp (fields[0], "SwapFree:") == 0)
223                         strtoderive (fields[1], &swap_free);
224                 else if (strcasecmp (fields[0], "SwapCached:") == 0)
225                         strtoderive (fields[1], &swap_cached);
226         }
227
228         if (fclose (fh))
229         {
230                 char errbuf[1024];
231                 WARNING ("memory: fclose: %s",
232                                 sstrerror (errno, errbuf, sizeof (errbuf)));
233         }
234
235         if ((swap_total == 0LL) || ((swap_free + swap_cached) > swap_total))
236                 return (-1);
237
238         swap_used = swap_total - (swap_free + swap_cached);
239
240         if ((fh = fopen ("/proc/vmstat", "r")) == NULL)
241         {
242                 // /proc/vmstat does not exist in kernels <2.6
243                 if ((fh = fopen ("/proc/stat", "r")) == NULL )
244                 {
245                         char errbuf[1024];
246                         WARNING ("swap: fopen: %s",
247                                         sstrerror (errno, errbuf, sizeof (errbuf)));
248                         return (-1);
249                 }
250                 else
251                         old_kernel = 1;
252         }
253
254         while (fgets (buffer, sizeof (buffer), fh) != NULL)
255         {
256                 numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
257
258                 if (!old_kernel)
259                 {
260                         if (numfields != 2)
261                                 continue;
262
263                         if (strcasecmp ("pswpin", fields[0]) == 0)
264                                 strtoderive (fields[1], &swap_in);
265                         else if (strcasecmp ("pswpout", fields[0]) == 0)
266                                 strtoderive (fields[1], &swap_out);
267                 }
268                 else /* if (old_kernel) */
269                 {
270                         if (numfields != 3)
271                                 continue;
272
273                         if (strcasecmp ("page", fields[0]) == 0)
274                         {
275                                 strtoderive (fields[1], &swap_in);
276                                 strtoderive (fields[2], &swap_out);
277                         }
278                 }
279         } /* while (fgets) */
280
281         if (fclose (fh))
282         {
283                 char errbuf[1024];
284                 WARNING ("swap: fclose: %s",
285                                 sstrerror (errno, errbuf, sizeof (errbuf)));
286         }
287
288         swap_submit ("used",   1024 * swap_used,   DS_TYPE_GAUGE);
289         swap_submit ("free",   1024 * swap_free,   DS_TYPE_GAUGE);
290         swap_submit ("cached", 1024 * swap_cached, DS_TYPE_GAUGE);
291         swap_submit ("in",  swap_in,  DS_TYPE_DERIVE);
292         swap_submit ("out", swap_out, DS_TYPE_DERIVE);
293 /* #endif KERNEL_LINUX */
294
295 #elif HAVE_LIBKSTAT
296         derive_t swap_alloc;
297         derive_t swap_resv;
298         derive_t swap_avail;
299
300         struct anoninfo ai;
301
302         if (swapctl (SC_AINFO, &ai) == -1)
303         {
304                 char errbuf[1024];
305                 ERROR ("swap plugin: swapctl failed: %s",
306                                 sstrerror (errno, errbuf, sizeof (errbuf)));
307                 return (-1);
308         }
309
310         /*
311          * Calculations from:
312          * http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/swap/swap.c
313          * Also see:
314          * http://www.itworld.com/Comp/2377/UIR980701perf/ (outdated?)
315          * /usr/include/vm/anon.h
316          *
317          * In short, swap -s shows: allocated + reserved = used, available
318          *
319          * However, Solaris does not allow to allocated/reserved more than the
320          * available swap (physical memory + disk swap), so the pedant may
321          * prefer: allocated + unallocated = reserved, available
322          *
323          * We map the above to: used + resv = n/a, free
324          *
325          * Does your brain hurt yet?  - Christophe Kalt
326          *
327          * Oh, and in case you wonder,
328          * swap_alloc = pagesize * ( ai.ani_max - ai.ani_free );
329          * can suffer from a 32bit overflow.
330          */
331         swap_alloc  = (derive_t) ((ai.ani_max - ai.ani_free) * pagesize);
332         swap_resv   = (derive_t) ((ai.ani_resv + ai.ani_free - ai.ani_max)
333                         * pagesize);
334         swap_avail  = (derive_t) ((ai.ani_max - ai.ani_resv) * pagesize);
335
336         swap_submit ("used", swap_alloc, DS_TYPE_GAUGE);
337         swap_submit ("free", swap_avail, DS_TYPE_GAUGE);
338         swap_submit ("reserved", swap_resv, DS_TYPE_GAUGE);
339 /* #endif HAVE_LIBKSTAT */
340
341 #elif HAVE_SWAPCTL
342  #if HAVE_SWAPCTL_TWO_ARGS
343         swaptbl_t *s;
344         char strtab[255];
345         int swap_num;
346         int status;
347         int i;
348
349         derive_t avail = 0;
350         derive_t total = 0;
351
352         swap_num = swapctl (SC_GETNSWP, NULL);
353         if (swap_num < 0)
354         {
355                 ERROR ("swap plugin: swapctl (SC_GETNSWP) failed with status %i.",
356                                 swap_num);
357                 return (-1);
358         }
359         else if (swap_num == 0)
360                 return (0);
361
362         s = (swaptbl_t *) smalloc (swap_num * sizeof (swapent_t) + sizeof (struct swaptable));
363         if (s == NULL)
364         {
365                 ERROR ("swap plugin: smalloc failed.");
366                 return (-1);
367         }
368         /* Initialize string pointers. We have them share the same buffer as we don't care
369          * about the device's name, only its size. This saves memory and alloc/free ops */
370         for (i = 0; i < (swap_num + 1); i++) {
371                 s->swt_ent[i].ste_path = strtab;
372         }
373         s->swt_n = swap_num + 1;
374         status = swapctl (SC_LIST, s);
375         if (status != swap_num)
376         {
377                 ERROR ("swap plugin: swapctl (SC_LIST) failed with status %i.",
378                                 status);
379                 sfree (s);
380                 return (-1);
381         }
382
383         for (i = 0; i < swap_num; i++)
384         {
385                 if ((s->swt_ent[i].ste_flags & ST_INDEL) != 0)
386                         continue;
387
388                 avail += ((derive_t) s->swt_ent[i].ste_free)
389                          * pagesize;
390                 total += ((derive_t) s->swt_ent[i].ste_pages)
391                          * pagesize;
392         }
393
394         if (total < avail)
395         {
396                 ERROR ("swap plugin: Total swap space (%"PRIi64") "
397                                 "is less than free swap space (%"PRIi64").",
398                                 total, avail);
399                 sfree (s);
400                 return (-1);
401         }
402
403         swap_submit ("used", total - avail, DS_TYPE_GAUGE);
404         swap_submit ("free", avail, DS_TYPE_GAUGE);
405
406         sfree (s);
407  /* #endif HAVE_SWAPCTL_TWO_ARGS */
408  #elif HAVE_SWAPCTL_THREE_ARGS
409
410         struct swapent *swap_entries;
411         int swap_num;
412         int status;
413         int i;
414
415         derive_t used  = 0;
416         derive_t total = 0;
417
418         swap_num = swapctl (SWAP_NSWAP, NULL, 0);
419         if (swap_num < 0)
420         {
421                 ERROR ("swap plugin: swapctl (SWAP_NSWAP) failed with status %i.",
422                                 swap_num);
423                 return (-1);
424         }
425         else if (swap_num == 0)
426                 return (0);
427
428         swap_entries = calloc (swap_num, sizeof (*swap_entries));
429         if (swap_entries == NULL)
430         {
431                 ERROR ("swap plugin: calloc failed.");
432                 return (-1);
433         }
434
435         status = swapctl (SWAP_STATS, swap_entries, swap_num);
436         if (status != swap_num)
437         {
438                 ERROR ("swap plugin: swapctl (SWAP_STATS) failed with status %i.",
439                                 status);
440                 sfree (swap_entries);
441                 return (-1);
442         }
443
444 #if defined(DEV_BSIZE) && (DEV_BSIZE > 0)
445 # define C_SWAP_BLOCK_SIZE ((derive_t) DEV_BSIZE)
446 #else
447 # define C_SWAP_BLOCK_SIZE ((derive_t) 512)
448 #endif
449
450         for (i = 0; i < swap_num; i++)
451         {
452                 if ((swap_entries[i].se_flags & SWF_ENABLE) == 0)
453                         continue;
454
455                 used  += ((derive_t) swap_entries[i].se_inuse)
456                         * C_SWAP_BLOCK_SIZE;
457                 total += ((derive_t) swap_entries[i].se_nblks)
458                         * C_SWAP_BLOCK_SIZE;
459         }
460
461         if (total < used)
462         {
463                 ERROR ("swap plugin: Total swap space (%"PRIu64") "
464                                 "is less than used swap space (%"PRIu64").",
465                                 total, used);
466                 return (-1);
467         }
468
469         swap_submit ("used", used, DS_TYPE_GAUGE);
470         swap_submit ("free", total - used, DS_TYPE_GAUGE);
471
472         sfree (swap_entries);
473  #endif /* HAVE_SWAPCTL_THREE_ARGS */
474 /* #endif HAVE_SWAPCTL */
475
476 #elif defined(VM_SWAPUSAGE)
477         int              mib[3];
478         size_t           mib_len;
479         struct xsw_usage sw_usage;
480         size_t           sw_usage_len;
481
482         mib_len = 2;
483         mib[0]  = CTL_VM;
484         mib[1]  = VM_SWAPUSAGE;
485
486         sw_usage_len = sizeof (struct xsw_usage);
487
488         if (sysctl (mib, mib_len, &sw_usage, &sw_usage_len, NULL, 0) != 0)
489                 return (-1);
490
491         /* The returned values are bytes. */
492         swap_submit ("used", (derive_t) sw_usage.xsu_used, DS_TYPE_GAUGE);
493         swap_submit ("free", (derive_t) sw_usage.xsu_avail, DS_TYPE_GAUGE);
494 /* #endif VM_SWAPUSAGE */
495
496 #elif HAVE_LIBKVM_GETSWAPINFO
497         struct kvm_swap data_s;
498         int             status;
499
500         derive_t used;
501         derive_t free;
502         derive_t total;
503
504         if (kvm_obj == NULL)
505                 return (-1);
506
507         /* only one structure => only get the grand total, no details */
508         status = kvm_getswapinfo (kvm_obj, &data_s, 1, 0);
509         if (status == -1)
510                 return (-1);
511
512         total = (derive_t) data_s.ksw_total;
513         used  = (derive_t) data_s.ksw_used;
514
515         total *= (derive_t) kvm_pagesize;
516         used  *= (derive_t) kvm_pagesize;
517
518         free = total - used;
519
520         swap_submit ("used", used, DS_TYPE_GAUGE);
521         swap_submit ("free", free, DS_TYPE_GAUGE);
522 /* #endif HAVE_LIBKVM_GETSWAPINFO */
523
524 #elif HAVE_LIBSTATGRAB
525         sg_swap_stats *swap;
526
527         swap = sg_get_swap_stats ();
528
529         if (swap == NULL)
530                 return (-1);
531
532         swap_submit ("used", (derive_t) swap->used, DS_TYPE_GAUGE);
533         swap_submit ("free", (derive_t) swap->free, DS_TYPE_GAUGE);
534 /* #endif  HAVE_LIBSTATGRAB */
535
536 #elif HAVE_PERFSTAT
537         if(perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1) < 0)
538         {
539                 char errbuf[1024];
540                 WARNING ("memory plugin: perfstat_memory_total failed: %s",
541                         sstrerror (errno, errbuf, sizeof (errbuf)));
542                 return (-1);
543         }
544         swap_submit ("used", (derive_t) (pmemory.pgsp_total - pmemory.pgsp_free) * pagesize, DS_TYPE_GAUGE);
545         swap_submit ("free", (derive_t) pmemory.pgsp_free * pagesize , DS_TYPE_GAUGE);
546 #endif /* HAVE_PERFSTAT */
547
548         return (0);
549 } /* int swap_read */
550
551 void module_register (void)
552 {
553         plugin_register_init ("swap", swap_init);
554         plugin_register_read ("swap", swap_read);
555 } /* void module_register */