hugepages plugin: Refactor the read_hugepage_entry() function.
[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 static const char g_cfg_rpt_numa[] = "ReportPerNodeHP";
36 static const char g_cfg_rpt_mm[] = "ReportRootHP";
37
38 static const char *g_config_keys[] = {
39     g_cfg_rpt_numa, g_cfg_rpt_mm,
40 };
41 static size_t g_config_keys_num = STATIC_ARRAY_SIZE(g_config_keys);
42 static int g_flag_rpt_numa = 1;
43 static int g_flag_rpt_mm = 1;
44
45 #define HP_HAVE_NR 0x01
46 #define HP_HAVE_SURPLUS 0x02
47 #define HP_HAVE_FREE 0x04
48 #define HP_HAVE_ALL 0x07
49
50 struct entry_info {
51   char *d_name;
52   const char *node;
53
54   gauge_t nr;
55   gauge_t surplus;
56   gauge_t free;
57   uint8_t flags;
58 };
59
60 static int huge_config_callback(const char *key, const char *val) {
61   DEBUG("%s: HugePages config key='%s', val='%s'", g_plugin_name, key, val);
62
63   if (strcasecmp(key, g_cfg_rpt_numa) == 0) {
64     g_flag_rpt_numa = IS_TRUE(val);
65     return 0;
66   }
67   if (strcasecmp(key, g_cfg_rpt_mm) == 0) {
68     g_flag_rpt_mm = IS_TRUE(val);
69     return 0;
70   }
71
72   return -1;
73 }
74
75 static void submit_hp(const char *plug_inst, const char *type_instance,
76                       gauge_t free_value, gauge_t used_value) {
77   value_list_t vl = VALUE_LIST_INIT;
78   value_t values[] = {
79     { .gauge = free_value },
80     { .gauge = used_value },
81   };
82
83   vl.values = values;
84   vl.values_len = STATIC_ARRAY_SIZE (values);
85   sstrncpy(vl.host, hostname_g, sizeof(vl.host));
86   sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
87   sstrncpy(vl.plugin_instance, plug_inst, sizeof(vl.plugin_instance));
88   sstrncpy(vl.type, "hugepages", sizeof(vl.type));
89
90   if (type_instance != NULL) {
91     sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
92   }
93
94   DEBUG("submit_hp pl_inst:%s, inst_type %s, free=%lf, used=%lf", plug_inst,
95         type_instance, free_value, used_value);
96
97   plugin_dispatch_values(&vl);
98 }
99
100 static int read_hugepage_entry(const char *path, const char *entry,
101                                void *e_info) {
102   char path2[PATH_MAX];
103   static const char partial_type_inst[] = "free_used";
104   char type_instance[PATH_MAX];
105   char *strin;
106   struct entry_info *info = e_info;
107   double value;
108
109   ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
110
111   FILE *fh = fopen(path2, "rt");
112   if (fh == NULL) {
113     ERROR("%s: cannot open %s", g_plugin_name, path2);
114     return -1;
115   }
116
117   if (fscanf(fh, "%lf", &value) != 1) {
118     ERROR("%s: cannot parse file %s", g_plugin_name, path2);
119     fclose(fh);
120     return -1;
121   }
122   fclose(fh);
123
124   if (strcmp(entry, "nr_hugepages") == 0) {
125     info->nr = value;
126     info->flags |= HP_HAVE_NR;
127   } else if (strcmp(entry, "surplus_hugepages") == 0) {
128     info->surplus = value;
129     info->flags |= HP_HAVE_SURPLUS;
130   } else if (strcmp(entry, "free_hugepages") == 0) {
131     info->free = value;
132     info->flags |= HP_HAVE_FREE;
133   }
134
135   if (info->flags != HP_HAVE_ALL) {
136     return 0;
137   }
138
139   /* Can now submit "used" and "free" values.
140    * 0x2D is the ASCII "-" character, after which the string
141    *   contains "<size>kB"
142    * The string passed as param 3 to submit_hp is of the format:
143    *   <type>-<partial_type_inst>-<size>kB
144    */
145   assert(strncmp(info->d_name, "hugepages-", strlen("hugepages-")) == 0);
146   strin = info->d_name += strlen("hugepages-");
147
148   ssnprintf(type_instance, sizeof(type_instance), "%s-%s", partial_type_inst,
149             strin);
150   submit_hp(info->node, type_instance, info->free,
151             (info->nr + info->surplus) - info->free);
152
153   /* Reset flags so subsequent calls don't submit again. */
154   info->flags = 0;
155   return 0;
156 }
157
158 static int read_syshugepages(const char *path, const char *node) {
159   static const char hugepages_dir[] = "hugepages-";
160   DIR *dir;
161   struct dirent *result;
162   char path2[PATH_MAX];
163
164   dir = opendir(path);
165   if (dir == NULL) {
166     ERROR("%s: cannot open directory %s", g_plugin_name, path);
167     return -1;
168   }
169
170   /* read "hugepages-XXXXXkB" entries */
171   while ((result = readdir(dir)) != NULL) {
172     if (strncmp(result->d_name, hugepages_dir, sizeof(hugepages_dir) - 1)) {
173       /* not node dir */
174       errno = 0;
175       continue;
176     }
177
178     /* /sys/devices/system/node/node?/hugepages/ */
179     ssnprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
180
181     walk_directory(path2, read_hugepage_entry,
182                    &(struct entry_info){
183                        .d_name = result->d_name, .node = node,
184                    },
185                    /* hidden = */ 0);
186     errno = 0;
187   }
188
189   /* Check if NULL return from readdir() was an error */
190   if (errno != 0) {
191     ERROR("%s: readdir failed", g_plugin_name);
192     closedir(dir);
193     return -1;
194   }
195
196   closedir(dir);
197   return 0;
198 }
199
200 static int read_nodes(void) {
201   static const char sys_node[] = "/sys/devices/system/node";
202   static const char node_string[] = "node";
203   static const char sys_node_hugepages[] =
204       "/sys/devices/system/node/%s/hugepages";
205   DIR *dir;
206   struct dirent *result;
207   char path[PATH_MAX];
208
209   dir = opendir(sys_node);
210   if (dir == NULL) {
211     ERROR("%s: cannot open directory %s", g_plugin_name, sys_node);
212     return -1;
213   }
214
215   while ((result = readdir(dir)) != NULL) {
216     if (strncmp(result->d_name, node_string, sizeof(node_string) - 1)) {
217       /* not node dir */
218       errno = 0;
219       continue;
220     }
221
222     ssnprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
223     read_syshugepages(path, result->d_name);
224     errno = 0;
225   }
226
227   /* Check if NULL return from readdir() was an error */
228   if (errno != 0) {
229     ERROR("%s: readdir failed", g_plugin_name);
230     closedir(dir);
231     return -1;
232   }
233
234   closedir(dir);
235   return 0;
236 }
237
238 static int huge_read(void) {
239   static const char sys_mm_hugepages[] = "/sys/kernel/mm/hugepages";
240
241   if (g_flag_rpt_mm) {
242     if (read_syshugepages(sys_mm_hugepages, "mm") != 0) {
243       return -1;
244     }
245   }
246   if (g_flag_rpt_numa) {
247     if (read_nodes() != 0) {
248       return -1;
249     }
250   }
251
252   return 0;
253 }
254
255 void module_register(void) {
256   plugin_register_config(g_plugin_name, huge_config_callback, g_config_keys,
257                          g_config_keys_num);
258   plugin_register_read(g_plugin_name, huge_read);
259 }