Merge pull request #3329 from efuss/fix-3311
[collectd.git] / src / vmem.c
1 /**
2  * collectd - src/vmem.c
3  * Copyright (C) 2008-2010  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28
29 #include "plugin.h"
30 #include "utils/common/common.h"
31
32 #if KERNEL_LINUX
33 static const char *config_keys[] = {"Verbose"};
34 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
35
36 static int verbose_output;
37 /* #endif KERNEL_LINUX */
38
39 #else
40 #error "No applicable input method."
41 #endif /* HAVE_LIBSTATGRAB */
42
43 static void submit(const char *plugin_instance, const char *type,
44                    const char *type_instance, value_t *values, int values_len) {
45   value_list_t vl = VALUE_LIST_INIT;
46
47   vl.values = values;
48   vl.values_len = values_len;
49
50   sstrncpy(vl.plugin, "vmem", sizeof(vl.plugin));
51   if (plugin_instance != NULL)
52     sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
53   sstrncpy(vl.type, type, sizeof(vl.type));
54   if (type_instance != NULL)
55     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
56
57   plugin_dispatch_values(&vl);
58 } /* void vmem_submit */
59
60 static void submit_two(const char *plugin_instance, const char *type,
61                        const char *type_instance, derive_t c0, derive_t c1) {
62   value_t values[] = {
63       {.derive = c0},
64       {.derive = c1},
65   };
66
67   submit(plugin_instance, type, type_instance, values,
68          STATIC_ARRAY_SIZE(values));
69 } /* void submit_one */
70
71 static void submit_one(const char *plugin_instance, const char *type,
72                        const char *type_instance, value_t value) {
73   submit(plugin_instance, type, type_instance, &value, 1);
74 } /* void submit_one */
75
76 static int vmem_config(const char *key, const char *value) {
77   if (strcasecmp("Verbose", key) == 0) {
78     if (IS_TRUE(value))
79       verbose_output = 1;
80     else
81       verbose_output = 0;
82   } else {
83     return -1;
84   }
85
86   return 0;
87 } /* int vmem_config */
88
89 static int vmem_read(void) {
90 #if KERNEL_LINUX
91   derive_t pgpgin = 0;
92   derive_t pgpgout = 0;
93   int pgpgvalid = 0;
94
95   derive_t pswpin = 0;
96   derive_t pswpout = 0;
97   int pswpvalid = 0;
98
99   derive_t pgfault = 0;
100   derive_t pgmajfault = 0;
101   int pgfaultvalid = 0;
102
103   FILE *fh;
104   char buffer[1024];
105
106   fh = fopen("/proc/vmstat", "r");
107   if (fh == NULL) {
108     ERROR("vmem plugin: fopen (/proc/vmstat) failed: %s", STRERRNO);
109     return -1;
110   }
111
112   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
113     char *fields[4];
114     int fields_num;
115     char *key;
116     char *endptr;
117     derive_t counter;
118     gauge_t gauge;
119
120     fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
121     if (fields_num != 2)
122       continue;
123
124     key = fields[0];
125
126     endptr = NULL;
127     counter = strtoll(fields[1], &endptr, 10);
128     if (fields[1] == endptr)
129       continue;
130
131     endptr = NULL;
132     gauge = strtod(fields[1], &endptr);
133     if (fields[1] == endptr)
134       continue;
135
136     /*
137      * Number of pages
138      *
139      * The total number of {inst} pages, e. g dirty pages.
140      */
141     if (strncmp("nr_", key, strlen("nr_")) == 0) {
142       char *inst = key + strlen("nr_");
143       if (strcmp(inst, "dirtied") == 0 || strcmp(inst, "written") == 0) {
144         value_t value = {.derive = counter};
145         submit_one(NULL, "vmpage_action", inst, value);
146       } else {
147         value_t value = {.gauge = gauge};
148         submit_one(NULL, "vmpage_number", inst, value);
149       }
150     }
151
152     /*
153      * Page in and page outs. For memory and swap.
154      */
155     else if (strcmp("pgpgin", key) == 0) {
156       pgpgin = counter;
157       pgpgvalid |= 0x01;
158     } else if (strcmp("pgpgout", key) == 0) {
159       pgpgout = counter;
160       pgpgvalid |= 0x02;
161     } else if (strcmp("pswpin", key) == 0) {
162       pswpin = counter;
163       pswpvalid |= 0x01;
164     } else if (strcmp("pswpout", key) == 0) {
165       pswpout = counter;
166       pswpvalid |= 0x02;
167     }
168
169     /*
170      * Pagefaults
171      */
172     else if (strcmp("pgfault", key) == 0) {
173       pgfault = counter;
174       pgfaultvalid |= 0x01;
175     } else if (strcmp("pgmajfault", key) == 0) {
176       pgmajfault = counter;
177       pgfaultvalid |= 0x02;
178     }
179
180     /*
181      * Skip the other statistics if verbose output is disabled.
182      */
183     else if (verbose_output == 0)
184       continue;
185
186     /*
187      * Number of page allocations, refills, steals and scans. This is collected
188      * ``per zone'', i. e. for DMA, DMA32, normal and possibly highmem.
189      */
190     else if (strncmp("pgalloc_", key, strlen("pgalloc_")) == 0) {
191       char *inst = key + strlen("pgalloc_");
192       value_t value = {.derive = counter};
193       submit_one(inst, "vmpage_action", "alloc", value);
194     } else if (strncmp("pgrefill_", key, strlen("pgrefill_")) == 0) {
195       char *inst = key + strlen("pgrefill_");
196       value_t value = {.derive = counter};
197       submit_one(inst, "vmpage_action", "refill", value);
198     } else if (strncmp("pgsteal_kswapd_", key, strlen("pgsteal_kswapd_")) ==
199                0) {
200       char *inst = key + strlen("pgsteal_kswapd_");
201       value_t value = {.derive = counter};
202       submit_one(inst, "vmpage_action", "steal_kswapd", value);
203     } else if (strncmp("pgsteal_direct_", key, strlen("pgsteal_direct_")) ==
204                0) {
205       char *inst = key + strlen("pgsteal_direct_");
206       value_t value = {.derive = counter};
207       submit_one(inst, "vmpage_action", "steal_direct", value);
208     }
209     /* For backwards compatibility (somewhen before 4.2.3) */
210     else if (strncmp("pgsteal_", key, strlen("pgsteal_")) == 0) {
211       char *inst = key + strlen("pgsteal_");
212       value_t value = {.derive = counter};
213       submit_one(inst, "vmpage_action", "steal", value);
214     } else if (strncmp("pgscan_kswapd_", key, strlen("pgscan_kswapd_")) == 0) {
215       char *inst = key + strlen("pgscan_kswapd_");
216       value_t value = {.derive = counter};
217       submit_one(inst, "vmpage_action", "scan_kswapd", value);
218     } else if (strncmp("pgscan_direct_", key, strlen("pgscan_direct_")) == 0) {
219       char *inst = key + strlen("pgscan_direct_");
220       value_t value = {.derive = counter};
221       submit_one(inst, "vmpage_action", "scan_direct", value);
222     }
223
224     /*
225      * Page action
226      *
227      * number of pages moved to the active or inactive lists and freed, i. e.
228      * removed from either list.
229      */
230     else if (strcmp("pgfree", key) == 0) {
231       value_t value = {.derive = counter};
232       submit_one(NULL, "vmpage_action", "free", value);
233     } else if (strcmp("pgactivate", key) == 0) {
234       value_t value = {.derive = counter};
235       submit_one(NULL, "vmpage_action", "activate", value);
236     } else if (strcmp("pgdeactivate", key) == 0) {
237       value_t value = {.derive = counter};
238       submit_one(NULL, "vmpage_action", "deactivate", value);
239     }
240   } /* while (fgets) */
241
242   fclose(fh);
243   fh = NULL;
244
245   if (pgfaultvalid == 0x03)
246     submit_two(NULL, "vmpage_faults", NULL, pgfault, pgmajfault);
247
248   if (pgpgvalid == 0x03)
249     submit_two(NULL, "vmpage_io", "memory", pgpgin, pgpgout);
250
251   if (pswpvalid == 0x03)
252     submit_two(NULL, "vmpage_io", "swap", pswpin, pswpout);
253 #endif /* KERNEL_LINUX */
254
255   return 0;
256 } /* int vmem_read */
257
258 void module_register(void) {
259   plugin_register_config("vmem", vmem_config, config_keys, config_keys_num);
260   plugin_register_read("vmem", vmem_read);
261 } /* void module_register */