hugepages plugin: a stats plugin for hugepages
[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 of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11  * of the Software, and to permit persons to whom the Software is furnished to do
12  * so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * 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  */
28
29 #include "collectd.h"
30 #include "common.h" /* auxiliary functions */
31 #include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <dirent.h>
36
37 static int huge_read (void);
38 static int huge_config_callback (const char *key, const char *val);
39
40 static const char PLUGIN_NAME[] = "hugepages";
41 static const char SYS_NODE[] = "/sys/devices/system/node";
42 static const char NODE[] = "node";
43 static const char HUGEPAGES_DIR[] = "hugepages";
44 static const char SYS_NODE_HUGEPAGES[] = "/sys/devices/system/node/%s/hugepages";
45 static const char SYS_MM_HUGEPAGES[] = "/sys/kernel/mm/hugepages";
46 static const char CONFIG_NAME[] = "hugepages";
47 static const char CFG_ENA_NUMA[] = "EnableNuma";
48 static const char CFG_ENA_MM[] = "EnableMM";
49
50 static const char *CONFIG_KEYS[] = {
51   CFG_ENA_NUMA,
52   CFG_ENA_MM,
53 };
54 static const size_t CONFIG_KEYS_NUM = sizeof(CONFIG_KEYS)/sizeof(*CONFIG_NAME);
55 static int g_config_ena_numa = 1;
56 static int g_config_ena_mm = 1;
57
58 static int huge_config_callback (const char *key, const char *val)
59 {
60   INFO("HugePages config key='%s', val='%s'", key, val);
61
62   if (0 == strcasecmp(key, CFG_ENA_NUMA)) {
63     g_config_ena_numa = IS_TRUE(val);
64     return 0;
65   }
66   if (0 == strcasecmp(key, CFG_ENA_MM)) {
67     g_config_ena_mm = IS_TRUE(val);
68     return 0;
69   }
70
71   return -1;
72 }
73
74 static void submit_one (const char *plug_inst, const char *type,
75   const char *type_instance, derive_t value)
76 {
77   value_t values[1];
78   value_list_t vl = VALUE_LIST_INIT;
79
80   values[0].derive = value;
81
82   vl.values = values;
83   vl.values_len = 1;
84   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
85   sstrncpy (vl.plugin, PLUGIN_NAME, sizeof (vl.plugin));
86   sstrncpy (vl.plugin_instance, plug_inst, sizeof (vl.plugin_instance));
87   sstrncpy (vl.type, type, sizeof (vl.type));
88
89   if (type_instance != NULL) {
90     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
91   }
92
93   DEBUG("submit_one pl_inst:%s, inst_type %s, type %s, val=%lu",
94       plug_inst, type_instance, type, value);
95
96   plugin_dispatch_values (&vl);
97 }
98
99 static int read_hugepage_entry(const char *path, const char* entry,
100     const char* plinst, const char* hpsize)
101 {
102   char path2[512];
103   long value = 0;
104   snprintf(path2, sizeof(path2), "%s/%s", path, entry);
105
106   FILE *fh = fopen(path2, "rt");
107   if (NULL == fh) {
108     ERROR("Cannot open %s", path2);
109     return -1;
110   }
111
112   if (fscanf(fh, "%ld", &value) !=1) {
113     ERROR("Cannot parse file %s", path2);
114     fclose(fh);
115     return -1;
116   }
117
118   submit_one (plinst, entry, hpsize, value);
119
120   fclose(fh);
121   return 0;
122 }
123
124 static int read_syshugepage_dir(const char* path, const char* dirhpsize,
125     const char* node)
126 {
127   DIR       *dir = NULL;
128   struct dirent *entry = NULL;
129   struct dirent *result = NULL;
130   size_t name_max = 0;
131   size_t len = 0;
132
133   dir = opendir(path);
134   if (NULL == dir) {
135     ERROR("Cannot open directory %s", path);
136     return -1;
137   }
138
139   name_max = pathconf(path, _PC_NAME_MAX);
140   if (name_max == -1) {    /* Limit not defined, or error */
141     name_max = 255;     /* Take a guess */
142   }
143
144   len = offsetof(struct dirent, d_name) + name_max + 1;
145   entry = malloc(len);
146   if (entry == NULL) {
147     ERROR("Malloc returned NULL");
148     return -1;
149   }
150
151   while (0 == readdir_r(dir, entry, &result)) {
152     if (NULL == result) {
153       /* end of dir */
154       break;
155     }
156     if (result->d_name[0] == '.') {
157       /* not interesting "." and ".." */
158       continue;
159     }
160
161     read_hugepage_entry(path, result->d_name, node, dirhpsize);
162   }
163
164   free(entry);
165   closedir(dir);
166
167
168   return 0;
169 }
170
171 static int read_syshugepages(const char* path, const char* node)
172 {
173   DIR       *dir = NULL;
174   struct dirent *entry = NULL;
175   struct dirent *result = NULL;
176   size_t name_max = 0;
177   size_t len = 0;
178   char path2[255];
179
180   dir = opendir(path);
181   if (NULL == dir) {
182     ERROR("Cannot open directory %s", path);
183     return -1;
184   }
185
186   name_max = pathconf(path, _PC_NAME_MAX);
187   if (name_max == -1) {    /* Limit not defined, or error */
188     name_max = 255;     /* Take a guess */
189   }
190   len = offsetof(struct dirent, d_name) + name_max + 1;
191   entry = malloc(len);
192   if (entry == NULL) {
193     ERROR("Malloc returned NULL");
194     return -1;
195   }
196
197   while (0 == readdir_r(dir, entry, &result)) {
198     /* read "hugepages-XXXXXkB" entries */
199     if (NULL == result) {
200       /* end of dir */
201       break;
202     }
203
204     if (strncmp(result->d_name, HUGEPAGES_DIR, sizeof(HUGEPAGES_DIR)-1)) {
205       /* not node dir */
206       continue;
207     }
208
209     /* /sys/devices/system/node/node?/hugepages/ */
210     snprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
211     read_syshugepage_dir(path2, result->d_name, node);
212   }
213
214   free(entry);
215   closedir(dir);
216
217   return 0;
218 }
219
220 static int read_nodes(void)
221 {
222   DIR       *dir = NULL;
223   struct dirent *entry = NULL;
224   struct dirent *result = NULL;
225   size_t name_max = 0;
226   size_t len = 0;
227   char path[255];
228
229   dir = opendir(SYS_NODE);
230   if (NULL == dir) {
231     ERROR("Cannot open directory %s", SYS_NODE);
232     return -1;
233   }
234
235   name_max = pathconf(SYS_NODE, _PC_NAME_MAX);
236   if (name_max == -1) {    /* Limit not defined, or error */
237     name_max = 255;     /* Take a guess */
238   }
239   len = offsetof(struct dirent, d_name) + name_max + 1;
240   entry = malloc(len);
241   if (entry == NULL) {
242     ERROR("Malloc returned NULL");
243     return -1;
244   }
245
246   while (0 == readdir_r(dir, entry, &result)) {
247     if (NULL == result) {
248       /* end of dir */
249       break;
250     }
251
252     if (strncmp(result->d_name, NODE, sizeof(NODE)-1)) {
253       /* not node dir */
254       continue;
255     }
256
257     snprintf(path, sizeof(path), SYS_NODE_HUGEPAGES, result->d_name);
258     read_syshugepages(path, result->d_name);
259   }
260
261   free(entry);
262   closedir(dir);
263
264   return 0;
265 }
266
267
268 static int huge_read (void)
269 {
270   if (g_config_ena_mm) {
271     read_syshugepages(SYS_MM_HUGEPAGES, "mm");
272   }
273   if (g_config_ena_numa) {
274     read_nodes();
275   }
276
277   return 0;
278 }
279
280 void module_register (void)
281 {
282         plugin_register_config(CONFIG_NAME, huge_config_callback, CONFIG_KEYS,
283                          CONFIG_KEYS_NUM);
284   plugin_register_read (PLUGIN_NAME, huge_read);
285 }
286