]> arthur.barton.de Git - netdata.git/commitdiff
added IPC semaphores charts
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 12 Nov 2016 23:49:09 +0000 (01:49 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 12 Nov 2016 23:49:09 +0000 (01:49 +0200)
CMakeLists.txt
src/Makefile.am
src/common.h
src/ipc.c [new file with mode: 0644]
src/ipc.h [new file with mode: 0644]
src/plugin_proc.c

index 9c4aa760729de421d1a1887ff4a66ffbd94b8a9b..fc9252aa411fc4ac81047c4c95ce1225be53eac0 100755 (executable)
@@ -26,6 +26,8 @@ set(NETDATA_SOURCE_FILES
         src/global_statistics.h
         src/health.c
         src/health.h
+        src/ipc.c
+        src/ipc.h
         src/log.c
         src/log.h
         src/main.c
index 86b9a9fe4dc9294958d2e7491bce15405c54e10b..32623f436d0650505a3e5e0331b65c2c79cb5949 100644 (file)
@@ -35,6 +35,7 @@ netdata_SOURCES = \
        eval.c eval.h \
        global_statistics.c global_statistics.h \
        health.c health.h \
+       ipc.c ipc.h \
        log.c log.h \
        main.c main.h \
        plugin_checks.c plugin_checks.h \
index dfb3c6085611529eb40186544b2027842f4fced6..58a6dbb8a5a035bfd1c06c4504e7c062bd32d898 100644 (file)
 #include "main.h"
 #include "unit_test.h"
 
+#include "ipc.h"
+
 #ifdef abs
 #undef abs
 #endif
diff --git a/src/ipc.c b/src/ipc.c
new file mode 100644 (file)
index 0000000..7e74bd5
--- /dev/null
+++ b/src/ipc.c
@@ -0,0 +1,236 @@
+#include "common.h"
+
+#include <sys/sem.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+
+#ifndef SEMVMX
+#define SEMVMX  32767  /* <= 32767 semaphore maximum value */
+#endif
+
+/* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */
+#ifndef IPC_INFO
+#define IPC_INFO   3
+#endif
+
+struct ipc_limits {
+    uint64_t        shmmni;     /* max number of segments */
+    uint64_t        shmmax;     /* max segment size */
+    uint64_t        shmall;     /* max total shared memory */
+    uint64_t        shmmin;     /* min segment size */
+
+    int             semmni;     /* max number of arrays */
+    int             semmsl;     /* max semaphores per array */
+    int             semmns;     /* max semaphores system wide */
+    int             semopm;     /* max ops per semop call */
+    unsigned int    semvmx;     /* semaphore max value (constant) */
+
+    int             msgmni;     /* max queues system wide */
+    size_t          msgmax;     /* max size of message */
+    int             msgmnb;     /* default max size of queue */
+};
+
+struct ipc_status {
+    int             semusz;     /* current number of arrays */
+    int             semaem;     /* current semaphores system wide */
+};
+
+/*
+ *  The last arg of semctl is a union semun, but where is it defined? X/OPEN
+ *  tells us to define it ourselves, but until recently Linux include files
+ *  would also define it.
+ */
+#ifndef HAVE_UNION_SEMUN
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+    int val;
+    struct semid_ds *buf;
+    unsigned short int *array;
+    struct seminfo *__buf;
+};
+#endif
+
+static inline int ipc_sem_get_limits(struct ipc_limits *lim) {
+    static procfile *ff = NULL;
+    static int error_shown = 0;
+    static char filename[FILENAME_MAX + 1] = "";
+
+    if(unlikely(!filename[0]))
+        snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/sem", global_host_prefix);
+
+    if(unlikely(!ff)) {
+        ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
+        if(unlikely(!ff)) {
+            if(unlikely(!error_shown)) {
+                error("IPC: Cannot open file '%s'.", filename);
+                error_shown = 1;
+            }
+            goto ipc;
+        }
+    }
+
+    ff = procfile_readall(ff);
+    if(unlikely(!ff)) {
+        if(unlikely(!error_shown)) {
+            error("IPC: Cannot read file '%s'.", filename);
+            error_shown = 1;
+        }
+        goto ipc;
+    }
+
+    if(procfile_lines(ff) >= 1 && procfile_linewords(ff, 0) >= 4) {
+        lim->semvmx = SEMVMX;
+        lim->semmsl = atoi(procfile_lineword(ff, 0, 0));
+        lim->semmns = atoi(procfile_lineword(ff, 0, 1));
+        lim->semopm = atoi(procfile_lineword(ff, 0, 2));
+        lim->semmni = atoi(procfile_lineword(ff, 0, 3));
+        return 0;
+    }
+    else {
+        if(unlikely(!error_shown)) {
+            error("IPC: Invalid content in file '%s'.", filename);
+            error_shown = 1;
+        }
+        goto ipc;
+    }
+
+ipc:
+    // cannot do it from the file
+    // query IPC
+    {
+        struct seminfo seminfo = {.semmni = 0};
+        union semun arg = {.array = (ushort *) &seminfo};
+
+        if(unlikely(semctl(0, 0, IPC_INFO, arg) < 0)) {
+            error("IPC: Failed to read '%s' and request IPC_INFO with semctl().", filename);
+            goto error;
+        }
+
+        lim->semvmx = SEMVMX;
+        lim->semmni = seminfo.semmni;
+        lim->semmsl = seminfo.semmsl;
+        lim->semmns = seminfo.semmns;
+        lim->semopm = seminfo.semopm;
+        return 0;
+    }
+
+error:
+    lim->semvmx = 0;
+    lim->semmni = 0;
+    lim->semmsl = 0;
+    lim->semmns = 0;
+    lim->semopm = 0;
+    return -1;
+}
+
+/*
+printf ("------ Semaphore Limits --------\n");
+printf ("max number of arrays = %d\n", limits.semmni);
+printf ("max semaphores per array = %d\n", limits.semmsl);
+printf ("max semaphores system wide = %d\n", limits.semmns);
+printf ("max ops per semop call = %d\n", limits.semopm);
+printf ("semaphore max value = %u\n", limits.semvmx);
+
+printf ("------ Semaphore Status --------\n");
+printf ("used arrays = %d\n", status.semusz);
+printf ("allocated semaphores = %d\n", status.semaem);
+*/
+
+static inline int ipc_sem_get_status(struct ipc_status *st) {
+    static int error_shown = 0;
+    struct seminfo seminfo;
+    union semun arg;
+
+    arg.array = (ushort *)  (void *) &seminfo;
+
+    if(unlikely(semctl (0, 0, SEM_INFO, arg) < 0)) {
+        /* kernel not configured for semaphores */
+        if(unlikely(!error_shown)) {
+            error("IPC: kernel is not configured for semaphores");
+            error_shown = 1;
+        }
+        st->semusz = 0;
+        st->semaem = 0;
+        return -1;
+    }
+
+    st->semusz = seminfo.semusz;
+    st->semaem = seminfo.semaem;
+    return 0;
+}
+
+int do_ipc(int update_every, unsigned long long dt) {
+    (void)dt;
+
+    static int initialized = 0, read_limits_next = 0;
+    static struct ipc_limits limits;
+    static struct ipc_status status;
+    static RRDVAR *arrays_max = NULL, *semaphores_max = NULL;
+    static RRDSET *semaphores = NULL, *arrays = NULL;
+
+    if(unlikely(!initialized)) {
+        // make sure it works
+        if(ipc_sem_get_limits(&limits) == -1) {
+            error("unable to fetch semaphore limits");
+            return 1;
+        }
+
+        // make sure it works
+        if(ipc_sem_get_status(&status) == -1) {
+            error("unable to fetch semaphore statistics");
+            return 1;
+        }
+
+        arrays_max     = rrdvar_custom_host_variable_create(&localhost, "ipc.semaphores.arrays.max");
+        semaphores_max = rrdvar_custom_host_variable_create(&localhost, "ipc.semaphores.max");
+
+        if(arrays_max)     rrdvar_custom_host_variable_set(arrays_max, limits.semmni);
+        if(semaphores_max) rrdvar_custom_host_variable_set(semaphores_max, limits.semmns);
+
+        // create the charts
+        semaphores = rrdset_find("system.ipc_semaphores");
+        if(!semaphores) {
+            semaphores = rrdset_create("system", "ipc_semaphores", NULL, "ipc semaphores", NULL, "IPC Semaphores", "semaphores", 1000, rrd_update_every, RRDSET_TYPE_AREA);
+            rrddim_add(semaphores, "semaphores", NULL, 1, 1, RRDDIM_ABSOLUTE);
+        }
+
+        arrays = rrdset_find("system.ipc_semaphore_arrays");
+        if(!arrays) {
+            arrays = rrdset_create("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL, "IPC Semaphore Arrays", "arrays", 1000, rrd_update_every, RRDSET_TYPE_AREA);
+            rrddim_add(arrays, "arrays", NULL, 1, 1, RRDDIM_ABSOLUTE);
+        }
+    }
+
+    if(unlikely(read_limits_next < 0)) {
+        if(unlikely(ipc_sem_get_limits(&limits) == -1)) {
+            error("Unable to fetch semaphore limits.");
+        }
+        else {
+            rrdvar_custom_host_variable_set(arrays_max, limits.semmni);
+            rrdvar_custom_host_variable_set(semaphores_max, limits.semmns);
+
+            arrays->red = limits.semmni;
+            semaphores->red = limits.semmns;
+
+            read_limits_next = 60 / update_every;
+        }
+    }
+    else
+        read_limits_next--;
+
+    if(unlikely(ipc_sem_get_status(&status) == -1)) {
+        error("Unable to get semaphore statistics");
+        return 0;
+    }
+
+    if(semaphores->counter_done) rrdset_next(semaphores);
+    rrddim_set(semaphores, "semaphores", status.semaem);
+    rrdset_done(semaphores);
+
+    if(arrays->counter_done) rrdset_next(arrays);
+    rrddim_set(arrays, "arrays", status.semusz);
+    rrdset_done(arrays);
+
+    return 0;
+}
diff --git a/src/ipc.h b/src/ipc.h
new file mode 100644 (file)
index 0000000..3762a69
--- /dev/null
+++ b/src/ipc.h
@@ -0,0 +1,7 @@
+#ifndef NETDATA_PLUGIN_IPC_H
+#define NETDATA_PLUGIN_IPC_H 1
+
+extern int do_ipc(int update_every, unsigned long long dt);
+
+#endif /* NETDATA_PLUGIN_IPC_H */
+
index a50a2251463bc5b49a73398292669348798cee79..1c461dabd301cb0e8952a2958d2923844b696726 100644 (file)
@@ -35,6 +35,7 @@ void *proc_main(void *ptr)
     int vdo_proc_softirqs           = !config_get_boolean("plugin:proc", "/proc/softirqs", 1);
     int vdo_proc_net_softnet_stat   = !config_get_boolean("plugin:proc", "/proc/net/softnet_stat", 1);
     int vdo_proc_loadavg            = !config_get_boolean("plugin:proc", "/proc/loadavg", 1);
+    int vdo_ipc                     = !config_get_boolean("plugin:proc", "ipc", 1);
     int vdo_sys_kernel_mm_ksm       = !config_get_boolean("plugin:proc", "/sys/kernel/mm/ksm", 1);
     int vdo_cpu_netdata             = !config_get_boolean("plugin:proc", "netdata server resources", 1);
 
@@ -57,6 +58,7 @@ void *proc_main(void *ptr)
     unsigned long long sutime_proc_softirqs = 0ULL;
     unsigned long long sutime_proc_net_softnet_stat = 0ULL;
     unsigned long long sutime_proc_loadavg = 0ULL;
+    unsigned long long sutime_ipc = 0ULL;
     unsigned long long sutime_sys_kernel_mm_ksm = 0ULL;
 
     // the next time we will run - aligned properly
@@ -95,6 +97,14 @@ void *proc_main(void *ptr)
         }
         if(unlikely(netdata_exit)) break;
 
+        if(!vdo_ipc) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_ipc().");
+            sunow = time_usec();
+            vdo_ipc = do_ipc(rrd_update_every, (sutime_ipc > 0)?sunow - sutime_ipc:0ULL);
+            sutime_ipc = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
         if(!vdo_proc_interrupts) {
             debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_interrupts().");
             sunow = time_usec();