hugepages plugin: Implement the "ValuesPages", "ValuesBytes" and "ValuesPercentage...
[collectd.git] / src / hugepages.c
1 /*-
2  * collectd - src/hugepages.c
3  * MIT License
4  *
5  * Copyright(c) 2016 Intel Corporation. All rights reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  *
25  * Authors:
26  *   Jaroslav Safka <jaroslavx.safka@intel.com>
27  *   Kim-Marie Jones <kim-marie.jones@intel.com>
28  */
29
30 #include "collectd.h"
31 #include "common.h" /* auxiliary functions */
32 #include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
33
34 static const char g_plugin_name[] = "hugepages";
35
36 static _Bool g_flag_rpt_numa = 1;
37 static _Bool g_flag_rpt_mm = 1;
38
39 static _Bool values_pages = 1;
40 static _Bool values_bytes = 0;
41 static _Bool values_percent = 0;
42
43 #define HP_HAVE_NR 0x01
44 #define HP_HAVE_SURPLUS 0x02
45 #define HP_HAVE_FREE 0x04
46 #define HP_HAVE_ALL 0x07
47
48 struct entry_info {
49   char *d_name;
50   const char *node;
51   size_t page_size_kb;
52
53   gauge_t nr;
54   gauge_t surplus;
55   gauge_t free;
56   uint8_t flags;
57 };
58
59 static int hp_config(oconfig_item_t *ci) {
60   for (int i = 0; i < ci->children_num; i++) {
61     oconfig_item_t *child = ci->children + i;
62     if (strcasecmp("ReportPerNodeHP", child->key) == 0)
63       cf_util_get_boolean(child, &g_flag_rpt_numa);
64     else if (strcasecmp("ReportRootHP", child->key) == 0)
65       cf_util_get_boolean(child, &g_flag_rpt_mm);
66     else if (strcasecmp("ValuesPages", child->key) == 0)
67       cf_util_get_boolean(child, &values_pages);
68     else if (strcasecmp("ValuesBytes", child->key) == 0)
69       cf_util_get_boolean(child, &values_bytes);
70     else if (strcasecmp("ValuesPercentage", child->key) == 0)
71       cf_util_get_boolean(child, &values_percent);
72     else
73       ERROR("%s: Invalid configuration option: \"%s\".", g_plugin_name,
74             child->key);
75   }
76
77   return (0);
78 }
79
80 static void submit_hp(const struct entry_info *info) {
81   value_list_t vl = VALUE_LIST_INIT;
82
83   vl.values = &(value_t) { .gauge = NAN };
84   vl.values_len = 1;
85
86   sstrncpy(vl.host, hostname_g, sizeof(vl.host));
87   sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
88   if (info->node) {
89     ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%zuKb",
90               info->node, info->page_size_kb);
91   } else {
92     ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%zuKb",
93               info->page_size_kb);
94   }
95
96   /* ensure all metrics have the same timestamp */
97   vl.time = cdtime();
98
99   gauge_t free = info->free;
100   gauge_t used = (info->nr + info->surplus) - info->free;
101
102   if (values_pages) {
103     sstrncpy(vl.type, "vmpage_number", sizeof(vl.type));
104     plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
105                                "free", free, "used", used, NULL);
106   }
107   if (values_bytes) {
108     gauge_t page_size = (gauge_t)(1024 * info->page_size_kb);
109     sstrncpy(vl.type, "memory", sizeof(vl.type));
110     plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
111                                "free", free * page_size, "used",
112                                used * page_size, NULL);
113   }
114   if (values_percent) {
115     sstrncpy(vl.type, "percent", sizeof(vl.type));
116     plugin_dispatch_multivalue(&vl, /* store_percentage = */ 1, DS_TYPE_GAUGE,
117                                "free", free, "used", used, NULL);
118   }
119 }
120
121 static int read_hugepage_entry(const char *path, const char *entry,
122                                void *e_info) {
123   char path2[PATH_MAX];
124   char type_instance[PATH_MAX];
125   struct entry_info *info = e_info;
126   double value;
127
128   ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
129
130   FILE *fh = fopen(path2, "rt");
131   if (fh == NULL) {
132     ERROR("%s: cannot open %s", g_plugin_name, path2);
133     return -1;
134   }
135
136   if (fscanf(fh, "%lf", &value) != 1) {
137     ERROR("%s: cannot parse file %s", g_plugin_name, path2);
138     fclose(fh);
139     return -1;
140   }
141   fclose(fh);
142
143   if (strcmp(entry, "nr_hugepages") == 0) {
144     info->nr = value;
145     info->flags |= HP_HAVE_NR;
146   } else if (strcmp(entry, "surplus_hugepages") == 0) {
147     info->surplus = value;
148     info->flags |= HP_HAVE_SURPLUS;
149   } else if (strcmp(entry, "free_hugepages") == 0) {
150     info->free = value;
151     info->flags |= HP_HAVE_FREE;
152   }
153
154   if (info->flags != HP_HAVE_ALL) {
155     return 0;
156   }
157
158   ssnprintf(type_instance, sizeof(type_instance), "free_used-%zukB",
159             info->page_size_kb);
160   submit_hp(info);
161
162   /* Reset flags so subsequent calls don't submit again. */
163   info->flags = 0;
164   return 0;
165 }
166
167 static int read_syshugepages(const char *path, const char *node) {
168   static const char hugepages_dir[] = "hugepages-";
169   DIR *dir;
170   struct dirent *result;
171   char path2[PATH_MAX];
172
173   dir = opendir(path);
174   if (dir == NULL) {
175     ERROR("%s: cannot open directory %s", g_plugin_name, path);
176     return -1;
177   }
178
179   /* read "hugepages-XXXXXkB" entries */
180   while ((result = readdir(dir)) != NULL) {
181     if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir) - 1)) {
182       /* not node dir */
183       errno = 0;
184       continue;
185     }
186
187     long page_size = strtol(result->d_name + strlen(hugepages_dir),
188                             /* endptr = */ NULL, /* base = */ 10);
189     if (errno != 0) {
190       char errbuf[1024];
191       ERROR("%s: failed to determine page size from directory name \"%s\": %s",
192             g_plugin_name, result->d_name,
193             sstrerror(errno, errbuf, sizeof(errbuf)));
194       continue;
195     }
196
197     /* /sys/devices/system/node/node?/hugepages/ */
198     ssnprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
199
200     walk_directory(path2, read_hugepage_entry,
201                    &(struct entry_info){
202                        .d_name = result->d_name,
203                        .node = node,
204                        .page_size_kb = (size_t)page_size,
205                    },
206                    /* hidden = */ 0);
207     errno = 0;
208   }
209
210   /* Check if NULL return from readdir() was an error */
211   if (errno != 0) {
212     ERROR("%s: readdir failed", g_plugin_name);
213     closedir(dir);
214     return -1;
215   }
216
217   closedir(dir);
218   return 0;
219 }
220
221 static int read_nodes(void) {
222   static const char sys_node[] = "/sys/devices/system/node";
223   static const char node_string[] = "node";
224   static const char sys_node_hugepages[] =
225       "/sys/devices/system/node/%s/hugepages";
226   DIR *dir;
227   struct dirent *result;
228   char path[PATH_MAX];
229
230   dir = opendir(sys_node);
231   if (dir == NULL) {
232     ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
233     return -1;
234   }
235
236   while ((result = readdir(dir)) != NULL) {
237     if (strncmp(result->d_name, node_string, sizeof(node_string) - 1)) {
238       /* not node dir */
239       errno = 0;
240       continue;
241     }
242
243     ssnprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
244     read_syshugepages(path, result->d_name);
245     errno = 0;
246   }
247
248   /* Check if NULL return from readdir() was an error */
249   if (errno != 0) {
250     ERROR("%s: readdir failed", g_plugin_name);
251     closedir(dir);
252     return -1;
253   }
254
255   closedir(dir);
256   return 0;
257 }
258
259 static int huge_read(void) {
260   static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
261
262   if (g_flag_rpt_mm) {
263     if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
264       return -1;
265     }
266   }
267   if (g_flag_rpt_numa) {
268     if (read_nodes() != 0) {
269       return -1;
270     }
271   }
272
273   return 0;
274 }
275
276 void module_register(void) {
277   plugin_register_complex_config(g_plugin_name, hp_config);
278   plugin_register_read(g_plugin_name, huge_read);
279 }