Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / hugepages.c
index 9fa535f..c5b0ecb 100644 (file)
  * Authors:
  *   Jaroslav Safka <jaroslavx.safka@intel.com>
  *   Kim-Marie Jones <kim-marie.jones@intel.com>
+ *   Florian Forster <octo at collectd.org>
  */
 
 #include "collectd.h"
+
 #include "common.h" /* auxiliary functions */
 #include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
 
 static const char g_plugin_name[] = "hugepages";
-static const char g_cfg_rpt_numa[] = "ReportPerNodeHP";
-static const char g_cfg_rpt_mm[] = "ReportRootHP";
 
-static const char *g_config_keys[] = {
-    g_cfg_rpt_numa, g_cfg_rpt_mm,
-};
-static size_t g_config_keys_num = STATIC_ARRAY_SIZE(g_config_keys);
-static int g_flag_rpt_numa = 1;
-static int g_flag_rpt_mm = 1;
+static _Bool g_flag_rpt_numa = 1;
+static _Bool g_flag_rpt_mm = 1;
+
+static _Bool g_values_pages = 1;
+static _Bool g_values_bytes = 0;
+static _Bool g_values_percent = 0;
 
 #define HP_HAVE_NR 0x01
 #define HP_HAVE_SURPLUS 0x02
@@ -50,6 +50,7 @@ static int g_flag_rpt_mm = 1;
 struct entry_info {
   char *d_name;
   const char *node;
+  size_t page_size_kb;
 
   gauge_t nr;
   gauge_t surplus;
@@ -57,56 +58,74 @@ struct entry_info {
   uint8_t flags;
 };
 
-static int huge_config_callback(const char *key, const char *val) {
-  DEBUG("%s: HugePages config key='%s', val='%s'", g_plugin_name, key, val);
-
-  if (strcasecmp(key, g_cfg_rpt_numa) == 0) {
-    g_flag_rpt_numa = IS_TRUE(val);
-    return 0;
-  }
-  if (strcasecmp(key, g_cfg_rpt_mm) == 0) {
-    g_flag_rpt_mm = IS_TRUE(val);
-    return 0;
+static int hp_config(oconfig_item_t *ci) {
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+    if (strcasecmp("ReportPerNodeHP", child->key) == 0)
+      cf_util_get_boolean(child, &g_flag_rpt_numa);
+    else if (strcasecmp("ReportRootHP", child->key) == 0)
+      cf_util_get_boolean(child, &g_flag_rpt_mm);
+    else if (strcasecmp("ValuesPages", child->key) == 0)
+      cf_util_get_boolean(child, &g_values_pages);
+    else if (strcasecmp("ValuesBytes", child->key) == 0)
+      cf_util_get_boolean(child, &g_values_bytes);
+    else if (strcasecmp("ValuesPercentage", child->key) == 0)
+      cf_util_get_boolean(child, &g_values_percent);
+    else
+      ERROR("%s: Invalid configuration option: \"%s\".", g_plugin_name,
+            child->key);
   }
 
-  return -1;
+  return 0;
 }
 
-static void submit_hp(const char *plug_inst, const char *type_instance,
-                      gauge_t free_value, gauge_t used_value) {
+static void submit_hp(const struct entry_info *info) {
   value_list_t vl = VALUE_LIST_INIT;
-  value_t values[] = {
-    { .gauge = free_value },
-    { .gauge = used_value },
-  };
-
-  vl.values = values;
-  vl.values_len = STATIC_ARRAY_SIZE (values);
-  sstrncpy(vl.host, hostname_g, sizeof(vl.host));
-  sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
-  sstrncpy(vl.plugin_instance, plug_inst, sizeof(vl.plugin_instance));
-  sstrncpy(vl.type, "hugepages", sizeof(vl.type));
 
-  if (type_instance != NULL) {
-    sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
+  vl.values = &(value_t){.gauge = NAN};
+  vl.values_len = 1;
+
+  sstrncpy(vl.plugin, g_plugin_name, sizeof(vl.plugin));
+  if (info->node) {
+    snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s-%zuKb",
+             info->node, info->page_size_kb);
+  } else {
+    snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%zuKb",
+             info->page_size_kb);
   }
 
-  DEBUG("submit_hp pl_inst:%s, inst_type %s, free=%lf, used=%lf", plug_inst,
-        type_instance, free_value, used_value);
+  /* ensure all metrics have the same timestamp */
+  vl.time = cdtime();
+
+  gauge_t free = info->free;
+  gauge_t used = (info->nr + info->surplus) - info->free;
 
-  plugin_dispatch_values(&vl);
+  if (g_values_pages) {
+    sstrncpy(vl.type, "vmpage_number", sizeof(vl.type));
+    plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
+                               "free", free, "used", used, NULL);
+  }
+  if (g_values_bytes) {
+    gauge_t page_size = (gauge_t)(1024 * info->page_size_kb);
+    sstrncpy(vl.type, "memory", sizeof(vl.type));
+    plugin_dispatch_multivalue(&vl, /* store_percentage = */ 0, DS_TYPE_GAUGE,
+                               "free", free * page_size, "used",
+                               used * page_size, NULL);
+  }
+  if (g_values_percent) {
+    sstrncpy(vl.type, "percent", sizeof(vl.type));
+    plugin_dispatch_multivalue(&vl, /* store_percentage = */ 1, DS_TYPE_GAUGE,
+                               "free", free, "used", used, NULL);
+  }
 }
 
 static int read_hugepage_entry(const char *path, const char *entry,
                                void *e_info) {
   char path2[PATH_MAX];
-  static const char partial_type_inst[] = "free_used";
-  char type_instance[PATH_MAX];
-  char *strin;
   struct entry_info *info = e_info;
   double value;
 
-  ssnprintf(path2, sizeof(path2), "%s/%s", path, entry);
+  snprintf(path2, sizeof(path2), "%s/%s", path, entry);
 
   FILE *fh = fopen(path2, "rt");
   if (fh == NULL) {
@@ -136,19 +155,7 @@ static int read_hugepage_entry(const char *path, const char *entry,
     return 0;
   }
 
-  /* Can now submit "used" and "free" values.
-   * 0x2D is the ASCII "-" character, after which the string
-   *   contains "<size>kB"
-   * The string passed as param 3 to submit_hp is of the format:
-   *   <type>-<partial_type_inst>-<size>kB
-   */
-  assert(strncmp(info->d_name, "hugepages-", strlen("hugepages-")) == 0);
-  strin = info->d_name += strlen("hugepages-");
-
-  ssnprintf(type_instance, sizeof(type_instance), "%s-%s", partial_type_inst,
-            strin);
-  submit_hp(info->node, type_instance, info->free,
-            (info->nr + info->surplus) - info->free);
+  submit_hp(info);
 
   /* Reset flags so subsequent calls don't submit again. */
   info->flags = 0;
@@ -175,12 +182,24 @@ static int read_syshugepages(const char *path, const char *node) {
       continue;
     }
 
+    long page_size = strtol(result->d_name + strlen(hugepages_dir),
+                            /* endptr = */ NULL, /* base = */ 10);
+    if (errno != 0) {
+      char errbuf[1024];
+      ERROR("%s: failed to determine page size from directory name \"%s\": %s",
+            g_plugin_name, result->d_name,
+            sstrerror(errno, errbuf, sizeof(errbuf)));
+      continue;
+    }
+
     /* /sys/devices/system/node/node?/hugepages/ */
-    ssnprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
+    snprintf(path2, sizeof(path2), "%s/%s", path, result->d_name);
 
     walk_directory(path2, read_hugepage_entry,
                    &(struct entry_info){
-                       .d_name = result->d_name, .node = node,
+                       .d_name = result->d_name,
+                       .node = node,
+                       .page_size_kb = (size_t)page_size,
                    },
                    /* hidden = */ 0);
     errno = 0;
@@ -219,7 +238,7 @@ static int read_nodes(void) {
       continue;
     }
 
-    ssnprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
+    snprintf(path, sizeof(path), sys_node_hugepages, result->d_name);
     read_syshugepages(path, result->d_name);
     errno = 0;
   }
@@ -253,7 +272,6 @@ static int huge_read(void) {
 }
 
 void module_register(void) {
-  plugin_register_config(g_plugin_name, huge_config_callback, g_config_keys,
-                         g_config_keys_num);
+  plugin_register_complex_config(g_plugin_name, hp_config);
   plugin_register_read(g_plugin_name, huge_read);
 }