collectd.conf(5): Document the memory plugin's Values{Absolute,Percentage} options.
[collectd.git] / src / memory.c
index b79a74b..a4b21da 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/memory.c
- * Copyright (C) 2005-2008  Florian octo Forster
+ * Copyright (C) 2005-2014  Florian octo Forster
  * Copyright (C) 2009       Simon Kuhnle
  * Copyright (C) 2009       Manuel Sanmartin
  *
@@ -84,12 +84,33 @@ static int pagesize;
 /* endif HAVE_LIBSTATGRAB */
 #elif HAVE_PERFSTAT
 static int pagesize;
-static perfstat_memory_total_t pmemory;
 /* endif HAVE_PERFSTAT */
 #else
 # error "No applicable input method."
 #endif
 
+static _Bool values_absolute = 1;
+static _Bool values_percentage = 0;
+
+static int memory_config (oconfig_item_t *ci) /* {{{ */
+{
+       int i;
+
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+               if (strcasecmp ("ValuesAbsolute", child->key) == 0)
+                       cf_util_get_boolean (child, &values_absolute);
+               else if (strcasecmp ("ValuesPercentage", child->key) == 0)
+                       cf_util_get_boolean (child, &values_percentage);
+               else
+                       ERROR ("memory plugin: Invalid configuration option: "
+                                       "\"%s\".", child->key);
+       }
+
+       return (0);
+} /* }}} int memory_config */
+
 static int memory_init (void)
 {
 #if HAVE_HOST_STATISTICS
@@ -134,21 +155,29 @@ static int memory_init (void)
        return (0);
 } /* int memory_init */
 
-static void memory_submit (const char *type_instance, gauge_t value)
+static void memory_submit (const char *type_instance, gauge_t value, gauge_t total)
 {
        value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].gauge = value;
-
        vl.values = values;
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "memory", sizeof (vl.plugin));
-       sstrncpy (vl.type, "memory", sizeof (vl.type));
        sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-       plugin_dispatch_values (&vl);
+       if (values_absolute)
+       {
+               values[0].gauge = value;
+               sstrncpy (vl.type, "memory", sizeof (vl.type));
+               plugin_dispatch_values (&vl);
+       }
+       if (values_percentage)
+       {
+               values[0].gauge = 100.0 * value / total;
+               sstrncpy (vl.type, "percent", sizeof (vl.type));
+               plugin_dispatch_values (&vl);
+       }
 }
 
 static int memory_read (void)
@@ -162,6 +191,7 @@ static int memory_read (void)
        gauge_t active;
        gauge_t inactive;
        gauge_t free;
+       gauge_t total;
 
        if (!port_host || !pagesize)
                return (-1);
@@ -199,11 +229,12 @@ static int memory_read (void)
        active   = (gauge_t) (((uint64_t) vm_data.active_count)   * ((uint64_t) pagesize));
        inactive = (gauge_t) (((uint64_t) vm_data.inactive_count) * ((uint64_t) pagesize));
        free     = (gauge_t) (((uint64_t) vm_data.free_count)     * ((uint64_t) pagesize));
+       total    = wired + active + inactive + free;
 
-       memory_submit ("wired",    wired);
-       memory_submit ("active",   active);
-       memory_submit ("inactive", inactive);
-       memory_submit ("free",     free);
+       memory_submit ("wired",    wired,    total);
+       memory_submit ("active",   active,   total);
+       memory_submit ("inactive", inactive, total);
+       memory_submit ("free",     free,     total);
 /* #endif HAVE_HOST_STATISTICS */
 
 #elif HAVE_SYSCTLBYNAME
@@ -228,6 +259,7 @@ static int memory_read (void)
                NULL
        };
        double sysctl_vals[8];
+       gauge_t total;
 
        int    i;
 
@@ -253,11 +285,12 @@ static int memory_read (void)
                if (!isnan (sysctl_vals[i]))
                        sysctl_vals[i] *= sysctl_vals[0];
 
-       memory_submit ("free",     sysctl_vals[2]);
-       memory_submit ("wired",    sysctl_vals[3]);
-       memory_submit ("active",   sysctl_vals[4]);
-       memory_submit ("inactive", sysctl_vals[5]);
-       memory_submit ("cache",    sysctl_vals[6]);
+       total = sysctl_vals[2] + sysctl_vals[3] + sysctl_vals[4] + sysctl_vals[5] + sysctl_vals[6];
+       memory_submit ("free",     sysctl_vals[2], total);
+       memory_submit ("wired",    sysctl_vals[3], total);
+       memory_submit ("active",   sysctl_vals[4], total);
+       memory_submit ("inactive", sysctl_vals[5], total);
+       memory_submit ("cache",    sysctl_vals[6], total);
 /* #endif HAVE_SYSCTLBYNAME */
 
 #elif KERNEL_LINUX
