summaryrefslogtreecommitdiffstats
path: root/src/collectors/apps.plugin/apps_proc_pid_limits.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/collectors/apps.plugin/apps_proc_pid_limits.c')
-rw-r--r--src/collectors/apps.plugin/apps_proc_pid_limits.c151
1 files changed, 151 insertions, 0 deletions
diff --git a/src/collectors/apps.plugin/apps_proc_pid_limits.c b/src/collectors/apps.plugin/apps_proc_pid_limits.c
new file mode 100644
index 000000000..a1e15f63c
--- /dev/null
+++ b/src/collectors/apps.plugin/apps_proc_pid_limits.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "apps_plugin.h"
+
+// ----------------------------------------------------------------------------
+
+#define MAX_PROC_PID_LIMITS 8192
+#define PROC_PID_LIMITS_MAX_OPEN_FILES_KEY "\nMax open files "
+
+static inline kernel_uint_t get_proc_pid_limits_limit(char *buf, const char *key, size_t key_len, kernel_uint_t def) {
+ char *line = strstr(buf, key);
+ if(!line)
+ return def;
+
+ char *v = &line[key_len];
+ while(isspace(*v)) v++;
+
+ if(strcmp(v, "unlimited") == 0)
+ return 0;
+
+ return str2ull(v, NULL);
+}
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+int read_proc_pid_limits_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
+ return false;
+}
+#endif
+
+#if !defined(__FreeBSD__) && !defined(__APPLE__)
+static inline bool read_proc_pid_limits_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
+ static char proc_pid_limits_buffer[MAX_PROC_PID_LIMITS + 1];
+ bool ret = false;
+ bool read_limits = false;
+
+ errno = 0;
+ proc_pid_limits_buffer[0] = '\0';
+
+ kernel_uint_t all_fds = pid_openfds_sum(p);
+ if(all_fds < p->limits.max_open_files / 2 && p->io_collected_usec > p->last_limits_collected_usec && p->io_collected_usec - p->last_limits_collected_usec <= 60 * USEC_PER_SEC) {
+ // too frequent, we want to collect limits once per minute
+ ret = true;
+ goto cleanup;
+ }
+
+ if(unlikely(!p->limits_filename)) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/limits", netdata_configured_host_prefix, p->pid);
+ p->limits_filename = strdupz(filename);
+ }
+
+ int fd = open(p->limits_filename, procfile_open_flags, 0666);
+ if(unlikely(fd == -1)) goto cleanup;
+
+ ssize_t bytes = read(fd, proc_pid_limits_buffer, MAX_PROC_PID_LIMITS);
+ close(fd);
+
+ if(bytes <= 0)
+ goto cleanup;
+
+ // make it '\0' terminated
+ if(bytes < MAX_PROC_PID_LIMITS)
+ proc_pid_limits_buffer[bytes] = '\0';
+ else
+ proc_pid_limits_buffer[MAX_PROC_PID_LIMITS - 1] = '\0';
+
+ p->limits.max_open_files = get_proc_pid_limits_limit(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY, sizeof(PROC_PID_LIMITS_MAX_OPEN_FILES_KEY) - 1, 0);
+ if(p->limits.max_open_files == 1) {
+ // it seems a bug in the kernel or something similar
+ // it sets max open files to 1 but the number of files
+ // the process has open are more than 1...
+ // https://github.com/netdata/netdata/issues/15443
+ p->limits.max_open_files = 0;
+ ret = true;
+ goto cleanup;
+ }
+
+ p->last_limits_collected_usec = p->io_collected_usec;
+ read_limits = true;
+
+ ret = true;
+
+cleanup:
+ if(p->limits.max_open_files)
+ p->openfds_limits_percent = (NETDATA_DOUBLE)all_fds * 100.0 / (NETDATA_DOUBLE)p->limits.max_open_files;
+ else
+ p->openfds_limits_percent = 0.0;
+
+ if(p->openfds_limits_percent > 100.0) {
+ if(!(p->log_thrown & PID_LOG_LIMITS_DETAIL)) {
+ char *line;
+
+ if(!read_limits) {
+ proc_pid_limits_buffer[0] = '\0';
+ line = "NOT READ";
+ }
+ else {
+ line = strstr(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY);
+ if (line) {
+ line++; // skip the initial newline
+
+ char *end = strchr(line, '\n');
+ if (end)
+ *end = '\0';
+ }
+ }
+
+ netdata_log_info(
+ "FDS_LIMITS: PID %d (%s) is using "
+ "%0.2f %% of its fds limits, "
+ "open fds = %"PRIu64 "("
+ "files = %"PRIu64 ", "
+ "pipes = %"PRIu64 ", "
+ "sockets = %"PRIu64", "
+ "inotifies = %"PRIu64", "
+ "eventfds = %"PRIu64", "
+ "timerfds = %"PRIu64", "
+ "signalfds = %"PRIu64", "
+ "eventpolls = %"PRIu64" "
+ "other = %"PRIu64" "
+ "), open fds limit = %"PRIu64", "
+ "%s, "
+ "original line [%s]",
+ p->pid, p->comm, p->openfds_limits_percent, all_fds,
+ p->openfds.files,
+ p->openfds.pipes,
+ p->openfds.sockets,
+ p->openfds.inotifies,
+ p->openfds.eventfds,
+ p->openfds.timerfds,
+ p->openfds.signalfds,
+ p->openfds.eventpolls,
+ p->openfds.other,
+ p->limits.max_open_files,
+ read_limits ? "and we have read the limits AFTER counting the fds"
+ : "but we have read the limits BEFORE counting the fds",
+ line);
+
+ p->log_thrown |= PID_LOG_LIMITS_DETAIL;
+ }
+ }
+ else
+ p->log_thrown &= ~PID_LOG_LIMITS_DETAIL;
+
+ return ret;
+}
+#endif // !__FreeBSD__ !__APPLE__
+
+int read_proc_pid_limits(struct pid_stat *p, void *ptr) {
+ return read_proc_pid_limits_per_os(p, ptr) ? 1 : 0;
+}