summaryrefslogtreecommitdiffstats
path: root/collectors/proc.plugin/ipc.c
diff options
context:
space:
mode:
Diffstat (limited to 'collectors/proc.plugin/ipc.c')
-rw-r--r--collectors/proc.plugin/ipc.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/collectors/proc.plugin/ipc.c b/collectors/proc.plugin/ipc.c
new file mode 100644
index 0000000..6c6bee5
--- /dev/null
+++ b/collectors/proc.plugin/ipc.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "plugin_proc.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", netdata_configured_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 = str2i(procfile_lineword(ff, 0, 0));
+ lim->semmns = str2i(procfile_lineword(ff, 0, 1));
+ lim->semopm = str2i(procfile_lineword(ff, 0, 2));
+ lim->semmni = str2i(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) {
+ 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 */
+ static int error_shown = 0;
+ 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, usec_t dt) {
+ (void)dt;
+
+ static int initialized = 0, read_limits_next = -1;
+ static struct ipc_limits limits;
+ static struct ipc_status status;
+ static RRDVAR *arrays_max = NULL, *semaphores_max = NULL;
+ static RRDSET *st_semaphores = NULL, *st_arrays = NULL;
+ static RRDDIM *rd_semaphores = NULL, *rd_arrays = NULL;
+
+ if(unlikely(!initialized)) {
+ initialized = 1;
+
+ // 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;
+ }
+
+ // create the charts
+ if(unlikely(!st_semaphores)) {
+ st_semaphores = rrdset_create_localhost(
+ "system"
+ , "ipc_semaphores"
+ , NULL
+ , "ipc semaphores"
+ , NULL
+ , "IPC Semaphores"
+ , "semaphores"
+ , PLUGIN_PROC_NAME
+ , "ipc"
+ , NETDATA_CHART_PRIO_SYSTEM_IPC_SEMAPHORES
+ , localhost->rrd_update_every
+ , RRDSET_TYPE_AREA
+ );
+ rd_semaphores = rrddim_add(st_semaphores, "semaphores", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ }
+
+ if(unlikely(!st_arrays)) {
+ st_arrays = rrdset_create_localhost(
+ "system"
+ , "ipc_semaphore_arrays"
+ , NULL
+ , "ipc semaphores"
+ , NULL
+ , "IPC Semaphore Arrays"
+ , "arrays"
+ , PLUGIN_PROC_NAME
+ , "ipc"
+ , NETDATA_CHART_PRIO_SYSTEM_IPC_SEM_ARRAYS
+ , localhost->rrd_update_every
+ , RRDSET_TYPE_AREA
+ );
+ rd_arrays = rrddim_add(st_arrays, "arrays", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ }
+
+ // variables
+ semaphores_max = rrdvar_custom_host_variable_create(localhost, "ipc_semaphores_max");
+ arrays_max = rrdvar_custom_host_variable_create(localhost, "ipc_semaphores_arrays_max");
+ }
+
+ if(unlikely(read_limits_next < 0)) {
+ if(unlikely(ipc_sem_get_limits(&limits) == -1)) {
+ error("Unable to fetch semaphore limits.");
+ }
+ else {
+ if(semaphores_max) rrdvar_custom_host_variable_set(localhost, semaphores_max, limits.semmns);
+ if(arrays_max) rrdvar_custom_host_variable_set(localhost, arrays_max, limits.semmni);
+
+ st_arrays->red = limits.semmni;
+ st_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(st_semaphores->counter_done) rrdset_next(st_semaphores);
+ rrddim_set_by_pointer(st_semaphores, rd_semaphores, status.semaem);
+ rrdset_done(st_semaphores);
+
+ if(st_arrays->counter_done) rrdset_next(st_arrays);
+ rrddim_set_by_pointer(st_arrays, rd_arrays, status.semusz);
+ rrdset_done(st_arrays);
+
+ return 0;
+}