@@ -267,6 +300,7 @@ static int memory_read (void)
        char *fields[8];
        int numfields;
 
+       long long mem_total = 0;
        long long mem_used = 0;
        long long mem_buffered = 0;
        long long mem_cached = 0;
@@ -285,7 +319,7 @@ static int memory_read (void)
                long long *val = NULL;
 
                if (strncasecmp (buffer, "MemTotal:", 9) == 0)
-                       val = &mem_used;
+                       val = &mem_total;
                else if (strncasecmp (buffer, "MemFree:", 8) == 0)
                        val = &mem_free;
                else if (strncasecmp (buffer, "Buffers:", 8) == 0)
@@ -310,20 +344,29 @@ static int memory_read (void)
                                sstrerror (errno, errbuf, sizeof (errbuf)));
        }
 
-       if (mem_used >= (mem_free + mem_buffered + mem_cached))
-       {
-               mem_used -= mem_free + mem_buffered + mem_cached;
-               memory_submit ("used",     mem_used);
-               memory_submit ("buffered", mem_buffered);
-               memory_submit ("cached",   mem_cached);
-               memory_submit ("free",     mem_free);
-       }
+       if (mem_total < (mem_free + mem_buffered + mem_cached))
+               return (0);
+
+       mem_used = mem_total - (mem_free + mem_buffered + mem_cached);
+       memory_submit ("used",     (gauge_t) mem_used,     (gauge_t) mem_total);
+       memory_submit ("buffered", (gauge_t) mem_buffered, (gauge_t) mem_total);
+       memory_submit ("cached",   (gauge_t) mem_cached,   (gauge_t) mem_total);
+       memory_submit ("free",     (gauge_t) mem_free,     (gauge_t) mem_total);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
+        /* Most of the additions here were taken as-is from the k9toolkit from
+         * Brendan Gregg and are subject to change I guess */
        long long mem_used;
        long long mem_free;
        long long mem_lock;
+       long long mem_kern;
+       long long mem_unus;
+       long long mem_total;
+
+       long long pp_kernel;
+       long long physmem;
+       long long availrmem;
 
        if (ksp == NULL)
                return (-1);
@@ -331,25 +374,71 @@ static int memory_read (void)
        mem_used = get_kstat_value (ksp, "pagestotal");
        mem_free = get_kstat_value (ksp, "pagesfree");
        mem_lock = get_kstat_value (ksp, "pageslocked");
+       mem_kern = 0;
+       mem_unus = 0;
+
+       pp_kernel = get_kstat_value (ksp, "pp_kernel");
+       physmem = get_kstat_value (ksp, "physmem");
+       availrmem = get_kstat_value (ksp, "availrmem");
 
        if ((mem_used < 0LL) || (mem_free < 0LL) || (mem_lock < 0LL))
+       {
+               WARNING ("memory plugin: one of used, free or locked is negative.");
                return (-1);
+       }
+
+       mem_unus = physmem - mem_used;
+
        if (mem_used < (mem_free + mem_lock))
-               return (-1);
+       {
+               /* source: http://wesunsolve.net/bugid/id/4909199
+                * this seems to happen when swap space is small, e.g. 2G on a 32G system
+                * we will make some assumptions here
+                * educated solaris internals help welcome here */
+               DEBUG ("memory plugin: pages total is smaller than \"free\" "
+                               "+ \"locked\". This is probably due to small "
+                               "swap space");
+               mem_free = availrmem;
+               mem_used = 0;
+       }
+       else
+       {
+               mem_used -= mem_free + mem_lock;
+       }
+
+       /* mem_kern is accounted for in mem_lock */
+       if (pp_kernel < mem_lock)
+       {
+               mem_kern = pp_kernel;
+               mem_lock -= pp_kernel;
+       }
+       else
+       {
+               mem_kern = mem_lock;
+               mem_lock = 0;
+       }
 
-       mem_used -= mem_free + mem_lock;
        mem_used *= pagesize; /* If this overflows you have some serious */
        mem_free *= pagesize; /* memory.. Why not call me up and give me */
        mem_lock *= pagesize; /* some? ;) */
