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