-
-       memory_submit ("used",   mem_used);
-       memory_submit ("free",   mem_free);
-       memory_submit ("locked", mem_lock);
+       mem_kern *= pagesize; /* it's 2011 RAM is cheap */
+       mem_unus *= pagesize;
+       mem_total = mem_used + mem_free + mem_lock + mem_kern + mem_unus;
+
+       memory_submit ("used",     (gauge_t) mem_used, (gauge_t) mem_total);
+       memory_submit ("free",     (gauge_t) mem_free, (gauge_t) mem_total);
+       memory_submit ("locked",   (gauge_t) mem_lock, (gauge_t) mem_total);
+       memory_submit ("kernel",   (gauge_t) mem_kern, (gauge_t) mem_total);
+       memory_submit ("unusable", (gauge_t) mem_unus, (gauge_t) mem_total);
 /* #endif HAVE_LIBKSTAT */
 
 #elif HAVE_SYSCTL
        int mib[] = {CTL_VM, VM_METER};
        struct vmtotal vmtotal;
+       gauge_t mem_active;
+       gauge_t mem_inactive;
+       gauge_t mem_free;
+       gauge_t mem_total;
        size_t size;
 
        memset (&vmtotal, 0, sizeof (vmtotal));
@@ -363,35 +452,56 @@ static int memory_read (void)
        }
 
        assert (pagesize > 0);
-       memory_submit ("active",   vmtotal.t_arm * pagesize);
-       memory_submit ("inactive", (vmtotal.t_rm - vmtotal.t_arm) * pagesize);
-       memory_submit ("free",     vmtotal.t_free * pagesize);
+       mem_active   = (gauge_t) (vmtotal.t_arm * pagesize);
+       mem_inactive = (gauge_t) ((vmtotal.t_rm - vmtotal.t_arm) * pagesize);
+       mem_free     = (gauge_t) (vmtotal.t_free * pagesize);
+       mem_total    = mem_active + mem_inactive + mem_free;
+
+       memory_submit ("active",   mem_active,   mem_total);
+       memory_submit ("inactive", mem_inactive, mem_total);
+       memory_submit ("free",     mem_free,     mem_total);
 /* #endif HAVE_SYSCTL */
 
 #elif HAVE_LIBSTATGRAB
        sg_mem_stats *ios;
+       gauge_t total;
 
-       if ((ios = sg_get_mem_stats ()) != NULL)
-       {
-               memory_submit ("used",   ios->used);
-               memory_submit ("cached", ios->cache);
-               memory_submit ("free",   ios->free);
-       }
+       ios = sg_get_mem_stats ();
+       if (ios == NULL)
+               return (-1);
+
+       total = (gauge_t) (ios->used + ios->cache + ios->free);
+       memory_submit ("used",   (gauge_t) ios->used,  total);
+       memory_submit ("cached", (gauge_t) ios->cache, total);
+       memory_submit ("free",   (gauge_t) ios->free,  total);
 /* #endif HAVE_LIBSTATGRAB */
 
 #elif HAVE_PERFSTAT
-       if (perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1) < 0)
+       perfstat_memory_total_t pmemory;
+
+       memset (&pmemory, 0, sizeof (pmemory));
+       if (perfstat_memory_total(NULL, &pmemory, sizeof(pmemory), 1) < 0)
        {
                char errbuf[1024];
                WARNING ("memory plugin: perfstat_memory_total failed: %s",
                        sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
-       memory_submit ("used",   pmemory.real_inuse * pagesize);
-       memory_submit ("free",   pmemory.real_free * pagesize);
-       memory_submit ("cached", pmemory.numperm * pagesize);
-       memory_submit ("system", pmemory.real_system * pagesize);
-       memory_submit ("user",   pmemory.real_process * pagesize);
+
+       /* Unfortunately, the AIX documentation is not very clear on how these
+        * numbers relate to one another. The only thing is states explcitly
+        * is:
+        *   real_total = real_process + real_free + numperm + real_system
+        *
+        * Another segmentation, which would be closer to the numbers reported
+        * by the "svmon" utility, would be:
+        *   real_total = real_free + real_inuse
+        *   real_inuse = "active" + real_pinned + numperm
+        */
+       memory_submit ("free",   pmemory.real_free    * pagesize, pmemory.real_total * pagesize);
+       memory_submit ("cached", pmemory.numperm      * pagesize, pmemory.real_total * pagesize);
+       memory_submit ("system", pmemory.real_system  * pagesize, pmemory.real_total * pagesize);
+       memory_submit ("user",   pmemory.real_process * pagesize, pmemory.real_total * pagesize);
 #endif /* HAVE_PERFSTAT */
 
        return (0);
@@ -399,6 +509,7 @@ static int memory_read (void)
 
 void module_register (void)
 {
+       plugin_register_complex_config ("memory", memory_config);
        plugin_register_init ("memory", memory_init);
        plugin_register_read ("memory", memory_read);
 } /* void module_register */