summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorFederico Ceratto <federico.ceratto@gmail.com>2016-11-23 15:49:14 +0000
committerFederico Ceratto <federico.ceratto@gmail.com>2016-11-23 15:49:14 +0000
commit68141d9dac0c08e51d257feef16a79086dd8a2df (patch)
treef4a0f5d31ed2194b5991130754b297b9c8c076e6 /src
parentRelease v. 1.3.0+dfsg-1 to Unstable (diff)
parentNew upstream version 1.4.0+dfsg (diff)
downloadnetdata-68141d9dac0c08e51d257feef16a79086dd8a2df.tar.xz
netdata-68141d9dac0c08e51d257feef16a79086dd8a2df.zip
Merge tag 'upstream/1.4.0+dfsg'
Upstream version 1.4.0+dfsg
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.in21
-rw-r--r--src/appconfig.c10
-rw-r--r--src/apps_plugin.c244
-rw-r--r--src/common.c300
-rw-r--r--src/common.h74
-rw-r--r--src/daemon.c9
-rw-r--r--src/eval.c79
-rw-r--r--src/eval.h1
-rw-r--r--src/global_statistics.c10
-rw-r--r--src/health.c906
-rw-r--r--src/health.h152
-rw-r--r--src/log.c39
-rw-r--r--src/log.h4
-rw-r--r--src/main.c36
-rw-r--r--src/plugin_nfacct.c12
-rw-r--r--src/plugin_proc.c24
-rw-r--r--src/plugin_proc.h2
-rw-r--r--src/plugin_tc.c161
-rw-r--r--src/plugins_d.c26
-rw-r--r--src/popen.c7
-rw-r--r--src/proc_interrupts.c14
-rw-r--r--src/proc_meminfo.c14
-rw-r--r--src/proc_net_ip_vs_stats.c6
-rw-r--r--src/proc_net_netstat.c787
-rw-r--r--src/proc_net_rpc_nfs.c370
-rw-r--r--src/proc_net_rpc_nfsd.c248
-rw-r--r--src/proc_net_snmp.c725
-rw-r--r--src/proc_net_softnet_stat.c119
-rw-r--r--src/proc_net_stat_conntrack.c12
-rw-r--r--src/proc_net_stat_synproxy.c8
-rw-r--r--src/proc_softirqs.c14
-rw-r--r--src/proc_vmstat.c12
-rw-r--r--src/registry.c68
-rw-r--r--src/rrd.c230
-rw-r--r--src/rrd.h3
-rw-r--r--src/rrd2json.c21
-rw-r--r--src/sys_fs_cgroup.c129
-rw-r--r--src/unit_test.c189
-rw-r--r--src/web_buffer.c1
-rw-r--r--src/web_buffer.h16
-rw-r--r--src/web_buffer_svg.c43
-rw-r--r--src/web_client.c180
-rw-r--r--src/web_server.c32
44 files changed, 4269 insertions, 1091 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 8fa6d5bdf..86b9a9fe4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,9 +52,11 @@ netdata_SOURCES = \
proc_net_dev.c \
proc_net_ip_vs_stats.c \
proc_net_netstat.c \
+ proc_net_rpc_nfs.c \
proc_net_rpc_nfsd.c \
proc_net_snmp.c \
proc_net_snmp6.c \
+ proc_net_softnet_stat.c \
proc_net_stat_conntrack.c \
proc_net_stat_synproxy.c \
proc_stat.c \
diff --git a/src/Makefile.in b/src/Makefile.in
index 1a721a045..6645f40de 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -86,8 +86,13 @@ DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
$(top_srcdir)/depcomp $(dist_cache_DATA) $(dist_log_DATA) \
$(dist_registry_DATA) $(dist_varlib_DATA)
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \
- $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___atomic.m4 \
+ $(top_srcdir)/m4/ax_c__generic.m4 \
+ $(top_srcdir)/m4/ax_c_mallinfo.m4 \
+ $(top_srcdir)/m4/ax_c_mallopt.m4 \
+ $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/jemalloc.m4 \
+ $(top_srcdir)/m4/tcmalloc.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
@@ -113,8 +118,9 @@ am_netdata_OBJECTS = appconfig.$(OBJEXT) avl.$(OBJEXT) \
proc_interrupts.$(OBJEXT) proc_softirqs.$(OBJEXT) \
proc_loadavg.$(OBJEXT) proc_meminfo.$(OBJEXT) \
proc_net_dev.$(OBJEXT) proc_net_ip_vs_stats.$(OBJEXT) \
- proc_net_netstat.$(OBJEXT) proc_net_rpc_nfsd.$(OBJEXT) \
- proc_net_snmp.$(OBJEXT) proc_net_snmp6.$(OBJEXT) \
+ proc_net_netstat.$(OBJEXT) proc_net_rpc_nfs.$(OBJEXT) \
+ proc_net_rpc_nfsd.$(OBJEXT) proc_net_snmp.$(OBJEXT) \
+ proc_net_snmp6.$(OBJEXT) proc_net_softnet_stat.$(OBJEXT) \
proc_net_stat_conntrack.$(OBJEXT) \
proc_net_stat_synproxy.$(OBJEXT) proc_stat.$(OBJEXT) \
proc_self_mountinfo.$(OBJEXT) \
@@ -279,6 +285,7 @@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LIBS = @PTHREAD_LIBS@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
+SSE_CANDIDATE = @SSE_CANDIDATE@
STRIP = @STRIP@
UUID_CFLAGS = @UUID_CFLAGS@
UUID_LIBS = @UUID_LIBS@
@@ -311,6 +318,8 @@ datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
+has_jemalloc = @has_jemalloc@
+has_tcmalloc = @has_tcmalloc@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
@@ -398,9 +407,11 @@ netdata_SOURCES = \
proc_net_dev.c \
proc_net_ip_vs_stats.c \
proc_net_netstat.c \
+ proc_net_rpc_nfs.c \
proc_net_rpc_nfsd.c \
proc_net_snmp.c \
proc_net_snmp6.c \
+ proc_net_softnet_stat.c \
proc_net_stat_conntrack.c \
proc_net_stat_synproxy.c \
proc_stat.c \
@@ -596,9 +607,11 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_dev.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_ip_vs_stats.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_netstat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_rpc_nfs.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_rpc_nfsd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_snmp.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_snmp6.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_softnet_stat.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_stat_conntrack.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_net_stat_synproxy.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc_self_mountinfo.Po@am__quote@
diff --git a/src/appconfig.c b/src/appconfig.c
index 34fb6d7d3..947407484 100644
--- a/src/appconfig.c
+++ b/src/appconfig.c
@@ -464,9 +464,17 @@ void generate_config(BUFFER *wb, int only_changed)
switch(i) {
case 0:
buffer_strcat(wb,
- "# NetData Configuration\n"
+ "# netdata configuration\n"
+ "#\n"
+ "# You can download the latest version of this file, using:\n"
+ "#\n"
+ "# wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf\n"
+ "# or\n"
+ "# curl -o /etc/netdata/netdata.conf http://localhost:19999/netdata.conf\n"
+ "#\n"
"# You can uncomment and change any of the options below.\n"
"# The value shown in the commented settings, is the default value.\n"
+ "#\n"
"\n# global netdata configuration\n");
break;
diff --git a/src/apps_plugin.c b/src/apps_plugin.c
index ee400b72e..f22a575ba 100644
--- a/src/apps_plugin.c
+++ b/src/apps_plugin.c
@@ -13,8 +13,6 @@
// etc.
#define RATES_DETAIL 10000ULL
-int processors = 1;
-pid_t pid_max = 32768;
int debug = 0;
int update_every = 1;
@@ -22,7 +20,6 @@ unsigned long long global_iterations_counter = 1;
unsigned long long file_counter = 0;
int proc_pid_cmdline_is_needed = 0;
int include_exited_childs = 1;
-char *host_prefix = "";
char *config_dir = CONFIG_DIR;
pid_t *all_pids_sortlist = NULL;
@@ -42,58 +39,6 @@ void netdata_cleanup_and_exit(int ret) {
// ----------------------------------------------------------------------------
-// system functions
-// to retrieve settings of the system
-
-long get_system_cpus(void) {
- procfile *ff = NULL;
-
- int processors = 0;
-
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
-
- ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
- if(!ff) return 1;
-
- ff = procfile_readall(ff);
- if(!ff)
- return 1;
-
- unsigned int i;
- for(i = 0; i < procfile_lines(ff); i++) {
- if(!procfile_linewords(ff, i)) continue;
-
- if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
- }
- processors--;
- if(processors < 1) processors = 1;
-
- procfile_close(ff);
- return processors;
-}
-
-pid_t get_system_pid_max(void) {
- procfile *ff = NULL;
- pid_t mpid = 32768;
-
- char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", host_prefix);
- ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
- if(!ff) return mpid;
-
- ff = procfile_readall(ff);
- if(!ff)
- return mpid;
-
- mpid = (pid_t)atoi(procfile_lineword(ff, 0, 0));
- if(!mpid) mpid = 32768;
-
- procfile_close(ff);
- return mpid;
-}
-
-// ----------------------------------------------------------------------------
// target
// target is the structure that process data are aggregated
@@ -121,23 +66,23 @@ struct target {
unsigned long long cstime;
unsigned long long cgtime;
unsigned long long num_threads;
- unsigned long long rss;
+ // unsigned long long rss;
unsigned long long statm_size;
unsigned long long statm_resident;
unsigned long long statm_share;
- unsigned long long statm_text;
- unsigned long long statm_lib;
- unsigned long long statm_data;
- unsigned long long statm_dirty;
+ // unsigned long long statm_text;
+ // unsigned long long statm_lib;
+ // unsigned long long statm_data;
+ // unsigned long long statm_dirty;
unsigned long long io_logical_bytes_read;
unsigned long long io_logical_bytes_written;
- unsigned long long io_read_calls;
- unsigned long long io_write_calls;
+ // unsigned long long io_read_calls;
+ // unsigned long long io_write_calls;
unsigned long long io_storage_bytes_read;
unsigned long long io_storage_bytes_written;
- unsigned long long io_cancelled_write_bytes;
+ // unsigned long long io_cancelled_write_bytes;
int *fds;
unsigned long long openfiles;
@@ -440,7 +385,7 @@ struct pid_stat {
// int64_t itrealvalue;
// unsigned long long starttime;
// unsigned long long vsize;
- unsigned long long rss;
+ // unsigned long long rss;
// unsigned long long rsslim;
// unsigned long long starcode;
// unsigned long long endcode;
@@ -466,26 +411,26 @@ struct pid_stat {
unsigned long long statm_size;
unsigned long long statm_resident;
unsigned long long statm_share;
- unsigned long long statm_text;
- unsigned long long statm_lib;
- unsigned long long statm_data;
- unsigned long long statm_dirty;
+ // unsigned long long statm_text;
+ // unsigned long long statm_lib;
+ // unsigned long long statm_data;
+ // unsigned long long statm_dirty;
unsigned long long io_logical_bytes_read_raw;
unsigned long long io_logical_bytes_written_raw;
- unsigned long long io_read_calls_raw;
- unsigned long long io_write_calls_raw;
+ // unsigned long long io_read_calls_raw;
+ // unsigned long long io_write_calls_raw;
unsigned long long io_storage_bytes_read_raw;
unsigned long long io_storage_bytes_written_raw;
- unsigned long long io_cancelled_write_bytes_raw;
+ // unsigned long long io_cancelled_write_bytes_raw;
unsigned long long io_logical_bytes_read;
unsigned long long io_logical_bytes_written;
- unsigned long long io_read_calls;
- unsigned long long io_write_calls;
+ // unsigned long long io_read_calls;
+ // unsigned long long io_write_calls;
unsigned long long io_storage_bytes_read;
unsigned long long io_storage_bytes_written;
- unsigned long long io_cancelled_write_bytes;
+ // unsigned long long io_cancelled_write_bytes;
int *fds; // array of fds it uses
int fds_size; // the size of the fds array
@@ -573,10 +518,10 @@ void del_pid_entry(pid_t pid) {
// update pids from proc
int read_proc_pid_cmdline(struct pid_stat *p) {
-
+
if(unlikely(!p->cmdline_filename)) {
char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", host_prefix, p->pid);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", global_host_prefix, p->pid);
p->cmdline_filename = strdupz(filename);
}
@@ -629,7 +574,7 @@ int read_proc_pid_stat(struct pid_stat *p) {
if(unlikely(!p->stat_filename)) {
char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", host_prefix, p->pid);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", global_host_prefix, p->pid);
p->stat_filename = strdupz(filename);
}
@@ -700,7 +645,7 @@ int read_proc_pid_stat(struct pid_stat *p) {
// p->itrealvalue = strtoull(procfile_lineword(ff, 0, 20), NULL, 10);
// p->starttime = strtoull(procfile_lineword(ff, 0, 21), NULL, 10);
// p->vsize = strtoull(procfile_lineword(ff, 0, 22), NULL, 10);
- p->rss = strtoull(procfile_lineword(ff, 0, 23), NULL, 10);
+ // p->rss = strtoull(procfile_lineword(ff, 0, 23), NULL, 10);
// p->rsslim = strtoull(procfile_lineword(ff, 0, 24), NULL, 10);
// p->starcode = strtoull(procfile_lineword(ff, 0, 25), NULL, 10);
// p->endcode = strtoull(procfile_lineword(ff, 0, 26), NULL, 10);
@@ -737,7 +682,7 @@ int read_proc_pid_stat(struct pid_stat *p) {
}
if(unlikely(debug || (p->target && p->target->debug)))
- fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu, threads=%d\n", host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
+ fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu, threads=%d\n", global_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
if(unlikely(global_iterations_counter == 1)) {
p->minflt = 0;
@@ -766,7 +711,7 @@ cleanup:
p->cstime = 0;
p->cgtime = 0;
p->num_threads = 0;
- p->rss = 0;
+ // p->rss = 0;
return 0;
}
@@ -775,7 +720,7 @@ int read_proc_pid_statm(struct pid_stat *p) {
if(unlikely(!p->statm_filename)) {
char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", host_prefix, p->pid);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", global_host_prefix, p->pid);
p->statm_filename = strdupz(filename);
}
@@ -790,10 +735,10 @@ int read_proc_pid_statm(struct pid_stat *p) {
p->statm_size = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
p->statm_resident = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
p->statm_share = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
- p->statm_text = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
- p->statm_lib = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
- p->statm_data = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
- p->statm_dirty = strtoull(procfile_lineword(ff, 0, 6), NULL, 10);
+ // p->statm_text = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
+ // p->statm_lib = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
+ // p->statm_data = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
+ // p->statm_dirty = strtoull(procfile_lineword(ff, 0, 6), NULL, 10);
return 1;
@@ -801,10 +746,10 @@ cleanup:
p->statm_size = 0;
p->statm_resident = 0;
p->statm_share = 0;
- p->statm_text = 0;
- p->statm_lib = 0;
- p->statm_data = 0;
- p->statm_dirty = 0;
+ // p->statm_text = 0;
+ // p->statm_lib = 0;
+ // p->statm_data = 0;
+ // p->statm_dirty = 0;
return 0;
}
@@ -813,7 +758,7 @@ int read_proc_pid_io(struct pid_stat *p) {
if(unlikely(!p->io_filename)) {
char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", host_prefix, p->pid);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", global_host_prefix, p->pid);
p->io_filename = strdupz(filename);
}
@@ -839,13 +784,13 @@ int read_proc_pid_io(struct pid_stat *p) {
p->io_logical_bytes_written_raw = strtoull(procfile_lineword(ff, 1, 1), NULL, 10);
p->io_logical_bytes_written = (p->io_logical_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
- last = p->io_read_calls_raw;
- p->io_read_calls_raw = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
- p->io_read_calls = (p->io_read_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+ // last = p->io_read_calls_raw;
+ // p->io_read_calls_raw = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
+ // p->io_read_calls = (p->io_read_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
- last = p->io_write_calls_raw;
- p->io_write_calls_raw = strtoull(procfile_lineword(ff, 3, 1), NULL, 10);
- p->io_write_calls = (p->io_write_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+ // last = p->io_write_calls_raw;
+ // p->io_write_calls_raw = strtoull(procfile_lineword(ff, 3, 1), NULL, 10);
+ // p->io_write_calls = (p->io_write_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
last = p->io_storage_bytes_read_raw;
p->io_storage_bytes_read_raw = strtoull(procfile_lineword(ff, 4, 1), NULL, 10);
@@ -855,18 +800,18 @@ int read_proc_pid_io(struct pid_stat *p) {
p->io_storage_bytes_written_raw = strtoull(procfile_lineword(ff, 5, 1), NULL, 10);
p->io_storage_bytes_written = (p->io_storage_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
- last = p->io_cancelled_write_bytes_raw;
- p->io_cancelled_write_bytes_raw = strtoull(procfile_lineword(ff, 6, 1), NULL, 10);
- p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+ // last = p->io_cancelled_write_bytes_raw;
+ // p->io_cancelled_write_bytes_raw = strtoull(procfile_lineword(ff, 6, 1), NULL, 10);
+ // p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
if(unlikely(global_iterations_counter == 1)) {
p->io_logical_bytes_read = 0;
p->io_logical_bytes_written = 0;
- p->io_read_calls = 0;
- p->io_write_calls = 0;
+ // p->io_read_calls = 0;
+ // p->io_write_calls = 0;
p->io_storage_bytes_read = 0;
p->io_storage_bytes_written = 0;
- p->io_cancelled_write_bytes = 0;
+ // p->io_cancelled_write_bytes = 0;
}
return 1;
@@ -874,11 +819,11 @@ int read_proc_pid_io(struct pid_stat *p) {
cleanup:
p->io_logical_bytes_read = 0;
p->io_logical_bytes_written = 0;
- p->io_read_calls = 0;
- p->io_write_calls = 0;
+ // p->io_read_calls = 0;
+ // p->io_write_calls = 0;
p->io_storage_bytes_read = 0;
p->io_storage_bytes_written = 0;
- p->io_cancelled_write_bytes = 0;
+ // p->io_cancelled_write_bytes = 0;
return 0;
}
@@ -892,7 +837,7 @@ int read_proc_stat() {
static unsigned long long utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0, collected_usec = 0, last_collected_usec = 0;
if(unlikely(!ff)) {
- snprintfz(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/stat", global_host_prefix);
ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
if(unlikely(!ff)) goto cleanup;
}
@@ -1194,7 +1139,7 @@ int file_descriptor_find_or_add(const char *name)
int read_pid_file_descriptors(struct pid_stat *p) {
char dirname[FILENAME_MAX+1];
- snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", host_prefix, p->pid);
+ snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", global_host_prefix, p->pid);
DIR *fds = opendir(dirname);
if(fds) {
int c;
@@ -1232,7 +1177,7 @@ int read_pid_file_descriptors(struct pid_stat *p) {
if(p->fds[fdid] == 0) {
// we don't know this fd, get it
- sprintf(fdname, "%s/proc/%d/fd/%s", host_prefix, p->pid, de->d_name);
+ sprintf(fdname, "%s/proc/%d/fd/%s", global_host_prefix, p->pid, de->d_name);
ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
if(l == -1) {
if(debug || (p->target && p->target->debug)) {
@@ -1328,14 +1273,14 @@ void find_lost_child_debug(struct pid_stat *pe, unsigned long long lost, int typ
found++;
}
break;
-
+
case 2:
if(p->cmajflt > lost) {
fprintf(stderr, " > process %d (%s) could use the lost exited child majflt %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
found++;
}
break;
-
+
case 3:
if(p->cutime > lost) {
fprintf(stderr, " > process %d (%s) could use the lost exited child utime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
@@ -1364,11 +1309,11 @@ void find_lost_child_debug(struct pid_stat *pe, unsigned long long lost, int typ
case 1:
fprintf(stderr, " > cannot find any process to use the lost exited child minflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
break;
-
+
case 2:
fprintf(stderr, " > cannot find any process to use the lost exited child majflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
break;
-
+
case 3:
fprintf(stderr, " > cannot find any process to use the lost exited child utime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
break;
@@ -1583,19 +1528,19 @@ static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
p->log_thrown |= log;
switch(log) {
case PID_LOG_IO:
- error("Cannot process %s/proc/%d/io (command '%s')", host_prefix, p->pid, p->comm);
+ error("Cannot process %s/proc/%d/io (command '%s')", global_host_prefix, p->pid, p->comm);
break;
case PID_LOG_STATM:
- error("Cannot process %s/proc/%d/statm (command '%s')", host_prefix, p->pid, p->comm);
+ error("Cannot process %s/proc/%d/statm (command '%s')", global_host_prefix, p->pid, p->comm);
break;
case PID_LOG_CMDLINE:
- error("Cannot process %s/proc/%d/cmdline (command '%s')", host_prefix, p->pid, p->comm);
+ error("Cannot process %s/proc/%d/cmdline (command '%s')", global_host_prefix, p->pid, p->comm);
break;
case PID_LOG_FDS:
- error("Cannot process entries in %s/proc/%d/fd (command '%s')", host_prefix, p->pid, p->comm);
+ error("Cannot process entries in %s/proc/%d/fd (command '%s')", global_host_prefix, p->pid, p->comm);
break;
case PID_LOG_STAT:
@@ -1751,7 +1696,7 @@ int collect_data_for_all_processes_from_proc(void) {
char dirname[FILENAME_MAX + 1];
- snprintfz(dirname, FILENAME_MAX, "%s/proc", host_prefix);
+ snprintfz(dirname, FILENAME_MAX, "%s/proc", global_host_prefix);
DIR *dir = opendir(dirname);
if(!dir) return 0;
@@ -1957,24 +1902,24 @@ long zero_all_targets(struct target *root) {
w->cstime = 0;
w->cgtime = 0;
w->num_threads = 0;
- w->rss = 0;
+ // w->rss = 0;
w->processes = 0;
w->statm_size = 0;
w->statm_resident = 0;
w->statm_share = 0;
- w->statm_text = 0;
- w->statm_lib = 0;
- w->statm_data = 0;
- w->statm_dirty = 0;
+ // w->statm_text = 0;
+ // w->statm_lib = 0;
+ // w->statm_data = 0;
+ // w->statm_dirty = 0;
w->io_logical_bytes_read = 0;
w->io_logical_bytes_written = 0;
- w->io_read_calls = 0;
- w->io_write_calls = 0;
+ // w->io_read_calls = 0;
+ // w->io_write_calls = 0;
w->io_storage_bytes_read = 0;
w->io_storage_bytes_written = 0;
- w->io_cancelled_write_bytes = 0;
+ // w->io_cancelled_write_bytes = 0;
}
return count;
@@ -1999,23 +1944,23 @@ void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target
w->minflt += p->minflt;
w->majflt += p->majflt;
- w->rss += p->rss;
+ // w->rss += p->rss;
w->statm_size += p->statm_size;
w->statm_resident += p->statm_resident;
w->statm_share += p->statm_share;
- w->statm_text += p->statm_text;
- w->statm_lib += p->statm_lib;
- w->statm_data += p->statm_data;
- w->statm_dirty += p->statm_dirty;
+ // w->statm_text += p->statm_text;
+ // w->statm_lib += p->statm_lib;
+ // w->statm_data += p->statm_data;
+ // w->statm_dirty += p->statm_dirty;
w->io_logical_bytes_read += p->io_logical_bytes_read;
w->io_logical_bytes_written += p->io_logical_bytes_written;
- w->io_read_calls += p->io_read_calls;
- w->io_write_calls += p->io_write_calls;
+ // w->io_read_calls += p->io_read_calls;
+ // w->io_write_calls += p->io_write_calls;
w->io_storage_bytes_read += p->io_storage_bytes_read;
w->io_storage_bytes_written += p->io_storage_bytes_written;
- w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
+ // w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
w->processes++;
w->num_threads += p->num_threads;
@@ -2230,8 +2175,8 @@ unsigned long long send_resource_usage_to_netdata() {
cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
- bcopy(&now, &last, sizeof(struct timeval));
- bcopy(&me, &me_last, sizeof(struct rusage));
+ memmove(&last, &now, sizeof(struct timeval));
+ memmove(&me_last, &me, sizeof(struct rusage));
}
buffer_sprintf(output,
@@ -2488,6 +2433,13 @@ void send_collected_data_to_netdata(struct target *root, const char *type, unsig
}
send_END();
+ send_BEGIN(type, "vmem", usec);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ send_SET(w->name, w->statm_size);
+ }
+ send_END();
+
send_BEGIN(type, "minor_faults", usec);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
@@ -2584,7 +2536,13 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const
buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu %s\n", w->name, hz * RATES_DETAIL / 100, w->hidden ? "hidden" : "");
}
- buffer_sprintf(output, "CHART %s.mem '' '%s Dedicated Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
+ buffer_sprintf(output, "CHART %s.mem '' '%s Real Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
+ for (w = root; w ; w = w->next) {
+ if(unlikely(w->exposed))
+ buffer_sprintf(output, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
+ }
+
+ buffer_sprintf(output, "CHART %s.vmem '' '%s Virtual Memory Size' 'MB' mem %s.vmem stacked 20004 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
buffer_sprintf(output, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
@@ -2803,12 +2761,12 @@ int main(int argc, char **argv)
error_log_errors_per_period = 100;
error_log_throttle_period = 3600;
- host_prefix = getenv("NETDATA_HOST_PREFIX");
- if(host_prefix == NULL) {
+ global_host_prefix = getenv("NETDATA_HOST_PREFIX");
+ if(global_host_prefix == NULL) {
// info("NETDATA_HOST_PREFIX is not passed from netdata");
- host_prefix = "";
+ global_host_prefix = "";
}
- // else info("Found NETDATA_HOST_PREFIX='%s'", host_prefix);
+ // else info("Found NETDATA_HOST_PREFIX='%s'", global_host_prefix);
config_dir = getenv("NETDATA_CONFIG_DIR");
if(config_dir == NULL) {
@@ -2830,9 +2788,9 @@ int main(int argc, char **argv)
time_t started_t = time(NULL);
time_t current_t;
- get_HZ();
- pid_max = get_system_pid_max();
- processors = get_system_cpus();
+ get_system_HZ();
+ get_system_pid_max();
+ get_system_cpus();
parse_args(argc, argv);
diff --git a/src/common.c b/src/common.c
index 7d0fac9aa..e1925ff5e 100644
--- a/src/common.c
+++ b/src/common.c
@@ -14,31 +14,181 @@ volatile sig_atomic_t netdata_exit = 0;
// its lifetime), these can be used to override the default system allocation
// routines.
+#ifdef NETDATA_LOG_ALLOCATIONS
+static struct memory_statistics {
+ volatile size_t malloc_calls_made;
+ volatile size_t calloc_calls_made;
+ volatile size_t realloc_calls_made;
+ volatile size_t strdup_calls_made;
+ volatile size_t free_calls_made;
+ volatile size_t memory_calls_made;
+ volatile size_t allocated_memory;
+ volatile size_t mmapped_memory;
+} memory_statistics;
+
+static inline void print_allocations(const char *file, const char *function, const unsigned long line) {
+ static struct memory_statistics old = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ //if(unlikely(!(memory_statistics.memory_calls_made % 5))) {
+ fprintf(stderr, "(%04lu@%-10.10s:%-15.15s): Allocated %zu KB (+%zu B), mmapped %zu KB (+%zu B): malloc %zu (+%zu), calloc %zu (+%zu), realloc %zu (+%zu), strdup %zu (+%zu), free %zu (+%zu)\n",
+ line, file, function,
+ (memory_statistics.allocated_memory + 512) / 1024, memory_statistics.allocated_memory - old.allocated_memory,
+ (memory_statistics.mmapped_memory + 512) / 1024, memory_statistics.mmapped_memory - old.mmapped_memory,
+ memory_statistics.malloc_calls_made, memory_statistics.malloc_calls_made - old.malloc_calls_made,
+ memory_statistics.calloc_calls_made, memory_statistics.calloc_calls_made - old.calloc_calls_made,
+ memory_statistics.realloc_calls_made, memory_statistics.realloc_calls_made - old.realloc_calls_made,
+ memory_statistics.strdup_calls_made, memory_statistics.strdup_calls_made - old.strdup_calls_made,
+ memory_statistics.free_calls_made, memory_statistics.free_calls_made - old.free_calls_made
+ );
+
+ memcpy(&old, &memory_statistics, sizeof(struct memory_statistics));
+ //}
+}
+
+static inline void malloc_accounting(const char *file, const char *function, const unsigned long line, size_t size) {
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
+ __atomic_fetch_add(&memory_statistics.memory_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.malloc_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.allocated_memory, size, __ATOMIC_SEQ_CST);
+#else
+ // this is for debugging - we don't care locking it
+ memory_statistics.memory_calls_made++;
+ memory_statistics.malloc_calls_made++;
+ memory_statistics.allocated_memory += size;
+#endif
+ print_allocations(file, function, line);
+}
+
+static inline void mmap_accounting(size_t size) {
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
+ __atomic_fetch_add(&memory_statistics.malloc_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.mmapped_memory, size, __ATOMIC_SEQ_CST);
+#else
+ // this is for debugging - we don't care locking it
+ memory_statistics.memory_calls_made++;
+ memory_statistics.mmapped_memory += size;
+#endif
+}
+
+static inline void calloc_accounting(const char *file, const char *function, const unsigned long line, size_t size) {
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
+ __atomic_fetch_add(&memory_statistics.memory_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.calloc_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.allocated_memory, size, __ATOMIC_SEQ_CST);
+#else
+ // this is for debugging - we don't care locking it
+ memory_statistics.memory_calls_made++;
+ memory_statistics.calloc_calls_made++;
+ memory_statistics.allocated_memory += size;
+#endif
+ print_allocations(file, function, line);
+}
+
+static inline void realloc_accounting(const char *file, const char *function, const unsigned long line, void *ptr, size_t size) {
+ (void)ptr;
+
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
+ __atomic_fetch_add(&memory_statistics.memory_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.realloc_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.allocated_memory, size, __ATOMIC_SEQ_CST);
+#else
+ // this is for debugging - we don't care locking it
+ memory_statistics.memory_calls_made++;
+ memory_statistics.realloc_calls_made++;
+ memory_statistics.allocated_memory += size;
+#endif
+ print_allocations(file, function, line);
+}
+
+static inline void strdup_accounting(const char *file, const char *function, const unsigned long line, const char *s) {
+ size_t size = strlen(s) + 1;
+
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
+ __atomic_fetch_add(&memory_statistics.memory_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.strdup_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.allocated_memory, size, __ATOMIC_SEQ_CST);
+#else
+ // this is for debugging - we don't care locking it
+ memory_statistics.memory_calls_made++;
+ memory_statistics.strdup_calls_made++;
+ memory_statistics.allocated_memory += size;
+#endif
+ print_allocations(file, function, line);
+}
+
+static inline void free_accounting(const char *file, const char *function, const unsigned long line, void *ptr) {
+ (void)file;
+ (void)function;
+ (void)line;
+
+ if(likely(ptr)) {
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
+ __atomic_fetch_add(&memory_statistics.memory_calls_made, 1, __ATOMIC_SEQ_CST);
+ __atomic_fetch_add(&memory_statistics.free_calls_made, 1, __ATOMIC_SEQ_CST);
+#else
+ // this is for debugging - we don't care locking it
+ memory_statistics.memory_calls_made++;
+ memory_statistics.free_calls_made++;
+#endif
+ }
+}
+#endif
+
+#ifdef NETDATA_LOG_ALLOCATIONS
+char *strdupz_int(const char *file, const char *function, const unsigned long line, const char *s) {
+ strdup_accounting(file, function, line, s);
+#else
char *strdupz(const char *s) {
+#endif
+
char *t = strdup(s);
if (unlikely(!t)) fatal("Cannot strdup() string '%s'", s);
return t;
}
+#ifdef NETDATA_LOG_ALLOCATIONS
+void *mallocz_int(const char *file, const char *function, const unsigned long line, size_t size) {
+ malloc_accounting(file, function, line, size);
+#else
void *mallocz(size_t size) {
+#endif
+
void *p = malloc(size);
if (unlikely(!p)) fatal("Cannot allocate %zu bytes of memory.", size);
return p;
}
+#ifdef NETDATA_LOG_ALLOCATIONS
+void *callocz_int(const char *file, const char *function, const unsigned long line, size_t nmemb, size_t size) {
+ calloc_accounting(file, function, line, nmemb * size);
+#else
void *callocz(size_t nmemb, size_t size) {
+#endif
+
void *p = calloc(nmemb, size);
if (unlikely(!p)) fatal("Cannot allocate %zu bytes of memory.", nmemb * size);
return p;
}
+#ifdef NETDATA_LOG_ALLOCATIONS
+void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size) {
+ realloc_accounting(file, function, line, ptr, size);
+#else
void *reallocz(void *ptr, size_t size) {
+#endif
+
void *p = realloc(ptr, size);
if (unlikely(!p)) fatal("Cannot re-allocate memory to %zu bytes.", size);
return p;
}
+#ifdef NETDATA_LOG_ALLOCATIONS
+void freez_int(const char *file, const char *function, const unsigned long line, void *ptr) {
+ free_accounting(file, function, line, ptr);
+#else
void freez(void *ptr) {
+#endif
+
free(ptr);
}
@@ -770,7 +920,14 @@ void *mymmap(const char *filename, size_t size, int flags, int ksm) {
if (flags & MAP_SHARED || !enable_ksm || !ksm) {
#endif
mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0);
- if (mem != MAP_FAILED) {
+ if (mem == MAP_FAILED) {
+ error("Cannot allocate SHARED memory for file '%s'.", filename);
+ mem = NULL;
+ }
+ else {
+#ifdef NETDATA_LOG_ALLOCATIONS
+ mmap_accounting(size);
+#endif
int advise = MADV_SEQUENTIAL | MADV_DONTFORK;
if (flags & MAP_SHARED) advise |= MADV_WILLNEED;
@@ -780,7 +937,8 @@ void *mymmap(const char *filename, size_t size, int flags, int ksm) {
}
}
#ifdef MADV_MERGEABLE
- } else {
+ }
+ else {
/*
// test - load the file into memory
mem = calloc(1, size);
@@ -794,7 +952,14 @@ void *mymmap(const char *filename, size_t size, int flags, int ksm) {
}
*/
mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0);
- if (mem != MAP_FAILED) {
+ if (mem == MAP_FAILED) {
+ error("Cannot allocate PRIVATE ANONYMOUS memory for KSM for file '%s'.", filename);
+ mem = NULL;
+ }
+ else {
+#ifdef NETDATA_LOG_ALLOCATIONS
+ mmap_accounting(size);
+#endif
if (lseek(fd, 0, SEEK_SET) == 0) {
if (read(fd, mem, size) != (ssize_t) size)
error("Cannot read from file '%s'", filename);
@@ -813,17 +978,19 @@ void *mymmap(const char *filename, size_t size, int flags, int ksm) {
filename);
log_madvise_3--;
}
- } else
- error("Cannot allocate PRIVATE ANONYMOUS memory for KSM for file '%s'.", filename);
+ }
}
#endif
- } else
+ }
+ else
error("Cannot write to file '%s' at position %zu.", filename, size);
- } else
+ }
+ else
error("Cannot seek file '%s' to size %zu.", filename, size);
close(fd);
- } else
+ }
+ else
error("Cannot create/open file '%s'.", filename);
return mem;
@@ -860,23 +1027,6 @@ int fd_is_valid(int fd) {
return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}
-/*
- ***************************************************************************
- * Get number of clock ticks per second.
- ***************************************************************************
- */
-unsigned int hz;
-
-void get_HZ(void) {
- long ticks;
-
- if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
- perror("sysconf");
- }
-
- hz = (unsigned int) ticks;
-}
-
pid_t gettid(void) {
return (pid_t)syscall(SYS_gettid);
}
@@ -933,3 +1083,103 @@ int snprintfz(char *dst, size_t n, const char *fmt, ...) {
return ret;
}
+
+// ----------------------------------------------------------------------------
+// system functions
+// to retrieve settings of the system
+
+int processors = 1;
+long get_system_cpus(void) {
+ procfile *ff = NULL;
+
+ processors = 1;
+
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/stat", global_host_prefix);
+
+ ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
+ if(!ff) {
+ error("Cannot open file '%s'. Assuming system has %d processors.", filename, processors);
+ return processors;
+ }
+
+ ff = procfile_readall(ff);
+ if(!ff) {
+ error("Cannot open file '%s'. Assuming system has %d processors.", filename, processors);
+ return processors;
+ }
+
+ processors = 0;
+ unsigned int i;
+ for(i = 0; i < procfile_lines(ff); i++) {
+ if(!procfile_linewords(ff, i)) continue;
+
+ if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
+ }
+ processors--;
+ if(processors < 1) processors = 1;
+
+ procfile_close(ff);
+
+ info("System has %d processors.", processors);
+ return processors;
+}
+
+pid_t pid_max = 32768;
+pid_t get_system_pid_max(void) {
+ procfile *ff = NULL;
+
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", global_host_prefix);
+ ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
+ if(!ff) {
+ error("Cannot open file '%s'. Assuming system supports %d pids.", filename, pid_max);
+ return pid_max;
+ }
+
+ ff = procfile_readall(ff);
+ if(!ff) {
+ error("Cannot read file '%s'. Assuming system supports %d pids.", filename, pid_max);
+ return pid_max;
+ }
+
+ pid_max = (pid_t)atoi(procfile_lineword(ff, 0, 0));
+ if(!pid_max) {
+ procfile_close(ff);
+ pid_max = 32768;
+ error("Cannot parse file '%s'. Assuming system supports %d pids.", filename, pid_max);
+ return pid_max;
+ }
+
+ procfile_close(ff);
+ info("System supports %d pids.", pid_max);
+ return pid_max;
+}
+
+unsigned int hz;
+void get_system_HZ(void) {
+ long ticks;
+
+ if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
+ perror("sysconf");
+ }
+
+ hz = (unsigned int) ticks;
+}
+
+int read_single_number_file(const char *filename, unsigned long long *result) {
+ char buffer[1024 + 1];
+
+ int fd = open(filename, O_RDONLY, 0666);
+ if(unlikely(fd == -1)) return 1;
+
+ ssize_t r = read(fd, buffer, 1024);
+ if(unlikely(r == -1)) {
+ close(fd);
+ return 2;
+ }
+
+ close(fd);
+ *result = strtoull(buffer, NULL, 0);
+ return 0;
+}
diff --git a/src/common.h b/src/common.h
index a6e85034b..9ffa8c8bc 100644
--- a/src/common.h
+++ b/src/common.h
@@ -5,6 +5,25 @@
#include <config.h>
#endif
+/* select the memory allocator, based on autoconf findings */
+#if defined(ENABLE_JEMALLOC)
+
+#if defined(HAVE_JEMALLOC_JEMALLOC_H)
+#include <jemalloc/jemalloc.h>
+#else
+#include <malloc.h>
+#endif
+
+#elif defined(ENABLE_TCMALLOC)
+
+#include <google/tcmalloc.h>
+
+#else /* !defined(ENABLE_JEMALLOC) && !defined(ENABLE_TCMALLOC) */
+
+#include <malloc.h>
+
+#endif
+
#include <pthread.h>
#include <errno.h>
@@ -27,7 +46,7 @@
#include <grp.h>
#include <pwd.h>
#include <locale.h>
-#include <malloc.h>
+
#include <netdb.h>
#include <poll.h>
#include <signal.h>
@@ -60,24 +79,16 @@
#include <zlib.h>
#endif
-#ifndef __ATOMIC_SEQ_CST
-#define NETDATA_NO_ATOMIC_INSTRUCTIONS 1
-#endif
-
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 10000 \
- + __GNUC_MINOR__ * 100 \
- + __GNUC_PATCHLEVEL__)
-
-#if __x86_64__ || __ppc64__
+#if (SIZEOF_VOID_P == 8)
#define ENVIRONMENT64
-#else
+#elif (SIZEOF_VOID_P == 4)
#define ENVIRONMENT32
+#else
+#error "Cannot detect if this is a 32 or 64 bit CPU"
#endif
-#else // !__GNUC__
-#define NETDATA_NO_ATOMIC_INSTRUCTIONS 1
-#define ENVIRONMENT32
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#endif // __GNUC__
#include "avl.h"
@@ -139,11 +150,25 @@ extern int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args);
extern int snprintfz(char *dst, size_t n, const char *fmt, ...) __attribute__ (( format (printf, 3, 4)));
// memory allocation functions that handle failures
+#ifdef NETDATA_LOG_ALLOCATIONS
+#define strdupz(s) strdupz_int(__FILE__, __FUNCTION__, __LINE__, s)
+#define callocz(nmemb, size) callocz_int(__FILE__, __FUNCTION__, __LINE__, nmemb, size)
+#define mallocz(size) mallocz_int(__FILE__, __FUNCTION__, __LINE__, size)
+#define reallocz(ptr, size) reallocz_int(__FILE__, __FUNCTION__, __LINE__, ptr, size)
+#define freez(ptr) freez_int(__FILE__, __FUNCTION__, __LINE__, ptr)
+
+extern char *strdupz_int(const char *file, const char *function, const unsigned long line, const char *s);
+extern void *callocz_int(const char *file, const char *function, const unsigned long line, size_t nmemb, size_t size);
+extern void *mallocz_int(const char *file, const char *function, const unsigned long line, size_t size);
+extern void *reallocz_int(const char *file, const char *function, const unsigned long line, void *ptr, size_t size);
+extern void freez_int(const char *file, const char *function, const unsigned long line, void *ptr);
+#else
extern char *strdupz(const char *s);
extern void *callocz(size_t nmemb, size_t size);
extern void *mallocz(size_t size);
-extern void freez(void *ptr);
extern void *reallocz(void *ptr, size_t size);
+extern void freez(void *ptr);
+#endif
extern void *mymmap(const char *filename, size_t size, int flags, int ksm);
extern int savememory(const char *filename, void *mem, size_t size);
@@ -153,10 +178,6 @@ extern int fd_is_valid(int fd);
extern char *global_host_prefix;
extern int enable_ksm;
-/* Number of ticks per second */
-extern unsigned int hz;
-extern void get_HZ(void);
-
extern pid_t gettid(void);
extern unsigned long long time_usec(void);
@@ -164,6 +185,17 @@ extern int sleep_usec(unsigned long long usec);
extern char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len);
+extern int processors;
+extern long get_system_cpus(void);
+
+extern pid_t pid_max;
+extern pid_t get_system_pid_max(void);
+
+/* Number of ticks per second */
+extern unsigned int hz;
+extern void get_system_HZ(void);
+
+
/* fix for alpine linux */
#ifndef RUSAGE_THREAD
#ifdef RUSAGE_CHILDREN
@@ -171,4 +203,6 @@ extern char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len);
#endif
#endif
+extern int read_single_number_file(const char *filename, unsigned long long *result);
+
#endif /* NETDATA_COMMON_H */
diff --git a/src/daemon.c b/src/daemon.c
index bc4614f07..1c34405d8 100644
--- a/src/daemon.c
+++ b/src/daemon.c
@@ -15,27 +15,30 @@ void sig_handler_exit(int signo)
void sig_handler_logrotate(int signo)
{
if(signo) {
- error_log_limit_reset();
+ error_log_limit_unlimited();
info("Received signal %d to re-open the log files", signo);
reopen_all_log_files();
+ error_log_limit_reset();
}
}
void sig_handler_save(int signo)
{
if(signo) {
- error_log_limit_reset();
+ error_log_limit_unlimited();
info("Received signal %d to save the database...", signo);
rrdset_save_all();
+ error_log_limit_reset();
}
}
void sig_handler_reload_health(int signo)
{
if(signo) {
- error_log_limit_reset();
+ error_log_limit_unlimited();
info("Received signal %d to reload health configuration...", signo);
health_reload();
+ error_log_limit_reset();
}
}
diff --git a/src/eval.c b/src/eval.c
index 8866ee95d..397b43bd7 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -74,7 +74,7 @@ static inline calculated_number eval_check_number(calculated_number n, int *erro
}
static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) {
- static uint32_t this_hash = 0, now_hash = 0, after_hash = 0, before_hash = 0;
+ static uint32_t this_hash = 0, now_hash = 0, after_hash = 0, before_hash = 0, status_hash = 0, removed_hash = 0, uninitialized_hash = 0, undefined_hash = 0, clear_hash = 0, warning_hash = 0, critical_hash = 0;
calculated_number n;
if(unlikely(this_hash == 0)) {
@@ -82,9 +82,16 @@ static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABL
now_hash = simple_hash("now");
after_hash = simple_hash("after");
before_hash = simple_hash("before");
+ status_hash = simple_hash("status");
+ removed_hash = simple_hash("REMOVED");
+ uninitialized_hash = simple_hash("UNINITIALIZED");
+ undefined_hash = simple_hash("UNDEFINED");
+ clear_hash = simple_hash("CLEAR");
+ warning_hash = simple_hash("WARNING");
+ critical_hash = simple_hash("CRITICAL");
}
- if(v->hash == this_hash && !strcmp(v->name, "this")) {
+ if(unlikely(v->hash == this_hash && !strcmp(v->name, "this"))) {
n = (exp->this)?*exp->this:NAN;
buffer_strcat(exp->error_msg, "[ $this = ");
print_parsed_as_constant(exp->error_msg, n);
@@ -92,7 +99,7 @@ static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABL
return n;
}
- if(v->hash == after_hash && !strcmp(v->name, "after")) {
+ if(unlikely(v->hash == after_hash && !strcmp(v->name, "after"))) {
n = (exp->after && *exp->after)?*exp->after:NAN;
buffer_strcat(exp->error_msg, "[ $after = ");
print_parsed_as_constant(exp->error_msg, n);
@@ -100,7 +107,7 @@ static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABL
return n;
}
- if(v->hash == before_hash && !strcmp(v->name, "before")) {
+ if(unlikely(v->hash == before_hash && !strcmp(v->name, "before"))) {
n = (exp->before && *exp->before)?*exp->before:NAN;
buffer_strcat(exp->error_msg, "[ $before = ");
print_parsed_as_constant(exp->error_msg, n);
@@ -108,7 +115,7 @@ static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABL
return n;
}
- if(v->hash == now_hash && !strcmp(v->name, "now")) {
+ if(unlikely(v->hash == now_hash && !strcmp(v->name, "now"))) {
n = time(NULL);
buffer_strcat(exp->error_msg, "[ $now = ");
print_parsed_as_constant(exp->error_msg, n);
@@ -116,6 +123,62 @@ static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABL
return n;
}
+ if(unlikely(v->hash == status_hash && !strcmp(v->name, "status"))) {
+ n = (exp->status)?*exp->status:RRDCALC_STATUS_UNINITIALIZED;
+ buffer_strcat(exp->error_msg, "[ $status = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(unlikely(v->hash == removed_hash && !strcmp(v->name, "REMOVED"))) {
+ n = RRDCALC_STATUS_REMOVED;
+ buffer_strcat(exp->error_msg, "[ $REMOVED = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(unlikely(v->hash == uninitialized_hash && !strcmp(v->name, "UNINITIALIZED"))) {
+ n = RRDCALC_STATUS_UNINITIALIZED;
+ buffer_strcat(exp->error_msg, "[ $UNINITIALIZED = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(unlikely(v->hash == undefined_hash && !strcmp(v->name, "UNDEFINED"))) {
+ n = RRDCALC_STATUS_UNDEFINED;
+ buffer_strcat(exp->error_msg, "[ $UNDEFINED = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(unlikely(v->hash == clear_hash && !strcmp(v->name, "CLEAR"))) {
+ n = RRDCALC_STATUS_CLEAR;
+ buffer_strcat(exp->error_msg, "[ $CLEAR = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(unlikely(v->hash == warning_hash && !strcmp(v->name, "WARNING"))) {
+ n = RRDCALC_STATUS_WARNING;
+ buffer_strcat(exp->error_msg, "[ $WARNING = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
+ if(unlikely(v->hash == critical_hash && !strcmp(v->name, "CRITICAL"))) {
+ n = RRDCALC_STATUS_CRITICAL;
+ buffer_strcat(exp->error_msg, "[ $CRITICAL = ");
+ print_parsed_as_constant(exp->error_msg, n);
+ buffer_strcat(exp->error_msg, " ] ");
+ return n;
+ }
+
if(exp->rrdcalc && health_variable_lookup(v->name, v->hash, exp->rrdcalc, &n)) {
buffer_sprintf(exp->error_msg, "[ $%s = ", v->name);
print_parsed_as_constant(exp->error_msg, n);
@@ -265,7 +328,6 @@ static struct operator {
// this is a random access array
// we always access it with a known EVAL_OPERATOR_X
- [EVAL_OPERATOR_IF_THEN_ELSE] = { "?", 1, 3, 0, eval_if_then_else },
[EVAL_OPERATOR_AND] = { "&&", 2, 2, 0, eval_and },
[EVAL_OPERATOR_OR] = { "||", 2, 2, 0, eval_or },
[EVAL_OPERATOR_GREATER_THAN_OR_EQUAL] = { ">=", 3, 2, 0, eval_greater_than_or_equal },
@@ -282,8 +344,9 @@ static struct operator {
[EVAL_OPERATOR_SIGN_PLUS] = { "+", 6, 1, 0, eval_sign_plus },
[EVAL_OPERATOR_SIGN_MINUS] = { "-", 6, 1, 0, eval_sign_minus },
[EVAL_OPERATOR_ABS] = { "abs(",6,1, 1, eval_abs },
- [EVAL_OPERATOR_NOP] = { NULL, 7, 1, 0, eval_nop },
- [EVAL_OPERATOR_EXPRESSION_OPEN] = { NULL, 7, 1, 0, eval_nop },
+ [EVAL_OPERATOR_IF_THEN_ELSE] = { "?", 7, 3, 0, eval_if_then_else },
+ [EVAL_OPERATOR_NOP] = { NULL, 8, 1, 0, eval_nop },
+ [EVAL_OPERATOR_EXPRESSION_OPEN] = { NULL, 8, 1, 0, eval_nop },
// this should exist in our evaluation list
[EVAL_OPERATOR_EXPRESSION_CLOSE] = { NULL, 99, 1, 0, eval_nop }
diff --git a/src/eval.h b/src/eval.h
index b509a9fa8..d68b9af47 100644
--- a/src/eval.h
+++ b/src/eval.h
@@ -14,6 +14,7 @@ typedef struct eval_expression {
const char *source;
const char *parsed_as;
+ int *status;
calculated_number *this;
time_t *after;
time_t *before;
diff --git a/src/global_statistics.c b/src/global_statistics.c
index f39a4cf25..bb2b1f08f 100644
--- a/src/global_statistics.c
+++ b/src/global_statistics.c
@@ -25,7 +25,7 @@ void finished_web_request_statistics(uint64_t dt,
uint64_t bytes_sent,
uint64_t content_size,
uint64_t compressed_content_size) {
-#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
uint64_t old_web_usec_max = global_statistics.web_usec_max;
while(dt > old_web_usec_max)
__atomic_compare_exchange(&global_statistics.web_usec_max, &old_web_usec_max, &dt, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
@@ -57,7 +57,7 @@ void finished_web_request_statistics(uint64_t dt,
}
void web_client_connected(void) {
-#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
__atomic_fetch_add(&global_statistics.connected_clients, 1, __ATOMIC_SEQ_CST);
#else
if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
@@ -71,7 +71,7 @@ void web_client_connected(void) {
}
void web_client_disconnected(void) {
-#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
__atomic_fetch_sub(&global_statistics.connected_clients, 1, __ATOMIC_SEQ_CST);
#else
if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
@@ -86,7 +86,7 @@ void web_client_disconnected(void) {
inline void global_statistics_copy(struct global_statistics *gs, uint8_t options) {
-#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+#if defined(HAVE_C___ATOMIC) && !defined(NETDATA_NO_ATOMIC_INSTRUCTIONS)
gs->connected_clients = __atomic_fetch_add(&global_statistics.connected_clients, 0, __ATOMIC_SEQ_CST);
gs->web_requests = __atomic_fetch_add(&global_statistics.web_requests, 0, __ATOMIC_SEQ_CST);
gs->web_usec = __atomic_fetch_add(&global_statistics.web_usec, 0, __ATOMIC_SEQ_CST);
@@ -259,4 +259,4 @@ void global_statistics_charts(void) {
rrddim_set(stcompression, "savings", compression_ratio);
rrdset_done(stcompression);
-} \ No newline at end of file
+}
diff --git a/src/health.c b/src/health.c
index 3156cd080..596b143a4 100644
--- a/src/health.c
+++ b/src/health.c
@@ -2,10 +2,179 @@
#define RRDVAR_MAX_LENGTH 1024
-static const char *health_default_exec = PLUGINS_DIR "/alarm-email.sh";
+struct health_options {
+ const char *health_default_exec;
+ const char *health_default_recipient;
+ const char *log_filename;
+ FILE *log_fp;
+};
+
+static struct health_options health = {
+ .health_default_exec = PLUGINS_DIR "/alarm-notify.sh",
+ .health_default_recipient = "root",
+ .log_filename = VARLIB_DIR "/health/alarm_log.db",
+ .log_fp = NULL
+};
+
int health_enabled = 1;
// ----------------------------------------------------------------------------
+// health alarm log load/save
+// no need for locking - only one thread is reading / writing the alarms log
+
+static inline int health_alarm_log_open(void) {
+ if(health.log_fp)
+ fclose(health.log_fp);
+
+ health.log_fp = fopen(health.log_filename, "a");
+
+ if(health.log_fp) {
+ if (setvbuf(health.log_fp, NULL, _IOLBF, 0) != 0)
+ error("Cannot set line buffering on health log file.");
+ return 0;
+ }
+
+ error("Cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", health.log_filename);
+ return -1;
+}
+
+static inline void health_alarm_log_close(void) {
+ if(health.log_fp) {
+ fclose(health.log_fp);
+ health.log_fp = NULL;
+ }
+}
+
+static inline void health_log_recreate(void) {
+ if(health.log_fp != NULL) {
+ health_alarm_log_close();
+
+ // open it with truncate
+ health.log_fp = fopen(health.log_filename, "w");
+ if(health.log_fp) fclose(health.log_fp);
+ else error("Cannot truncate health log '%s'", health.log_filename);
+
+ health.log_fp = NULL;
+
+ health_alarm_log_open();
+ }
+}
+
+static inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) {
+ (void)host;
+ (void)ae;
+
+/* if(likely(health.log_fp)) {
+ if(unlikely(fprintf(health.log_fp, "A\t%s\t%08x\t%08x\t%08x\t%08x\t%08x\t%08x\t%s\t%s\t%s\t%s\t%s\t%08x\n",
+ host->hostname,
+ ae->unique_id,
+ ae->alarm_id,
+ ae->alarm_event_id,
+ (uint32_t)ae->when,
+ (uint32_t)ae->duration,
+ (uint32_t)ae->non_clear_duration,
+ (uint32_t)ae->exec_run_timestamp,
+ ae->name,
+ ae->chart,
+ ae->family,
+ ae->exec,
+ ae->recipient
+ ) < 0))
+ error("Health: failed to save alarm log entry. Health data may be lost in case of abnormal restart.");
+ }
+*/
+}
+
+static inline void health_alarm_log_load(RRDHOST *host) {
+ (void)host;
+
+}
+
+// ----------------------------------------------------------------------------
+// health alarm log management
+
+static inline void health_alarm_log(RRDHOST *host,
+ uint32_t alarm_id, uint32_t alarm_event_id,
+ time_t when,
+ const char *name, const char *chart, const char *family,
+ const char *exec, const char *recipient, time_t duration,
+ calculated_number old_value, calculated_number new_value,
+ int old_status, int new_status,
+ const char *source,
+ const char *units,
+ const char *info,
+ int delay
+) {
+ debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id);
+
+ ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY));
+ ae->name = strdupz(name);
+ ae->hash_name = simple_hash(ae->name);
+
+ if(chart) {
+ ae->chart = strdupz(chart);
+ ae->hash_chart = simple_hash(ae->chart);
+ }
+
+ if(family)
+ ae->family = strdupz(family);
+
+ if(exec) ae->exec = strdupz(exec);
+ if(recipient) ae->recipient = strdupz(recipient);
+ if(source) ae->source = strdupz(source);
+ if(units) ae->units = strdupz(units);
+ if(info) ae->info = strdupz(info);
+
+ ae->unique_id = host->health_log.next_log_id++;
+ ae->alarm_id = alarm_id;
+ ae->alarm_event_id = alarm_event_id;
+ ae->when = when;
+ ae->old_value = old_value;
+ ae->new_value = new_value;
+ ae->old_status = old_status;
+ ae->new_status = new_status;
+ ae->duration = duration;
+ ae->delay = delay;
+ ae->delay_up_to_timestamp = when + delay;
+
+ if(ae->old_status == RRDCALC_STATUS_WARNING || ae->old_status == RRDCALC_STATUS_CRITICAL)
+ ae->non_clear_duration += ae->duration;
+
+ // link it
+ pthread_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
+ ae->next = host->health_log.alarms;
+ host->health_log.alarms = ae;
+ host->health_log.count++;
+ pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+
+ // match previous alarms
+ pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
+ ALARM_ENTRY *t;
+ for(t = host->health_log.alarms ; t ; t = t->next) {
+ if(t != ae && t->alarm_id == ae->alarm_id) {
+ if(!(t->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED) && !t->updated_by_id) {
+ t->notifications |= HEALTH_ENTRY_NOTIFICATIONS_UPDATED;
+ t->updated_by_id = ae->unique_id;
+ ae->updates_id = t->unique_id;
+
+ if((t->new_status == RRDCALC_STATUS_WARNING || t->new_status == RRDCALC_STATUS_CRITICAL) &&
+ (t->old_status == RRDCALC_STATUS_WARNING || t->old_status == RRDCALC_STATUS_CRITICAL))
+ ae->non_clear_duration += t->non_clear_duration;
+
+ health_alarm_log_save(host, t);
+ }
+ else {
+ // no need to continue
+ break;
+ }
+ }
+ }
+ pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+
+ health_alarm_log_save(host, ae);
+}
+
+// ----------------------------------------------------------------------------
// RRDVAR management
static inline int rrdvar_fix_name(char *variable) {
@@ -410,12 +579,15 @@ void rrddimvar_free(RRDDIMVAR *rs) {
static inline const char *rrdcalc_status2string(int status) {
switch(status) {
- case RRDCALC_STATUS_UNINITIALIZED:
- return "UNINITIALIZED";
+ case RRDCALC_STATUS_REMOVED:
+ return "REMOVED";
case RRDCALC_STATUS_UNDEFINED:
return "UNDEFINED";
+ case RRDCALC_STATUS_UNINITIALIZED:
+ return "UNINITIALIZED";
+
case RRDCALC_STATUS_CLEAR:
return "CLEAR";
@@ -429,6 +601,7 @@ static inline const char *rrdcalc_status2string(int status) {
return "CRITICAL";
default:
+ error("Unknown alarm status %d", status);
return "UNKNOWN";
}
}
@@ -441,6 +614,10 @@ static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
rc->rrdset_next = st->alarms;
rc->rrdset_prev = NULL;
+
+ if(rc->rrdset_next)
+ rc->rrdset_next->rrdset_prev = rc;
+
st->alarms = rc;
if(rc->update_every < rc->rrdset->update_every) {
@@ -469,6 +646,11 @@ static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
rc->hostname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, fullname, RRDVAR_TYPE_CALCULATED, &rc->value);
if(!rc->units) rc->units = strdupz(st->units);
+
+ {
+ time_t now = time(NULL);
+ health_alarm_log(st->rrdhost, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, rc->recipient, now - rc->last_status_change, rc->old_value, rc->value, rc->status, RRDCALC_STATUS_UNINITIALIZED, rc->source, rc->units, rc->info, 0);
+ }
}
static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) {
@@ -485,9 +667,10 @@ inline void rrdsetcalc_link_matching(RRDSET *st) {
RRDCALC *rc;
for(rc = st->rrdhost->alarms; rc ; rc = rc->next) {
- if(rc->rrdset) continue;
+ if(unlikely(rc->rrdset))
+ continue;
- if(rrdcalc_is_matching_this_rrdset(rc, st))
+ if(unlikely(rrdcalc_is_matching_this_rrdset(rc, st)))
rrdsetcalc_link(st, rc);
}
}
@@ -497,10 +680,16 @@ inline void rrdsetcalc_unlink(RRDCALC *rc) {
RRDSET *st = rc->rrdset;
if(!st) {
+ debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
return;
}
+ {
+ time_t now = time(NULL);
+ health_alarm_log(st->rrdhost, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, rc->recipient, now - rc->last_status_change, rc->old_value, rc->value, rc->status, RRDCALC_STATUS_REMOVED, rc->source, rc->units, rc->info, 0);
+ }
+
RRDHOST *host = st->rrdhost;
debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname);
@@ -541,20 +730,29 @@ RRDCALC *rrdcalc_find(RRDSET *st, const char *name) {
uint32_t hash = simple_hash(name);
for( rc = st->alarms; rc ; rc = rc->rrdset_next ) {
- if(rc->hash == hash && !strcmp(rc->name, name))
+ if(unlikely(rc->hash == hash && !strcmp(rc->name, name)))
return rc;
}
return NULL;
}
-static inline int rrdcalc_exists(RRDHOST *host, const char *name, uint32_t hash) {
+static inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name) {
RRDCALC *rc;
+ if(unlikely(!chart)) {
+ error("attempt to find RRDCALC '%s' without giving a chart name", name);
+ return 1;
+ }
+
+ if(unlikely(!hash_chart)) hash_chart = simple_hash(chart);
+ if(unlikely(!hash_name)) hash_name = simple_hash(name);
+
// make sure it does not already exist
for(rc = host->alarms; rc ; rc = rc->next) {
- if (rc->hash == hash && !strcmp(name, rc->name)) {
- error("Health alarm '%s' already exists in host '%s'.", name, host->hostname);
+ if (unlikely(rc->chart && rc->hash == hash_name && rc->hash_chart == hash_chart && !strcmp(name, rc->name) && !strcmp(chart, rc->chart))) {
+ debug(D_HEALTH, "Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname);
+ error("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname);
return 1;
}
}
@@ -562,10 +760,29 @@ static inline int rrdcalc_exists(RRDHOST *host, const char *name, uint32_t hash)
return 0;
}
+static inline uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id) {
+ if(chart && name) {
+ uint32_t hash_chart = simple_hash(chart);
+ uint32_t hash_name = simple_hash(name);
+
+ // re-use old IDs, by looking them up in the alarm log
+ ALARM_ENTRY *ae;
+ for(ae = host->health_log.alarms; ae ;ae = ae->next) {
+ if(unlikely(ae->hash_name == hash_name && ae->hash_chart == hash_chart && !strcmp(name, ae->name) && !strcmp(chart, ae->chart))) {
+ if(next_event_id) *next_event_id = ae->alarm_event_id + 1;
+ return ae->alarm_id;
+ }
+ }
+ }
+
+ return host->health_log.next_alarm_id++;
+}
+
static inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
rrdhost_check_rdlock(host);
if(rc->calculation) {
+ rc->calculation->status = &rc->status;
rc->calculation->this = &rc->value;
rc->calculation->after = &rc->db_after;
rc->calculation->before = &rc->db_before;
@@ -573,6 +790,7 @@ static inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
}
if(rc->warning) {
+ rc->warning->status = &rc->status;
rc->warning->this = &rc->value;
rc->warning->after = &rc->db_after;
rc->warning->before = &rc->db_before;
@@ -580,6 +798,7 @@ static inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
}
if(rc->critical) {
+ rc->critical->status = &rc->status;
rc->critical->this = &rc->value;
rc->critical->after = &rc->db_after;
rc->critical->before = &rc->db_before;
@@ -587,8 +806,15 @@ static inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
}
// link it to the host
- rc->next = host->alarms;
- host->alarms = rc;
+ if(likely(host->alarms)) {
+ // append it
+ RRDCALC *t;
+ for(t = host->alarms; t && t->next ; t = t->next) ;
+ t->next = rc;
+ }
+ else {
+ host->alarms = rc;
+ }
// link it to its chart
RRDSET *st;
@@ -600,71 +826,66 @@ static inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
}
}
-static inline uint32_t rrdcalc_fullname(char *fullname, size_t len, const char *chart, const char *name) {
- snprintfz(fullname, len - 1, "%s%s%s", chart?chart:"", chart?".":"", name);
- rrdvar_fix_name(fullname);
- return simple_hash(fullname);
-}
-
-static inline RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, const char *dimensions,
- const char *units, const char *info,
- int group_method, int after, int before, int update_every, uint32_t options,
- calculated_number green, calculated_number red,
- const char *exec, const char *source,
- const char *calc, const char *warn, const char *crit) {
+static inline RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart) {
- char fullname[RRDVAR_MAX_LENGTH + 1];
- uint32_t hash = rrdcalc_fullname(fullname, RRDVAR_MAX_LENGTH + 1, chart, name);
+ debug(D_HEALTH, "Health creating dynamic alarm (from template) '%s.%s'", chart, rt->name);
- if(rrdcalc_exists(host, fullname, hash))
+ if(rrdcalc_exists(host, chart, rt->name, 0, 0))
return NULL;
RRDCALC *rc = callocz(1, sizeof(RRDCALC));
-
- rc->name = strdupz(name);
+ rc->next_event_id = 1;
+ rc->id = rrdcalc_get_unique_id(host, chart, rt->name, &rc->next_event_id);
+ rc->name = strdupz(rt->name);
rc->hash = simple_hash(rc->name);
-
rc->chart = strdupz(chart);
rc->hash_chart = simple_hash(rc->chart);
- if(dimensions) rc->dimensions = strdupz(dimensions);
+ if(rt->dimensions) rc->dimensions = strdupz(rt->dimensions);
- rc->green = green;
- rc->red = red;
+ rc->green = rt->green;
+ rc->red = rt->red;
rc->value = NAN;
rc->old_value = NAN;
- rc->group = group_method;
- rc->after = after;
- rc->before = before;
- rc->update_every = update_every;
- rc->options = options;
-
- if(exec) rc->exec = strdupz(exec);
- if(source) rc->source = strdupz(source);
- if(units) rc->units = strdupz(units);
- if(info) rc->info = strdupz(info);
-
- if(calc) {
- rc->calculation = expression_parse(calc, NULL, NULL);
+ rc->delay_up_duration = rt->delay_up_duration;
+ rc->delay_down_duration = rt->delay_down_duration;
+ rc->delay_max_duration = rt->delay_max_duration;
+ rc->delay_multiplier = rt->delay_multiplier;
+
+ rc->group = rt->group;
+ rc->after = rt->after;
+ rc->before = rt->before;
+ rc->update_every = rt->update_every;
+ rc->options = rt->options;
+
+ if(rt->exec) rc->exec = strdupz(rt->exec);
+ if(rt->recipient) rc->recipient = strdupz(rt->recipient);
+ if(rt->source) rc->source = strdupz(rt->source);
+ if(rt->units) rc->units = strdupz(rt->units);
+ if(rt->info) rc->info = strdupz(rt->info);
+
+ if(rt->calculation) {
+ rc->calculation = expression_parse(rt->calculation->source, NULL, NULL);
if(!rc->calculation)
- error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, name, calc);
+ error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, rt->name, rt->calculation->source);
}
- if(warn) {
- rc->warning = expression_parse(warn, NULL, NULL);
+ if(rt->warning) {
+ rc->warning = expression_parse(rt->warning->source, NULL, NULL);
if(!rc->warning)
- error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, name, warn);
+ error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, rt->name, rt->warning->source);
}
- if(crit) {
- rc->critical = expression_parse(crit, NULL, NULL);
+ if(rt->critical) {
+ rc->critical = expression_parse(rt->critical->source, NULL, NULL);
if(!rc->critical)
- error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, name, crit);
+ error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, rt->name, rt->critical->source);
}
- debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s",
+ debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f",
(rc->chart)?rc->chart:"NOCHART",
rc->name,
(rc->exec)?rc->exec:"DEFAULT",
+ (rc->recipient)?rc->recipient:"DEFAULT",
rc->green,
rc->red,
rc->group,
@@ -676,7 +897,11 @@ static inline RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const cha
(rc->calculation)?rc->calculation->parsed_as:"NONE",
(rc->warning)?rc->warning->parsed_as:"NONE",
(rc->critical)?rc->critical->parsed_as:"NONE",
- rc->source
+ rc->source,
+ rc->delay_up_duration,
+ rc->delay_down_duration,
+ rc->delay_max_duration,
+ rc->delay_multiplier
);
rrdcalc_create_part2(host, rc);
@@ -692,14 +917,13 @@ void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
if(rc->rrdset) rrdsetcalc_unlink(rc);
// unlink it from RRDHOST
- if(rc == host->alarms)
+ if(unlikely(rc == host->alarms))
host->alarms = rc->next;
- else if(host->alarms) {
+ else if(likely(host->alarms)) {
RRDCALC *t, *last = host->alarms;
-
for(t = last->next; t && t != rc; last = t, t = t->next) ;
- if(last && last->next == rc)
+ if(last->next == rc)
last->next = rc->next;
else
error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
@@ -716,6 +940,7 @@ void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
freez(rc->family);
freez(rc->dimensions);
freez(rc->exec);
+ freez(rc->recipient);
freez(rc->source);
freez(rc->units);
freez(rc->info);
@@ -730,22 +955,13 @@ void rrdcalctemplate_link_matching(RRDSET *st) {
for(rt = st->rrdhost->templates; rt ; rt = rt->next) {
if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context)) {
-
- RRDCALC *rc = rrdcalc_create(st->rrdhost, rt->name, st->id,
- rt->dimensions, rt->units, rt->info, rt->group, rt->after, rt->before, rt->update_every, rt->options,
- rt->green, rt->red, rt->exec, rt->source,
- (rt->calculation)?rt->calculation->source:NULL,
- (rt->warning)?rt->warning->source:NULL,
- (rt->critical)?rt->critical->source:NULL);
-
- if(!rc)
+ RRDCALC *rc = rrdcalc_create(st->rrdhost, rt, st->id);
+ if(unlikely(!rc))
error("Health tried to create alarm from template '%s', but it failed", rt->name);
#ifdef NETDATA_INTERNAL_CHECKS
else if(rc->rrdset != st)
error("Health alarm '%s.%s' should be linked to chart '%s', but it is not", rc->chart?rc->chart:"NOCHART", rc->name, st->id);
-#else
- (void)rc;
#endif
}
}
@@ -776,6 +992,7 @@ static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
freez(rt->name);
freez(rt->exec);
+ freez(rt->recipient);
freez(rt->context);
freez(rt->source);
freez(rt->units);
@@ -800,18 +1017,12 @@ static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
#define HEALTH_WARN_KEY "warn"
#define HEALTH_CRIT_KEY "crit"
#define HEALTH_EXEC_KEY "exec"
+#define HEALTH_RECIPIENT_KEY "to"
#define HEALTH_UNITS_KEY "units"
#define HEALTH_INFO_KEY "info"
+#define HEALTH_DELAY_KEY "delay"
static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
- {
- char fullname[RRDVAR_MAX_LENGTH + 1];
- uint32_t hash = rrdcalc_fullname(fullname, RRDVAR_MAX_LENGTH + 1, rc->chart, rc->name);
-
- if (rrdcalc_exists(host, fullname, hash))
- return 0;
- }
-
if(!rc->chart) {
error("Health configuration for alarm '%s' does not have a chart", rc->name);
return 0;
@@ -827,10 +1038,17 @@ static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
return 0;
}
- debug(D_HEALTH, "Health configuration adding alarm '%s.%s': exec '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s",
+ if (rrdcalc_exists(host, rc->chart, rc->name, rc->hash_chart, rc->hash))
+ return 0;
+
+ rc->id = rrdcalc_get_unique_id(&localhost, rc->chart, rc->name, &rc->next_event_id);
+
+ debug(D_HEALTH, "Health configuration adding alarm '%s.%s' (%u): exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f",
rc->chart?rc->chart:"NOCHART",
rc->name,
+ rc->id,
(rc->exec)?rc->exec:"DEFAULT",
+ (rc->recipient)?rc->recipient:"DEFAULT",
rc->green,
rc->red,
rc->group,
@@ -842,7 +1060,11 @@ static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
(rc->calculation)?rc->calculation->parsed_as:"NONE",
(rc->warning)?rc->warning->parsed_as:"NONE",
(rc->critical)?rc->critical->parsed_as:"NONE",
- rc->source
+ rc->source,
+ rc->delay_up_duration,
+ rc->delay_down_duration,
+ rc->delay_max_duration,
+ rc->delay_multiplier
);
rrdcalc_create_part2(host, rc);
@@ -850,33 +1072,34 @@ static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
}
static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) {
- if(!rt->context) {
+ if(unlikely(!rt->context)) {
error("Health configuration for template '%s' does not have a context", rt->name);
return 0;
}
- if(!rt->update_every) {
+ if(unlikely(!rt->update_every)) {
error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rt->name);
return 0;
}
- if(!RRDCALCTEMPLATE_HAS_CALCULATION(rt) && !rt->warning && !rt->critical) {
+ if(unlikely(!RRDCALCTEMPLATE_HAS_CALCULATION(rt) && !rt->warning && !rt->critical)) {
error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rt->name);
return 0;
}
- RRDCALCTEMPLATE *t;
- for (t = host->templates; t ; t = t->next) {
- if(t->hash_name == rt->hash_name && !strcmp(t->name, rt->name)) {
+ RRDCALCTEMPLATE *t, *last = NULL;
+ for (t = host->templates; t ; last = t, t = t->next) {
+ if(unlikely(t->hash_name == rt->hash_name && !strcmp(t->name, rt->name))) {
error("Health configuration template '%s' already exists for host '%s'.", rt->name, host->hostname);
return 0;
}
}
- debug(D_HEALTH, "Health configuration adding template '%s': context '%s', exec '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s'",
+ debug(D_HEALTH, "Health configuration adding template '%s': context '%s', exec '%s', recipient '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f",
rt->name,
(rt->context)?rt->context:"NONE",
(rt->exec)?rt->exec:"DEFAULT",
+ (rt->recipient)?rt->recipient:"DEFAULT",
rt->green,
rt->red,
rt->group,
@@ -888,11 +1111,21 @@ static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCAL
(rt->calculation)?rt->calculation->parsed_as:"NONE",
(rt->warning)?rt->warning->parsed_as:"NONE",
(rt->critical)?rt->critical->parsed_as:"NONE",
- rt->source
+ rt->source,
+ rt->delay_up_duration,
+ rt->delay_down_duration,
+ rt->delay_max_duration,
+ rt->delay_multiplier
);
- rt->next = host->templates;
- host->templates = rt;
+ if(likely(last)) {
+ last->next = rt;
+ }
+ else {
+ rt->next = host->templates;
+ host->templates = rt;
+ }
+
return 1;
}
@@ -938,6 +1171,86 @@ static inline int health_parse_duration(char *string, int *result) {
return 1;
}
+static inline int health_parse_delay(
+ size_t line, const char *path, const char *file, char *string,
+ int *delay_up_duration,
+ int *delay_down_duration,
+ int *delay_max_duration,
+ float *delay_multiplier) {
+
+ char given_up = 0;
+ char given_down = 0;
+ char given_max = 0;
+ char given_multiplier = 0;
+
+ char *s = string;
+ while(*s) {
+ char *key = s;
+
+ while(*s && !isspace(*s)) s++;
+ while(*s && isspace(*s)) *s++ = '\0';
+
+ if(!*key) break;
+
+ char *value = s;
+ while(*s && !isspace(*s)) s++;
+ while(*s && isspace(*s)) *s++ = '\0';
+
+ if(!strcasecmp(key, "up")) {
+ if (!health_parse_duration(value, delay_up_duration)) {
+ error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
+ line, path, file, value, key);
+ }
+ else given_up = 1;
+ }
+ else if(!strcasecmp(key, "down")) {
+ if (!health_parse_duration(value, delay_down_duration)) {
+ error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
+ line, path, file, value, key);
+ }
+ else given_down = 1;
+ }
+ else if(!strcasecmp(key, "multiplier")) {
+ *delay_multiplier = strtof(value, NULL);
+ if(isnan(*delay_multiplier) || isinf(*delay_multiplier) || islessequal(*delay_multiplier, 0)) {
+ error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
+ line, path, file, value, key);
+ }
+ else given_multiplier = 1;
+ }
+ else if(!strcasecmp(key, "max")) {
+ if (!health_parse_duration(value, delay_max_duration)) {
+ error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
+ line, path, file, value, key);
+ }
+ else given_max = 1;
+ }
+ else {
+ error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'",
+ line, path, file, key);
+ }
+ }
+
+ if(!given_up)
+ *delay_up_duration = 0;
+
+ if(!given_down)
+ *delay_down_duration = 0;
+
+ if(!given_multiplier)
+ *delay_multiplier = 1.0;
+
+ if(!given_max) {
+ if((*delay_max_duration) < (*delay_up_duration) * (*delay_multiplier))
+ *delay_max_duration = (*delay_up_duration) * (*delay_multiplier);
+
+ if((*delay_max_duration) < (*delay_down_duration) * (*delay_multiplier))
+ *delay_max_duration = (*delay_down_duration) * (*delay_multiplier);
+ }
+
+ return 1;
+}
+
static inline int health_parse_db_lookup(
size_t line, const char *path, const char *file, char *string,
int *group_method, int *after, int *before, int *every,
@@ -1056,7 +1369,7 @@ static inline void strip_quotes(char *s) {
int health_readfile(const char *path, const char *filename) {
debug(D_HEALTH, "Health configuration reading file '%s/%s'", path, filename);
- static uint32_t hash_alarm = 0, hash_template = 0, hash_on = 0, hash_calc = 0, hash_green = 0, hash_red = 0, hash_warn = 0, hash_crit = 0, hash_exec = 0, hash_every = 0, hash_lookup = 0, hash_units = 0, hash_info = 0;
+ static uint32_t hash_alarm = 0, hash_template = 0, hash_on = 0, hash_calc = 0, hash_green = 0, hash_red = 0, hash_warn = 0, hash_crit = 0, hash_exec = 0, hash_every = 0, hash_lookup = 0, hash_units = 0, hash_info = 0, hash_recipient = 0, hash_delay = 0;
char buffer[HEALTH_CONF_MAX_LINE + 1];
if(unlikely(!hash_alarm)) {
@@ -1073,6 +1386,8 @@ int health_readfile(const char *path, const char *filename) {
hash_every = simple_uhash(HEALTH_EVERY_KEY);
hash_units = simple_hash(HEALTH_UNITS_KEY);
hash_info = simple_hash(HEALTH_INFO_KEY);
+ hash_recipient = simple_hash(HEALTH_RECIPIENT_KEY);
+ hash_delay = simple_uhash(HEALTH_DELAY_KEY);
}
snprintfz(buffer, HEALTH_CONF_MAX_LINE, "%s/%s", path, filename);
@@ -1144,6 +1459,7 @@ int health_readfile(const char *path, const char *filename) {
}
rc = callocz(1, sizeof(RRDCALC));
+ rc->next_event_id = 1;
rc->name = strdupz(value);
rc->hash = simple_hash(rc->name);
rc->source = health_source_file(line, path, filename);
@@ -1151,6 +1467,7 @@ int health_readfile(const char *path, const char *filename) {
rc->red = NAN;
rc->value = NAN;
rc->old_value = NAN;
+ rc->delay_multiplier = 1.0;
if(rrdvar_fix_name(rc->name))
error("Health configuration renamed alarm '%s' to '%s'", value, rc->name);
@@ -1171,6 +1488,7 @@ int health_readfile(const char *path, const char *filename) {
rt->source = health_source_file(line, path, filename);
rt->green = NAN;
rt->red = NAN;
+ rt->delay_multiplier = 1.0;
if(rrdvar_fix_name(rt->name))
error("Health configuration renamed template '%s' to '%s'", value, rt->name);
@@ -1250,6 +1568,16 @@ int health_readfile(const char *path, const char *filename) {
}
rc->exec = strdupz(value);
}
+ else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
+ if(rc->recipient) {
+ if(strcmp(rc->recipient, value))
+ info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rc->name, key, rc->recipient, value, value);
+
+ freez(rc->recipient);
+ }
+ rc->recipient = strdupz(value);
+ }
else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
if(rc->units) {
if(strcmp(rc->units, value))
@@ -1272,6 +1600,9 @@ int health_readfile(const char *path, const char *filename) {
rc->info = strdupz(value);
strip_quotes(rc->info);
}
+ else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
+ health_parse_delay(line, path, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier);
+ }
else {
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has unknown key '%s'.",
line, path, filename, rc->name, key);
@@ -1352,6 +1683,16 @@ int health_readfile(const char *path, const char *filename) {
}
rt->exec = strdupz(value);
}
+ else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
+ if(rt->recipient) {
+ if(strcmp(rt->recipient, value))
+ info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+ line, path, filename, rt->name, key, rt->recipient, value, value);
+
+ freez(rt->recipient);
+ }
+ rt->recipient = strdupz(value);
+ }
else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
if(rt->units) {
if(strcmp(rt->units, value))
@@ -1374,6 +1715,9 @@ int health_readfile(const char *path, const char *filename) {
rt->info = strdupz(value);
strip_quotes(rt->info);
}
+ else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
+ health_parse_delay(line, path, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier);
+ }
else {
error("Health configuration at line %zu of file '%s/%s' for template '%s' has unknown key '%s'.",
line, path, filename, rt->name, key);
@@ -1414,8 +1758,10 @@ void health_readdir(const char *path) {
&& (
(de->d_name[0] == '.' && de->d_name[1] == '\0')
|| (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
- ))
+ )) {
+ debug(D_HEALTH, "Ignoring directory '%s'", de->d_name);
continue;
+ }
else if(de->d_type == DT_DIR) {
char *s = mallocz(pathlen + strlen(de->d_name) + 2);
@@ -1427,10 +1773,12 @@ void health_readdir(const char *path) {
continue;
}
- else if((de->d_type == DT_LNK || de->d_type == DT_REG) &&
+ else if((de->d_type == DT_LNK || de->d_type == DT_REG || de->d_type == DT_UNKNOWN) &&
len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
health_readfile(path, de->d_name);
}
+
+ else debug(D_HEALTH, "Ignoring file '%s'", de->d_name);
}
closedir(dir);
@@ -1450,16 +1798,18 @@ void health_init(void) {
return;
}
+ health_alarm_log_load(&localhost);
+
char *path = health_config_dir();
{
char buffer[FILENAME_MAX + 1];
- snprintfz(buffer, FILENAME_MAX, "%s/alarm-email.sh", config_get("global", "plugins directory", PLUGINS_DIR));
- health_default_exec = config_get("health", "script to execute on alarm", buffer);
+ snprintfz(buffer, FILENAME_MAX, "%s/alarm-notify.sh", config_get("global", "plugins directory", PLUGINS_DIR));
+ health.health_default_exec = config_get("health", "script to execute on alarm", buffer);
}
long n = config_get_number("health", "in memory max health log entries", (long)localhost.health_log.max);
- if(n < 2) {
+ if(n < 10) {
error("Health configuration has invalid max log entries %ld. Using default %u", n, localhost.health_log.max);
config_set_number("health", "in memory max health log entries", (long)localhost.health_log.max);
}
@@ -1480,35 +1830,47 @@ static inline void health_string2json(BUFFER *wb, const char *prefix, const char
buffer_sprintf(wb, "%s\"%s\":null%s", prefix, label, suffix);
}
-static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
+static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host) {
buffer_sprintf(wb, "\n\t{\n"
- "\t\t\"id\":%u,\n"
- "\t\t\"name\":\"%s\",\n"
- "\t\t\"chart\":\"%s\",\n"
- "\t\t\"family\":\"%s\",\n"
- "\t\t\"processed\":%s,\n"
- "\t\t\"updated\":%s,\n"
- "\t\t\"exec_run\":%s,\n"
- "\t\t\"exec_failed\":%s,\n"
- "\t\t\"exec\":\"%s\",\n"
- "\t\t\"exec_code\":%d,\n"
- "\t\t\"source\":\"%s\",\n"
- "\t\t\"units\":\"%s\",\n"
- "\t\t\"info\":\"%s\",\n"
- "\t\t\"when\":%lu,\n"
- "\t\t\"duration\":%lu,\n"
- "\t\t\"non_clear_duration\":%lu,\n"
- "\t\t\"status\":\"%s\",\n"
- "\t\t\"old_status\":\"%s\",\n",
- ae->id,
+ "\t\t\"hostname\": \"%s\",\n"
+ "\t\t\"unique_id\": %u,\n"
+ "\t\t\"alarm_id\": %u,\n"
+ "\t\t\"alarm_event_id\": %u,\n"
+ "\t\t\"name\": \"%s\",\n"
+ "\t\t\"chart\": \"%s\",\n"
+ "\t\t\"family\": \"%s\",\n"
+ "\t\t\"processed\": %s,\n"
+ "\t\t\"updated\": %s,\n"
+ "\t\t\"exec_run\": %lu,\n"
+ "\t\t\"exec_failed\": %s,\n"
+ "\t\t\"exec\": \"%s\",\n"
+ "\t\t\"recipient\": \"%s\",\n"
+ "\t\t\"exec_code\": %d,\n"
+ "\t\t\"source\": \"%s\",\n"
+ "\t\t\"units\": \"%s\",\n"
+ "\t\t\"info\": \"%s\",\n"
+ "\t\t\"when\": %lu,\n"
+ "\t\t\"duration\": %lu,\n"
+ "\t\t\"non_clear_duration\": %lu,\n"
+ "\t\t\"status\": \"%s\",\n"
+ "\t\t\"old_status\": \"%s\",\n"
+ "\t\t\"delay\": %d,\n"
+ "\t\t\"delay_up_to_timestamp\": %lu,\n"
+ "\t\t\"updated_by_id\": %u,\n"
+ "\t\t\"updates_id\": %u,\n",
+ host->hostname,
+ ae->unique_id,
+ ae->alarm_id,
+ ae->alarm_event_id,
ae->name,
ae->chart,
ae->family,
(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED)?"true":"false",
(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED)?"true":"false",
- (ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN)?"true":"false",
+ (unsigned long)ae->exec_run_timestamp,
(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED)?"true":"false",
- ae->exec?ae->exec:health_default_exec,
+ ae->exec?ae->exec:health.health_default_exec,
+ ae->recipient?ae->recipient:health.health_default_recipient,
ae->exec_code,
ae->source,
ae->units?ae->units:"",
@@ -1517,7 +1879,11 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
(unsigned long)ae->duration,
(unsigned long)ae->non_clear_duration,
rrdcalc_status2string(ae->new_status),
- rrdcalc_status2string(ae->old_status)
+ rrdcalc_status2string(ae->old_status),
+ ae->delay,
+ (unsigned long)ae->delay_up_to_timestamp,
+ ae->updated_by_id,
+ ae->updates_id
);
buffer_strcat(wb, "\t\t\"value\":");
@@ -1531,7 +1897,7 @@ static inline void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae) {
buffer_strcat(wb, "\t}");
}
-void health_alarm_log2json(RRDHOST *host, BUFFER *wb) {
+void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after) {
pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
buffer_strcat(wb, "[");
@@ -1540,8 +1906,10 @@ void health_alarm_log2json(RRDHOST *host, BUFFER *wb) {
unsigned int count = 0;
ALARM_ENTRY *ae;
for(ae = host->health_log.alarms; ae && count < max ; count++, ae = ae->next) {
- if(likely(count)) buffer_strcat(wb, ",");
- health_alarm_entry2json_nolock(wb, ae);
+ if(ae->unique_id > after) {
+ if(likely(count)) buffer_strcat(wb, ",");
+ health_alarm_entry2json_nolock(wb, ae, host);
+ }
}
buffer_strcat(wb, "\n]\n");
@@ -1552,11 +1920,13 @@ void health_alarm_log2json(RRDHOST *host, BUFFER *wb) {
static inline void health_rrdcalc2json_nolock(BUFFER *wb, RRDCALC *rc) {
buffer_sprintf(wb,
"\t\t\"%s.%s\": {\n"
+ "\t\t\t\"id\": %lu,\n"
"\t\t\t\"name\": \"%s\",\n"
"\t\t\t\"chart\": \"%s\",\n"
"\t\t\t\"family\": \"%s\",\n"
"\t\t\t\"active\": %s,\n"
"\t\t\t\"exec\": \"%s\",\n"
+ "\t\t\t\"recipient\": \"%s\",\n"
"\t\t\t\"source\": \"%s\",\n"
"\t\t\t\"units\": \"%s\",\n"
"\t\t\t\"info\": \"%s\",\n"
@@ -1565,12 +1935,20 @@ static inline void health_rrdcalc2json_nolock(BUFFER *wb, RRDCALC *rc) {
"\t\t\t\"last_updated\": %lu,\n"
"\t\t\t\"next_update\": %lu,\n"
"\t\t\t\"update_every\": %d,\n"
+ "\t\t\t\"delay_up_duration\": %d,\n"
+ "\t\t\t\"delay_down_duration\": %d,\n"
+ "\t\t\t\"delay_max_duration\": %d,\n"
+ "\t\t\t\"delay_multiplier\": %f,\n"
+ "\t\t\t\"delay\": %d,\n"
+ "\t\t\t\"delay_up_to_timestamp\": %lu,\n"
, rc->chart, rc->name
+ , (unsigned long)rc->id
, rc->name
, rc->chart
, (rc->rrdset && rc->rrdset->family)?rc->rrdset->family:""
, (rc->rrdset)?"true":"false"
- , rc->exec?rc->exec:health_default_exec
+ , rc->exec?rc->exec:health.health_default_exec
+ , rc->recipient?rc->recipient:health.health_default_recipient
, rc->source
, rc->units?rc->units:""
, rc->info?rc->info:""
@@ -1579,6 +1957,12 @@ static inline void health_rrdcalc2json_nolock(BUFFER *wb, RRDCALC *rc) {
, (unsigned long)rc->last_updated
, (unsigned long)rc->next_update
, rc->update_every
+ , rc->delay_up_duration
+ , rc->delay_down_duration
+ , rc->delay_max_duration
+ , rc->delay_multiplier
+ , rc->delay_last
+ , (unsigned long)rc->delay_up_to_timestamp
);
if(RRDCALC_HAS_DB_LOOKUP(rc)) {
@@ -1638,15 +2022,24 @@ static inline void health_rrdcalc2json_nolock(BUFFER *wb, RRDCALC *rc) {
void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) {
int i;
+
rrdhost_rdlock(&localhost);
+ buffer_sprintf(wb, "{\n\t\"hostname\": \"%s\","
+ "\n\t\"latest_alarm_log_unique_id\": %u,"
+ "\n\t\"status\": %s,"
+ "\n\t\"now\": %lu,"
+ "\n\t\"alarms\": {\n",
+ host->hostname,
+ (host->health_log.next_log_id > 0)?(host->health_log.next_log_id - 1):0,
+ health_enabled?"true":"false",
+ (unsigned long)time(NULL));
- buffer_strcat(wb, "{\n\t\"alarms\": {\n");
RRDCALC *rc;
for(i = 0, rc = host->alarms; rc ; rc = rc->next) {
- if(!rc->rrdset)
+ if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
continue;
- if(!all && !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL))
+ if(likely(!all && !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL)))
continue;
if(likely(i)) buffer_strcat(wb, ",\n");
@@ -1655,12 +2048,11 @@ void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) {
}
// buffer_strcat(wb, "\n\t},\n\t\"templates\": {");
-
// RRDCALCTEMPLATE *rt;
// for(rt = host->templates; rt ; rt = rt->next)
// health_rrdcalctemplate2json_nolock(wb, rt);
- buffer_sprintf(wb, "\n\t},\n\t\"now\": %lu\n}\n", (unsigned long)time(NULL));
+ buffer_strcat(wb, "\n\t}\n}\n");
rrdhost_unlock(&localhost);
}
@@ -1684,20 +2076,31 @@ void health_reload(void) {
char *path = health_config_dir();
+ // free all running alarms
rrdhost_rwlock(&localhost);
health_free_all_nolock(&localhost);
rrdhost_unlock(&localhost);
+ // invalidate all previous entries in the alarm log
+ ALARM_ENTRY *t;
+ for(t = localhost.health_log.alarms ; t ; t = t->next) {
+ if(t->new_status != RRDCALC_STATUS_REMOVED)
+ t->notifications |= HEALTH_ENTRY_NOTIFICATIONS_UPDATED;
+ }
+
+ // reset all thresholds to all charts
RRDSET *st;
for(st = localhost.rrdset_root; st ; st = st->next) {
st->green = NAN;
st->red = NAN;
}
+ // load the new alarms
rrdhost_rwlock(&localhost);
health_readdir(path);
rrdhost_unlock(&localhost);
+ // link the loaded alarms to their charts
for(st = localhost.rrdset_root; st ; st = st->next) {
rrdhost_rwlock(&localhost);
@@ -1708,50 +2111,54 @@ void health_reload(void) {
}
}
-
// ----------------------------------------------------------------------------
// health main thread and friends
-static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) {
- if (unlikely(!rc->rrdset)) {
- debug(D_HEALTH, "Health not running alarm '%s.%s'. It is not linked to a chart.", rc->chart?rc->chart:"NOCHART", rc->name);
- return 0;
- }
-
- if (unlikely(!rc->update_every)) {
- debug(D_HEALTH, "Health not running alarm '%s.%s'. It does not have an update frequency", rc->chart?rc->chart:"NOCHART", rc->name);
- return 0;
- }
-
- if (unlikely(rc->next_update > now)) {
- if (*next_run > rc->next_update)
- *next_run = rc->next_update;
-
- debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rc->chart?rc->chart:"NOCHART", rc->name, (int) (rc->next_update - now));
- return 0;
- }
-
- return 1;
-}
-
static inline int rrdcalc_value2status(calculated_number n) {
if(isnan(n)) return RRDCALC_STATUS_UNDEFINED;
if(n) return RRDCALC_STATUS_RAISED;
return RRDCALC_STATUS_CLEAR;
}
-static inline void health_alarm_execute(ALARM_ENTRY *ae) {
- if(ae->old_status == RRDCALC_STATUS_UNINITIALIZED && ae->new_status == RRDCALC_STATUS_CLEAR)
- return;
+static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
+ ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_PROCESSED;
+
+ // find the previous notification for the same alarm
+ ALARM_ENTRY *t;
+ for(t = ae->next; t ;t = t->next) {
+ if(t->alarm_id == ae->alarm_id && t->notifications & HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN)
+ break;
+ }
+
+ if(t && t->new_status == ae->new_status) {
+ // don't send the same notification again
+ info("Health not sending again notification for alarm '%s.%s' status %s", ae->chart, ae->name, rrdcalc_status2string(ae->new_status));
+ goto done;
+ }
+
+ if((ae->old_status == RRDCALC_STATUS_UNDEFINED && ae->new_status == RRDCALC_STATUS_UNINITIALIZED)
+ || (ae->old_status == RRDCALC_STATUS_UNINITIALIZED && ae->new_status == RRDCALC_STATUS_CLEAR)) {
+ info("Health not sending notification for first initialization of alarm '%s.%s' status %s", ae->chart, ae->name, rrdcalc_status2string(ae->new_status));
+ goto done;
+ }
char buffer[FILENAME_MAX + 1];
pid_t command_pid;
const char *exec = ae->exec;
- if(!exec) exec = health_default_exec;
+ if(!exec) exec = health.health_default_exec;
- snprintfz(buffer, FILENAME_MAX, "exec %s '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s'",
+ const char *recipient = ae->recipient;
+ if(!recipient) recipient = health.health_default_recipient;
+
+ snprintfz(buffer, FILENAME_MAX, "exec %s '%s' '%s' '%u' '%u' '%u' '%lu' '%s' '%s' '%s' '%s' '%s' '%0.0Lf' '%0.0Lf' '%s' '%u' '%u' '%s' '%s'",
exec,
+ recipient,
+ host->hostname,
+ ae->unique_id,
+ ae->alarm_id,
+ ae->alarm_event_id,
+ (unsigned long)ae->when,
ae->name,
ae->chart?ae->chart:"NOCAHRT",
ae->family?ae->family:"NOFAMILY",
@@ -1767,25 +2174,29 @@ static inline void health_alarm_execute(ALARM_ENTRY *ae) {
);
ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_EXEC_RUN;
+ ae->exec_run_timestamp = time(NULL);
debug(D_HEALTH, "executing command '%s'", buffer);
FILE *fp = mypopen(buffer, &command_pid);
if(!fp) {
error("HEALTH: Cannot popen(\"%s\", \"r\").", buffer);
- return;
+ goto done;
}
debug(D_HEALTH, "HEALTH reading from command");
char *s = fgets(buffer, FILENAME_MAX, fp);
(void)s;
- debug(D_HEALTH, "HEALTH closing command");
ae->exec_code = mypclose(fp, command_pid);
debug(D_HEALTH, "done executing command - returned with code %d", ae->exec_code);
if(ae->exec_code != 0)
ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED;
+
+done:
+ health_alarm_log_save(host, ae);
+ return;
}
-static inline void health_process_notifications(ALARM_ENTRY *ae) {
+static inline void health_process_notifications(RRDHOST *host, ALARM_ENTRY *ae) {
info("Health alarm '%s.%s' = %0.2Lf - changed status from %s to %s",
ae->chart?ae->chart:"NOCHART", ae->name,
ae->new_value,
@@ -1793,98 +2204,33 @@ static inline void health_process_notifications(ALARM_ENTRY *ae) {
rrdcalc_status2string(ae->new_status)
);
- health_alarm_execute(ae);
+ health_alarm_execute(host, ae);
}
-static inline void health_alarm_log(RRDHOST *host, time_t when,
- const char *name, const char *chart, const char *family,
- const char *exec, time_t duration,
- calculated_number old_value, calculated_number new_value,
- int old_status, int new_status,
- const char *source,
- const char *units,
- const char *info
-) {
- ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY));
- ae->name = strdupz(name);
- ae->hash_name = simple_hash(ae->name);
-
- if(chart) {
- ae->chart = strdupz(chart);
- ae->hash_chart = simple_hash(ae->chart);
- }
-
- if(family)
- ae->family = strdupz(family);
-
- if(exec) ae->exec = strdupz(exec);
- if(source) ae->source = strdupz(source);
- if(units) ae->units = strdupz(units);
- if(info) ae->info = strdupz(info);
-
- ae->id = host->health_log.nextid++;
- ae->when = when;
- ae->old_value = old_value;
- ae->new_value = new_value;
- ae->old_status = old_status;
- ae->new_status = new_status;
- ae->duration = duration;
-
- if(ae->old_status == RRDCALC_STATUS_WARNING || ae->old_status == RRDCALC_STATUS_CRITICAL)
- ae->non_clear_duration += ae->duration;
-
- // link it
- pthread_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
- ae->next = host->health_log.alarms;
- host->health_log.alarms = ae;
- host->health_log.count++;
- pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
+static inline void health_alarm_log_process(RRDHOST *host) {
+ static uint32_t stop_at_id = 0;
+ uint32_t first_waiting = (host->health_log.alarms)?host->health_log.alarms->unique_id:0;
+ time_t now = time(NULL);
- // match previous alarms
pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
- ALARM_ENTRY *t;
- for(t = host->health_log.alarms ; t ; t = t->next) {
- if(t != ae &&
- t->hash_name == ae->hash_name &&
- t->hash_chart == ae->hash_chart &&
- !strcmp(t->name, ae->name) &&
- t->chart && ae->chart && !strcmp(t->chart, ae->chart)) {
-
- if(!(t->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED) && !t->updated_by) {
- t->notifications |= HEALTH_ENTRY_NOTIFICATIONS_UPDATED;
- t->updated_by = ae;
- if((t->new_status == RRDCALC_STATUS_WARNING || t->new_status == RRDCALC_STATUS_CRITICAL) &&
- (t->old_status == RRDCALC_STATUS_WARNING || t->old_status == RRDCALC_STATUS_CRITICAL))
- ae->non_clear_duration += t->non_clear_duration;
- }
- else {
- // no need to continue
- break;
- }
- }
- }
- pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
-}
-
-static inline void health_alarm_log_process(RRDHOST *host) {
- static uint32_t last_processed = 0;
ALARM_ENTRY *ae;
+ for(ae = host->health_log.alarms; ae && ae->unique_id >= stop_at_id ; ae = ae->next) {
+ if(unlikely(
+ !(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED) &&
+ !(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED)
+ )) {
- pthread_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
-
- for(ae = host->health_log.alarms; ae ;ae = ae->next) {
- if(last_processed >= ae->id) break;
+ if(unlikely(ae->unique_id < first_waiting))
+ first_waiting = ae->unique_id;
- if(!(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_PROCESSED) &&
- !(ae->notifications & HEALTH_ENTRY_NOTIFICATIONS_UPDATED)) {
- ae->notifications |= HEALTH_ENTRY_NOTIFICATIONS_PROCESSED;
- health_process_notifications(ae);
+ if(likely(now >= ae->delay_up_to_timestamp))
+ health_process_notifications(host, ae);
}
}
- if(host->health_log.alarms)
- last_processed = host->health_log.alarms->id;
+ // remember this for the next iteration
+ stop_at_id = first_waiting;
pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
@@ -1895,7 +2241,7 @@ static inline void health_alarm_log_process(RRDHOST *host) {
pthread_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
ALARM_ENTRY *last = NULL;
- unsigned int count = host->health_log.max;
+ unsigned int count = host->health_log.max * 2 / 3;
for(ae = host->health_log.alarms; ae && count ; count--, last = ae, ae = ae->next) ;
if(ae && last && last->next == ae)
@@ -1904,23 +2250,60 @@ static inline void health_alarm_log_process(RRDHOST *host) {
ae = NULL;
while(ae) {
+ debug(D_HEALTH, "Health removing alarm log entry with id: %u", ae->unique_id);
+
ALARM_ENTRY *t = ae->next;
freez(ae->name);
freez(ae->chart);
freez(ae->family);
freez(ae->exec);
+ freez(ae->recipient);
freez(ae->source);
freez(ae->units);
freez(ae->info);
freez(ae);
ae = t;
+ host->health_log.count--;
}
pthread_rwlock_unlock(&host->health_log.alarm_log_rwlock);
}
+static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) {
+ if (unlikely(!rc->rrdset)) {
+ debug(D_HEALTH, "Health not running alarm '%s.%s'. It is not linked to a chart.", rc->chart?rc->chart:"NOCHART", rc->name);
+ return 0;
+ }
+
+ if (unlikely(!rc->rrdset->last_collected_time.tv_sec)) {
+ debug(D_HEALTH, "Health not running alarm '%s.%s'. Chart is not yet collected.", rc->chart?rc->chart:"NOCHART", rc->name);
+ return 0;
+ }
+
+ if (unlikely(!rc->update_every)) {
+ debug(D_HEALTH, "Health not running alarm '%s.%s'. It does not have an update frequency", rc->chart?rc->chart:"NOCHART", rc->name);
+ return 0;
+ }
+
+ if (unlikely(rc->next_update > now)) {
+ if (unlikely(*next_run > rc->next_update))
+ *next_run = rc->next_update;
+
+ debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rc->chart?rc->chart:"NOCHART", rc->name, (int) (rc->next_update - now));
+ return 0;
+ }
+
+ // FIXME
+ // we should check that the DB lookup is possible
+ // i.e.
+ // - the duration of the chart includes the required timeframe
+ // we SHOULD NOT check the dimensions - there might be alarms that refer non-existing dimensions (e.g. cpu steal)
+
+ return 1;
+}
+
void *health_main(void *ptr) {
(void)ptr;
@@ -1938,7 +2321,7 @@ void *health_main(void *ptr) {
BUFFER *wb = buffer_create(100);
unsigned int loop = 0;
- while(health_enabled) {
+ while(health_enabled && !netdata_exit) {
loop++;
debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
@@ -2053,7 +2436,7 @@ void *health_main(void *ptr) {
}
rrdhost_unlock(&localhost);
- if (runnable) {
+ if (unlikely(runnable && !netdata_exit)) {
rrdhost_rdlock(&localhost);
for (rc = localhost.alarms; rc; rc = rc->next) {
@@ -2063,7 +2446,7 @@ void *health_main(void *ptr) {
int warning_status = RRDCALC_STATUS_UNDEFINED;
int critical_status = RRDCALC_STATUS_UNDEFINED;
- if(unlikely(rc->warning)) {
+ if(likely(rc->warning)) {
if(unlikely(!expression_evaluate(rc->warning))) {
// calculation failed
@@ -2093,7 +2476,7 @@ void *health_main(void *ptr) {
}
}
- if(unlikely(rc->critical)) {
+ if(likely(rc->critical)) {
if(unlikely(!expression_evaluate(rc->critical))) {
// calculation failed
@@ -2153,7 +2536,34 @@ void *health_main(void *ptr) {
}
if(status != rc->status) {
- health_alarm_log(&localhost, time(NULL), rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, now - rc->last_status_change, rc->old_value, rc->value, rc->status, status, rc->source, rc->units, rc->info);
+ int delay = 0;
+
+ if(now > rc->delay_up_to_timestamp) {
+ rc->delay_up_current = rc->delay_up_duration;
+ rc->delay_down_current = rc->delay_down_duration;
+ rc->delay_last = 0;
+ rc->delay_up_to_timestamp = 0;
+ }
+ else {
+ rc->delay_up_current = (int)(rc->delay_up_current * rc->delay_multiplier);
+ if(rc->delay_up_current > rc->delay_max_duration) rc->delay_up_current = rc->delay_max_duration;
+
+ rc->delay_down_current = (int)(rc->delay_down_current * rc->delay_multiplier);
+ if(rc->delay_down_current > rc->delay_max_duration) rc->delay_down_current = rc->delay_max_duration;
+ }
+
+ if(status > rc->status)
+ delay = rc->delay_up_current;
+ else
+ delay = rc->delay_down_current;
+
+ // COMMENTED: because we do need to send raising alarms
+ // if(now + delay < rc->delay_up_to_timestamp)
+ // delay = (int)(rc->delay_up_to_timestamp - now);
+
+ rc->delay_last = delay;
+ rc->delay_up_to_timestamp = now + delay;
+ health_alarm_log(&localhost, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id, rc->rrdset->family, rc->exec, rc->recipient, now - rc->last_status_change, rc->old_value, rc->value, rc->status, status, rc->source, rc->units, rc->info, rc->delay_last);
rc->last_status_change = now;
rc->status = status;
}
@@ -2171,10 +2581,16 @@ void *health_main(void *ptr) {
if (unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
+ if(unlikely(netdata_exit))
+ break;
+
// execute notifications
// and cleanup
health_alarm_log_process(&localhost);
+ if(unlikely(netdata_exit))
+ break;
+
now = time(NULL);
if(now < next_run) {
debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs",
diff --git a/src/health.h b/src/health.h
index ef1158a29..9d5834fca 100644
--- a/src/health.h
+++ b/src/health.h
@@ -105,8 +105,9 @@ typedef struct rrddimvar {
// having as RRDSET.calculations the RRDCALC to be processed
// next.
-#define RRDCALC_STATUS_UNINITIALIZED 0
+#define RRDCALC_STATUS_REMOVED -2
#define RRDCALC_STATUS_UNDEFINED -1
+#define RRDCALC_STATUS_UNINITIALIZED 0
#define RRDCALC_STATUS_CLEAR 1
#define RRDCALC_STATUS_RAISED 2
#define RRDCALC_STATUS_WARNING 3
@@ -120,52 +121,89 @@ typedef struct rrddimvar {
#define RRDCALC_FLAG_CRIT_ERROR 0x00000020
typedef struct rrdcalc {
- char *name;
- uint32_t hash;
+ uint32_t id; // the unique id of this alarm
+ uint32_t next_event_id; // the next event id that will be used for this alarm
- char *exec;
+ char *name; // the name of this alarm
+ uint32_t hash;
- char *chart; // the chart id this should be linked to
+ char *exec; // the command to execute when this alarm switches state
+ char *recipient; // the recipient of the alarm (the first parameter to exec)
+
+ char *chart; // the chart id this should be linked to
uint32_t hash_chart;
- char *source; // the source of this calculation
- char *units;
- char *info;
+ char *source; // the source of this alarm
+ char *units; // the units of the alarm
+ char *info; // a short description of the alarm
+
+ int update_every; // update frequency for the alarm
+
+ // the red and green threshold of this alarm (to be set to the chart)
+ calculated_number green;
+ calculated_number red;
- char *dimensions; // the chart dimensions
+ // ------------------------------------------------------------------------
+ // database lookup settings
- int group; // grouping method: average, max, etc.
- int before; // ending point in time-series
- int after; // starting point in time-series
- uint32_t options; // calculation options
- int update_every; // update frequency for the calculation
+ char *dimensions; // the chart dimensions
+ int group; // grouping method: average, max, etc.
+ int before; // ending point in time-series
+ int after; // starting point in time-series
+ uint32_t options; // calculation options
- time_t last_updated;
- time_t next_update;
+ // ------------------------------------------------------------------------
+ // expressions related to the alarm
- EVAL_EXPRESSION *calculation;
- EVAL_EXPRESSION *warning;
- EVAL_EXPRESSION *critical;
+ EVAL_EXPRESSION *calculation; // expression to calculate the value of the alarm
+ EVAL_EXPRESSION *warning; // expression to check the warning condition
+ EVAL_EXPRESSION *critical; // expression to check the critical condition
- uint32_t rrdcalc_flags;
- int status;
+ // ------------------------------------------------------------------------
+ // notification delay settings
- time_t db_after;
- time_t db_before;
- time_t last_status_change;
+ int delay_up_duration; // duration to delay notifications when alarm raises
+ int delay_down_duration; // duration to delay notifications when alarm lowers
+ int delay_max_duration; // the absolute max delay to apply to this alarm
+ float delay_multiplier; // multiplier for all delays when alarms switch status
+ // while now < delay_up_to
- calculated_number value;
- calculated_number old_value;
+ // ------------------------------------------------------------------------
+ // runtime information
- calculated_number green;
- calculated_number red;
+ int status; // the current status of the alarm
+
+ calculated_number value; // the current value of the alarm
+ calculated_number old_value; // the previous value of the alarm
+
+ uint32_t rrdcalc_flags; // check RRDCALC_FLAG_*
+
+ time_t last_updated; // the last update timestamp of the alarm
+ time_t next_update; // the next update timestamp of the alarm
+ time_t last_status_change; // the timestamp of the last time this alarm changed status
+
+ time_t db_after; // the first timestamp evaluated by the db lookup
+ time_t db_before; // the last timestamp evaluated by the db lookup
+
+ time_t delay_up_to_timestamp; // the timestamp up to which we should delay notifications
+ int delay_up_current; // the current up notification delay duration
+ int delay_down_current; // the current down notification delay duration
+ int delay_last; // the last delay we used
+
+ // ------------------------------------------------------------------------
+ // variables this alarm exposes to the rest of the alarms
RRDVAR *local;
RRDVAR *family;
RRDVAR *hostid;
RRDVAR *hostname;
+ // ------------------------------------------------------------------------
+ // the chart this alarm it is linked to
+
struct rrdset *rrdset;
+
+ // linking of this alarm on its chart
struct rrdcalc *rrdset_next;
struct rrdcalc *rrdset_prev;
@@ -182,29 +220,45 @@ typedef struct rrdcalctemplate {
uint32_t hash_name;
char *exec;
+ char *recipient;
char *context;
uint32_t hash_context;
- char *source; // the source of this template
- char *units;
- char *info;
+ char *source; // the source of this alarm
+ char *units; // the units of the alarm
+ char *info; // a short description of the alarm
+
+ int update_every; // update frequency for the alarm
- char *dimensions;
+ // the red and green threshold of this alarm (to be set to the chart)
+ calculated_number green;
+ calculated_number red;
+
+ // ------------------------------------------------------------------------
+ // database lookup settings
+
+ char *dimensions; // the chart dimensions
+ int group; // grouping method: average, max, etc.
+ int before; // ending point in time-series
+ int after; // starting point in time-series
+ uint32_t options; // calculation options
+
+ // ------------------------------------------------------------------------
+ // notification delay settings
- int group; // grouping method: average, max, etc.
- int before; // ending point in time-series
- int after; // starting point in time-series
- uint32_t options; // calculation options
- int update_every; // update frequency for the calculation
+ int delay_up_duration; // duration to delay notifications when alarm raises
+ int delay_down_duration; // duration to delay notifications when alarm lowers
+ int delay_max_duration; // the absolute max delay to apply to this alarm
+ float delay_multiplier; // multiplier for all delays when alarms switch status
+
+ // ------------------------------------------------------------------------
+ // expressions related to the alarm
EVAL_EXPRESSION *calculation;
EVAL_EXPRESSION *warning;
EVAL_EXPRESSION *critical;
- calculated_number green;
- calculated_number red;
-
struct rrdcalctemplate *next;
} RRDCALCTEMPLATE;
@@ -216,7 +270,9 @@ typedef struct rrdcalctemplate {
#define HEALTH_ENTRY_NOTIFICATIONS_EXEC_FAILED 0x00000008
typedef struct alarm_entry {
- uint32_t id;
+ uint32_t unique_id;
+ uint32_t alarm_id;
+ uint32_t alarm_event_id;
time_t when;
time_t duration;
@@ -231,6 +287,8 @@ typedef struct alarm_entry {
char *family;
char *exec;
+ char *recipient;
+ time_t exec_run_timestamp;
int exec_code;
char *source;
@@ -244,12 +302,18 @@ typedef struct alarm_entry {
uint32_t notifications;
- struct alarm_entry *updated_by;
+ int delay;
+ time_t delay_up_to_timestamp;
+
+ uint32_t updated_by_id;
+ uint32_t updates_id;
+
struct alarm_entry *next;
} ALARM_ENTRY;
typedef struct alarm_log {
- uint32_t nextid;
+ uint32_t next_log_id;
+ uint32_t next_alarm_id;
unsigned int count;
unsigned int max;
ALARM_ENTRY *alarms;
@@ -278,6 +342,6 @@ extern void health_reload(void);
extern int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result);
extern void health_alarms2json(RRDHOST *host, BUFFER *wb, int all);
-extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb);
+extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb, uint32_t after);
#endif //NETDATA_HEALTH_H
diff --git a/src/log.c b/src/log.c
index e952c5724..b7ec64bcb 100644
--- a/src/log.c
+++ b/src/log.c
@@ -90,27 +90,28 @@ int open_log_file(int fd, FILE **fp, const char *filename, int *enabled_syslog)
void reopen_all_log_files() {
if(stdout_filename)
- open_log_file(STDOUT_FILENO, &stdout, stdout_filename, &output_log_syslog);
+ open_log_file(STDOUT_FILENO, (FILE **)&stdout, stdout_filename, &output_log_syslog);
if(stderr_filename)
- open_log_file(STDERR_FILENO, &stderr, stderr_filename, &error_log_syslog);
+ open_log_file(STDERR_FILENO, (FILE **)&stderr, stderr_filename, &error_log_syslog);
if(stdaccess_filename)
- stdaccess_fd = open_log_file(stdaccess_fd, &stdaccess, stdaccess_filename, &access_log_syslog);
+ stdaccess_fd = open_log_file(stdaccess_fd, (FILE **)&stdaccess, stdaccess_filename, &access_log_syslog);
}
void open_all_log_files() {
// disable stdin
- open_log_file(STDIN_FILENO, &stdin, "/dev/null", NULL);
+ open_log_file(STDIN_FILENO, (FILE **)&stdin, "/dev/null", NULL);
- open_log_file(STDOUT_FILENO, &stdout, stdout_filename, &output_log_syslog);
- open_log_file(STDERR_FILENO, &stderr, stderr_filename, &error_log_syslog);
- stdaccess_fd = open_log_file(stdaccess_fd, &stdaccess, stdaccess_filename, &access_log_syslog);
+ open_log_file(STDOUT_FILENO, (FILE **)&stdout, stdout_filename, &output_log_syslog);
+ open_log_file(STDERR_FILENO, (FILE **)&stderr, stderr_filename, &error_log_syslog);
+ stdaccess_fd = open_log_file(stdaccess_fd, (FILE **)&stdaccess, stdaccess_filename, &access_log_syslog);
}
// ----------------------------------------------------------------------------
// error log throttling
+time_t error_log_throttle_period_backup = 0;
time_t error_log_throttle_period = 1200;
unsigned long error_log_errors_per_period = 200;
@@ -265,6 +266,28 @@ void info_int( const char *file, const char *function, const unsigned long line,
// ----------------------------------------------------------------------------
// error log
+#if defined(STRERROR_R_CHAR_P)
+// GLIBC version of strerror_r
+static const char *strerror_result(const char *a, const char *b) { (void)b; return a; }
+#elif defined(HAVE_STRERROR_R)
+// POSIX version of strerror_r
+static const char *strerror_result(int a, const char *b) { (void)a; return b; }
+#elif defined(HAVE_C__GENERIC)
+
+// what a trick!
+// http://stackoverflow.com/questions/479207/function-overloading-in-c
+static const char *strerror_result_int(int a, const char *b) { (void)a; return b; }
+static const char *strerror_result_string(const char *a, const char *b) { (void)b; return a; }
+
+#define strerror_result(a, b) _Generic((a), \
+ int: strerror_result_int, \
+ char *: strerror_result_string \
+ )(a, b)
+
+#else
+#error "cannot detect the format of function strerror_r()"
+#endif
+
void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... )
{
va_list args;
@@ -282,7 +305,7 @@ void error_int( const char *prefix, const char *file, const char *function, cons
if(errno) {
char buf[1024];
- fprintf(stderr, " (errno %d, %s)\n", errno, strerror_r(errno, buf, 1023));
+ fprintf(stderr, " (errno %d, %s)\n", errno, strerror_result(strerror_r(errno, buf, 1023), buf));
errno = 0;
}
else
diff --git a/src/log.h b/src/log.h
index 93d95321e..3a022fc93 100644
--- a/src/log.h
+++ b/src/log.h
@@ -45,14 +45,14 @@ extern int access_log_syslog;
extern int error_log_syslog;
extern int output_log_syslog;
-extern time_t error_log_throttle_period;
+extern time_t error_log_throttle_period, error_log_throttle_period_backup;
extern unsigned long error_log_errors_per_period;
extern int error_log_limit(int reset);
extern void open_all_log_files();
extern void reopen_all_log_files();
-#define error_log_limit_reset() do { error_log_limit(1); } while(0)
+#define error_log_limit_reset() do { error_log_throttle_period = error_log_throttle_period_backup; error_log_limit(1); } while(0)
#define error_log_limit_unlimited() do { error_log_throttle_period = 0; } while(0)
#define debug(type, args...) do { if(unlikely(debug_flags & type)) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
diff --git a/src/main.c b/src/main.c
index 4d55a1425..3e6aa5046 100644
--- a/src/main.c
+++ b/src/main.c
@@ -276,20 +276,16 @@ static const char *verify_required_directory(const char *dir) {
int main(int argc, char **argv)
{
+ char *hostname = "localhost";
int i, check_config = 0;
int config_loaded = 0;
int dont_fork = 0;
size_t wanted_stacksize = 0, stacksize = 0;
pthread_attr_t attr;
- // global initialization
- get_HZ();
-
// set the name for logging
program_name = "netdata";
- // parse command line.
-
// parse depercated options
// TODO: Remove this block with the next major release.
{
@@ -378,7 +374,7 @@ int main(int argc, char **argv)
break;
case 'v':
// TODO: Outsource version to makefile which can compute version from git.
- printf("netdata 1.3.0\n");
+ printf("netdata %s\n", VERSION);
return 0;
case 'W':
{
@@ -411,6 +407,16 @@ int main(int argc, char **argv)
load_config(NULL, 0);
{
+ char *pmax = config_get("global", "glibc malloc arena max for plugins", "1");
+ if(pmax && *pmax)
+ setenv("MALLOC_ARENA_MAX", pmax, 1);
+
+#if defined(HAVE_C_MALLOPT)
+ int i = config_get_number("global", "glibc malloc arena max for netdata", 1);
+ if(i > 0)
+ mallopt(M_ARENA_MAX, 1);
+#endif
+
char *config_dir = config_get("global", "config directory", CONFIG_DIR);
// prepare configuration environment variables for the plugins
@@ -471,13 +477,18 @@ int main(int argc, char **argv)
global_host_prefix = config_get("global", "host access prefix", "");
setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
+ get_system_HZ();
+ get_system_cpus();
+ get_system_pid_max();
+
// --------------------------------------------------------------------
stdout_filename = config_get("global", "debug log", LOG_DIR "/debug.log");
stderr_filename = config_get("global", "error log", LOG_DIR "/error.log");
stdaccess_filename = config_get("global", "access log", LOG_DIR "/access.log");
- error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
+ error_log_throttle_period_backup =
+ error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period" , ""), 1);
error_log_errors_per_period = (unsigned long)config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
@@ -488,6 +499,7 @@ int main(int argc, char **argv)
error_log_throttle_period = 0;
error_log_errors_per_period = 0;
}
+ error_log_limit_unlimited();
// --------------------------------------------------------------------
@@ -647,6 +659,11 @@ int main(int argc, char **argv)
}
// ------------------------------------------------------------------------
+ // initialize rrd host
+
+ rrdhost_init(hostname);
+
+ // ------------------------------------------------------------------------
// initialize the registry
registry_init();
@@ -660,6 +677,11 @@ int main(int argc, char **argv)
exit(1);
// ------------------------------------------------------------------------
+ // enable log flood protection
+
+ error_log_limit_reset();
+
+ // ------------------------------------------------------------------------
// spawn the threads
web_server_threading_selection();
diff --git a/src/plugin_nfacct.c b/src/plugin_nfacct.c
index b2396fac1..7843161d3 100644
--- a/src/plugin_nfacct.c
+++ b/src/plugin_nfacct.c
@@ -27,11 +27,7 @@ static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
info("nfacct.plugin: increasing nfacct_list to size %d", size);
- nfacct_list = realloc(nfacct_list, sizeof(struct nfacct_list) + (sizeof(struct mynfacct) * size));
- if(!nfacct_list) {
- error("nfacct.plugin: cannot allocate nfacct_list.");
- return MNL_CB_OK;
- }
+ nfacct_list = reallocz(nfacct_list, sizeof(struct nfacct_list) + (sizeof(struct mynfacct) * size));
nfacct_list->data[len].nfacct = nfacct_alloc();
if(!nfacct_list->data[size - 1].nfacct) {
@@ -150,7 +146,7 @@ void *nfacct_main(void *ptr) {
st = rrdset_find_bytype("netfilter", "nfacct_packets");
if(!st) {
- st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 1006, rrd_update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 3206, rrd_update_every, RRDSET_TYPE_STACKED);
for(i = 0; i < nfacct_list->len ; i++)
rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
@@ -170,7 +166,7 @@ void *nfacct_main(void *ptr) {
st = rrdset_find_bytype("netfilter", "nfacct_bytes");
if(!st) {
- st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 1007, rrd_update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 3207, rrd_update_every, RRDSET_TYPE_STACKED);
for(i = 0; i < nfacct_list->len ; i++)
rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
@@ -192,7 +188,7 @@ void *nfacct_main(void *ptr) {
usleep(susec);
// copy current to last
- bcopy(&now, &last, sizeof(struct timeval));
+ memmove(&last, &now, sizeof(struct timeval));
}
mnl_socket_close(nl);
diff --git a/src/plugin_proc.c b/src/plugin_proc.c
index a1bf314de..a50a22514 100644
--- a/src/plugin_proc.c
+++ b/src/plugin_proc.c
@@ -28,10 +28,12 @@ void *proc_main(void *ptr)
int vdo_proc_stat = !config_get_boolean("plugin:proc", "/proc/stat", 1);
int vdo_proc_meminfo = !config_get_boolean("plugin:proc", "/proc/meminfo", 1);
int vdo_proc_vmstat = !config_get_boolean("plugin:proc", "/proc/vmstat", 1);
+ int vdo_proc_net_rpc_nfs = !config_get_boolean("plugin:proc", "/proc/net/rpc/nfs", 1);
int vdo_proc_net_rpc_nfsd = !config_get_boolean("plugin:proc", "/proc/net/rpc/nfsd", 1);
int vdo_proc_sys_kernel_random_entropy_avail = !config_get_boolean("plugin:proc", "/proc/sys/kernel/random/entropy_avail", 1);
int vdo_proc_interrupts = !config_get_boolean("plugin:proc", "/proc/interrupts", 1);
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_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);
@@ -48,10 +50,12 @@ void *proc_main(void *ptr)
unsigned long long sutime_proc_stat = 0ULL;
unsigned long long sutime_proc_meminfo = 0ULL;
unsigned long long sutime_proc_vmstat = 0ULL;
+ unsigned long long sutime_proc_net_rpc_nfs = 0ULL;
unsigned long long sutime_proc_net_rpc_nfsd = 0ULL;
unsigned long long sutime_proc_sys_kernel_random_entropy_avail = 0ULL;
unsigned long long sutime_proc_interrupts = 0ULL;
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_sys_kernel_mm_ksm = 0ULL;
@@ -59,7 +63,7 @@ void *proc_main(void *ptr)
unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
unsigned long long sunow;
- for(;1;) {
+ for(;;) {
if(unlikely(netdata_exit)) break;
// delay until it is our time to run
@@ -107,6 +111,14 @@ void *proc_main(void *ptr)
}
if(unlikely(netdata_exit)) break;
+ if(!vdo_proc_net_softnet_stat) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_softnet_stat().");
+ sunow = time_usec();
+ vdo_proc_net_softnet_stat = do_proc_net_softnet_stat(rrd_update_every, (sutime_proc_net_softnet_stat > 0)?sunow - sutime_proc_net_softnet_stat:0ULL);
+ sutime_proc_net_softnet_stat = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
if(!vdo_proc_sys_kernel_random_entropy_avail) {
debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_sys_kernel_random_entropy_avail().");
sunow = time_usec();
@@ -211,6 +223,14 @@ void *proc_main(void *ptr)
}
if(unlikely(netdata_exit)) break;
+ if(!vdo_proc_net_rpc_nfs) {
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_rpc_nfs().");
+ sunow = time_usec();
+ vdo_proc_net_rpc_nfs = do_proc_net_rpc_nfs(rrd_update_every, (sutime_proc_net_rpc_nfs > 0)?sunow - sutime_proc_net_rpc_nfs:0ULL);
+ sutime_proc_net_rpc_nfs = sunow;
+ }
+ if(unlikely(netdata_exit)) break;
+
// END -- the job is done
// --------------------------------------------------------------------
@@ -221,6 +241,8 @@ void *proc_main(void *ptr)
}
}
+ info("PROC thread exiting");
+
pthread_exit(NULL);
return NULL;
}
diff --git a/src/plugin_proc.h b/src/plugin_proc.h
index a512e1cdf..f72a9970f 100644
--- a/src/plugin_proc.h
+++ b/src/plugin_proc.h
@@ -13,6 +13,7 @@ extern int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt);
extern int do_proc_stat(int update_every, unsigned long long dt);
extern int do_proc_meminfo(int update_every, unsigned long long dt);
extern int do_proc_vmstat(int update_every, unsigned long long dt);
+extern int do_proc_net_rpc_nfs(int update_every, unsigned long long dt);
extern int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt);
extern int do_proc_sys_kernel_random_entropy_avail(int update_every, unsigned long long dt);
extern int do_proc_interrupts(int update_every, unsigned long long dt);
@@ -20,5 +21,6 @@ extern int do_proc_softirqs(int update_every, unsigned long long dt);
extern int do_sys_kernel_mm_ksm(int update_every, unsigned long long dt);
extern int do_proc_loadavg(int update_every, unsigned long long dt);
extern int do_proc_net_stat_synproxy(int update_every, unsigned long long dt);
+extern int do_proc_net_softnet_stat(int update_every, unsigned long long dt);
#endif /* NETDATA_PLUGIN_PROC_H */
diff --git a/src/plugin_tc.c b/src/plugin_tc.c
index 408069dba..399fcd6db 100644
--- a/src/plugin_tc.c
+++ b/src/plugin_tc.c
@@ -39,6 +39,8 @@ struct tc_class {
RRDDIM *rd_bytes;
RRDDIM *rd_packets;
RRDDIM *rd_dropped;
+ RRDDIM *rd_tokens;
+ RRDDIM *rd_ctokens;
char name_updated;
char updated; // updated bytes
@@ -64,10 +66,14 @@ struct tc_device {
char enabled_bytes;
char enabled_packets;
char enabled_dropped;
+ char enabled_tokens;
+ char enabled_ctokens;
RRDSET *st_bytes;
RRDSET *st_packets;
RRDSET *st_dropped;
+ RRDSET *st_tokens;
+ RRDSET *st_ctokens;
avl_tree classes_index;
@@ -177,18 +183,20 @@ static inline void tc_device_classes_cleanup(struct tc_device *d) {
}
static inline void tc_device_commit(struct tc_device *d) {
- static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1;
+ static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1, enable_tokens = -1, enable_ctokens = -1;
if(unlikely(enable_new_interfaces == -1)) {
enable_new_interfaces = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_YES);
enable_bytes = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
enable_packets = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
enable_dropped = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+ enable_tokens = config_get_boolean_ondemand("plugin:tc", "enable tokens charts for all interfaces", CONFIG_ONDEMAND_NO);
+ enable_ctokens = config_get_boolean_ondemand("plugin:tc", "enable ctokens charts for all interfaces", CONFIG_ONDEMAND_NO);
}
// we only need to add leaf classes
struct tc_class *c, *x;
- unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0;
+ unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0, tokens_sum = 0, ctokens_sum = 0;
int active_classes = 0;
// set all classes
@@ -216,23 +224,26 @@ static inline void tc_device_commit(struct tc_device *d) {
}
}
- // debugging:
+ // debugging only
/*
- for ( c = d->classes ; c ; c = c->next) {
- if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device %s, class %s, OK", d->name, c->id);
- else debug(D_TC_LOOP, "TC: Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid);
+ if(unlikely(debug_flags & D_TC_LOOP)) {
+ for(c = d->classes ; c ; c = c->next) {
+ if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device '%s', class %s, OK", d->name, c->id);
+ else debug(D_TC_LOOP, "TC: Device '%s', class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name?d->name:d->id, c->id, c->isleaf, c->hasparent, c->parentid?c->parentid:"(unset)");
+ }
}
*/
// we need at least a class
for(c = d->classes ; c ; c = c->next) {
// debug(D_TC_LOOP, "TC: Device '%s', class '%s', isLeaf=%d, HasParent=%d, Seen=%d", d->name?d->name:d->id, c->name?c->name:c->id, c->isleaf, c->hasparent, c->seen);
- if(!c->updated) continue;
- if(c->isleaf && c->hasparent) {
+ if(unlikely(c->updated && c->isleaf && c->hasparent)) {
active_classes++;
bytes_sum += c->bytes;
packets_sum += c->packets;
dropped_sum += c->dropped;
+ tokens_sum += c->tokens;
+ ctokens_sum += c->ctokens;
}
}
@@ -242,7 +253,7 @@ static inline void tc_device_commit(struct tc_device *d) {
return;
}
- if(unlikely(d->enabled == -1)) {
+ if(unlikely(d->enabled == (char)-1)) {
char var_name[CONFIG_MAX_NAME + 1];
snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id);
d->enabled = config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces);
@@ -255,8 +266,30 @@ static inline void tc_device_commit(struct tc_device *d) {
snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id);
d->enabled_dropped = config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped);
+
+ snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id);
+ d->enabled_tokens = config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens);
+
+ snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id);
+ d->enabled_ctokens = config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens);
}
+ debug(D_TC_LOOP, "TC: evaluating TC device '%s'. enabled = %d/%d (bytes: %d/%d, packets: %d/%d, dropped: %d/%d, tokens: %d/%d, ctokens: %d/%d), classes = %d (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).",
+ d->name?d->name:d->id,
+ d->enabled, enable_new_interfaces,
+ d->enabled_bytes, enable_bytes,
+ d->enabled_packets, enable_packets,
+ d->enabled_dropped, enable_dropped,
+ d->enabled_tokens, enable_tokens,
+ d->enabled_ctokens, enable_ctokens,
+ active_classes,
+ bytes_sum,
+ packets_sum,
+ dropped_sum,
+ tokens_sum,
+ ctokens_sum
+ );
+
if(likely(d->enabled)) {
// --------------------------------------------------------------------
// bytes
@@ -421,6 +454,114 @@ static inline void tc_device_commit(struct tc_device *d) {
}
rrdset_done(d->st_dropped);
}
+
+ // --------------------------------------------------------------------
+ // tokens
+
+ if(d->enabled_tokens == CONFIG_ONDEMAND_YES || (d->enabled_tokens == CONFIG_ONDEMAND_ONDEMAND && tokens_sum)) {
+ d->enabled_tokens = CONFIG_ONDEMAND_YES;
+
+ if(unlikely(!d->st_tokens)) {
+ char id[RRD_ID_LENGTH_MAX + 1];
+ char name[RRD_ID_LENGTH_MAX + 1];
+ snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id);
+ snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id);
+
+ d->st_tokens = rrdset_find_bytype(RRD_TYPE_TC, id);
+ if(unlikely(!d->st_tokens)) {
+ debug(D_TC_LOOP, "TC: Creating new _tokens chart for device '%s'", d->name?d->name:d->id);
+ d->st_tokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_tokens", "Class Tokens", "tokens", 7030, rrd_update_every, RRDSET_TYPE_LINE);
+ }
+ }
+ else {
+ debug(D_TC_LOOP, "TC: Updating _tokens chart for device '%s'", d->name?d->name:d->id);
+ rrdset_next_plugins(d->st_tokens);
+
+ // FIXME
+ // update the family
+ }
+
+ for(c = d->classes ; c ; c = c->next) {
+ if(unlikely(!c->updated)) continue;
+
+ if(c->isleaf && c->hasparent) {
+ if(unlikely(!c->rd_tokens)) {
+ c->rd_tokens = rrddim_find(d->st_tokens, c->id);
+ if(unlikely(!c->rd_tokens)) {
+ debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_tokens->id, c->id, c->name);
+
+ // new class, we have to add it
+ c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_tokens->id, c->id);
+ }
+
+ rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens);
+
+ // if it has a name, different to the id
+ if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+ // update the rrd dimension with the new name
+ debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_tokens->id, c->rd_tokens->id, c->name);
+ rrddim_set_name(d->st_tokens, c->rd_tokens, c->name);
+ }
+ }
+ }
+ rrdset_done(d->st_tokens);
+ }
+
+ // --------------------------------------------------------------------
+ // ctokens
+
+ if(d->enabled_ctokens == CONFIG_ONDEMAND_YES || (d->enabled_ctokens == CONFIG_ONDEMAND_ONDEMAND && ctokens_sum)) {
+ d->enabled_ctokens = CONFIG_ONDEMAND_YES;
+
+ if(unlikely(!d->st_ctokens)) {
+ char id[RRD_ID_LENGTH_MAX + 1];
+ char name[RRD_ID_LENGTH_MAX + 1];
+ snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id);
+ snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id);
+
+ d->st_ctokens = rrdset_find_bytype(RRD_TYPE_TC, id);
+ if(unlikely(!d->st_ctokens)) {
+ debug(D_TC_LOOP, "TC: Creating new _ctokens chart for device '%s'", d->name?d->name:d->id);
+ d->st_ctokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_ctokens", "Class cTokens", "ctokens", 7040, rrd_update_every, RRDSET_TYPE_LINE);
+ }
+ }
+ else {
+ debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id);
+ rrdset_next_plugins(d->st_ctokens);
+
+ // FIXME
+ // update the family
+ }
+
+ for(c = d->classes ; c ; c = c->next) {
+ if(unlikely(!c->updated)) continue;
+
+ if(c->isleaf && c->hasparent) {
+ if(unlikely(!c->rd_ctokens)) {
+ c->rd_ctokens = rrddim_find(d->st_ctokens, c->id);
+ if(unlikely(!c->rd_ctokens)) {
+ debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_ctokens->id, c->id, c->name);
+
+ // new class, we have to add it
+ c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE);
+ }
+ else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_ctokens->id, c->id);
+ }
+
+ rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens);
+
+ // if it has a name, different to the id
+ if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+ // update the rrd dimension with the new name
+ debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_ctokens->id, c->rd_ctokens->id, c->name);
+ rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name);
+ }
+ }
+ }
+ rrdset_done(d->st_ctokens);
+ }
}
tc_device_classes_cleanup(d);
@@ -475,7 +616,7 @@ static inline struct tc_device *tc_device_create(char *id)
d->id = strdupz(id);
d->hash = simple_hash(d->id);
- d->enabled = -1;
+ d->enabled = (char)-1;
avl_init(&d->classes_index, tc_class_compare);
tc_device_index_add(d);
diff --git a/src/plugins_d.c b/src/plugins_d.c
index 627cc90e5..0030e2216 100644
--- a/src/plugins_d.c
+++ b/src/plugins_d.c
@@ -109,7 +109,7 @@ void *pluginsd_worker_thread(void *arg)
size_t count = 0;
- while(likely(1)) {
+ for(;;) {
if(unlikely(netdata_exit)) break;
FILE *fp = mypopen(cd->cmd, &cd->pid);
@@ -358,7 +358,7 @@ void *pluginsd_worker_thread(void *arg)
error("PLUGINSD: %s sleeping for %llu. Will kill with SIGCONT pid %d to wake it up.\n", cd->fullfilename, susec, cd->pid);
usleep(susec);
killpid(cd->pid, SIGCONT);
- bcopy(&now, &last, sizeof(struct timeval));
+ memmove(&last, &now, sizeof(struct timeval));
break;
}
#endif
@@ -380,16 +380,9 @@ void *pluginsd_worker_thread(void *arg)
// get the return code
int code = mypclose(fp, cd->pid);
-
- if(netdata_exit) {
- cd->pid = 0;
- cd->enabled = 0;
- cd->obsolete = 1;
- pthread_exit(NULL);
- return NULL;
- }
-
- if(code != 0) {
+
+ if(unlikely(netdata_exit)) break;
+ else if(code != 0) {
// the plugin reports failure
if(likely(!cd->successful_collections)) {
@@ -430,10 +423,11 @@ void *pluginsd_worker_thread(void *arg)
}
cd->pid = 0;
- if(unlikely(!cd->enabled))
- break;
+ if(unlikely(!cd->enabled)) break;
}
+ info("PLUGINSD: '%s' thread exiting", cd->fullfilename);
+
cd->obsolete = 1;
pthread_exit(NULL);
return NULL;
@@ -462,7 +456,7 @@ void *pluginsd_main(void *ptr) {
if(scan_frequency < 1) scan_frequency = 1;
- while(likely(1)) {
+ for(;;) {
if(unlikely(netdata_exit)) break;
dir = opendir(dir_name);
@@ -542,6 +536,8 @@ void *pluginsd_main(void *ptr) {
sleep((unsigned int) scan_frequency);
}
+ info("PLUGINS.D thread exiting");
+
pthread_exit(NULL);
return NULL;
}
diff --git a/src/popen.c b/src/popen.c
index 193efc0f3..ad8d7596f 100644
--- a/src/popen.c
+++ b/src/popen.c
@@ -147,6 +147,13 @@ int mypclose(FILE *fp, pid_t pid) {
debug(D_EXIT, "Request to mypclose() on pid %d", pid);
/*mypopen_del(fp);*/
+
+ // close the pipe fd
+ // this is required in musl
+ // without it the childs do not exit
+ close(fileno(fp));
+
+ // close the pipe file pointer
fclose(fp);
siginfo_t info;
diff --git a/src/proc_interrupts.c b/src/proc_interrupts.c
index fe0e9b1a5..f277a5a90 100644
--- a/src/proc_interrupts.c
+++ b/src/proc_interrupts.c
@@ -21,8 +21,7 @@ static inline struct interrupt *get_interrupts_array(int lines, int cpus) {
static struct interrupt *irrs = NULL;
static int allocated = 0;
- if(lines < allocated) return irrs;
- else {
+ if(lines > allocated) {
irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus));
allocated = lines;
}
@@ -143,15 +142,14 @@ int do_proc_interrupts(int update_every, unsigned long long dt) {
int c;
for(c = 0; c < cpus ; c++) {
- char id[256+1];
- snprintfz(id, 256, "cpu%d_interrupts", c);
+ char id[50+1];
+ snprintfz(id, 50, "cpu%d_interrupts", c);
st = rrdset_find_bytype("cpu", id);
if(!st) {
- char name[256+1], title[256+1];
- snprintfz(name, 256, "cpu%d_interrupts", c);
- snprintfz(title, 256, "CPU%d Interrupts", c);
- st = rrdset_create("cpu", id, name, "interrupts", "cpu.interrupts", title, "interrupts/s", 2000 + c, update_every, RRDSET_TYPE_STACKED);
+ char title[100+1];
+ snprintfz(title, 100, "CPU%d Interrupts", c);
+ st = rrdset_create("cpu", id, NULL, "interrupts", "cpu.interrupts", title, "interrupts/s", 1100 + c, update_every, RRDSET_TYPE_STACKED);
for(l = 0; l < lines ;l++) {
struct interrupt *irr = irrindex(irrs, l, cpus);
diff --git a/src/proc_meminfo.c b/src/proc_meminfo.c
index 4295cd6da..999c9538d 100644
--- a/src/proc_meminfo.c
+++ b/src/proc_meminfo.c
@@ -9,14 +9,14 @@ int do_proc_meminfo(int update_every, unsigned long long dt) {
static int do_ram = -1, do_swap = -1, do_hwcorrupt = -1, do_committed = -1, do_writeback = -1, do_kernel = -1, do_slab = -1;
if(do_ram == -1) do_ram = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1);
- if(do_swap == -1) do_swap = config_get_boolean("plugin:proc:/proc/meminfo", "system swap", 1);
+ if(do_swap == -1) do_swap = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "system swap", CONFIG_ONDEMAND_ONDEMAND);
if(do_hwcorrupt == -1) do_hwcorrupt = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_ONDEMAND_ONDEMAND);
if(do_committed == -1) do_committed = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1);
if(do_writeback == -1) do_writeback = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1);
if(do_kernel == -1) do_kernel = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1);
if(do_slab == -1) do_slab = config_get_boolean("plugin:proc:/proc/meminfo", "slab memory", 1);
- if(dt) {};
+ (void)dt;
if(!ff) {
char filename[FILENAME_MAX + 1];
@@ -105,15 +105,15 @@ int do_proc_meminfo(int update_every, unsigned long long dt) {
if(!st) {
st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
- rrddim_add(st, "buffers", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "free", NULL, 1, 1024, RRDDIM_ABSOLUTE);
rrddim_add(st, "used", NULL, 1, 1024, RRDDIM_ABSOLUTE);
rrddim_add(st, "cached", NULL, 1, 1024, RRDDIM_ABSOLUTE);
- rrddim_add(st, "free", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "buffers", NULL, 1, 1024, RRDDIM_ABSOLUTE);
}
else rrdset_next(st);
- rrddim_set(st, "used", MemUsed);
rrddim_set(st, "free", MemFree);
+ rrddim_set(st, "used", MemUsed);
rrddim_set(st, "cached", Cached);
rrddim_set(st, "buffers", Buffers);
rrdset_done(st);
@@ -123,7 +123,9 @@ int do_proc_meminfo(int update_every, unsigned long long dt) {
unsigned long long SwapUsed = SwapTotal - SwapFree;
- if(do_swap) {
+ if(SwapTotal || SwapUsed || SwapFree || do_swap == CONFIG_ONDEMAND_YES) {
+ do_swap = CONFIG_ONDEMAND_YES;
+
st = rrdset_find("system.swap");
if(!st) {
st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
diff --git a/src/proc_net_ip_vs_stats.c b/src/proc_net_ip_vs_stats.c
index 96efd7925..de3e0e460 100644
--- a/src/proc_net_ip_vs_stats.c
+++ b/src/proc_net_ip_vs_stats.c
@@ -44,7 +44,7 @@ int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt) {
if(do_sockets) {
st = rrdset_find(RRD_TYPE_NET_IPVS ".sockets");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS New Connections", "connections/s", 3101, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_INCREMENTAL);
}
@@ -59,7 +59,7 @@ int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt) {
if(do_packets) {
st = rrdset_find(RRD_TYPE_NET_IPVS ".packets");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Packets", "packets/s", 1002, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Packets", "packets/s", 3102, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
@@ -76,7 +76,7 @@ int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt) {
if(do_bandwidth) {
st = rrdset_find(RRD_TYPE_NET_IPVS ".net");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Bandwidth", "kilobits/s", 1000, update_every, RRDSET_TYPE_AREA);
+ st = rrdset_create(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Bandwidth", "kilobits/s", 3100, update_every, RRDSET_TYPE_AREA);
rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
diff --git a/src/proc_net_netstat.c b/src/proc_net_netstat.c
index fe1c4d979..ea38acf29 100644
--- a/src/proc_net_netstat.c
+++ b/src/proc_net_netstat.c
@@ -1,177 +1,838 @@
#include "common.h"
+
+struct netstat_columns {
+ char *name;
+ uint32_t hash;
+ unsigned long long value;
+ int multiplier; // not needed everywhere
+ char *label; // not needed everywhere
+};
+
+static struct netstat_columns tcpext_data[] = {
+ { "SyncookiesSent", 0, 0, 1, NULL },
+ { "SyncookiesRecv", 0, 0, 1, NULL },
+ { "SyncookiesFailed", 0, 0, 1, NULL },
+ { "EmbryonicRsts", 0, 0, 1, NULL },
+ { "PruneCalled", 0, 0, 1, NULL },
+ { "RcvPruned", 0, 0, 1, NULL },
+ { "OfoPruned", 0, 0, 1, NULL },
+ { "OutOfWindowIcmps", 0, 0, 1, NULL },
+ { "LockDroppedIcmps", 0, 0, 1, NULL },
+ { "ArpFilter", 0, 0, 1, NULL },
+ { "TW", 0, 0, 1, NULL },
+ { "TWRecycled", 0, 0, 1, NULL },
+ { "TWKilled", 0, 0, 1, NULL },
+ { "PAWSPassive", 0, 0, 1, NULL },
+ { "PAWSActive", 0, 0, 1, NULL },
+ { "PAWSEstab", 0, 0, 1, NULL },
+ { "DelayedACKs", 0, 0, 1, NULL },
+ { "DelayedACKLocked", 0, 0, 1, NULL },
+ { "DelayedACKLost", 0, 0, 1, NULL },
+ { "ListenOverflows", 0, 0, 1, NULL },
+ { "ListenDrops", 0, 0, 1, NULL },
+ { "TCPPrequeued", 0, 0, 1, NULL },
+ { "TCPDirectCopyFromBacklog", 0, 0, 1, NULL },
+ { "TCPDirectCopyFromPrequeue", 0, 0, 1, NULL },
+ { "TCPPrequeueDropped", 0, 0, 1, NULL },
+ { "TCPHPHits", 0, 0, 1, NULL },
+ { "TCPHPHitsToUser", 0, 0, 1, NULL },
+ { "TCPPureAcks", 0, 0, 1, NULL },
+ { "TCPHPAcks", 0, 0, 1, NULL },
+ { "TCPRenoRecovery", 0, 0, 1, NULL },
+ { "TCPSackRecovery", 0, 0, 1, NULL },
+ { "TCPSACKReneging", 0, 0, 1, NULL },
+ { "TCPFACKReorder", 0, 0, 1, NULL },
+ { "TCPSACKReorder", 0, 0, 1, NULL },
+ { "TCPRenoReorder", 0, 0, 1, NULL },
+ { "TCPTSReorder", 0, 0, 1, NULL },
+ { "TCPFullUndo", 0, 0, 1, NULL },
+ { "TCPPartialUndo", 0, 0, 1, NULL },
+ { "TCPDSACKUndo", 0, 0, 1, NULL },
+ { "TCPLossUndo", 0, 0, 1, NULL },
+ { "TCPLostRetransmit", 0, 0, 1, NULL },
+ { "TCPRenoFailures", 0, 0, 1, NULL },
+ { "TCPSackFailures", 0, 0, 1, NULL },
+ { "TCPLossFailures", 0, 0, 1, NULL },
+ { "TCPFastRetrans", 0, 0, 1, NULL },
+ { "TCPForwardRetrans", 0, 0, 1, NULL },
+ { "TCPSlowStartRetrans", 0, 0, 1, NULL },
+ { "TCPTimeouts", 0, 0, 1, NULL },
+ { "TCPLossProbes", 0, 0, 1, NULL },
+ { "TCPLossProbeRecovery", 0, 0, 1, NULL },
+ { "TCPRenoRecoveryFail", 0, 0, 1, NULL },
+ { "TCPSackRecoveryFail", 0, 0, 1, NULL },
+ { "TCPSchedulerFailed", 0, 0, 1, NULL },
+ { "TCPRcvCollapsed", 0, 0, 1, NULL },
+ { "TCPDSACKOldSent", 0, 0, 1, NULL },
+ { "TCPDSACKOfoSent", 0, 0, 1, NULL },
+ { "TCPDSACKRecv", 0, 0, 1, NULL },
+ { "TCPDSACKOfoRecv", 0, 0, 1, NULL },
+ { "TCPAbortOnData", 0, 0, 1, NULL },
+ { "TCPAbortOnClose", 0, 0, 1, NULL },
+ { "TCPAbortOnMemory", 0, 0, 1, NULL },
+ { "TCPAbortOnTimeout", 0, 0, 1, NULL },
+ { "TCPAbortOnLinger", 0, 0, 1, NULL },
+ { "TCPAbortFailed", 0, 0, 1, NULL },
+ { "TCPMemoryPressures", 0, 0, 1, NULL },
+ { "TCPSACKDiscard", 0, 0, 1, NULL },
+ { "TCPDSACKIgnoredOld", 0, 0, 1, NULL },
+ { "TCPDSACKIgnoredNoUndo", 0, 0, 1, NULL },
+ { "TCPSpuriousRTOs", 0, 0, 1, NULL },
+ { "TCPMD5NotFound", 0, 0, 1, NULL },
+ { "TCPMD5Unexpected", 0, 0, 1, NULL },
+ { "TCPSackShifted", 0, 0, 1, NULL },
+ { "TCPSackMerged", 0, 0, 1, NULL },
+ { "TCPSackShiftFallback", 0, 0, 1, NULL },
+ { "TCPBacklogDrop", 0, 0, 1, NULL },
+ { "TCPMinTTLDrop", 0, 0, 1, NULL },
+ { "TCPDeferAcceptDrop", 0, 0, 1, NULL },
+ { "IPReversePathFilter", 0, 0, 1, NULL },
+ { "TCPTimeWaitOverflow", 0, 0, 1, NULL },
+ { "TCPReqQFullDoCookies", 0, 0, 1, NULL },
+ { "TCPReqQFullDrop", 0, 0, 1, NULL },
+ { "TCPRetransFail", 0, 0, 1, NULL },
+ { "TCPRcvCoalesce", 0, 0, 1, NULL },
+ { "TCPOFOQueue", 0, 0, 1, NULL },
+ { "TCPOFODrop", 0, 0, 1, NULL },
+ { "TCPOFOMerge", 0, 0, 1, NULL },
+ { "TCPChallengeACK", 0, 0, 1, NULL },
+ { "TCPSYNChallenge", 0, 0, 1, NULL },
+ { "TCPFastOpenActive", 0, 0, 1, NULL },
+ { "TCPFastOpenActiveFail", 0, 0, 1, NULL },
+ { "TCPFastOpenPassive", 0, 0, 1, NULL },
+ { "TCPFastOpenPassiveFail", 0, 0, 1, NULL },
+ { "TCPFastOpenListenOverflow", 0, 0, 1, NULL },
+ { "TCPFastOpenCookieReqd", 0, 0, 1, NULL },
+ { "TCPSpuriousRtxHostQueues", 0, 0, 1, NULL },
+ { "BusyPollRxPackets", 0, 0, 1, NULL },
+ { "TCPAutoCorking", 0, 0, 1, NULL },
+ { "TCPFromZeroWindowAdv", 0, 0, 1, NULL },
+ { "TCPToZeroWindowAdv", 0, 0, 1, NULL },
+ { "TCPWantZeroWindowAdv", 0, 0, 1, NULL },
+ { "TCPSynRetrans", 0, 0, 1, NULL },
+ { "TCPOrigDataSent", 0, 0, 1, NULL },
+ { "TCPHystartTrainDetect", 0, 0, 1, NULL },
+ { "TCPHystartTrainCwnd", 0, 0, 1, NULL },
+ { "TCPHystartDelayDetect", 0, 0, 1, NULL },
+ { "TCPHystartDelayCwnd", 0, 0, 1, NULL },
+ { "TCPACKSkippedSynRecv", 0, 0, 1, NULL },
+ { "TCPACKSkippedPAWS", 0, 0, 1, NULL },
+ { "TCPACKSkippedSeq", 0, 0, 1, NULL },
+ { "TCPACKSkippedFinWait2", 0, 0, 1, NULL },
+ { "TCPACKSkippedTimeWait", 0, 0, 1, NULL },
+ { "TCPACKSkippedChallenge", 0, 0, 1, NULL },
+ { "TCPWinProbe", 0, 0, 1, NULL },
+ { "TCPKeepAlive", 0, 0, 1, NULL },
+ { "TCPMTUPFail", 0, 0, 1, NULL },
+ { "TCPMTUPSuccess", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns ipext_data[] = {
+ { "InNoRoutes", 0, 0, 1, NULL },
+ { "InTruncatedPkts", 0, 0, 1, NULL },
+ { "InMcastPkts", 0, 0, 1, NULL },
+ { "OutMcastPkts", 0, 0, 1, NULL },
+ { "InBcastPkts", 0, 0, 1, NULL },
+ { "OutBcastPkts", 0, 0, 1, NULL },
+ { "InOctets", 0, 0, 1, NULL },
+ { "OutOctets", 0, 0, 1, NULL },
+ { "InMcastOctets", 0, 0, 1, NULL },
+ { "OutMcastOctets", 0, 0, 1, NULL },
+ { "InBcastOctets", 0, 0, 1, NULL },
+ { "OutBcastOctets", 0, 0, 1, NULL },
+ { "InCsumErrors", 0, 0, 1, NULL },
+ { "InNoECTPkts", 0, 0, 1, NULL },
+ { "InECT1Pkts", 0, 0, 1, NULL },
+ { "InECT0Pkts", 0, 0, 1, NULL },
+ { "InCEPkts", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static void hash_array(struct netstat_columns *nc) {
+ int i;
+
+ for(i = 0; nc[i].name ;i++)
+ nc[i].hash = simple_hash(nc[i].name);
+}
+
+static unsigned long long *netstat_columns_find(struct netstat_columns *nc, const char *name) {
+ uint32_t i, hash = simple_hash(name);
+
+ for(i = 0; nc[i].name ;i++)
+ if(unlikely(nc[i].hash == hash && !strcmp(nc[i].name, name)))
+ return &nc[i].value;
+
+ fatal("Cannot find key '%s' in /proc/net/netstat internal array.", name);
+}
+
+static void parse_line_pair(procfile *ff, struct netstat_columns *nc, uint32_t header_line, uint32_t values_line) {
+ uint32_t hwords = procfile_linewords(ff, header_line);
+ uint32_t vwords = procfile_linewords(ff, values_line);
+ uint32_t w, i;
+
+ if(unlikely(vwords > hwords)) {
+ error("File /proc/net/netstat on header line %u has %u words, but on value line %u has %u words.", header_line, hwords, values_line, vwords);
+ vwords = hwords;
+ }
+
+ for(w = 1; w < vwords ;w++) {
+ char *key = procfile_lineword(ff, header_line, w);
+ uint32_t hash = simple_hash(key);
+
+ for(i = 0 ; nc[i].name ;i++) {
+ if(unlikely(hash == nc[i].hash && !strcmp(key, nc[i].name))) {
+ nc[i].value = strtoull(procfile_lineword(ff, values_line, w), NULL, 10);
+ break;
+ }
+ }
+ }
+}
+
+
int do_proc_net_netstat(int update_every, unsigned long long dt) {
- static int do_bandwidth = -1, do_inerrors = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, do_bcast_p = -1;
+ (void)dt;
+
+ static int do_bandwidth = -1, do_inerrors = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, do_bcast_p = -1, do_ecn = -1, \
+ do_tcpext_reorder = -1, do_tcpext_syscookies = -1, do_tcpext_ofo = -1, do_tcpext_connaborts = -1, do_tcpext_memory = -1;
+ static uint32_t hash_ipext = 0, hash_tcpext = 0;
static procfile *ff = NULL;
- if(do_bandwidth == -1) do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
- if(do_inerrors == -1) do_inerrors = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_ONDEMAND_ONDEMAND);
- if(do_mcast == -1) do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
- if(do_bcast == -1) do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
- if(do_mcast_p == -1) do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
- if(do_bcast_p == -1) do_bcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_ONDEMAND_ONDEMAND);
+ static unsigned long long *tcpext_TCPRenoReorder = NULL;
+ static unsigned long long *tcpext_TCPFACKReorder = NULL;
+ static unsigned long long *tcpext_TCPSACKReorder = NULL;
+ static unsigned long long *tcpext_TCPTSReorder = NULL;
+
+ static unsigned long long *tcpext_SyncookiesSent = NULL;
+ static unsigned long long *tcpext_SyncookiesRecv = NULL;
+ static unsigned long long *tcpext_SyncookiesFailed = NULL;
- if(dt) {};
+ static unsigned long long *tcpext_TCPOFOQueue = NULL; // Number of packets queued in OFO queue
+ static unsigned long long *tcpext_TCPOFODrop = NULL; // Number of packets meant to be queued in OFO but dropped because socket rcvbuf limit hit.
+ static unsigned long long *tcpext_TCPOFOMerge = NULL; // Number of packets in OFO that were merged with other packets.
+ static unsigned long long *tcpext_OfoPruned = NULL; // packets dropped from out-of-order queue because of socket buffer overrun
+
+ static unsigned long long *tcpext_TCPAbortOnData = NULL; // connections reset due to unexpected data
+ static unsigned long long *tcpext_TCPAbortOnClose = NULL; // connections reset due to early user close
+ static unsigned long long *tcpext_TCPAbortOnMemory = NULL; // connections aborted due to memory pressure
+ static unsigned long long *tcpext_TCPAbortOnTimeout = NULL; // connections aborted due to timeout
+ static unsigned long long *tcpext_TCPAbortOnLinger = NULL; // connections aborted after user close in linger timeout
+ static unsigned long long *tcpext_TCPAbortFailed = NULL; // times unable to send RST due to no memory
+
+ static unsigned long long *tcpext_TCPMemoryPressures = NULL;
+
+/*
+ // connection rejects
+ static unsigned long long *tcpext_PAWSActive = NULL; // active connections rejected because of time stamp
+ static unsigned long long *tcpext_PAWSPassive = NULL; // passive connections rejected because of time stamp
+
+ static unsigned long long *tcpext_TCPTimeouts = NULL;
+
+ static unsigned long long *tcpext_TCPDSACKUndo = NULL;
+ static unsigned long long *tcpext_TCPDSACKOldSent = NULL;
+ static unsigned long long *tcpext_TCPDSACKOfoSent = NULL;
+ static unsigned long long *tcpext_TCPDSACKRecv = NULL;
+ static unsigned long long *tcpext_TCPDSACKOfoRecv = NULL;
+ static unsigned long long *tcpext_TCPDSACKIgnoredOld = NULL;
+ static unsigned long long *tcpext_TCPDSACKIgnoredNoUndo = NULL;
+
+
+ static unsigned long long *tcpext_EmbryonicRsts = NULL;
+
+ static unsigned long long *tcpext_PruneCalled = NULL;
+ static unsigned long long *tcpext_RcvPruned = NULL;
+ static unsigned long long *tcpext_OutOfWindowIcmps = NULL;
+ static unsigned long long *tcpext_LockDroppedIcmps = NULL;
+ static unsigned long long *tcpext_ArpFilter = NULL;
+
+ static unsigned long long *tcpext_TW = NULL;
+ static unsigned long long *tcpext_TWRecycled = NULL;
+ static unsigned long long *tcpext_TWKilled = NULL;
+
+ static unsigned long long *tcpext_PAWSEstab = NULL;
+
+ static unsigned long long *tcpext_DelayedACKs = NULL;
+ static unsigned long long *tcpext_DelayedACKLocked = NULL;
+ static unsigned long long *tcpext_DelayedACKLost = NULL;
+
+ static unsigned long long *tcpext_ListenOverflows = NULL;
+ static unsigned long long *tcpext_ListenDrops = NULL;
+
+ static unsigned long long *tcpext_TCPPrequeued = NULL;
+
+ static unsigned long long *tcpext_TCPDirectCopyFromBacklog = NULL;
+ static unsigned long long *tcpext_TCPDirectCopyFromPrequeue = NULL;
+ static unsigned long long *tcpext_TCPPrequeueDropped = NULL;
+
+ static unsigned long long *tcpext_TCPHPHits = NULL;
+ static unsigned long long *tcpext_TCPHPHitsToUser = NULL;
+ static unsigned long long *tcpext_TCPHPAcks = NULL;
+
+ static unsigned long long *tcpext_TCPPureAcks = NULL;
+ static unsigned long long *tcpext_TCPRenoRecovery = NULL;
+
+ static unsigned long long *tcpext_TCPSackRecovery = NULL;
+ static unsigned long long *tcpext_TCPSackFailures = NULL;
+ static unsigned long long *tcpext_TCPSACKReneging = NULL;
+ static unsigned long long *tcpext_TCPSackRecoveryFail = NULL;
+ static unsigned long long *tcpext_TCPSACKDiscard = NULL;
+ static unsigned long long *tcpext_TCPSackShifted = NULL;
+ static unsigned long long *tcpext_TCPSackMerged = NULL;
+ static unsigned long long *tcpext_TCPSackShiftFallback = NULL;
+
+
+ static unsigned long long *tcpext_TCPFullUndo = NULL;
+ static unsigned long long *tcpext_TCPPartialUndo = NULL;
+
+ static unsigned long long *tcpext_TCPLossUndo = NULL;
+ static unsigned long long *tcpext_TCPLostRetransmit = NULL;
+
+ static unsigned long long *tcpext_TCPRenoFailures = NULL;
+
+ static unsigned long long *tcpext_TCPLossFailures = NULL;
+ static unsigned long long *tcpext_TCPFastRetrans = NULL;
+ static unsigned long long *tcpext_TCPForwardRetrans = NULL;
+ static unsigned long long *tcpext_TCPSlowStartRetrans = NULL;
+ static unsigned long long *tcpext_TCPLossProbes = NULL;
+ static unsigned long long *tcpext_TCPLossProbeRecovery = NULL;
+ static unsigned long long *tcpext_TCPRenoRecoveryFail = NULL;
+ static unsigned long long *tcpext_TCPSchedulerFailed = NULL;
+ static unsigned long long *tcpext_TCPRcvCollapsed = NULL;
+
+ static unsigned long long *tcpext_TCPSpuriousRTOs = NULL;
+ static unsigned long long *tcpext_TCPMD5NotFound = NULL;
+ static unsigned long long *tcpext_TCPMD5Unexpected = NULL;
+
+ static unsigned long long *tcpext_TCPBacklogDrop = NULL;
+ static unsigned long long *tcpext_TCPMinTTLDrop = NULL;
+ static unsigned long long *tcpext_TCPDeferAcceptDrop = NULL;
+ static unsigned long long *tcpext_IPReversePathFilter = NULL;
+ static unsigned long long *tcpext_TCPTimeWaitOverflow = NULL;
+ static unsigned long long *tcpext_TCPReqQFullDoCookies = NULL;
+ static unsigned long long *tcpext_TCPReqQFullDrop = NULL;
+ static unsigned long long *tcpext_TCPRetransFail = NULL;
+ static unsigned long long *tcpext_TCPRcvCoalesce = NULL;
+
+ static unsigned long long *tcpext_TCPChallengeACK = NULL;
+ static unsigned long long *tcpext_TCPSYNChallenge = NULL;
+
+ static unsigned long long *tcpext_TCPFastOpenActive = NULL;
+ static unsigned long long *tcpext_TCPFastOpenActiveFail = NULL;
+ static unsigned long long *tcpext_TCPFastOpenPassive = NULL;
+ static unsigned long long *tcpext_TCPFastOpenPassiveFail = NULL;
+ static unsigned long long *tcpext_TCPFastOpenListenOverflow = NULL;
+ static unsigned long long *tcpext_TCPFastOpenCookieReqd = NULL;
+
+ static unsigned long long *tcpext_TCPSpuriousRtxHostQueues = NULL;
+ static unsigned long long *tcpext_BusyPollRxPackets = NULL;
+ static unsigned long long *tcpext_TCPAutoCorking = NULL;
+ static unsigned long long *tcpext_TCPFromZeroWindowAdv = NULL;
+ static unsigned long long *tcpext_TCPToZeroWindowAdv = NULL;
+ static unsigned long long *tcpext_TCPWantZeroWindowAdv = NULL;
+ static unsigned long long *tcpext_TCPSynRetrans = NULL;
+ static unsigned long long *tcpext_TCPOrigDataSent = NULL;
+
+ static unsigned long long *tcpext_TCPHystartTrainDetect = NULL;
+ static unsigned long long *tcpext_TCPHystartTrainCwnd = NULL;
+ static unsigned long long *tcpext_TCPHystartDelayDetect = NULL;
+ static unsigned long long *tcpext_TCPHystartDelayCwnd = NULL;
+
+ static unsigned long long *tcpext_TCPACKSkippedSynRecv = NULL;
+ static unsigned long long *tcpext_TCPACKSkippedPAWS = NULL;
+ static unsigned long long *tcpext_TCPACKSkippedSeq = NULL;
+ static unsigned long long *tcpext_TCPACKSkippedFinWait2 = NULL;
+ static unsigned long long *tcpext_TCPACKSkippedTimeWait = NULL;
+ static unsigned long long *tcpext_TCPACKSkippedChallenge = NULL;
+
+ static unsigned long long *tcpext_TCPWinProbe = NULL;
+ static unsigned long long *tcpext_TCPKeepAlive = NULL;
+
+ static unsigned long long *tcpext_TCPMTUPFail = NULL;
+ static unsigned long long *tcpext_TCPMTUPSuccess = NULL;
+*/
+
+ static unsigned long long *ipext_InNoRoutes = NULL;
+ static unsigned long long *ipext_InTruncatedPkts = NULL;
+ static unsigned long long *ipext_InMcastPkts = NULL;
+ static unsigned long long *ipext_OutMcastPkts = NULL;
+ static unsigned long long *ipext_InBcastPkts = NULL;
+ static unsigned long long *ipext_OutBcastPkts = NULL;
+ static unsigned long long *ipext_InOctets = NULL;
+ static unsigned long long *ipext_OutOctets = NULL;
+ static unsigned long long *ipext_InMcastOctets = NULL;
+ static unsigned long long *ipext_OutMcastOctets = NULL;
+ static unsigned long long *ipext_InBcastOctets = NULL;
+ static unsigned long long *ipext_OutBcastOctets = NULL;
+ static unsigned long long *ipext_InCsumErrors = NULL;
+ static unsigned long long *ipext_InNoECTPkts = NULL;
+ static unsigned long long *ipext_InECT1Pkts = NULL;
+ static unsigned long long *ipext_InECT0Pkts = NULL;
+ static unsigned long long *ipext_InCEPkts = NULL;
+
+ if(unlikely(do_bandwidth == -1)) {
+ do_bandwidth = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+ do_inerrors = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_ONDEMAND_ONDEMAND);
+ do_mcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+ do_bcast = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+ do_mcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
+ do_bcast_p = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_ONDEMAND_ONDEMAND);
+ do_ecn = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "ECN packets", CONFIG_ONDEMAND_ONDEMAND);
+
+ do_tcpext_reorder = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP reorders", CONFIG_ONDEMAND_ONDEMAND);
+ do_tcpext_syscookies = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP SYN cookies", CONFIG_ONDEMAND_ONDEMAND);
+ do_tcpext_ofo = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP out-of-order queue", CONFIG_ONDEMAND_ONDEMAND);
+ do_tcpext_connaborts = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP connection aborts", CONFIG_ONDEMAND_ONDEMAND);
+ do_tcpext_memory = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "TCP memory pressures", CONFIG_ONDEMAND_ONDEMAND);
+
+ hash_ipext = simple_hash("IpExt");
+ hash_tcpext = simple_hash("TcpExt");
+
+ hash_array(ipext_data);
+ hash_array(tcpext_data);
+
+ // Reordering
+ tcpext_TCPFACKReorder = netstat_columns_find(tcpext_data, "TCPFACKReorder");
+ tcpext_TCPSACKReorder = netstat_columns_find(tcpext_data, "TCPSACKReorder");
+ tcpext_TCPRenoReorder = netstat_columns_find(tcpext_data, "TCPRenoReorder");
+ tcpext_TCPTSReorder = netstat_columns_find(tcpext_data, "TCPTSReorder");
+
+ // SYN Cookies
+ tcpext_SyncookiesSent = netstat_columns_find(tcpext_data, "SyncookiesSent");
+ tcpext_SyncookiesRecv = netstat_columns_find(tcpext_data, "SyncookiesRecv");
+ tcpext_SyncookiesFailed = netstat_columns_find(tcpext_data, "SyncookiesFailed");
+
+ // Out Of Order Queue
+ // http://www.spinics.net/lists/netdev/msg204696.html
+ tcpext_TCPOFOQueue = netstat_columns_find(tcpext_data, "TCPOFOQueue"); // Number of packets queued in OFO queue
+ tcpext_TCPOFODrop = netstat_columns_find(tcpext_data, "TCPOFODrop"); // Number of packets meant to be queued in OFO but dropped because socket rcvbuf limit hit.
+ tcpext_TCPOFOMerge = netstat_columns_find(tcpext_data, "TCPOFOMerge"); // Number of packets in OFO that were merged with other packets.
+ tcpext_OfoPruned = netstat_columns_find(tcpext_data, "OfoPruned"); // packets dropped from out-of-order queue because of socket buffer overrun
+
+ // connection resets
+ // https://github.com/ecki/net-tools/blob/bd8bceaed2311651710331a7f8990c3e31be9840/statistics.c
+ tcpext_TCPAbortOnData = netstat_columns_find(tcpext_data, "TCPAbortOnData"); // connections reset due to unexpected data
+ tcpext_TCPAbortOnClose = netstat_columns_find(tcpext_data, "TCPAbortOnClose"); // connections reset due to early user close
+ tcpext_TCPAbortOnMemory = netstat_columns_find(tcpext_data, "TCPAbortOnMemory"); // connections aborted due to memory pressure
+ tcpext_TCPAbortOnTimeout = netstat_columns_find(tcpext_data, "TCPAbortOnTimeout"); // connections aborted due to timeout
+ tcpext_TCPAbortOnLinger = netstat_columns_find(tcpext_data, "TCPAbortOnLinger"); // connections aborted after user close in linger timeout
+ tcpext_TCPAbortFailed = netstat_columns_find(tcpext_data, "TCPAbortFailed"); // times unable to send RST due to no memory
+
+ tcpext_TCPMemoryPressures = netstat_columns_find(tcpext_data, "TCPMemoryPressures");
+
+ /*
+ tcpext_EmbryonicRsts = netstat_columns_find(tcpext_data, "EmbryonicRsts");
+ tcpext_PruneCalled = netstat_columns_find(tcpext_data, "PruneCalled");
+ tcpext_RcvPruned = netstat_columns_find(tcpext_data, "RcvPruned");
+ tcpext_OutOfWindowIcmps = netstat_columns_find(tcpext_data, "OutOfWindowIcmps");
+ tcpext_LockDroppedIcmps = netstat_columns_find(tcpext_data, "LockDroppedIcmps");
+ tcpext_ArpFilter = netstat_columns_find(tcpext_data, "ArpFilter");
+ tcpext_TW = netstat_columns_find(tcpext_data, "TW");
+ tcpext_TWRecycled = netstat_columns_find(tcpext_data, "TWRecycled");
+ tcpext_TWKilled = netstat_columns_find(tcpext_data, "TWKilled");
+ tcpext_PAWSPassive = netstat_columns_find(tcpext_data, "PAWSPassive");
+ tcpext_PAWSActive = netstat_columns_find(tcpext_data, "PAWSActive");
+ tcpext_PAWSEstab = netstat_columns_find(tcpext_data, "PAWSEstab");
+ tcpext_DelayedACKs = netstat_columns_find(tcpext_data, "DelayedACKs");
+ tcpext_DelayedACKLocked = netstat_columns_find(tcpext_data, "DelayedACKLocked");
+ tcpext_DelayedACKLost = netstat_columns_find(tcpext_data, "DelayedACKLost");
+ tcpext_ListenOverflows = netstat_columns_find(tcpext_data, "ListenOverflows");
+ tcpext_ListenDrops = netstat_columns_find(tcpext_data, "ListenDrops");
+ tcpext_TCPPrequeued = netstat_columns_find(tcpext_data, "TCPPrequeued");
+ tcpext_TCPDirectCopyFromBacklog = netstat_columns_find(tcpext_data, "TCPDirectCopyFromBacklog");
+ tcpext_TCPDirectCopyFromPrequeue = netstat_columns_find(tcpext_data, "TCPDirectCopyFromPrequeue");
+ tcpext_TCPPrequeueDropped = netstat_columns_find(tcpext_data, "TCPPrequeueDropped");
+ tcpext_TCPHPHits = netstat_columns_find(tcpext_data, "TCPHPHits");
+ tcpext_TCPHPHitsToUser = netstat_columns_find(tcpext_data, "TCPHPHitsToUser");
+ tcpext_TCPPureAcks = netstat_columns_find(tcpext_data, "TCPPureAcks");
+ tcpext_TCPHPAcks = netstat_columns_find(tcpext_data, "TCPHPAcks");
+ tcpext_TCPRenoRecovery = netstat_columns_find(tcpext_data, "TCPRenoRecovery");
+ tcpext_TCPSackRecovery = netstat_columns_find(tcpext_data, "TCPSackRecovery");
+ tcpext_TCPSACKReneging = netstat_columns_find(tcpext_data, "TCPSACKReneging");
+ tcpext_TCPFullUndo = netstat_columns_find(tcpext_data, "TCPFullUndo");
+ tcpext_TCPPartialUndo = netstat_columns_find(tcpext_data, "TCPPartialUndo");
+ tcpext_TCPDSACKUndo = netstat_columns_find(tcpext_data, "TCPDSACKUndo");
+ tcpext_TCPLossUndo = netstat_columns_find(tcpext_data, "TCPLossUndo");
+ tcpext_TCPLostRetransmit = netstat_columns_find(tcpext_data, "TCPLostRetransmit");
+ tcpext_TCPRenoFailures = netstat_columns_find(tcpext_data, "TCPRenoFailures");
+ tcpext_TCPSackFailures = netstat_columns_find(tcpext_data, "TCPSackFailures");
+ tcpext_TCPLossFailures = netstat_columns_find(tcpext_data, "TCPLossFailures");
+ tcpext_TCPFastRetrans = netstat_columns_find(tcpext_data, "TCPFastRetrans");
+ tcpext_TCPForwardRetrans = netstat_columns_find(tcpext_data, "TCPForwardRetrans");
+ tcpext_TCPSlowStartRetrans = netstat_columns_find(tcpext_data, "TCPSlowStartRetrans");
+ tcpext_TCPTimeouts = netstat_columns_find(tcpext_data, "TCPTimeouts");
+ tcpext_TCPLossProbes = netstat_columns_find(tcpext_data, "TCPLossProbes");
+ tcpext_TCPLossProbeRecovery = netstat_columns_find(tcpext_data, "TCPLossProbeRecovery");
+ tcpext_TCPRenoRecoveryFail = netstat_columns_find(tcpext_data, "TCPRenoRecoveryFail");
+ tcpext_TCPSackRecoveryFail = netstat_columns_find(tcpext_data, "TCPSackRecoveryFail");
+ tcpext_TCPSchedulerFailed = netstat_columns_find(tcpext_data, "TCPSchedulerFailed");
+ tcpext_TCPRcvCollapsed = netstat_columns_find(tcpext_data, "TCPRcvCollapsed");
+ tcpext_TCPDSACKOldSent = netstat_columns_find(tcpext_data, "TCPDSACKOldSent");
+ tcpext_TCPDSACKOfoSent = netstat_columns_find(tcpext_data, "TCPDSACKOfoSent");
+ tcpext_TCPDSACKRecv = netstat_columns_find(tcpext_data, "TCPDSACKRecv");
+ tcpext_TCPDSACKOfoRecv = netstat_columns_find(tcpext_data, "TCPDSACKOfoRecv");
+ tcpext_TCPSACKDiscard = netstat_columns_find(tcpext_data, "TCPSACKDiscard");
+ tcpext_TCPDSACKIgnoredOld = netstat_columns_find(tcpext_data, "TCPDSACKIgnoredOld");
+ tcpext_TCPDSACKIgnoredNoUndo = netstat_columns_find(tcpext_data, "TCPDSACKIgnoredNoUndo");
+ tcpext_TCPSpuriousRTOs = netstat_columns_find(tcpext_data, "TCPSpuriousRTOs");
+ tcpext_TCPMD5NotFound = netstat_columns_find(tcpext_data, "TCPMD5NotFound");
+ tcpext_TCPMD5Unexpected = netstat_columns_find(tcpext_data, "TCPMD5Unexpected");
+ tcpext_TCPSackShifted = netstat_columns_find(tcpext_data, "TCPSackShifted");
+ tcpext_TCPSackMerged = netstat_columns_find(tcpext_data, "TCPSackMerged");
+ tcpext_TCPSackShiftFallback = netstat_columns_find(tcpext_data, "TCPSackShiftFallback");
+ tcpext_TCPBacklogDrop = netstat_columns_find(tcpext_data, "TCPBacklogDrop");
+ tcpext_TCPMinTTLDrop = netstat_columns_find(tcpext_data, "TCPMinTTLDrop");
+ tcpext_TCPDeferAcceptDrop = netstat_columns_find(tcpext_data, "TCPDeferAcceptDrop");
+ tcpext_IPReversePathFilter = netstat_columns_find(tcpext_data, "IPReversePathFilter");
+ tcpext_TCPTimeWaitOverflow = netstat_columns_find(tcpext_data, "TCPTimeWaitOverflow");
+ tcpext_TCPReqQFullDoCookies = netstat_columns_find(tcpext_data, "TCPReqQFullDoCookies");
+ tcpext_TCPReqQFullDrop = netstat_columns_find(tcpext_data, "TCPReqQFullDrop");
+ tcpext_TCPRetransFail = netstat_columns_find(tcpext_data, "TCPRetransFail");
+ tcpext_TCPRcvCoalesce = netstat_columns_find(tcpext_data, "TCPRcvCoalesce");
+ tcpext_TCPChallengeACK = netstat_columns_find(tcpext_data, "TCPChallengeACK");
+ tcpext_TCPSYNChallenge = netstat_columns_find(tcpext_data, "TCPSYNChallenge");
+ tcpext_TCPFastOpenActive = netstat_columns_find(tcpext_data, "TCPFastOpenActive");
+ tcpext_TCPFastOpenActiveFail = netstat_columns_find(tcpext_data, "TCPFastOpenActiveFail");
+ tcpext_TCPFastOpenPassive = netstat_columns_find(tcpext_data, "TCPFastOpenPassive");
+ tcpext_TCPFastOpenPassiveFail = netstat_columns_find(tcpext_data, "TCPFastOpenPassiveFail");
+ tcpext_TCPFastOpenListenOverflow = netstat_columns_find(tcpext_data, "TCPFastOpenListenOverflow");
+ tcpext_TCPFastOpenCookieReqd = netstat_columns_find(tcpext_data, "TCPFastOpenCookieReqd");
+ tcpext_TCPSpuriousRtxHostQueues = netstat_columns_find(tcpext_data, "TCPSpuriousRtxHostQueues");
+ tcpext_BusyPollRxPackets = netstat_columns_find(tcpext_data, "BusyPollRxPackets");
+ tcpext_TCPAutoCorking = netstat_columns_find(tcpext_data, "TCPAutoCorking");
+ tcpext_TCPFromZeroWindowAdv = netstat_columns_find(tcpext_data, "TCPFromZeroWindowAdv");
+ tcpext_TCPToZeroWindowAdv = netstat_columns_find(tcpext_data, "TCPToZeroWindowAdv");
+ tcpext_TCPWantZeroWindowAdv = netstat_columns_find(tcpext_data, "TCPWantZeroWindowAdv");
+ tcpext_TCPSynRetrans = netstat_columns_find(tcpext_data, "TCPSynRetrans");
+ tcpext_TCPOrigDataSent = netstat_columns_find(tcpext_data, "TCPOrigDataSent");
+ tcpext_TCPHystartTrainDetect = netstat_columns_find(tcpext_data, "TCPHystartTrainDetect");
+ tcpext_TCPHystartTrainCwnd = netstat_columns_find(tcpext_data, "TCPHystartTrainCwnd");
+ tcpext_TCPHystartDelayDetect = netstat_columns_find(tcpext_data, "TCPHystartDelayDetect");
+ tcpext_TCPHystartDelayCwnd = netstat_columns_find(tcpext_data, "TCPHystartDelayCwnd");
+ tcpext_TCPACKSkippedSynRecv = netstat_columns_find(tcpext_data, "TCPACKSkippedSynRecv");
+ tcpext_TCPACKSkippedPAWS = netstat_columns_find(tcpext_data, "TCPACKSkippedPAWS");
+ tcpext_TCPACKSkippedSeq = netstat_columns_find(tcpext_data, "TCPACKSkippedSeq");
+ tcpext_TCPACKSkippedFinWait2 = netstat_columns_find(tcpext_data, "TCPACKSkippedFinWait2");
+ tcpext_TCPACKSkippedTimeWait = netstat_columns_find(tcpext_data, "TCPACKSkippedTimeWait");
+ tcpext_TCPACKSkippedChallenge = netstat_columns_find(tcpext_data, "TCPACKSkippedChallenge");
+ tcpext_TCPWinProbe = netstat_columns_find(tcpext_data, "TCPWinProbe");
+ tcpext_TCPKeepAlive = netstat_columns_find(tcpext_data, "TCPKeepAlive");
+ tcpext_TCPMTUPFail = netstat_columns_find(tcpext_data, "TCPMTUPFail");
+ tcpext_TCPMTUPSuccess = netstat_columns_find(tcpext_data, "TCPMTUPSuccess");
+*/
+ ipext_InNoRoutes = netstat_columns_find(ipext_data, "InNoRoutes");
+ ipext_InTruncatedPkts = netstat_columns_find(ipext_data, "InTruncatedPkts");
+ ipext_InMcastPkts = netstat_columns_find(ipext_data, "InMcastPkts");
+ ipext_OutMcastPkts = netstat_columns_find(ipext_data, "OutMcastPkts");
+ ipext_InBcastPkts = netstat_columns_find(ipext_data, "InBcastPkts");
+ ipext_OutBcastPkts = netstat_columns_find(ipext_data, "OutBcastPkts");
+ ipext_InOctets = netstat_columns_find(ipext_data, "InOctets");
+ ipext_OutOctets = netstat_columns_find(ipext_data, "OutOctets");
+ ipext_InMcastOctets = netstat_columns_find(ipext_data, "InMcastOctets");
+ ipext_OutMcastOctets = netstat_columns_find(ipext_data, "OutMcastOctets");
+ ipext_InBcastOctets = netstat_columns_find(ipext_data, "InBcastOctets");
+ ipext_OutBcastOctets = netstat_columns_find(ipext_data, "OutBcastOctets");
+ ipext_InCsumErrors = netstat_columns_find(ipext_data, "InCsumErrors");
+ ipext_InNoECTPkts = netstat_columns_find(ipext_data, "InNoECTPkts");
+ ipext_InECT1Pkts = netstat_columns_find(ipext_data, "InECT1Pkts");
+ ipext_InECT0Pkts = netstat_columns_find(ipext_data, "InECT0Pkts");
+ ipext_InCEPkts = netstat_columns_find(ipext_data, "InCEPkts");
+ }
- if(!ff) {
+ if(unlikely(!ff)) {
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/netstat");
ff = procfile_open(config_get("plugin:proc:/proc/net/netstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
}
- if(!ff) return 1;
+ if(unlikely(!ff)) return 1;
ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
+ if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time
uint32_t lines = procfile_lines(ff), l;
uint32_t words;
for(l = 0; l < lines ;l++) {
- if(strcmp(procfile_lineword(ff, l, 0), "IpExt") == 0) {
- l++; // we need the next line
+ char *key = procfile_lineword(ff, l, 0);
+ uint32_t hash = simple_hash(key);
+
+ if(unlikely(hash == hash_ipext && strcmp(key, "IpExt") == 0)) {
+ uint32_t h = l++;
if(strcmp(procfile_lineword(ff, l, 0), "IpExt") != 0) {
error("Cannot read IpExt line from /proc/net/netstat.");
break;
}
words = procfile_linewords(ff, l);
- if(words < 12) {
- error("Cannot read /proc/net/netstat IpExt line. Expected 12 params, read %u.", words);
+ if(words < 2) {
+ error("Cannot read /proc/net/netstat IpExt line. Expected 2+ params, read %u.", words);
continue;
}
- unsigned long long
- InNoRoutes = 0, InTruncatedPkts = 0,
- InOctets = 0, InMcastPkts = 0, InBcastPkts = 0, InMcastOctets = 0, InBcastOctets = 0,
- OutOctets = 0, OutMcastPkts = 0, OutBcastPkts = 0, OutMcastOctets = 0, OutBcastOctets = 0;
-
- InNoRoutes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- InTruncatedPkts = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- InMcastPkts = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- OutMcastPkts = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- InBcastPkts = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- OutBcastPkts = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
- InOctets = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
- OutOctets = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
- InMcastOctets = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
- OutMcastOctets = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
- InBcastOctets = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
- OutBcastOctets = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+ parse_line_pair(ff, ipext_data, h, l);
RRDSET *st;
// --------------------------------------------------------------------
- if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (InOctets || OutOctets))) {
+ if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InOctets || *ipext_OutOctets))) {
do_bandwidth = CONFIG_ONDEMAND_YES;
st = rrdset_find("system.ipv4");
if(!st) {
st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InOctets", "received", 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "sent", OutOctets);
- rrddim_set(st, "received", InOctets);
+ rrddim_set(st, "InOctets", *ipext_InOctets);
+ rrddim_set(st, "OutOctets", *ipext_OutOctets);
rrdset_done(st);
}
// --------------------------------------------------------------------
- if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (InNoRoutes || InTruncatedPkts))) {
+ if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InNoRoutes || *ipext_InTruncatedPkts))) {
do_inerrors = CONFIG_ONDEMAND_YES;
st = rrdset_find("ipv4.inerrors");
if(!st) {
st = rrdset_create("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors", "packets/s", 4000, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "noroutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "truncated", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InNoRoutes", "noroutes", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InTruncatedPkts", "truncated", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InCsumErrors", "checksum", 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "noroutes", InNoRoutes);
- rrddim_set(st, "truncated", InTruncatedPkts);
+ rrddim_set(st, "InNoRoutes", *ipext_InNoRoutes);
+ rrddim_set(st, "InTruncatedPkts", *ipext_InTruncatedPkts);
+ rrddim_set(st, "InCsumErrors", *ipext_InCsumErrors);
rrdset_done(st);
}
// --------------------------------------------------------------------
- if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (InMcastOctets || OutMcastOctets))) {
+ if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InMcastOctets || *ipext_OutMcastOctets))) {
do_mcast = CONFIG_ONDEMAND_YES;
st = rrdset_find("ipv4.mcast");
if(!st) {
st = rrdset_create("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
st->isdetail = 1;
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InMcastOctets", "received", 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutMcastOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "sent", OutMcastOctets);
- rrddim_set(st, "received", InMcastOctets);
+ rrddim_set(st, "InMcastOctets", *ipext_InMcastOctets);
+ rrddim_set(st, "OutMcastOctets", *ipext_OutMcastOctets);
rrdset_done(st);
}
// --------------------------------------------------------------------
- if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (InBcastOctets || OutBcastOctets))) {
+ if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InBcastOctets || *ipext_OutBcastOctets))) {
do_bcast = CONFIG_ONDEMAND_YES;
st = rrdset_find("ipv4.bcast");
if(!st) {
st = rrdset_create("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
st->isdetail = 1;
- rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InBcastOctets", "received", 8, 1024, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutBcastOctets", "sent", -8, 1024, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "sent", OutBcastOctets);
- rrddim_set(st, "received", InBcastOctets);
+ rrddim_set(st, "InBcastOctets", *ipext_InBcastOctets);
+ rrddim_set(st, "OutBcastOctets", *ipext_OutBcastOctets);
rrdset_done(st);
}
// --------------------------------------------------------------------
- if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (InMcastPkts || OutMcastPkts))) {
+ if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InMcastPkts || *ipext_OutMcastPkts))) {
do_mcast_p = CONFIG_ONDEMAND_YES;
st = rrdset_find("ipv4.mcastpkts");
if(!st) {
- st = rrdset_create("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets", "packets/s", 8600, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InMcastPkts", "received", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutMcastPkts", "sent", -1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "sent", OutMcastPkts);
- rrddim_set(st, "received", InMcastPkts);
+ rrddim_set(st, "InMcastPkts", *ipext_InMcastPkts);
+ rrddim_set(st, "OutMcastPkts", *ipext_OutMcastPkts);
rrdset_done(st);
}
// --------------------------------------------------------------------
- if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (InBcastPkts || OutBcastPkts))) {
+ if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InBcastPkts || *ipext_OutBcastPkts))) {
do_bcast_p = CONFIG_ONDEMAND_YES;
st = rrdset_find("ipv4.bcastpkts");
if(!st) {
st = rrdset_create("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets", "packets/s", 8500, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InBcastPkts", "received", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutBcastPkts", "sent", -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InBcastPkts", *ipext_InBcastPkts);
+ rrddim_set(st, "OutBcastPkts", *ipext_OutBcastPkts);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_ecn == CONFIG_ONDEMAND_YES || (do_ecn == CONFIG_ONDEMAND_ONDEMAND && (*ipext_InCEPkts || *ipext_InECT0Pkts || *ipext_InECT1Pkts || *ipext_InNoECTPkts))) {
+ do_ecn = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.ecnpkts");
+ if(!st) {
+ st = rrdset_create("ipv4", "ecnpkts", NULL, "ecn", NULL, "IPv4 ECN Statistics", "packets/s", 8700, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "InCEPkts", "CEP", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InNoECTPkts", "NoECTP", -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InECT0Pkts", "ECTP0", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InECT1Pkts", "ECTP1", 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InCEPkts", *ipext_InCEPkts);
+ rrddim_set(st, "InNoECTPkts", *ipext_InNoECTPkts);
+ rrddim_set(st, "InECT0Pkts", *ipext_InECT0Pkts);
+ rrddim_set(st, "InECT1Pkts", *ipext_InECT1Pkts);
+ rrdset_done(st);
+ }
+ }
+ else if(unlikely(hash == hash_tcpext && strcmp(key, "TcpExt") == 0)) {
+ uint32_t h = l++;
+
+ if(strcmp(procfile_lineword(ff, l, 0), "TcpExt") != 0) {
+ error("Cannot read TcpExt line from /proc/net/netstat.");
+ break;
+ }
+ words = procfile_linewords(ff, l);
+ if(words < 2) {
+ error("Cannot read /proc/net/netstat TcpExt line. Expected 2+ params, read %u.", words);
+ continue;
+ }
+
+ parse_line_pair(ff, tcpext_data, h, l);
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ if(do_tcpext_memory == CONFIG_ONDEMAND_YES || (do_tcpext_memory == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_TCPMemoryPressures))) {
+ do_tcpext_memory = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.tcpmemorypressures");
+ if(!st) {
+ st = rrdset_create("ipv4", "tcpmemorypressures", NULL, "tcp", NULL, "TCP Memory Pressures", "events/s", 3000, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "TCPMemoryPressures", "pressures", 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "TCPMemoryPressures", *tcpext_TCPMemoryPressures);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_tcpext_connaborts == CONFIG_ONDEMAND_YES || (do_tcpext_connaborts == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_TCPAbortOnData || *tcpext_TCPAbortOnClose || *tcpext_TCPAbortOnMemory || *tcpext_TCPAbortOnTimeout || *tcpext_TCPAbortOnLinger || *tcpext_TCPAbortFailed))) {
+ do_tcpext_connaborts = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.tcpconnaborts");
+ if(!st) {
+ st = rrdset_create("ipv4", "tcpconnaborts", NULL, "tcp", NULL, "TCP Connection Aborts", "connections/s", 3010, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "TCPAbortOnData", "baddata", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPAbortOnClose", "userclosed", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPAbortOnMemory", "nomemory", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPAbortOnTimeout", "timeout", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPAbortOnLinger", "linger", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPAbortFailed", "failed", -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "TCPAbortOnData", *tcpext_TCPAbortOnData);
+ rrddim_set(st, "TCPAbortOnClose", *tcpext_TCPAbortOnClose);
+ rrddim_set(st, "TCPAbortOnMemory", *tcpext_TCPAbortOnMemory);
+ rrddim_set(st, "TCPAbortOnTimeout", *tcpext_TCPAbortOnTimeout);
+ rrddim_set(st, "TCPAbortOnLinger", *tcpext_TCPAbortOnLinger);
+ rrddim_set(st, "TCPAbortFailed", *tcpext_TCPAbortFailed);
+ rrdset_done(st);
+ }
+ // --------------------------------------------------------------------
+
+ if(do_tcpext_reorder == CONFIG_ONDEMAND_YES || (do_tcpext_reorder == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_TCPRenoReorder || *tcpext_TCPFACKReorder || *tcpext_TCPSACKReorder || *tcpext_TCPTSReorder))) {
+ do_tcpext_reorder = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.tcpreorders");
+ if(!st) {
+ st = rrdset_create("ipv4", "tcpreorders", NULL, "tcp", NULL, "TCP Reordered Packets by Detection Method", "packets/s", 3020, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "TCPTSReorder", "timestamp", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPSACKReorder", "sack", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPFACKReorder", "fack", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPRenoReorder", "reno", 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "TCPTSReorder", *tcpext_TCPTSReorder);
+ rrddim_set(st, "TCPSACKReorder", *tcpext_TCPSACKReorder);
+ rrddim_set(st, "TCPFACKReorder", *tcpext_TCPFACKReorder);
+ rrddim_set(st, "TCPRenoReorder", *tcpext_TCPRenoReorder);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_tcpext_ofo == CONFIG_ONDEMAND_YES || (do_tcpext_ofo == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_TCPOFOQueue || *tcpext_TCPOFODrop || *tcpext_TCPOFOMerge))) {
+ do_tcpext_ofo = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.tcpofo");
+ if(!st) {
+ st = rrdset_create("ipv4", "tcpofo", NULL, "tcp", NULL, "TCP Out-Of-Order Queue", "packets/s", 3050, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "TCPOFOQueue", "inqueue", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPOFODrop", "dropped", -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "TCPOFOMerge", "merged", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OfoPruned", "pruned", -1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "sent", OutBcastPkts);
- rrddim_set(st, "received", InBcastPkts);
+ rrddim_set(st, "TCPOFOQueue", *tcpext_TCPOFOQueue);
+ rrddim_set(st, "TCPOFODrop", *tcpext_TCPOFODrop);
+ rrddim_set(st, "TCPOFOMerge", *tcpext_TCPOFOMerge);
+ rrddim_set(st, "OfoPruned", *tcpext_OfoPruned);
rrdset_done(st);
}
+
+ // --------------------------------------------------------------------
+
+ if(do_tcpext_syscookies == CONFIG_ONDEMAND_YES || (do_tcpext_syscookies == CONFIG_ONDEMAND_ONDEMAND && (*tcpext_SyncookiesSent || *tcpext_SyncookiesRecv || *tcpext_SyncookiesFailed))) {
+ do_tcpext_syscookies = CONFIG_ONDEMAND_YES;
+ st = rrdset_find("ipv4.tcpsyncookies");
+ if(!st) {
+ st = rrdset_create("ipv4", "tcpsyncookies", NULL, "tcp", NULL, "TCP SYN Cookies", "packets/s", 3100, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "SyncookiesRecv", "received", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "SyncookiesSent", "sent", -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "SyncookiesFailed", "failed", -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "SyncookiesRecv", *tcpext_SyncookiesRecv);
+ rrddim_set(st, "SyncookiesSent", *tcpext_SyncookiesSent);
+ rrddim_set(st, "SyncookiesFailed", *tcpext_SyncookiesFailed);
+ rrdset_done(st);
+ }
+
}
}
diff --git a/src/proc_net_rpc_nfs.c b/src/proc_net_rpc_nfs.c
new file mode 100644
index 000000000..98acdd814
--- /dev/null
+++ b/src/proc_net_rpc_nfs.c
@@ -0,0 +1,370 @@
+#include "common.h"
+
+struct nfs_procs {
+ char name[30];
+ unsigned long long value;
+ int present;
+};
+
+struct nfs_procs nfs_proc2_values[] = {
+ { "null", 0ULL, 0 },
+ { "getattr", 0ULL, 0 },
+ { "setattr", 0ULL, 0 },
+ { "root", 0ULL, 0 },
+ { "lookup", 0ULL, 0 },
+ { "readlink", 0ULL, 0 },
+ { "read", 0ULL, 0 },
+ { "wrcache", 0ULL, 0 },
+ { "write", 0ULL, 0 },
+ { "create", 0ULL, 0 },
+ { "remove", 0ULL, 0 },
+ { "rename", 0ULL, 0 },
+ { "link", 0ULL, 0 },
+ { "symlink", 0ULL, 0 },
+ { "mkdir", 0ULL, 0 },
+ { "rmdir", 0ULL, 0 },
+ { "readdir", 0ULL, 0 },
+ { "fsstat", 0ULL, 0 },
+
+ /* termination */
+ { "", 0ULL, 0 }
+};
+
+struct nfs_procs nfs_proc3_values[] = {
+ { "null", 0ULL, 0 },
+ { "getattr", 0ULL, 0 },
+ { "setattr", 0ULL, 0 },
+ { "lookup", 0ULL, 0 },
+ { "access", 0ULL, 0 },
+ { "readlink", 0ULL, 0 },
+ { "read", 0ULL, 0 },
+ { "write", 0ULL, 0 },
+ { "create", 0ULL, 0 },
+ { "mkdir", 0ULL, 0 },
+ { "symlink", 0ULL, 0 },
+ { "mknod", 0ULL, 0 },
+ { "remove", 0ULL, 0 },
+ { "rmdir", 0ULL, 0 },
+ { "rename", 0ULL, 0 },
+ { "link", 0ULL, 0 },
+ { "readdir", 0ULL, 0 },
+ { "readdirplus", 0ULL, 0 },
+ { "fsstat", 0ULL, 0 },
+ { "fsinfo", 0ULL, 0 },
+ { "pathconf", 0ULL, 0 },
+ { "commit", 0ULL, 0 },
+
+ /* termination */
+ { "", 0ULL, 0 }
+};
+
+struct nfs_procs nfs_proc4_values[] = {
+ { "null", 0ULL, 0 },
+ { "read", 0ULL, 0 },
+ { "write", 0ULL, 0 },
+ { "commit", 0ULL, 0 },
+ { "open", 0ULL, 0 },
+ { "open_conf", 0ULL, 0 },
+ { "open_noat", 0ULL, 0 },
+ { "open_dgrd", 0ULL, 0 },
+ { "close", 0ULL, 0 },
+ { "setattr", 0ULL, 0 },
+ { "fsinfo", 0ULL, 0 },
+ { "renew", 0ULL, 0 },
+ { "setclntid", 0ULL, 0 },
+ { "confirm", 0ULL, 0 },
+ { "lock", 0ULL, 0 },
+ { "lockt", 0ULL, 0 },
+ { "locku", 0ULL, 0 },
+ { "access", 0ULL, 0 },
+ { "getattr", 0ULL, 0 },
+ { "lookup", 0ULL, 0 },
+ { "lookup_root", 0ULL, 0 },
+ { "remove", 0ULL, 0 },
+ { "rename", 0ULL, 0 },
+ { "link", 0ULL, 0 },
+ { "symlink", 0ULL, 0 },
+ { "create", 0ULL, 0 },
+ { "pathconf", 0ULL, 0 },
+ { "statfs", 0ULL, 0 },
+ { "readlink", 0ULL, 0 },
+ { "readdir", 0ULL, 0 },
+ { "server_caps", 0ULL, 0 },
+ { "delegreturn", 0ULL, 0 },
+ { "getacl", 0ULL, 0 },
+ { "setacl", 0ULL, 0 },
+ { "fs_locations", 0ULL, 0 },
+ { "rel_lkowner", 0ULL, 0 },
+ { "secinfo", 0ULL, 0 },
+ { "fsid_present", 0ULL, 0 },
+
+ /* nfsv4.1 client ops */
+ { "exchange_id", 0ULL, 0 },
+ { "create_session", 0ULL, 0 },
+ { "destroy_session", 0ULL, 0 },
+ { "sequence", 0ULL, 0 },
+ { "get_lease_time", 0ULL, 0 },
+ { "reclaim_comp", 0ULL, 0 },
+ { "layoutget", 0ULL, 0 },
+ { "getdevinfo", 0ULL, 0 },
+ { "layoutcommit", 0ULL, 0 },
+ { "layoutreturn", 0ULL, 0 },
+ { "secinfo_no", 0ULL, 0 },
+ { "test_stateid", 0ULL, 0 },
+ { "free_stateid", 0ULL, 0 },
+ { "getdevicelist", 0ULL, 0 },
+ { "bind_conn_to_ses", 0ULL, 0 },
+ { "destroy_clientid", 0ULL, 0 },
+
+ /* nfsv4.2 client ops */
+ { "seek", 0ULL, 0 },
+ { "allocate", 0ULL, 0 },
+ { "deallocate", 0ULL, 0 },
+ { "layoutstats", 0ULL, 0 },
+ { "clone", 0ULL, 0 },
+
+ /* termination */
+ { "", 0ULL, 0 }
+};
+
+int do_proc_net_rpc_nfs(int update_every, unsigned long long dt) {
+ (void)dt;
+
+ static procfile *ff = NULL;
+ static int do_net = -1, do_rpc = -1, do_proc2 = -1, do_proc3 = -1, do_proc4 = -1;
+ static int proc2_warning = 0, proc3_warning = 0, proc4_warning = 0;
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/rpc/nfs");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/rpc/nfs", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ if(do_net == -1) do_net = config_get_boolean("plugin:proc:/proc/net/rpc/nfs", "network", 1);
+ if(do_rpc == -1) do_rpc = config_get_boolean("plugin:proc:/proc/net/rpc/nfs", "rpc", 1);
+ if(do_proc2 == -1) do_proc2 = config_get_boolean("plugin:proc:/proc/net/rpc/nfs", "NFS v2 procedures", 1);
+ if(do_proc3 == -1) do_proc3 = config_get_boolean("plugin:proc:/proc/net/rpc/nfs", "NFS v3 procedures", 1);
+ if(do_proc4 == -1) do_proc4 = config_get_boolean("plugin:proc:/proc/net/rpc/nfs", "NFS v4 procedures", 1);
+
+ // if they are enabled, reset them to 1
+ // later we do them =2 to avoid doing strcmp for all lines
+ if(do_net) do_net = 1;
+ if(do_rpc) do_rpc = 1;
+ if(do_proc2) do_proc2 = 1;
+ if(do_proc3) do_proc3 = 1;
+ if(do_proc4) do_proc4 = 1;
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words;
+
+ char *type;
+ unsigned long long net_count = 0, net_udp_count = 0, net_tcp_count = 0, net_tcp_connections = 0;
+ unsigned long long rpc_calls = 0, rpc_retransmits = 0, rpc_auth_refresh = 0;
+
+ for(l = 0; l < lines ;l++) {
+ words = procfile_linewords(ff, l);
+ if(!words) continue;
+
+ type = procfile_lineword(ff, l, 0);
+
+ if(do_net == 1 && strcmp(type, "net") == 0) {
+ if(words < 5) {
+ error("%s line of /proc/net/rpc/nfs has %u words, expected %d", type, words, 5);
+ continue;
+ }
+
+ net_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ net_udp_count = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ net_tcp_count = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+ net_tcp_connections = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+
+ unsigned long long sum = net_count + net_udp_count + net_tcp_count + net_tcp_connections;
+ if(sum == 0ULL) do_net = -1;
+ else do_net = 2;
+ }
+ else if(do_rpc == 1 && strcmp(type, "rpc") == 0) {
+ if(words < 4) {
+ error("%s line of /proc/net/rpc/nfs has %u words, expected %d", type, words, 6);
+ continue;
+ }
+
+ rpc_calls = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ rpc_retransmits = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+ rpc_auth_refresh = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+
+ unsigned long long sum = rpc_calls + rpc_retransmits + rpc_auth_refresh;
+ if(sum == 0ULL) do_rpc = -1;
+ else do_rpc = 2;
+ }
+ else if(do_proc2 == 1 && strcmp(type, "proc2") == 0) {
+ // the first number is the count of numbers present
+ // so we start for word 2
+
+ unsigned long long sum = 0;
+ unsigned int i, j;
+ for(i = 0, j = 2; j < words && nfs_proc2_values[i].name[0] ; i++, j++) {
+ nfs_proc2_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfs_proc2_values[i].present = 1;
+ sum += nfs_proc2_values[i].value;
+ }
+
+ if(sum == 0ULL) {
+ if(!proc2_warning) {
+ error("Disabling /proc/net/rpc/nfs v2 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+ proc2_warning = 1;
+ }
+ do_proc2 = 0;
+ }
+ else do_proc2 = 2;
+ }
+ else if(do_proc3 == 1 && strcmp(type, "proc3") == 0) {
+ // the first number is the count of numbers present
+ // so we start for word 2
+
+ unsigned long long sum = 0;
+ unsigned int i, j;
+ for(i = 0, j = 2; j < words && nfs_proc3_values[i].name[0] ; i++, j++) {
+ nfs_proc3_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfs_proc3_values[i].present = 1;
+ sum += nfs_proc3_values[i].value;
+ }
+
+ if(sum == 0ULL) {
+ if(!proc3_warning) {
+ info("Disabling /proc/net/rpc/nfs v3 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+ proc3_warning = 1;
+ }
+ do_proc3 = 0;
+ }
+ else do_proc3 = 2;
+ }
+ else if(do_proc4 == 1 && strcmp(type, "proc4") == 0) {
+ // the first number is the count of numbers present
+ // so we start for word 2
+
+ unsigned long long sum = 0;
+ unsigned int i, j;
+ for(i = 0, j = 2; j < words && nfs_proc4_values[i].name[0] ; i++, j++) {
+ nfs_proc4_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfs_proc4_values[i].present = 1;
+ sum += nfs_proc4_values[i].value;
+ }
+
+ if(sum == 0ULL) {
+ if(!proc4_warning) {
+ info("Disabling /proc/net/rpc/nfs v4 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+ proc4_warning = 1;
+ }
+ do_proc4 = 0;
+ }
+ else do_proc4 = 2;
+ }
+ }
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ if(do_net == 2) {
+ st = rrdset_find_bytype("nfs", "net");
+ if(!st) {
+ st = rrdset_create("nfs", "net", NULL, "network", NULL, "NFS Client Network", "operations/s", 5007, update_every, RRDSET_TYPE_STACKED);
+ st->isdetail = 1;
+
+ rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "tcp", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ // ignore net_count, net_tcp_connections
+ if(net_count) {};
+ if(net_tcp_connections) {};
+
+ rrddim_set(st, "udp", net_udp_count);
+ rrddim_set(st, "tcp", net_tcp_count);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_rpc == 2) {
+ st = rrdset_find_bytype("nfs", "rpc");
+ if(!st) {
+ st = rrdset_create("nfs", "rpc", NULL, "rpc", NULL, "NFS Client Remote Procedure Calls Statistics", "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
+ st->isdetail = 1;
+
+ rrddim_add(st, "calls", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "retransmits", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "auth_refresh", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "calls", rpc_calls);
+ rrddim_set(st, "retransmits", rpc_retransmits);
+ rrddim_set(st, "auth_refresh", rpc_auth_refresh);
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_proc2 == 2) {
+ unsigned int i;
+ st = rrdset_find_bytype("nfs", "proc2");
+ if(!st) {
+ st = rrdset_create("nfs", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Client Remote Procedure Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
+
+ for(i = 0; nfs_proc2_values[i].present ; i++)
+ rrddim_add(st, nfs_proc2_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ for(i = 0; nfs_proc2_values[i].present ; i++)
+ rrddim_set(st, nfs_proc2_values[i].name, nfs_proc2_values[i].value);
+
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_proc3 == 2) {
+ unsigned int i;
+ st = rrdset_find_bytype("nfs", "proc3");
+ if(!st) {
+ st = rrdset_create("nfs", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Client Remote Procedure Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
+
+ for(i = 0; nfs_proc3_values[i].present ; i++)
+ rrddim_add(st, nfs_proc3_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ for(i = 0; nfs_proc3_values[i].present ; i++)
+ rrddim_set(st, nfs_proc3_values[i].name, nfs_proc3_values[i].value);
+
+ rrdset_done(st);
+ }
+
+ // --------------------------------------------------------------------
+
+ if(do_proc4 == 2) {
+ unsigned int i;
+ st = rrdset_find_bytype("nfs", "proc4");
+ if(!st) {
+ st = rrdset_create("nfs", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Client Remote Procedure Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
+
+ for(i = 0; nfs_proc4_values[i].present ; i++)
+ rrddim_add(st, nfs_proc4_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ for(i = 0; nfs_proc4_values[i].present ; i++)
+ rrddim_set(st, nfs_proc4_values[i].name, nfs_proc4_values[i].value);
+
+ rrdset_done(st);
+ }
+
+ return 0;
+}
diff --git a/src/proc_net_rpc_nfsd.c b/src/proc_net_rpc_nfsd.c
index 0323b4dfb..817e6c86a 100644
--- a/src/proc_net_rpc_nfsd.c
+++ b/src/proc_net_rpc_nfsd.c
@@ -2,47 +2,135 @@
struct nfsd_procs {
char name[30];
- unsigned long long proc2;
- unsigned long long proc3;
- unsigned long long proc4;
- int present2;
- int present3;
- int present4;
+ unsigned long long value;
+ int present;
};
-struct nfsd_procs nfsd_proc_values[] = {
- { "null", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "getattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "setattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "lookup", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "access", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "readlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "read", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "write", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "create", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "mkdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "symlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "mknod", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "remove", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "rmdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "rename", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "link", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "readdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "readdirplus", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "fsstat", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "fsinfo", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "pathconf", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "commit", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
- { "", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+struct nfsd_procs nfsd_proc2_values[] = {
+ { "null", 0ULL, 0 },
+ { "getattr", 0ULL, 0 },
+ { "setattr", 0ULL, 0 },
+ { "root", 0ULL, 0 },
+ { "lookup", 0ULL, 0 },
+ { "readlink", 0ULL, 0 },
+ { "read", 0ULL, 0 },
+ { "wrcache", 0ULL, 0 },
+ { "write", 0ULL, 0 },
+ { "create", 0ULL, 0 },
+ { "remove", 0ULL, 0 },
+ { "rename", 0ULL, 0 },
+ { "link", 0ULL, 0 },
+ { "symlink", 0ULL, 0 },
+ { "mkdir", 0ULL, 0 },
+ { "rmdir", 0ULL, 0 },
+ { "readdir", 0ULL, 0 },
+ { "fsstat", 0ULL, 0 },
+
+ /* termination */
+ { "", 0ULL, 0 }
};
-struct nfsd4_ops {
- char name[30];
- unsigned long long value;
- int present;
+struct nfsd_procs nfsd_proc3_values[] = {
+ { "null", 0ULL, 0 },
+ { "getattr", 0ULL, 0 },
+ { "setattr", 0ULL, 0 },
+ { "lookup", 0ULL, 0 },
+ { "access", 0ULL, 0 },
+ { "readlink", 0ULL, 0 },
+ { "read", 0ULL, 0 },
+ { "write", 0ULL, 0 },
+ { "create", 0ULL, 0 },
+ { "mkdir", 0ULL, 0 },
+ { "symlink", 0ULL, 0 },
+ { "mknod", 0ULL, 0 },
+ { "remove", 0ULL, 0 },
+ { "rmdir", 0ULL, 0 },
+ { "rename", 0ULL, 0 },
+ { "link", 0ULL, 0 },
+ { "readdir", 0ULL, 0 },
+ { "readdirplus", 0ULL, 0 },
+ { "fsstat", 0ULL, 0 },
+ { "fsinfo", 0ULL, 0 },
+ { "pathconf", 0ULL, 0 },
+ { "commit", 0ULL, 0 },
+
+ /* termination */
+ { "", 0ULL, 0 }
+};
+
+struct nfsd_procs nfsd_proc4_values[] = {
+ { "null", 0ULL, 0 },
+ { "read", 0ULL, 0 },
+ { "write", 0ULL, 0 },
+ { "commit", 0ULL, 0 },
+ { "open", 0ULL, 0 },
+ { "open_conf", 0ULL, 0 },
+ { "open_noat", 0ULL, 0 },
+ { "open_dgrd", 0ULL, 0 },
+ { "close", 0ULL, 0 },
+ { "setattr", 0ULL, 0 },
+ { "fsinfo", 0ULL, 0 },
+ { "renew", 0ULL, 0 },
+ { "setclntid", 0ULL, 0 },
+ { "confirm", 0ULL, 0 },
+ { "lock", 0ULL, 0 },
+ { "lockt", 0ULL, 0 },
+ { "locku", 0ULL, 0 },
+ { "access", 0ULL, 0 },
+ { "getattr", 0ULL, 0 },
+ { "lookup", 0ULL, 0 },
+ { "lookup_root", 0ULL, 0 },
+ { "remove", 0ULL, 0 },
+ { "rename", 0ULL, 0 },
+ { "link", 0ULL, 0 },
+ { "symlink", 0ULL, 0 },
+ { "create", 0ULL, 0 },
+ { "pathconf", 0ULL, 0 },
+ { "statfs", 0ULL, 0 },
+ { "readlink", 0ULL, 0 },
+ { "readdir", 0ULL, 0 },
+ { "server_caps", 0ULL, 0 },
+ { "delegreturn", 0ULL, 0 },
+ { "getacl", 0ULL, 0 },
+ { "setacl", 0ULL, 0 },
+ { "fs_locations", 0ULL, 0 },
+ { "rel_lkowner", 0ULL, 0 },
+ { "secinfo", 0ULL, 0 },
+ { "fsid_present", 0ULL, 0 },
+
+ /* nfsv4.1 client ops */
+ { "exchange_id", 0ULL, 0 },
+ { "create_session", 0ULL, 0 },
+ { "destroy_session", 0ULL, 0 },
+ { "sequence", 0ULL, 0 },
+ { "get_lease_time", 0ULL, 0 },
+ { "reclaim_comp", 0ULL, 0 },
+ { "layoutget", 0ULL, 0 },
+ { "getdevinfo", 0ULL, 0 },
+ { "layoutcommit", 0ULL, 0 },
+ { "layoutreturn", 0ULL, 0 },
+ { "secinfo_no", 0ULL, 0 },
+ { "test_stateid", 0ULL, 0 },
+ { "free_stateid", 0ULL, 0 },
+ { "getdevicelist", 0ULL, 0 },
+ { "bind_conn_to_ses", 0ULL, 0 },
+ { "destroy_clientid", 0ULL, 0 },
+
+ /* nfsv4.2 client ops */
+ { "seek", 0ULL, 0 },
+ { "allocate", 0ULL, 0 },
+ { "deallocate", 0ULL, 0 },
+ { "layoutstats", 0ULL, 0 },
+ { "clone", 0ULL, 0 },
+
+ /* termination */
+ { "", 0ULL, 0 }
};
-struct nfsd4_ops nfsd4_ops_values[] = {
+struct nfsd_procs nfsd4_ops_values[] = {
+ { "unused_op0", 0ULL, 0},
+ { "unused_op1", 0ULL, 0},
+ { "future_op2", 0ULL, 0},
{ "access", 0ULL, 0},
{ "close", 0ULL, 0},
{ "commit", 0ULL, 0},
@@ -56,7 +144,7 @@ struct nfsd4_ops nfsd4_ops_values[] = {
{ "lockt", 0ULL, 0},
{ "locku", 0ULL, 0},
{ "lookup", 0ULL, 0},
- { "lookupp", 0ULL, 0},
+ { "lookup_root", 0ULL, 0},
{ "nverify", 0ULL, 0},
{ "open", 0ULL, 0},
{ "openattr", 0ULL, 0},
@@ -107,7 +195,7 @@ struct nfsd4_ops nfsd4_ops_values[] = {
{ "copy", 0ULL, 0},
{ "copy_notify", 0ULL, 0},
{ "deallocate", 0ULL, 0},
- { "io_advise", 0ULL, 0},
+ { "ioadvise", 0ULL, 0},
{ "layouterror", 0ULL, 0},
{ "layoutstats", 0ULL, 0},
{ "offload_cancel", 0ULL, 0},
@@ -174,13 +262,13 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
unsigned long long th_threads = 0, th_fullcnt = 0, th_hist10 = 0, th_hist20 = 0, th_hist30 = 0, th_hist40 = 0, th_hist50 = 0, th_hist60 = 0, th_hist70 = 0, th_hist80 = 0, th_hist90 = 0, th_hist100 = 0;
unsigned long long ra_size = 0, ra_hist10 = 0, ra_hist20 = 0, ra_hist30 = 0, ra_hist40 = 0, ra_hist50 = 0, ra_hist60 = 0, ra_hist70 = 0, ra_hist80 = 0, ra_hist90 = 0, ra_hist100 = 0, ra_none = 0;
unsigned long long net_count = 0, net_udp_count = 0, net_tcp_count = 0, net_tcp_connections = 0;
- unsigned long long rpc_count = 0, rpc_bad_format = 0, rpc_bad_auth = 0, rpc_bad_client = 0;
+ unsigned long long rpc_calls = 0, rpc_bad_format = 0, rpc_bad_auth = 0, rpc_bad_client = 0;
for(l = 0; l < lines ;l++) {
words = procfile_linewords(ff, l);
if(!words) continue;
- type = procfile_lineword(ff, l, 0);
+ type = procfile_lineword(ff, l, 0);
if(do_rc == 1 && strcmp(type, "rc") == 0) {
if(words < 4) {
@@ -306,12 +394,12 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
continue;
}
- rpc_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+ rpc_calls = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
rpc_bad_format = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
rpc_bad_auth = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
rpc_bad_client = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- unsigned long long sum = rpc_count + rpc_bad_format + rpc_bad_auth + rpc_bad_client;
+ unsigned long long sum = rpc_calls + rpc_bad_format + rpc_bad_auth + rpc_bad_client;
if(sum == 0ULL) do_rpc = -1;
else do_rpc = 2;
}
@@ -321,10 +409,10 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
unsigned long long sum = 0;
unsigned int i, j;
- for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
- nfsd_proc_values[i].proc2 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
- nfsd_proc_values[i].present2 = 1;
- sum += nfsd_proc_values[i].proc2;
+ for(i = 0, j = 2; j < words && nfsd_proc2_values[i].name[0] ; i++, j++) {
+ nfsd_proc2_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfsd_proc2_values[i].present = 1;
+ sum += nfsd_proc2_values[i].value;
}
if(sum == 0ULL) {
@@ -342,10 +430,10 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
unsigned long long sum = 0;
unsigned int i, j;
- for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
- nfsd_proc_values[i].proc3 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
- nfsd_proc_values[i].present3 = 1;
- sum += nfsd_proc_values[i].proc3;
+ for(i = 0, j = 2; j < words && nfsd_proc3_values[i].name[0] ; i++, j++) {
+ nfsd_proc3_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfsd_proc3_values[i].present = 1;
+ sum += nfsd_proc3_values[i].value;
}
if(sum == 0ULL) {
@@ -363,10 +451,10 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
unsigned long long sum = 0;
unsigned int i, j;
- for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
- nfsd_proc_values[i].proc4 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
- nfsd_proc_values[i].present4 = 1;
- sum += nfsd_proc_values[i].proc4;
+ for(i = 0, j = 2; j < words && nfsd_proc4_values[i].name[0] ; i++, j++) {
+ nfsd_proc4_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+ nfsd_proc4_values[i].present = 1;
+ sum += nfsd_proc4_values[i].value;
}
if(sum == 0ULL) {
@@ -408,7 +496,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
if(do_rc == 2) {
st = rrdset_find_bytype("nfsd", "readcache");
if(!st) {
- st = rrdset_create("nfsd", "readcache", NULL, "nfsd", NULL, "Read Cache", "reads/s", 5000, update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_create("nfsd", "readcache", NULL, "cache", NULL, "NFS Server Read Cache", "reads/s", 5000, update_every, RRDSET_TYPE_STACKED);
rrddim_add(st, "hits", NULL, 1, 1, RRDDIM_INCREMENTAL);
rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_INCREMENTAL);
@@ -427,7 +515,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
if(do_fh == 2) {
st = rrdset_find_bytype("nfsd", "filehandles");
if(!st) {
- st = rrdset_create("nfsd", "filehandles", NULL, "nfsd", NULL, "File Handles", "handles/s", 5001, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create("nfsd", "filehandles", NULL, "filehandles", NULL, "NFS Server File Handles", "handles/s", 5001, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
rrddim_add(st, "stale", NULL, 1, 1, RRDDIM_ABSOLUTE);
@@ -451,7 +539,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
if(do_io == 2) {
st = rrdset_find_bytype("nfsd", "io");
if(!st) {
- st = rrdset_create("nfsd", "io", NULL, "nfsd", NULL, "I/O", "kilobytes/s", 5002, update_every, RRDSET_TYPE_AREA);
+ st = rrdset_create("nfsd", "io", NULL, "io", NULL, "NFS Server I/O", "kilobytes/s", 5002, update_every, RRDSET_TYPE_AREA);
rrddim_add(st, "read", NULL, 1, 1000, RRDDIM_INCREMENTAL);
rrddim_add(st, "write", NULL, -1, 1000, RRDDIM_INCREMENTAL);
@@ -468,7 +556,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
if(do_th == 2) {
st = rrdset_find_bytype("nfsd", "threads");
if(!st) {
- st = rrdset_create("nfsd", "threads", NULL, "nfsd", NULL, "Threads", "threads", 5003, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create("nfsd", "threads", NULL, "threads", NULL, "NFS Server Threads", "threads", 5003, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "threads", NULL, 1, 1, RRDDIM_ABSOLUTE);
}
@@ -479,7 +567,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
st = rrdset_find_bytype("nfsd", "threads_fullcnt");
if(!st) {
- st = rrdset_create("nfsd", "threads_fullcnt", NULL, "nfsd", NULL, "Threads Full Count", "ops/s", 5004, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create("nfsd", "threads_fullcnt", NULL, "threads", NULL, "NFS Server Threads Full Count", "ops/s", 5004, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "full_count", NULL, 1, 1, RRDDIM_INCREMENTAL);
}
@@ -490,7 +578,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
st = rrdset_find_bytype("nfsd", "threads_histogram");
if(!st) {
- st = rrdset_create("nfsd", "threads_histogram", NULL, "nfsd", NULL, "Threads Usage Histogram", "percentage", 5005, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create("nfsd", "threads_histogram", NULL, "threads", NULL, "NFS Server Threads Usage Histogram", "percentage", 5005, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "0%-10%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
rrddim_add(st, "10%-20%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
@@ -523,7 +611,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
if(do_ra == 2) {
st = rrdset_find_bytype("nfsd", "readahead");
if(!st) {
- st = rrdset_create("nfsd", "readahead", NULL, "nfsd", NULL, "Read Ahead Depth", "percentage", 5005, update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_create("nfsd", "readahead", NULL, "readahead", NULL, "NFS Server Read Ahead Depth", "percentage", 5005, update_every, RRDSET_TYPE_STACKED);
rrddim_add(st, "10%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
rrddim_add(st, "20%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
@@ -561,7 +649,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
if(do_net == 2) {
st = rrdset_find_bytype("nfsd", "net");
if(!st) {
- st = rrdset_create("nfsd", "net", NULL, "nfsd", NULL, "Network Reads", "reads/s", 5007, update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_create("nfsd", "net", NULL, "network", NULL, "NFS Server Network Statistics", "packets/s", 5007, update_every, RRDSET_TYPE_STACKED);
st->isdetail = 1;
rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL);
@@ -583,10 +671,10 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
if(do_rpc == 2) {
st = rrdset_find_bytype("nfsd", "rpc");
if(!st) {
- st = rrdset_create("nfsd", "rpc", NULL, "nfsd", NULL, "Remote Procedure Calls", "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create("nfsd", "rpc", NULL, "rpc", NULL, "NFS Server Remote Procedure Calls Statistics", "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "calls", NULL, 1, 1, RRDDIM_INCREMENTAL);
rrddim_add(st, "bad_format", NULL, -1, 1, RRDDIM_INCREMENTAL);
rrddim_add(st, "bad_auth", NULL, -1, 1, RRDDIM_INCREMENTAL);
}
@@ -595,7 +683,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
// ignore rpc_bad_client
if(rpc_bad_client) {};
- rrddim_set(st, "all", rpc_count);
+ rrddim_set(st, "calls", rpc_calls);
rrddim_set(st, "bad_format", rpc_bad_format);
rrddim_set(st, "bad_auth", rpc_bad_auth);
rrdset_done(st);
@@ -607,15 +695,15 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
unsigned int i;
st = rrdset_find_bytype("nfsd", "proc2");
if(!st) {
- st = rrdset_create("nfsd", "proc2", NULL, "nfsd", NULL, "NFS v2 Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_create("nfsd", "proc2", NULL, "nfsv2rpc", NULL, "NFS v2 Server Remote Procedure Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
- for(i = 0; nfsd_proc_values[i].present2 ; i++)
- rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ for(i = 0; nfsd_proc2_values[i].present ; i++)
+ rrddim_add(st, nfsd_proc2_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- for(i = 0; nfsd_proc_values[i].present2 ; i++)
- rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc2);
+ for(i = 0; nfsd_proc2_values[i].present ; i++)
+ rrddim_set(st, nfsd_proc2_values[i].name, nfsd_proc2_values[i].value);
rrdset_done(st);
}
@@ -626,15 +714,15 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
unsigned int i;
st = rrdset_find_bytype("nfsd", "proc3");
if(!st) {
- st = rrdset_create("nfsd", "proc3", NULL, "nfsd", NULL, "NFS v3 Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_create("nfsd", "proc3", NULL, "nfsv3rpc", NULL, "NFS v3 Server Remote Procedure Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
- for(i = 0; nfsd_proc_values[i].present3 ; i++)
- rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ for(i = 0; nfsd_proc3_values[i].present ; i++)
+ rrddim_add(st, nfsd_proc3_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- for(i = 0; nfsd_proc_values[i].present3 ; i++)
- rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc3);
+ for(i = 0; nfsd_proc3_values[i].present ; i++)
+ rrddim_set(st, nfsd_proc3_values[i].name, nfsd_proc3_values[i].value);
rrdset_done(st);
}
@@ -645,15 +733,15 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
unsigned int i;
st = rrdset_find_bytype("nfsd", "proc4");
if(!st) {
- st = rrdset_create("nfsd", "proc4", NULL, "nfsd", NULL, "NFS v4 Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_create("nfsd", "proc4", NULL, "nfsv4rpc", NULL, "NFS v4 Server Remote Procedure Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
- for(i = 0; nfsd_proc_values[i].present4 ; i++)
- rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+ for(i = 0; nfsd_proc4_values[i].present ; i++)
+ rrddim_add(st, nfsd_proc4_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- for(i = 0; nfsd_proc_values[i].present4 ; i++)
- rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc4);
+ for(i = 0; nfsd_proc4_values[i].present ; i++)
+ rrddim_set(st, nfsd_proc4_values[i].name, nfsd_proc4_values[i].value);
rrdset_done(st);
}
@@ -664,7 +752,7 @@ int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
unsigned int i;
st = rrdset_find_bytype("nfsd", "proc4ops");
if(!st) {
- st = rrdset_create("nfsd", "proc4ops", NULL, "nfsd", NULL, "NFS v4 Operations", "operations/s", 5012, update_every, RRDSET_TYPE_STACKED);
+ st = rrdset_create("nfsd", "proc4ops", NULL, "nfsv2ops", NULL, "NFS v4 Server Operations", "operations/s", 5012, update_every, RRDSET_TYPE_STACKED);
for(i = 0; nfsd4_ops_values[i].present ; i++)
rrddim_add(st, nfsd4_ops_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
diff --git a/src/proc_net_snmp.c b/src/proc_net_snmp.c
index a773f55f6..a75c0a96a 100644
--- a/src/proc_net_snmp.c
+++ b/src/proc_net_snmp.c
@@ -3,34 +3,368 @@
#define RRD_TYPE_NET_SNMP "ipv4"
#define RRD_TYPE_NET_SNMP_LEN strlen(RRD_TYPE_NET_SNMP)
+#define NETSTAT_PRESENT 0x00000001
+
+struct netstat_columns {
+ char *name;
+ uint32_t hash;
+ unsigned long long value;
+ int multiplier; // not needed everywhere
+ char *label; // not needed everywhere
+};
+
+static struct netstat_columns ip_data[] = {
+// { "Forwarding", 0, 0, 1, NULL },
+// { "DefaultTTL", 0, 0, 1, NULL },
+ { "InReceives", 0, 0, 1, NULL },
+ { "InHdrErrors", 0, 0, 1, NULL },
+ { "InAddrErrors", 0, 0, 1, NULL },
+ { "ForwDatagrams", 0, 0, 1, NULL },
+ { "InUnknownProtos", 0, 0, 1, NULL },
+ { "InDiscards", 0, 0, 1, NULL },
+ { "InDelivers", 0, 0, 1, NULL },
+ { "OutRequests", 0, 0, 1, NULL },
+ { "OutDiscards", 0, 0, 1, NULL },
+ { "OutNoRoutes", 0, 0, 1, NULL },
+// { "ReasmTimeout", 0, 0, 1, NULL },
+ { "ReasmReqds", 0, 0, 1, NULL },
+ { "ReasmOKs", 0, 0, 1, NULL },
+ { "ReasmFails", 0, 0, 1, NULL },
+ { "FragOKs", 0, 0, 1, NULL },
+ { "FragFails", 0, 0, 1, NULL },
+ { "FragCreates", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns icmp_data[] = {
+ { "InMsgs", 0, 0, 1, NULL },
+ { "OutMsgs", 0, 0, -1, NULL },
+ { "InErrors", 0, 0, 1, NULL },
+ { "OutErrors", 0, 0, -1, NULL },
+ { "InCsumErrors", 0, 0, 1, NULL },
+
+ // all these are available in icmpmsg
+// { "InDestUnreachs", 0, 0, 1, NULL },
+// { "OutDestUnreachs", 0, 0, -1, NULL },
+// { "InTimeExcds", 0, 0, 1, NULL },
+// { "OutTimeExcds", 0, 0, -1, NULL },
+// { "InParmProbs", 0, 0, 1, NULL },
+// { "OutParmProbs", 0, 0, -1, NULL },
+// { "InSrcQuenchs", 0, 0, 1, NULL },
+// { "OutSrcQuenchs", 0, 0, -1, NULL },
+// { "InRedirects", 0, 0, 1, NULL },
+// { "OutRedirects", 0, 0, -1, NULL },
+// { "InEchos", 0, 0, 1, NULL },
+// { "OutEchos", 0, 0, -1, NULL },
+// { "InEchoReps", 0, 0, 1, NULL },
+// { "OutEchoReps", 0, 0, -1, NULL },
+// { "InTimestamps", 0, 0, 1, NULL },
+// { "OutTimestamps", 0, 0, -1, NULL },
+// { "InTimestampReps", 0, 0, 1, NULL },
+// { "OutTimestampReps", 0, 0, -1, NULL },
+// { "InAddrMasks", 0, 0, 1, NULL },
+// { "OutAddrMasks", 0, 0, -1, NULL },
+// { "InAddrMaskReps", 0, 0, 1, NULL },
+// { "OutAddrMaskReps", 0, 0, -1, NULL },
+
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns icmpmsg_data[] = {
+ { "InType0", 0, 0, 1, "InEchoReps" },
+ { "OutType0", 0, 0, -1, "OutEchoReps" },
+// { "InType1", 0, 0, 1, NULL }, // unassigned
+// { "OutType1", 0, 0, -1, NULL }, // unassigned
+// { "InType2", 0, 0, 1, NULL }, // unassigned
+// { "OutType2", 0, 0, -1, NULL }, // unassigned
+ { "InType3", 0, 0, 1, "InDestUnreachs" },
+ { "OutType3", 0, 0, -1, "OutDestUnreachs" },
+// { "InType4", 0, 0, 1, "InSrcQuenchs" }, // deprecated
+// { "OutType4", 0, 0, -1, "OutSrcQuenchs" }, // deprecated
+ { "InType5", 0, 0, 1, "InRedirects" },
+ { "OutType5", 0, 0, -1, "OutRedirects" },
+// { "InType6", 0, 0, 1, "InAlterHostAddr" }, // deprecated
+// { "OutType6", 0, 0, -1, "OutAlterHostAddr" }, // deprecated
+// { "InType7", 0, 0, 1, NULL }, // unassigned
+// { "OutType7", 0, 0, -1, NULL }, // unassigned
+ { "InType8", 0, 0, 1, "InEchos" },
+ { "OutType8", 0, 0, -1, "OutEchos" },
+ { "InType9", 0, 0, 1, "InRouterAdvert" },
+ { "OutType9", 0, 0, -1, "OutRouterAdvert" },
+ { "InType10", 0, 0, 1, "InRouterSelect" },
+ { "OutType10", 0, 0, -1, "OutRouterSelect" },
+ { "InType11", 0, 0, 1, "InTimeExcds" },
+ { "OutType11", 0, 0, -1, "OutTimeExcds" },
+ { "InType12", 0, 0, 1, "InParmProbs" },
+ { "OutType12", 0, 0, -1, "OutParmProbs" },
+ { "InType13", 0, 0, 1, "InTimestamps" },
+ { "OutType13", 0, 0, -1, "OutTimestamps" },
+ { "InType14", 0, 0, 1, "InTimestampReps" },
+ { "OutType14", 0, 0, -1, "OutTimestampReps" },
+// { "InType15", 0, 0, 1, "InInfos" }, // deprecated
+// { "OutType15", 0, 0, -1, "OutInfos" }, // deprecated
+// { "InType16", 0, 0, 1, "InInfoReps" }, // deprecated
+// { "OutType16", 0, 0, -1, "OutInfoReps" }, // deprecated
+// { "InType17", 0, 0, 1, "InAddrMasks" }, // deprecated
+// { "OutType17", 0, 0, -1, "OutAddrMasks" }, // deprecated
+// { "InType18", 0, 0, 1, "InAddrMaskReps" }, // deprecated
+// { "OutType18", 0, 0, -1, "OutAddrMaskReps" }, // deprecated
+// { "InType30", 0, 0, 1, "InTraceroute" }, // deprecated
+// { "OutType30", 0, 0, -1, "OutTraceroute" }, // deprecated
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns tcp_data[] = {
+// { "RtoAlgorithm", 0, 0, 1, NULL },
+// { "RtoMin", 0, 0, 1, NULL },
+// { "RtoMax", 0, 0, 1, NULL },
+// { "MaxConn", 0, 0, 1, NULL },
+ { "ActiveOpens", 0, 0, 1, NULL },
+ { "PassiveOpens", 0, 0, 1, NULL },
+ { "AttemptFails", 0, 0, 1, NULL },
+ { "EstabResets", 0, 0, 1, NULL },
+ { "CurrEstab", 0, 0, 1, NULL },
+ { "InSegs", 0, 0, 1, NULL },
+ { "OutSegs", 0, 0, 1, NULL },
+ { "RetransSegs", 0, 0, 1, NULL },
+ { "InErrs", 0, 0, 1, NULL },
+ { "OutRsts", 0, 0, 1, NULL },
+ { "InCsumErrors", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns udp_data[] = {
+ { "InDatagrams", 0, 0, 1, NULL },
+ { "NoPorts", 0, 0, 1, NULL },
+ { "InErrors", 0, 0, 1, NULL },
+ { "OutDatagrams", 0, 0, 1, NULL },
+ { "RcvbufErrors", 0, 0, 1, NULL },
+ { "SndbufErrors", 0, 0, 1, NULL },
+ { "InCsumErrors", 0, 0, 1, NULL },
+ { "IgnoredMulti", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static struct netstat_columns udplite_data[] = {
+ { "InDatagrams", 0, 0, 1, NULL },
+ { "NoPorts", 0, 0, 1, NULL },
+ { "InErrors", 0, 0, 1, NULL },
+ { "OutDatagrams", 0, 0, 1, NULL },
+ { "RcvbufErrors", 0, 0, 1, NULL },
+ { "SndbufErrors", 0, 0, 1, NULL },
+ { "InCsumErrors", 0, 0, 1, NULL },
+ { "IgnoredMulti", 0, 0, 1, NULL },
+ { NULL, 0, 0, 0, NULL }
+};
+
+static void hash_array(struct netstat_columns *nc) {
+ int i;
+
+ for(i = 0; nc[i].name ;i++)
+ nc[i].hash = simple_hash(nc[i].name);
+}
+
+static unsigned long long *netstat_columns_find(struct netstat_columns *nc, const char *name) {
+ uint32_t i, hash = simple_hash(name);
+
+ for(i = 0; nc[i].name ;i++)
+ if(unlikely(nc[i].hash == hash && !strcmp(nc[i].name, name)))
+ return &nc[i].value;
+
+ fatal("Cannot find key '%s' in /proc/net/snmp internal array.", name);
+}
+
+static void parse_line_pair(procfile *ff, struct netstat_columns *nc, uint32_t header_line, uint32_t values_line) {
+ uint32_t hwords = procfile_linewords(ff, header_line);
+ uint32_t vwords = procfile_linewords(ff, values_line);
+ uint32_t w, i;
+
+ if(unlikely(vwords > hwords)) {
+ error("File /proc/net/snmp on header line %u has %u words, but on value line %u has %u words.", header_line, hwords, values_line, vwords);
+ vwords = hwords;
+ }
+
+ for(w = 1; w < vwords ;w++) {
+ char *key = procfile_lineword(ff, header_line, w);
+ uint32_t hash = simple_hash(key);
+
+ for(i = 0 ; nc[i].name ;i++) {
+ if(unlikely(hash == nc[i].hash && !strcmp(key, nc[i].name))) {
+ nc[i].value = strtoull(procfile_lineword(ff, values_line, w), NULL, 10);
+ break;
+ }
+ }
+ }
+}
+
int do_proc_net_snmp(int update_every, unsigned long long dt) {
+ (void)dt;
+
static procfile *ff = NULL;
static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1,
- do_udp_packets = -1, do_udp_errors = -1;
-
- if(do_ip_packets == -1) do_ip_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 packets", 1);
- if(do_ip_fragsout == -1) do_ip_fragsout = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments sent", 1);
- if(do_ip_fragsin == -1) do_ip_fragsin = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments assembly", 1);
- if(do_ip_errors == -1) do_ip_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 errors", 1);
- if(do_tcp_sockets == -1) do_tcp_sockets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP connections", 1);
- if(do_tcp_packets == -1) do_tcp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP packets", 1);
- if(do_tcp_errors == -1) do_tcp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP errors", 1);
- if(do_tcp_handshake == -1) do_tcp_handshake = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP handshake issues", 1);
- if(do_udp_packets == -1) do_udp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP packets", 1);
- if(do_udp_errors == -1) do_udp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP errors", 1);
-
- if(dt) {};
-
- if(!ff) {
+ do_udp_packets = -1, do_udp_errors = -1, do_icmp_packets = -1, do_icmpmsg = -1, do_udplite_packets = -1;
+ static uint32_t hash_ip = 0, hash_icmp = 0, hash_tcp = 0, hash_udp = 0, hash_icmpmsg = 0, hash_udplite = 0;
+
+ //static unsigned long long *ip_Forwarding = NULL;
+ //static unsigned long long *ip_DefaultTTL = NULL;
+ static unsigned long long *ip_InReceives = NULL;
+ static unsigned long long *ip_InHdrErrors = NULL;
+ static unsigned long long *ip_InAddrErrors = NULL;
+ static unsigned long long *ip_ForwDatagrams = NULL;
+ static unsigned long long *ip_InUnknownProtos = NULL;
+ static unsigned long long *ip_InDiscards = NULL;
+ static unsigned long long *ip_InDelivers = NULL;
+ static unsigned long long *ip_OutRequests = NULL;
+ static unsigned long long *ip_OutDiscards = NULL;
+ static unsigned long long *ip_OutNoRoutes = NULL;
+ //static unsigned long long *ip_ReasmTimeout = NULL;
+ static unsigned long long *ip_ReasmReqds = NULL;
+ static unsigned long long *ip_ReasmOKs = NULL;
+ static unsigned long long *ip_ReasmFails = NULL;
+ static unsigned long long *ip_FragOKs = NULL;
+ static unsigned long long *ip_FragFails = NULL;
+ static unsigned long long *ip_FragCreates = NULL;
+
+ static unsigned long long *icmp_InMsgs = NULL;
+ static unsigned long long *icmp_OutMsgs = NULL;
+ static unsigned long long *icmp_InErrors = NULL;
+ static unsigned long long *icmp_OutErrors = NULL;
+ static unsigned long long *icmp_InCsumErrors = NULL;
+
+ //static unsigned long long *tcp_RtoAlgorithm = NULL;
+ //static unsigned long long *tcp_RtoMin = NULL;
+ //static unsigned long long *tcp_RtoMax = NULL;
+ //static unsigned long long *tcp_MaxConn = NULL;
+ static unsigned long long *tcp_ActiveOpens = NULL;
+ static unsigned long long *tcp_PassiveOpens = NULL;
+ static unsigned long long *tcp_AttemptFails = NULL;
+ static unsigned long long *tcp_EstabResets = NULL;
+ static unsigned long long *tcp_CurrEstab = NULL;
+ static unsigned long long *tcp_InSegs = NULL;
+ static unsigned long long *tcp_OutSegs = NULL;
+ static unsigned long long *tcp_RetransSegs = NULL;
+ static unsigned long long *tcp_InErrs = NULL;
+ static unsigned long long *tcp_OutRsts = NULL;
+ static unsigned long long *tcp_InCsumErrors = NULL;
+
+ static unsigned long long *udp_InDatagrams = NULL;
+ static unsigned long long *udp_NoPorts = NULL;
+ static unsigned long long *udp_InErrors = NULL;
+ static unsigned long long *udp_OutDatagrams = NULL;
+ static unsigned long long *udp_RcvbufErrors = NULL;
+ static unsigned long long *udp_SndbufErrors = NULL;
+ static unsigned long long *udp_InCsumErrors = NULL;
+ static unsigned long long *udp_IgnoredMulti = NULL;
+
+ static unsigned long long *udplite_InDatagrams = NULL;
+ static unsigned long long *udplite_NoPorts = NULL;
+ static unsigned long long *udplite_InErrors = NULL;
+ static unsigned long long *udplite_OutDatagrams = NULL;
+ static unsigned long long *udplite_RcvbufErrors = NULL;
+ static unsigned long long *udplite_SndbufErrors = NULL;
+ static unsigned long long *udplite_InCsumErrors = NULL;
+ static unsigned long long *udplite_IgnoredMulti = NULL;
+
+ if(unlikely(do_ip_packets == -1)) {
+ do_ip_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 packets", 1);
+ do_ip_fragsout = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments sent", 1);
+ do_ip_fragsin = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments assembly", 1);
+ do_ip_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 errors", 1);
+ do_tcp_sockets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP connections", 1);
+ do_tcp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP packets", 1);
+ do_tcp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP errors", 1);
+ do_tcp_handshake = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP handshake issues", 1);
+ do_udp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP packets", 1);
+ do_udp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP errors", 1);
+ do_icmp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 ICMP packets", 1);
+ do_icmpmsg = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 ICMP messages", 1);
+ do_udplite_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDPLite packets", 1);
+
+ hash_ip = simple_hash("Ip");
+ hash_tcp = simple_hash("Tcp");
+ hash_udp = simple_hash("Udp");
+ hash_icmp = simple_hash("Icmp");
+ hash_icmpmsg = simple_hash("IcmpMsg");
+ hash_udplite = simple_hash("UdpLite");
+
+ hash_array(ip_data);
+ hash_array(tcp_data);
+ hash_array(udp_data);
+ hash_array(icmp_data);
+ hash_array(icmpmsg_data);
+ hash_array(udplite_data);
+
+ //ip_Forwarding = netstat_columns_find(ip_data, "Forwarding");
+ //ip_DefaultTTL = netstat_columns_find(ip_data, "DefaultTTL");
+ ip_InReceives = netstat_columns_find(ip_data, "InReceives");
+ ip_InHdrErrors = netstat_columns_find(ip_data, "InHdrErrors");
+ ip_InAddrErrors = netstat_columns_find(ip_data, "InAddrErrors");
+ ip_ForwDatagrams = netstat_columns_find(ip_data, "ForwDatagrams");
+ ip_InUnknownProtos = netstat_columns_find(ip_data, "InUnknownProtos");
+ ip_InDiscards = netstat_columns_find(ip_data, "InDiscards");
+ ip_InDelivers = netstat_columns_find(ip_data, "InDelivers");
+ ip_OutRequests = netstat_columns_find(ip_data, "OutRequests");
+ ip_OutDiscards = netstat_columns_find(ip_data, "OutDiscards");
+ ip_OutNoRoutes = netstat_columns_find(ip_data, "OutNoRoutes");
+ //ip_ReasmTimeout = netstat_columns_find(ip_data, "ReasmTimeout");
+ ip_ReasmReqds = netstat_columns_find(ip_data, "ReasmReqds");
+ ip_ReasmOKs = netstat_columns_find(ip_data, "ReasmOKs");
+ ip_ReasmFails = netstat_columns_find(ip_data, "ReasmFails");
+ ip_FragOKs = netstat_columns_find(ip_data, "FragOKs");
+ ip_FragFails = netstat_columns_find(ip_data, "FragFails");
+ ip_FragCreates = netstat_columns_find(ip_data, "FragCreates");
+
+ icmp_InMsgs = netstat_columns_find(icmp_data, "InMsgs");
+ icmp_OutMsgs = netstat_columns_find(icmp_data, "OutMsgs");
+ icmp_InErrors = netstat_columns_find(icmp_data, "InErrors");
+ icmp_OutErrors = netstat_columns_find(icmp_data, "OutErrors");
+ icmp_InCsumErrors = netstat_columns_find(icmp_data, "InCsumErrors");
+
+ //tcp_RtoAlgorithm = netstat_columns_find(tcp_data, "RtoAlgorithm");
+ //tcp_RtoMin = netstat_columns_find(tcp_data, "RtoMin");
+ //tcp_RtoMax = netstat_columns_find(tcp_data, "RtoMax");
+ //tcp_MaxConn = netstat_columns_find(tcp_data, "MaxConn");
+ tcp_ActiveOpens = netstat_columns_find(tcp_data, "ActiveOpens");
+ tcp_PassiveOpens = netstat_columns_find(tcp_data, "PassiveOpens");
+ tcp_AttemptFails = netstat_columns_find(tcp_data, "AttemptFails");
+ tcp_EstabResets = netstat_columns_find(tcp_data, "EstabResets");
+ tcp_CurrEstab = netstat_columns_find(tcp_data, "CurrEstab");
+ tcp_InSegs = netstat_columns_find(tcp_data, "InSegs");
+ tcp_OutSegs = netstat_columns_find(tcp_data, "OutSegs");
+ tcp_RetransSegs = netstat_columns_find(tcp_data, "RetransSegs");
+ tcp_InErrs = netstat_columns_find(tcp_data, "InErrs");
+ tcp_OutRsts = netstat_columns_find(tcp_data, "OutRsts");
+ tcp_InCsumErrors = netstat_columns_find(tcp_data, "InCsumErrors");
+
+ udp_InDatagrams = netstat_columns_find(udp_data, "InDatagrams");
+ udp_NoPorts = netstat_columns_find(udp_data, "NoPorts");
+ udp_InErrors = netstat_columns_find(udp_data, "InErrors");
+ udp_OutDatagrams = netstat_columns_find(udp_data, "OutDatagrams");
+ udp_RcvbufErrors = netstat_columns_find(udp_data, "RcvbufErrors");
+ udp_SndbufErrors = netstat_columns_find(udp_data, "SndbufErrors");
+ udp_InCsumErrors = netstat_columns_find(udp_data, "InCsumErrors");
+ udp_IgnoredMulti = netstat_columns_find(udp_data, "IgnoredMulti");
+
+ udplite_InDatagrams = netstat_columns_find(udplite_data, "InDatagrams");
+ udplite_NoPorts = netstat_columns_find(udplite_data, "NoPorts");
+ udplite_InErrors = netstat_columns_find(udplite_data, "InErrors");
+ udplite_OutDatagrams = netstat_columns_find(udplite_data, "OutDatagrams");
+ udplite_RcvbufErrors = netstat_columns_find(udplite_data, "RcvbufErrors");
+ udplite_SndbufErrors = netstat_columns_find(udplite_data, "SndbufErrors");
+ udplite_InCsumErrors = netstat_columns_find(udplite_data, "InCsumErrors");
+ udplite_IgnoredMulti = netstat_columns_find(udplite_data, "IgnoredMulti");
+ }
+
+ if(unlikely(!ff)) {
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp");
ff = procfile_open(config_get("plugin:proc:/proc/net/snmp", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
}
- if(!ff) return 1;
+ if(unlikely(!ff)) return 1;
ff = procfile_readall(ff);
- if(!ff) return 0; // we return 0, so that we will retry to open it next time
+ if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time
uint32_t lines = procfile_lines(ff), l;
uint32_t words;
@@ -38,8 +372,11 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
RRDSET *st;
for(l = 0; l < lines ;l++) {
- if(strcmp(procfile_lineword(ff, l, 0), "Ip") == 0) {
- l++;
+ char *key = procfile_lineword(ff, l, 0);
+ uint32_t hash = simple_hash(key);
+
+ if(unlikely(hash == hash_ip && strcmp(key, "Ip") == 0)) {
+ uint32_t h = l++;
if(strcmp(procfile_lineword(ff, l, 0), "Ip") != 0) {
error("Cannot read Ip line from /proc/net/snmp.");
@@ -47,42 +384,13 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
}
words = procfile_linewords(ff, l);
- if(words < 20) {
- error("Cannot read /proc/net/snmp Ip line. Expected 20 params, read %u.", words);
+ if(words < 3) {
+ error("Cannot read /proc/net/snmp Ip line. Expected 3+ params, read %u.", words);
continue;
}
// see also http://net-snmp.sourceforge.net/docs/mibs/ip.html
- unsigned long long Forwarding, DefaultTTL, InReceives, InHdrErrors, InAddrErrors, ForwDatagrams, InUnknownProtos, InDiscards, InDelivers,
- OutRequests, OutDiscards, OutNoRoutes, ReasmTimeout, ReasmReqds, ReasmOKs, ReasmFails, FragOKs, FragFails, FragCreates;
-
- Forwarding = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- DefaultTTL = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- InReceives = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- InHdrErrors = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- InAddrErrors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- ForwDatagrams = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
- InUnknownProtos = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
- InDiscards = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
- InDelivers = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
- OutRequests = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
- OutDiscards = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
- OutNoRoutes = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
- ReasmTimeout = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
- ReasmReqds = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
- ReasmOKs = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
- ReasmFails = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
- FragOKs = strtoull(procfile_lineword(ff, l, 17), NULL, 10);
- FragFails = strtoull(procfile_lineword(ff, l, 18), NULL, 10);
- FragCreates = strtoull(procfile_lineword(ff, l, 19), NULL, 10);
-
- // these are not counters
- if(Forwarding) {}; // is forwarding enabled?
- if(DefaultTTL) {}; // the default ttl on packets
- if(ReasmTimeout) {}; // Reassembly timeout
-
- // this counter is not used
- if(InDelivers) {}; // total number of packets delivered to IP user-protocols
+ parse_line_pair(ff, ip_data, h, l);
// --------------------------------------------------------------------
@@ -91,15 +399,17 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
if(!st) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InReceives", "received", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutRequests", "sent", -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "ForwDatagrams", "forwarded", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InDelivers", "delivered", 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "sent", OutRequests);
- rrddim_set(st, "received", InReceives);
- rrddim_set(st, "forwarded", ForwDatagrams);
+ rrddim_set(st, "OutRequests", *ip_OutRequests);
+ rrddim_set(st, "InReceives", *ip_InReceives);
+ rrddim_set(st, "ForwDatagrams", *ip_ForwDatagrams);
+ rrddim_set(st, "InDelivers", *ip_InDelivers);
rrdset_done(st);
}
@@ -111,15 +421,15 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "FragOKs", "ok", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "FragFails", "failed", -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "FragCreates", "created", 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "ok", FragOKs);
- rrddim_set(st, "failed", FragFails);
- rrddim_set(st, "all", FragCreates);
+ rrddim_set(st, "FragOKs", *ip_FragOKs);
+ rrddim_set(st, "FragFails", *ip_FragFails);
+ rrddim_set(st, "FragCreates", *ip_FragCreates);
rrdset_done(st);
}
@@ -131,15 +441,15 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsin", NULL, "fragments", NULL, "IPv4 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "ReasmOKs", "ok", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "ReasmFails", "failed", -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "ReasmReqds", "all", 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "ok", ReasmOKs);
- rrddim_set(st, "failed", ReasmFails);
- rrddim_set(st, "all", ReasmReqds);
+ rrddim_set(st, "ReasmOKs", *ip_ReasmOKs);
+ rrddim_set(st, "ReasmFails", *ip_ReasmFails);
+ rrddim_set(st, "ReasmReqds", *ip_ReasmReqds);
rrdset_done(st);
}
@@ -151,28 +461,108 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "InDiscards", InDiscards);
- rrddim_set(st, "OutDiscards", OutDiscards);
- rrddim_set(st, "InHdrErrors", InHdrErrors);
- rrddim_set(st, "InAddrErrors", InAddrErrors);
- rrddim_set(st, "InUnknownProtos", InUnknownProtos);
- rrddim_set(st, "OutNoRoutes", OutNoRoutes);
+ rrddim_set(st, "InDiscards", *ip_InDiscards);
+ rrddim_set(st, "OutDiscards", *ip_OutDiscards);
+ rrddim_set(st, "InHdrErrors", *ip_InHdrErrors);
+ rrddim_set(st, "InAddrErrors", *ip_InAddrErrors);
+ rrddim_set(st, "InUnknownProtos", *ip_InUnknownProtos);
+ rrddim_set(st, "OutNoRoutes", *ip_OutNoRoutes);
rrdset_done(st);
}
}
- else if(strcmp(procfile_lineword(ff, l, 0), "Tcp") == 0) {
- l++;
+ else if(unlikely(hash == hash_icmp && strcmp(key, "Icmp") == 0)) {
+ uint32_t h = l++;
+
+ if(strcmp(procfile_lineword(ff, l, 0), "Icmp") != 0) {
+ error("Cannot read Icmp line from /proc/net/snmp.");
+ break;
+ }
+
+ words = procfile_linewords(ff, l);
+ if(words < 3) {
+ error("Cannot read /proc/net/snmp Icmp line. Expected 3+ params, read %u.", words);
+ continue;
+ }
+
+ parse_line_pair(ff, icmp_data, h, l);
+
+ // --------------------------------------------------------------------
+
+ if(do_icmp_packets) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".icmp");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "icmp", NULL, "icmp", NULL, "IPv4 ICMP Packets", "packets/s", 2602, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InMsgs", "received", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutMsgs", "sent", -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InMsgs", *icmp_InMsgs);
+ rrddim_set(st, "OutMsgs", *icmp_OutMsgs);
+
+ rrdset_done(st);
+
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".icmp_errors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "icmp_errors", NULL, "icmp", NULL, "IPv4 ICMP Errors", "packets/s", 2603, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InErrors", *icmp_InErrors);
+ rrddim_set(st, "OutErrors", *icmp_OutErrors);
+ rrddim_set(st, "InCsumErrors", *icmp_InCsumErrors);
+
+ rrdset_done(st);
+ }
+ }
+ else if(unlikely(hash == hash_icmpmsg && strcmp(key, "IcmpMsg") == 0)) {
+ uint32_t h = l++;
+
+ if(strcmp(procfile_lineword(ff, l, 0), "IcmpMsg") != 0) {
+ error("Cannot read IcmpMsg line from /proc/net/snmp.");
+ break;
+ }
+
+ parse_line_pair(ff, icmpmsg_data, h, l);
+
+ // --------------------------------------------------------------------
+
+ if(do_icmpmsg) {
+ int i;
+
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".icmpmsg");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "icmpmsg", NULL, "icmp", NULL, "IPv4 ICMP Messsages", "packets/s", 2604, update_every, RRDSET_TYPE_LINE);
+
+ for(i = 0; icmpmsg_data[i].name ;i++)
+ rrddim_add(st, icmpmsg_data[i].name, icmpmsg_data[i].label, icmpmsg_data[i].multiplier, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ for(i = 0; icmpmsg_data[i].name ;i++)
+ rrddim_set(st, icmpmsg_data[i].name, icmpmsg_data[i].value);
+
+ rrdset_done(st);
+ }
+ }
+ else if(unlikely(hash == hash_tcp && strcmp(key, "Tcp") == 0)) {
+ uint32_t h = l++;
if(strcmp(procfile_lineword(ff, l, 0), "Tcp") != 0) {
error("Cannot read Tcp line from /proc/net/snmp.");
@@ -180,34 +570,12 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
}
words = procfile_linewords(ff, l);
- if(words < 15) {
- error("Cannot read /proc/net/snmp Tcp line. Expected 15 params, read %u.", words);
+ if(words < 3) {
+ error("Cannot read /proc/net/snmp Tcp line. Expected 3+ params, read %u.", words);
continue;
}
- unsigned long long RtoAlgorithm, RtoMin, RtoMax, MaxConn, ActiveOpens, PassiveOpens, AttemptFails, EstabResets,
- CurrEstab, InSegs, OutSegs, RetransSegs, InErrs, OutRsts;
-
- RtoAlgorithm = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- RtoMin = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- RtoMax = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- MaxConn = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- ActiveOpens = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- PassiveOpens = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
- AttemptFails = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
- EstabResets = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
- CurrEstab = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
- InSegs = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
- OutSegs = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
- RetransSegs = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
- InErrs = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
- OutRsts = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
-
- // these are not counters
- if(RtoAlgorithm) {};
- if(RtoMin) {};
- if(RtoMax) {};
- if(MaxConn) {};
+ parse_line_pair(ff, tcp_data, h, l);
// --------------------------------------------------------------------
@@ -217,11 +585,11 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
if(!st) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections", "active connections", 2500, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "CurrEstab", "connections", 1, 1, RRDDIM_ABSOLUTE);
}
else rrdset_next(st);
- rrddim_set(st, "connections", CurrEstab);
+ rrddim_set(st, "CurrEstab", *tcp_CurrEstab);
rrdset_done(st);
}
@@ -232,13 +600,13 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
if(!st) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", "packets/s", 2600, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InSegs", "received", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutSegs", "sent", -1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "received", InSegs);
- rrddim_set(st, "sent", OutSegs);
+ rrddim_set(st, "InSegs", *tcp_InSegs);
+ rrddim_set(st, "OutSegs", *tcp_OutSegs);
rrdset_done(st);
}
@@ -250,13 +618,15 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", "packets/s", 2700, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "InErrs", InErrs);
- rrddim_set(st, "RetransSegs", RetransSegs);
+ rrddim_set(st, "InErrs", *tcp_InErrs);
+ rrddim_set(st, "InCsumErrors", *tcp_InCsumErrors);
+ rrddim_set(st, "RetransSegs", *tcp_RetransSegs);
rrdset_done(st);
}
@@ -268,24 +638,24 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", NULL, "IPv4 TCP Handshake Issues", "events/s", 2900, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "OutRsts", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutRsts", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "EstabResets", EstabResets);
- rrddim_set(st, "OutRsts", OutRsts);
- rrddim_set(st, "ActiveOpens", ActiveOpens);
- rrddim_set(st, "PassiveOpens", PassiveOpens);
- rrddim_set(st, "AttemptFails", AttemptFails);
+ rrddim_set(st, "EstabResets", *tcp_EstabResets);
+ rrddim_set(st, "OutRsts", *tcp_OutRsts);
+ rrddim_set(st, "ActiveOpens", *tcp_ActiveOpens);
+ rrddim_set(st, "PassiveOpens", *tcp_PassiveOpens);
+ rrddim_set(st, "AttemptFails", *tcp_AttemptFails);
rrdset_done(st);
}
}
- else if(strcmp(procfile_lineword(ff, l, 0), "Udp") == 0) {
- l++;
+ else if(unlikely(hash == hash_udp && strcmp(key, "Udp") == 0)) {
+ uint32_t h = l++;
if(strcmp(procfile_lineword(ff, l, 0), "Udp") != 0) {
error("Cannot read Udp line from /proc/net/snmp.");
@@ -293,19 +663,12 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
}
words = procfile_linewords(ff, l);
- if(words < 7) {
- error("Cannot read /proc/net/snmp Udp line. Expected 7 params, read %u.", words);
+ if(words < 3) {
+ error("Cannot read /proc/net/snmp Udp line. Expected 3+ params, read %u.", words);
continue;
}
- unsigned long long InDatagrams, NoPorts, InErrors, OutDatagrams, RcvbufErrors, SndbufErrors;
-
- InDatagrams = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
- NoPorts = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
- InErrors = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
- OutDatagrams = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
- RcvbufErrors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
- SndbufErrors = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+ parse_line_pair(ff, udp_data, h, l);
// --------------------------------------------------------------------
@@ -315,13 +678,13 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
if(!st) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
- rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "received", InDatagrams);
- rrddim_set(st, "sent", OutDatagrams);
+ rrddim_set(st, "InDatagrams", *udp_InDatagrams);
+ rrddim_set(st, "OutDatagrams", *udp_OutDatagrams);
rrdset_done(st);
}
@@ -333,17 +696,75 @@ int do_proc_net_snmp(int update_every, unsigned long long dt) {
st = rrdset_create(RRD_TYPE_NET_SNMP, "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s", 2701, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
- rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InErrors", *udp_InErrors);
+ rrddim_set(st, "NoPorts", *udp_NoPorts);
+ rrddim_set(st, "RcvbufErrors", *udp_RcvbufErrors);
+ rrddim_set(st, "SndbufErrors", *udp_SndbufErrors);
+ rrddim_set(st, "InCsumErrors", *udp_InCsumErrors);
+ rrddim_set(st, "IgnoredMulti", *udp_IgnoredMulti);
+ rrdset_done(st);
+ }
+ }
+ else if(unlikely(hash == hash_udplite && strcmp(key, "UdpLite") == 0)) {
+ uint32_t h = l++;
+
+ if(strcmp(procfile_lineword(ff, l, 0), "UdpLite") != 0) {
+ error("Cannot read UdpLite line from /proc/net/snmp.");
+ break;
+ }
+
+ words = procfile_linewords(ff, l);
+ if(words < 3) {
+ error("Cannot read /proc/net/snmp UdpLite line. Expected 3+ params, read %u.", words);
+ continue;
+ }
+
+ parse_line_pair(ff, udplite_data, h, l);
+
+ // --------------------------------------------------------------------
+
+ if(do_udplite_packets) {
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".udplite");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "udplite", NULL, "udplite", NULL, "IPv4 UDPLite Packets", "packets/s", 2603, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "InDatagrams", "received", 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "OutDatagrams", "sent", -1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "InDatagrams", *udplite_InDatagrams);
+ rrddim_set(st, "OutDatagrams", *udplite_OutDatagrams);
+ rrdset_done(st);
+
+ st = rrdset_find(RRD_TYPE_NET_SNMP ".udplite_errors");
+ if(!st) {
+ st = rrdset_create(RRD_TYPE_NET_SNMP, "udplite_errors", NULL, "udplite", NULL, "IPv4 UDPLite Errors", "packets/s", 2604, update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
- rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
}
else rrdset_next(st);
- rrddim_set(st, "InErrors", InErrors);
- rrddim_set(st, "NoPorts", NoPorts);
- rrddim_set(st, "RcvbufErrors", RcvbufErrors);
- rrddim_set(st, "SndbufErrors", SndbufErrors);
+ rrddim_set(st, "NoPorts", *udplite_NoPorts);
+ rrddim_set(st, "InErrors", *udplite_InErrors);
+ rrddim_set(st, "InCsumErrors", *udplite_InCsumErrors);
+ rrddim_set(st, "RcvbufErrors", *udplite_RcvbufErrors);
+ rrddim_set(st, "SndbufErrors", *udplite_SndbufErrors);
+ rrddim_set(st, "IgnoredMulti", *udplite_IgnoredMulti);
rrdset_done(st);
}
}
diff --git a/src/proc_net_softnet_stat.c b/src/proc_net_softnet_stat.c
new file mode 100644
index 000000000..b01315863
--- /dev/null
+++ b/src/proc_net_softnet_stat.c
@@ -0,0 +1,119 @@
+#include "common.h"
+
+static inline char *softnet_column_name(uint32_t column) {
+ switch(column) {
+ // https://github.com/torvalds/linux/blob/a7fd20d1c476af4563e66865213474a2f9f473a4/net/core/net-procfs.c#L161-L166
+ case 0: return "processed";
+ case 1: return "dropped";
+ case 2: return "squeezed";
+ case 9: return "received_rps";
+ case 10: return "flow_limit_count";
+ default: return NULL;
+ }
+}
+
+int do_proc_net_softnet_stat(int update_every, unsigned long long dt) {
+ (void)dt;
+
+ static procfile *ff = NULL;
+ static int do_per_core = -1;
+ static uint32_t allocated_lines = 0, allocated_columns = 0, *data = NULL;
+
+ if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/net/softnet_stat", "softnet_stat per core", 1);
+
+ if(!ff) {
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/softnet_stat");
+ ff = procfile_open(config_get("plugin:proc:/proc/net/softnet_stat", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+ }
+ if(!ff) return 1;
+
+ ff = procfile_readall(ff);
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+ uint32_t lines = procfile_lines(ff), l;
+ uint32_t words = procfile_linewords(ff, 0), w;
+
+ if(!lines || !words) {
+ error("Cannot read /proc/net/softnet_stat, %u lines and %u columns reported.", lines, words);
+ return 1;
+ }
+
+ if(lines > 200) lines = 200;
+ if(words > 50) words = 50;
+
+ if(unlikely(!data || lines > allocated_lines || words > allocated_columns)) {
+ freez(data);
+ allocated_lines = lines;
+ allocated_columns = words;
+ data = mallocz((allocated_lines + 1) * allocated_columns * sizeof(uint32_t));
+ }
+
+ // initialize to zero
+ memset(data, 0, (allocated_lines + 1) * allocated_columns * sizeof(uint32_t));
+
+ // parse the values
+ for(l = 0; l < lines ;l++) {
+ words = procfile_linewords(ff, l);
+ if(!words) continue;
+
+ if(words > allocated_columns) words = allocated_columns;
+
+ for(w = 0; w < words ; w++) {
+ if(unlikely(softnet_column_name(w))) {
+ uint32_t t = strtoul(procfile_lineword(ff, l, w), NULL, 16);
+ data[w] += t;
+ data[((l + 1) * allocated_columns) + w] = t;
+ }
+ }
+ }
+
+ if(data[(lines * allocated_columns)] == 0)
+ lines--;
+
+ RRDSET *st;
+
+ // --------------------------------------------------------------------
+
+ st = rrdset_find_bytype("system", "softnet_stat");
+ if(!st) {
+ st = rrdset_create("system", "softnet_stat", NULL, "softnet_stat", NULL, "System softnet_stat", "events/s", 955, update_every, RRDSET_TYPE_LINE);
+ for(w = 0; w < allocated_columns ;w++)
+ if(unlikely(softnet_column_name(w)))
+ rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ for(w = 0; w < allocated_columns ;w++)
+ if(unlikely(softnet_column_name(w)))
+ rrddim_set(st, softnet_column_name(w), data[w]);
+
+ rrdset_done(st);
+
+ if(do_per_core) {
+ for(l = 0; l < lines ;l++) {
+ char id[50+1];
+ snprintfz(id, 50, "cpu%u_softnet_stat", l);
+
+ st = rrdset_find_bytype("cpu", id);
+ if(!st) {
+ char title[100+1];
+ snprintfz(title, 100, "CPU%u softnet_stat", l);
+
+ st = rrdset_create("cpu", id, NULL, "softnet_stat", NULL, title, "events/s", 4101 + l, update_every, RRDSET_TYPE_LINE);
+ for(w = 0; w < allocated_columns ;w++)
+ if(unlikely(softnet_column_name(w)))
+ rrddim_add(st, softnet_column_name(w), NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ for(w = 0; w < allocated_columns ;w++)
+ if(unlikely(softnet_column_name(w)))
+ rrddim_set(st, softnet_column_name(w), data[((l + 1) * allocated_columns) + w]);
+
+ rrdset_done(st);
+ }
+ }
+
+ return 0;
+}
diff --git a/src/proc_net_stat_conntrack.c b/src/proc_net_stat_conntrack.c
index 8234b20d2..54e250bf2 100644
--- a/src/proc_net_stat_conntrack.c
+++ b/src/proc_net_stat_conntrack.c
@@ -88,7 +88,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
if(do_sockets) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections", "active connections", 1000, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections", "active connections", 3000, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
}
@@ -103,7 +103,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
if(do_new) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections", "connections/s", 3001, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
rrddim_add(st, "ignore", NULL, -1, 1, RRDDIM_INCREMENTAL);
@@ -122,7 +122,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
if(do_changes) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s", 1002, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s", 3002, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
rrddim_add(st, "inserted", NULL, 1, 1, RRDDIM_INCREMENTAL);
@@ -142,7 +142,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
if(do_expect) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations", "expectations/s", 1003, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations", "expectations/s", 3003, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
rrddim_add(st, "created", NULL, 1, 1, RRDDIM_INCREMENTAL);
@@ -162,7 +162,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
if(do_search) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches", "searches/s", 1010, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches", "searches/s", 3010, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
rrddim_add(st, "searched", NULL, 1, 1, RRDDIM_INCREMENTAL);
@@ -182,7 +182,7 @@ int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
if(do_errors) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s", 1005, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s", 3005, update_every, RRDSET_TYPE_LINE);
st->isdetail = 1;
rrddim_add(st, "icmp_error", NULL, 1, 1, RRDDIM_INCREMENTAL);
diff --git a/src/proc_net_stat_synproxy.c b/src/proc_net_stat_synproxy.c
index 758c35dee..102805f70 100644
--- a/src/proc_net_stat_synproxy.c
+++ b/src/proc_net_stat_synproxy.c
@@ -58,7 +58,7 @@ int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 1004, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 3304, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "entries", NULL, 1, 1, RRDDIM_ABSOLUTE);
}
@@ -75,7 +75,7 @@ int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_syn_received", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY SYN Packets received", "SYN/s", 1001, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_syn_received", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY SYN Packets received", "SYN/s", 3301, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
}
@@ -92,7 +92,7 @@ int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened", "connections/s", 1003, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened", "connections/s", 3303, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "reopened", NULL, 1, 1, RRDDIM_INCREMENTAL);
}
@@ -109,7 +109,7 @@ int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) {
st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies");
if(!st) {
- st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 1002, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 3302, update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "valid", NULL, 1, 1, RRDDIM_INCREMENTAL);
rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
diff --git a/src/proc_softirqs.c b/src/proc_softirqs.c
index a5165040a..ebbbf2aeb 100644
--- a/src/proc_softirqs.c
+++ b/src/proc_softirqs.c
@@ -21,8 +21,7 @@ static inline struct interrupt *get_interrupts_array(int lines, int cpus) {
static struct interrupt *irrs = NULL;
static int allocated = 0;
- if(lines < allocated) return irrs;
- else {
+ if(lines > allocated) {
irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus));
allocated = lines;
}
@@ -134,8 +133,8 @@ int do_proc_softirqs(int update_every, unsigned long long dt) {
int c;
for(c = 0; c < cpus ; c++) {
- char id[256+1];
- snprintfz(id, 256, "cpu%d_softirqs", c);
+ char id[50+1];
+ snprintfz(id, 50, "cpu%d_softirqs", c);
st = rrdset_find_bytype("cpu", id);
if(!st) {
@@ -148,10 +147,9 @@ int do_proc_softirqs(int update_every, unsigned long long dt) {
}
if(core_sum == 0) continue; // try next core
- char name[256+1], title[256+1];
- snprintfz(name, 256, "cpu%d_softirqs", c);
- snprintfz(title, 256, "CPU%d softirqs", c);
- st = rrdset_create("cpu", id, name, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c, update_every, RRDSET_TYPE_STACKED);
+ char title[100+1];
+ snprintfz(title, 100, "CPU%d softirqs", c);
+ st = rrdset_create("cpu", id, NULL, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c, update_every, RRDSET_TYPE_STACKED);
for(l = 0; l < lines ;l++) {
struct interrupt *irr = irrindex(irrs, l, cpus);
diff --git a/src/proc_vmstat.c b/src/proc_vmstat.c
index f25d50c50..5f4e5aadd 100644
--- a/src/proc_vmstat.c
+++ b/src/proc_vmstat.c
@@ -193,11 +193,11 @@ int do_proc_vmstat(int update_every, unsigned long long dt) {
// hash_unevictable_pgs_stranded = simple_hash("unevictable_pgs_stranded");
}
- if(do_swapio == -1) do_swapio = config_get_boolean("plugin:proc:/proc/vmstat", "swap i/o", 1);
- if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1);
- if(do_pgfaults == -1) do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1);
+ if(do_swapio == -1) do_swapio = config_get_boolean_ondemand("plugin:proc:/proc/vmstat", "swap i/o", CONFIG_ONDEMAND_ONDEMAND);
+ if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1);
+ if(do_pgfaults == -1) do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1);
- if(dt) {};
+ (void)dt;
if(!ff) {
char filename[FILENAME_MAX + 1];
@@ -415,7 +415,9 @@ int do_proc_vmstat(int update_every, unsigned long long dt) {
// --------------------------------------------------------------------
- if(do_swapio) {
+ if(pswpin || pswpout || do_swapio == CONFIG_ONDEMAND_YES) {
+ do_swapio = CONFIG_ONDEMAND_YES;
+
static RRDSET *st_swapio = NULL;
if(!st_swapio) {
st_swapio = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
diff --git a/src/registry.c b/src/registry.c
index f2319c478..a0fb629ad 100644
--- a/src/registry.c
+++ b/src/registry.c
@@ -27,8 +27,15 @@
// - [DONE] limit the size of PERSON_URL names
// - limit the number of requests that add data to the registry,
// per client IP per hour
-
-
+//
+// 3. lower memory requirements
+//
+// - embed avl structures directly into registry objects, instead of DICTIONARY
+// - store GUIDs in memory as UUID instead of char *
+// (this will also remove the index hash, since UUIDs can be compared directly)
+// - do not track persons using the demo machines only
+// (i.e. start tracking them only when they access a non-demo machine)
+// - [DONE] do not track custom dashboards by default
#define REGISTRY_URL_FLAGS_DEFAULT 0x00
#define REGISTRY_URL_FLAGS_EXPIRED 0x01
@@ -328,10 +335,8 @@ static inline URL *registry_url_allocate_nolock(const char *url, size_t urllen)
return u;
}
-static inline URL *registry_url_get(const char *url, size_t urllen) {
- debug(D_REGISTRY, "Registry: registry_url_get('%s')", url);
-
- registry_urls_lock();
+static inline URL *registry_url_get_nolock(const char *url, size_t urllen) {
+ debug(D_REGISTRY, "Registry: registry_url_get_nolock('%s')", url);
URL *u = dictionary_get(registry.urls, url);
if(!u) {
@@ -339,6 +344,16 @@ static inline URL *registry_url_get(const char *url, size_t urllen) {
registry.urls_count++;
}
+ return u;
+}
+
+static inline URL *registry_url_get(const char *url, size_t urllen) {
+ debug(D_REGISTRY, "Registry: registry_url_get('%s')", url);
+
+ registry_urls_lock();
+
+ URL *u = registry_url_get_nolock(url, urllen);
+
registry_urls_unlock();
return u;
@@ -555,7 +570,6 @@ static inline PERSON *registry_person_get(const char *person_guid, time_t when)
else {
person_guid = buf;
p = registry_person_find(person_guid);
- if(!p) person_guid = NULL;
}
}
@@ -782,6 +796,7 @@ int registry_log_load(void) {
else
registry_request_delete(p->guid, machine_guid, url, name, when);
+ registry.log_count++;
break;
default:
@@ -789,7 +804,7 @@ int registry_log_load(void) {
break;
}
}
-
+
fclose(fp);
}
@@ -955,7 +970,7 @@ MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *u
// make sure the machine exists
m = registry_machine_find(request_machine);
if(!m) {
- info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
+ info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, machine_guid, pu->url->url, request_machine);
return NULL;
}
@@ -1227,6 +1242,21 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
// ----------------------------------------------------------------------------
// REGISTRY THIS MACHINE UNIQUE ID
+static inline int is_machine_guid_blacklisted(const char *guid) {
+ // these are machine GUIDs that have been included in distribution packages.
+ // we blacklist them here, so that the next version of netdata will generate
+ // new ones.
+
+ if(!strcmp(guid, "8a795b0c-2311-11e6-8563-000c295076a6")
+ || !strcmp(guid, "4aed1458-1c3e-11e6-a53f-000c290fc8f5")
+ ) {
+ error("Blacklisted machine GUID '%s' found.", guid);
+ return 1;
+ }
+
+ return 0;
+}
+
char *registry_get_this_machine_guid(void) {
if(likely(registry.machine_guid[0]))
return registry.machine_guid;
@@ -1245,6 +1275,8 @@ char *registry_get_this_machine_guid(void) {
registry.machine_guid[0] = '\0';
}
+ else if(is_machine_guid_blacklisted(registry.machine_guid))
+ registry.machine_guid[0] = '\0';
}
close(fd);
}
@@ -1376,6 +1408,8 @@ int registry_save(void) {
return -2;
}
+ error_log_limit_unlimited();
+
char tmp_filename[FILENAME_MAX + 1];
char old_filename[FILENAME_MAX + 1];
@@ -1387,6 +1421,7 @@ int registry_save(void) {
if(!fp) {
error("Registry: Cannot create file: %s", tmp_filename);
registry_log_unlock();
+ error_log_limit_reset();
return -1;
}
@@ -1398,6 +1433,7 @@ int registry_save(void) {
error("Registry: Cannot save registry machines - return value %d", bytes1);
fclose(fp);
registry_log_unlock();
+ error_log_limit_reset();
return bytes1;
}
debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1);
@@ -1408,6 +1444,7 @@ int registry_save(void) {
error("Registry: Cannot save registry persons - return value %d", bytes2);
fclose(fp);
registry_log_unlock();
+ error_log_limit_reset();
return bytes2;
}
debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2);
@@ -1461,13 +1498,13 @@ int registry_save(void) {
// it has been moved successfully
// discard the current registry log
registry_log_recreate_nolock();
-
registry.log_count = 0;
}
}
// continue operations
registry_log_unlock();
+ error_log_limit_reset();
return -1;
}
@@ -1559,7 +1596,8 @@ static inline size_t registry_load(void) {
}
*url++ = '\0';
- u = registry_url_allocate_nolock(url, strlen(url));
+ // u = registry_url_allocate_nolock(url, strlen(url));
+ u = registry_url_get_nolock(url, strlen(url));
time_t first_t = strtoul(&s[2], NULL, 16);
@@ -1586,7 +1624,8 @@ static inline size_t registry_load(void) {
}
s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
- u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
+ // u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
+ u = registry_url_get_nolock(&s[32], strlen(&s[32]));
MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16));
mu->last_t = strtoul(&s[11], NULL, 16);
@@ -1635,7 +1674,7 @@ int registry_init(void) {
registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400;
registry.registry_domain = config_get("registry", "registry domain", "");
registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io");
- registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", hostname));
+ registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", localhost.hostname));
registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1);
setenv("NETDATA_REGISTRY_HOSTNAME", registry.hostname, 1);
@@ -1685,6 +1724,9 @@ int registry_init(void) {
registry_log_open_nolock();
registry_load();
registry_log_load();
+
+ if(unlikely(registry_should_save_db()))
+ registry_save();
}
return 0;
diff --git a/src/rrd.c b/src/rrd.c
index 61f99eda8..f9b5d4e04 100644
--- a/src/rrd.c
+++ b/src/rrd.c
@@ -5,10 +5,12 @@
// ----------------------------------------------------------------------------
// globals
+/*
// if not zero it gives the time (in seconds) to remove un-updated dimensions
// DO NOT ENABLE
// if dimensions are removed, the chart generation will have to run again
int rrd_delete_unupdated_dimensions = 0;
+*/
int rrd_update_every = UPDATE_EVERY;
int rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
@@ -42,7 +44,8 @@ RRDHOST localhost = {
AVL_LOCK_INITIALIZER
},
.health_log = {
- .nextid = 1,
+ .next_log_id = 1,
+ .next_alarm_id = 1,
.count = 0,
.max = 1000,
.alarms = NULL,
@@ -50,6 +53,12 @@ RRDHOST localhost = {
}
};
+void rrdhost_init(char *hostname) {
+ localhost.hostname = hostname;
+ localhost.health_log.next_log_id =
+ localhost.health_log.next_alarm_id = time(NULL);
+}
+
void rrdhost_rwlock(RRDHOST *host) {
pthread_rwlock_wrlock(&host->rrdset_root_rwlock);
}
@@ -413,8 +422,29 @@ void rrdset_reset(RRDSET *st)
rd->last_collected_time.tv_sec = 0;
rd->last_collected_time.tv_usec = 0;
rd->counter = 0;
- bzero(rd->values, rd->entries * sizeof(storage_number));
+ memset(rd->values, 0, rd->entries * sizeof(storage_number));
+ }
+}
+static long align_entries_to_pagesize(long entries) {
+ if(entries < 5) entries = 5;
+ if(entries > RRD_HISTORY_ENTRIES_MAX) entries = RRD_HISTORY_ENTRIES_MAX;
+
+#ifdef NETDATA_LOG_ALLOCATIONS
+ long page = (size_t)sysconf(_SC_PAGESIZE);
+
+ long size = sizeof(RRDDIM) + entries * sizeof(storage_number);
+ if(size % page) {
+ size -= (size % page);
+ size += page;
+
+ long n = (size - sizeof(RRDDIM)) / sizeof(storage_number);
+ return n;
}
+
+ return entries;
+#else
+ return entries;
+#endif
}
RRDSET *rrdset_create(const char *type, const char *id, const char *name, const char *family, const char *context, const char *title, const char *units, long priority, int update_every, int chart_type)
@@ -441,9 +471,9 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const
return st;
}
- long entries = config_get_number(fullid, "history", rrd_default_history_entries);
- if(entries < 5) entries = config_set_number(fullid, "history", 5);
- if(entries > RRD_HISTORY_ENTRIES_MAX) entries = config_set_number(fullid, "history", RRD_HISTORY_ENTRIES_MAX);
+ long rentries = config_get_number(fullid, "history", rrd_default_history_entries);
+ long entries = align_entries_to_pagesize(rentries);
+ if(entries != rentries) entries = config_set_number(fullid, "history", entries);
int enabled = config_get_boolean(fullid, "enabled", 1);
if(!enabled) entries = 5;
@@ -459,29 +489,29 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const
if(strcmp(st->magic, RRDSET_MAGIC) != 0) {
errno = 0;
info("Initializing file %s.", fullfilename);
- bzero(st, size);
+ memset(st, 0, size);
}
else if(strcmp(st->id, fullid) != 0) {
errno = 0;
error("File %s contents are not for chart %s. Clearing it.", fullfilename, fullid);
// munmap(st, size);
// st = NULL;
- bzero(st, size);
+ memset(st, 0, size);
}
else if(st->memsize != size || st->entries != entries) {
errno = 0;
error("File %s does not have the desired size. Clearing it.", fullfilename);
- bzero(st, size);
+ memset(st, 0, size);
}
else if(st->update_every != update_every) {
errno = 0;
error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
- bzero(st, size);
+ memset(st, 0, size);
}
else if((time(NULL) - st->last_updated.tv_sec) > update_every * entries) {
errno = 0;
error("File %s is too old. Clearing it.", fullfilename);
- bzero(st, size);
+ memset(st, 0, size);
}
}
@@ -496,6 +526,7 @@ RRDSET *rrdset_create(const char *type, const char *id, const char *name, const
st->next = NULL;
st->mapped = rrd_memory_mode;
st->variables = NULL;
+ st->alarms = NULL;
}
else {
st = callocz(1, size);
@@ -606,44 +637,44 @@ RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier
if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) {
errno = 0;
info("Initializing file %s.", fullfilename);
- bzero(rd, size);
+ memset(rd, 0, size);
}
else if(rd->memsize != size) {
errno = 0;
error("File %s does not have the desired size. Clearing it.", fullfilename);
- bzero(rd, size);
+ memset(rd, 0, size);
}
else if(rd->multiplier != multiplier) {
errno = 0;
error("File %s does not have the same multiplier. Clearing it.", fullfilename);
- bzero(rd, size);
+ memset(rd, 0, size);
}
else if(rd->divisor != divisor) {
errno = 0;
error("File %s does not have the same divisor. Clearing it.", fullfilename);
- bzero(rd, size);
+ memset(rd, 0, size);
}
else if(rd->algorithm != algorithm) {
errno = 0;
error("File %s does not have the same algorithm. Clearing it.", fullfilename);
- bzero(rd, size);
+ memset(rd, 0, size);
}
else if(rd->update_every != st->update_every) {
errno = 0;
error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
- bzero(rd, size);
+ memset(rd, 0, size);
}
else if(usec_dt(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * 1000000ULL)) {
errno = 0;
error("File %s is too old. Clearing it.", fullfilename);
- bzero(rd, size);
+ memset(rd, 0, size);
}
else if(strcmp(rd->id, id) != 0) {
errno = 0;
error("File %s contents are not for dimension %s. Clearing it.", fullfilename, id);
// munmap(rd, size);
// rd = NULL;
- bzero(rd, size);
+ memset(rd, 0, size);
}
}
@@ -927,6 +958,8 @@ collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number
rd->updated = 1;
rd->counter++;
+ // fprintf(stderr, "%s.%s %llu " COLLECTED_NUMBER_FORMAT " dt %0.6f" " rate " CALCULATED_NUMBER_FORMAT "\n", st->name, rd->name, st->usec_since_last_update, value, (float)((double)st->usec_since_last_update / (double)1000000), (calculated_number)((value - rd->last_collected_value) * (calculated_number)rd->multiplier / (calculated_number)rd->divisor * 1000000.0 / (calculated_number)st->usec_since_last_update));
+
return rd->last_collected_value;
}
@@ -978,25 +1011,42 @@ unsigned long long rrdset_done(RRDSET *st)
debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name);
- RRDDIM *rd, *last;
- int oldstate, store_this_entry = 1, first_entry = 0;
- unsigned long long last_ut, now_ut, next_ut, stored_entries = 0;
+ RRDDIM *rd;
+
+ int
+ pthreadoldcancelstate; // store the old cancelable pthread state, to restore it at the end
+
+ char
+ store_this_entry = 1, // boolean: 1 = store this entry, 0 = don't store this entry
+ first_entry = 0; // boolean: 1 = this is the first entry seen for this chart, 0 = all other entries
+
+ unsigned int
+ stored_entries = 0; // the number of entries we have stored in the db, during this call to rrdset_done()
+
+ unsigned long long
+ last_collect_ut, // the timestamp in microseconds, of the last collected value
+ now_collect_ut, // the timestamp in microseconds, of this collected value (this is NOW)
+ last_stored_ut, // the timestamp in microseconds, of the last stored entry in the db
+ next_store_ut, // the timestamp in microseconds, of the next entry to store in the db
+ update_every_ut = st->update_every * 1000000ULL; // st->update_every in microseconds
- if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
+ if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &pthreadoldcancelstate) != 0))
error("Cannot set pthread cancel state to DISABLE.");
// a read lock is OK here
pthread_rwlock_rdlock(&st->rwlock);
+/*
// enable the chart, if it was disabled
if(unlikely(rrd_delete_unupdated_dimensions) && !st->enabled)
st->enabled = 1;
+*/
// check if the chart has a long time to be updated
- if(unlikely(st->usec_since_last_update > st->entries * st->update_every * 1000000ULL)) {
+ if(unlikely(st->usec_since_last_update > st->entries * update_every_ut)) {
info("%s: took too long to be updated (%0.3Lf secs). Reseting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0));
rrdset_reset(st);
- st->usec_since_last_update = st->update_every * 1000000ULL;
+ st->usec_since_last_update = update_every_ut;
first_entry = 1;
}
if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: microseconds since last update: %llu", st->name, st->usec_since_last_update);
@@ -1006,6 +1056,7 @@ unsigned long long rrdset_done(RRDSET *st)
// it is the first entry
// set the last_collected_time to now
gettimeofday(&st->last_collected_time, NULL);
+ last_collect_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - update_every_ut;
// the first entry should not be stored
store_this_entry = 0;
@@ -1016,7 +1067,8 @@ unsigned long long rrdset_done(RRDSET *st)
else {
// it is not the first entry
// calculate the proper last_collected_time, using usec_since_last_update
- unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec + st->usec_since_last_update;
+ last_collect_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec;
+ unsigned long long ut = last_collect_ut + st->usec_since_last_update;
st->last_collected_time.tv_sec = (time_t) (ut / 1000000ULL);
st->last_collected_time.tv_usec = (suseconds_t) (ut % 1000000ULL);
}
@@ -1038,11 +1090,11 @@ unsigned long long rrdset_done(RRDSET *st)
}
// check if we will re-write the entire data set
- if(unlikely(usec_dt(&st->last_collected_time, &st->last_updated) > st->update_every * st->entries * 1000000ULL)) {
+ if(unlikely(usec_dt(&st->last_collected_time, &st->last_updated) > st->entries * update_every_ut)) {
info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Reseting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec);
rrdset_reset(st);
- st->usec_since_last_update = st->update_every * 1000000ULL;
+ st->usec_since_last_update = update_every_ut;
gettimeofday(&st->last_collected_time, NULL);
@@ -1056,21 +1108,18 @@ unsigned long long rrdset_done(RRDSET *st)
}
// these are the 3 variables that will help us in interpolation
- // last_ut = the last time we added a value to the storage
- // now_ut = the time the current value is taken at
- // next_ut = the time of the next interpolation point
- last_ut = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
- now_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec;
- next_ut = (st->last_updated.tv_sec + st->update_every) * 1000000ULL;
-
- if(unlikely(!first_entry && now_ut < next_ut)) {
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name);
- }
+ // last_stored_ut = the last time we added a value to the storage
+ // now_collect_ut = the time the current value has been collected
+ // next_store_ut = the time of the next interpolation point
+ last_stored_ut = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
+ now_collect_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec;
+ next_store_ut = (st->last_updated.tv_sec + st->update_every) * 1000000ULL;
if(unlikely(st->debug)) {
- debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
- debug(D_RRD_STATS, "%s: now ut = %0.3Lf (current update time)", st->name, (long double)now_ut/1000000.0);
- debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
+ debug(D_RRD_STATS, "%s: last_collect_ut = %0.3Lf (last collection time)", st->name, (long double)last_collect_ut/1000000.0);
+ debug(D_RRD_STATS, "%s: now_collect_ut = %0.3Lf (current collection time)", st->name, (long double)now_collect_ut/1000000.0);
+ debug(D_RRD_STATS, "%s: last_stored_ut = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0);
+ debug(D_RRD_STATS, "%s: next_store_ut = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0);
}
if(unlikely(!st->counter_done)) {
@@ -1082,7 +1131,7 @@ unsigned long long rrdset_done(RRDSET *st)
// calculate totals and count the dimensions
int dimensions;
st->collected_total = 0;
- for( rd = st->dimensions, dimensions = 0 ; likely(rd) ; rd = rd->next, dimensions++ )
+ for( rd = st->dimensions, dimensions = 0 ; rd ; rd = rd->next, dimensions++ )
if(likely(rd->updated)) st->collected_total += rd->collected_value;
uint32_t storage_flags = SN_EXISTS;
@@ -1090,7 +1139,7 @@ unsigned long long rrdset_done(RRDSET *st)
// process all dimensions to calculate their values
// based on the collected figures only
// at this stage we do not interpolate anything
- for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
+ for( rd = st->dimensions ; rd ; rd = rd->next ) {
if(unlikely(!rd->updated)) {
rd->calculated_value = 0;
@@ -1169,7 +1218,7 @@ unsigned long long rrdset_done(RRDSET *st)
rd->last_collected_value = rd->collected_value;
}
- rd->calculated_value =
+ rd->calculated_value +=
(calculated_number)(rd->collected_value - rd->last_collected_value)
* (calculated_number)rd->multiplier
/ (calculated_number)rd->divisor;
@@ -1259,21 +1308,26 @@ unsigned long long rrdset_done(RRDSET *st)
// at this point we have all the calculated values ready
// it is now time to interpolate values on a second boundary
- unsigned long long first_ut = last_ut;
- long long iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL);
- if((now_ut % (st->update_every * 1000000ULL)) == 0) iterations++;
+ if(unlikely(now_collect_ut < next_store_ut)) {
+ // this is collected in the same interpolation point
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name);
+ }
- for( ; likely(next_ut <= now_ut) ; next_ut += st->update_every * 1000000ULL, iterations-- ) {
+ unsigned long long first_ut = last_stored_ut;
+ long long iterations = (now_collect_ut - last_stored_ut) / (update_every_ut);
+ if((now_collect_ut % (update_every_ut)) == 0) iterations++;
+
+ for( ; next_store_ut <= now_collect_ut ; last_collect_ut = next_store_ut, next_store_ut += update_every_ut, iterations-- ) {
#ifdef NETDATA_INTERNAL_CHECKS
- if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_ut = %llu, next_ut = %llu, now_ut = %llu", st->name, first_ut, last_ut, next_ut, now_ut); }
+ if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_stored_ut = %llu, next_store_ut = %llu, now_collect_ut = %llu", st->name, first_ut, last_stored_ut, next_store_ut, now_collect_ut); }
#endif
if(unlikely(st->debug)) {
- debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
- debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
+ debug(D_RRD_STATS, "%s: last_stored_ut = %0.3Lf (last updated time)", st->name, (long double)last_stored_ut/1000000.0);
+ debug(D_RRD_STATS, "%s: next_store_ut = %0.3Lf (next interpolation point)", st->name, (long double)next_store_ut/1000000.0);
}
- st->last_updated.tv_sec = (time_t) (next_ut / 1000000ULL);
+ st->last_updated.tv_sec = (time_t) (next_store_ut / 1000000ULL);
st->last_updated.tv_usec = 0;
for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
@@ -1283,8 +1337,8 @@ unsigned long long rrdset_done(RRDSET *st)
case RRDDIM_INCREMENTAL:
new_value = (calculated_number)
( rd->calculated_value
- * (calculated_number)(next_ut - last_ut)
- / (calculated_number)(now_ut - last_ut)
+ * (calculated_number)(next_store_ut - last_collect_ut)
+ / (calculated_number)(now_collect_ut - last_collect_ut)
);
if(unlikely(st->debug))
@@ -1296,14 +1350,23 @@ unsigned long long rrdset_done(RRDSET *st)
, st->id, rd->name
, new_value
, rd->calculated_value
- , (next_ut - last_ut)
- , (now_ut - last_ut)
+ , (next_store_ut - last_stored_ut)
+ , (now_collect_ut - last_stored_ut)
);
rd->calculated_value -= new_value;
new_value += rd->last_calculated_value;
rd->last_calculated_value = 0;
new_value /= (calculated_number)st->update_every;
+
+ if(unlikely(next_store_ut - last_stored_ut < update_every_ut)) {
+ if(unlikely(st->debug))
+ debug(D_RRD_STATS, "%s/%s: COLLECTION POINT IS SHORT " CALCULATED_NUMBER_FORMAT " - EXTRAPOLATING",
+ st->id, rd->name
+ , (calculated_number)(next_store_ut - last_stored_ut)
+ );
+ new_value = new_value * (calculated_number)(st->update_every * 1000000) / (calculated_number)(next_store_ut - last_stored_ut);
+ }
break;
case RRDDIM_ABSOLUTE:
@@ -1323,8 +1386,8 @@ unsigned long long rrdset_done(RRDSET *st)
new_value = (calculated_number)
( ( (rd->calculated_value - rd->last_calculated_value)
- * (calculated_number)(next_ut - first_ut)
- / (calculated_number)(now_ut - first_ut)
+ * (calculated_number)(next_store_ut - last_collect_ut)
+ / (calculated_number)(now_collect_ut - last_collect_ut)
)
+ rd->last_calculated_value
);
@@ -1338,20 +1401,15 @@ unsigned long long rrdset_done(RRDSET *st)
, st->id, rd->name
, new_value
, rd->calculated_value, rd->last_calculated_value
- , (next_ut - first_ut)
- , (now_ut - first_ut), rd->last_calculated_value
+ , (next_store_ut - first_ut)
+ , (now_collect_ut - first_ut), rd->last_calculated_value
);
-
- // this is wrong
- // it fades the value towards the target
- // while we know the calculated value is different
- // if(likely(next_ut + st->update_every * 1000000ULL > now_ut)) rd->calculated_value = new_value;
}
break;
}
if(unlikely(!store_this_entry)) {
- // store_this_entry = 1;
+ rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
continue;
}
@@ -1411,25 +1469,34 @@ unsigned long long rrdset_done(RRDSET *st)
st->counter++;
st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
- last_ut = next_ut;
+ last_stored_ut = next_store_ut;
}
- // align next interpolation to last collection point
- if(likely(stored_entries || !store_this_entry)) {
- st->last_updated.tv_sec = st->last_collected_time.tv_sec;
- st->last_updated.tv_usec = st->last_collected_time.tv_usec;
- st->last_collected_total = st->collected_total;
- }
+ st->last_collected_total = st->collected_total;
- for( rd = st->dimensions; likely(rd) ; rd = rd->next ) {
+ for( rd = st->dimensions; rd ; rd = rd->next ) {
if(unlikely(!rd->updated)) continue;
- if(likely(stored_entries || !store_this_entry)) {
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_collected_value, rd->collected_value);
- rd->last_collected_value = rd->collected_value;
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_collected_value, rd->collected_value);
+ rd->last_collected_value = rd->collected_value;
+
+ switch(rd->algorithm) {
+ case RRDDIM_INCREMENTAL:
+ if(unlikely(!first_entry)) {
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value + rd->calculated_value, rd->calculated_value);
+ rd->last_calculated_value += rd->calculated_value;
+ }
+ else {
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS THE FIRST POINT", st->name);
+ }
+ break;
- if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value, rd->calculated_value);
- rd->last_calculated_value = rd->calculated_value;
+ case RRDDIM_ABSOLUTE:
+ case RRDDIM_PCENT_OVER_ROW_TOTAL:
+ case RRDDIM_PCENT_OVER_DIFF_TOTAL:
+ if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value, rd->calculated_value);
+ rd->last_calculated_value = rd->calculated_value;
+ break;
}
rd->calculated_value = 0;
@@ -1452,6 +1519,7 @@ unsigned long long rrdset_done(RRDSET *st)
// ALL DONE ABOUT THE DATA UPDATE
// --------------------------------------------------------------------
+/*
// find if there are any obsolete dimensions (not updated recently)
if(unlikely(rrd_delete_unupdated_dimensions)) {
@@ -1460,6 +1528,7 @@ unsigned long long rrdset_done(RRDSET *st)
break;
if(unlikely(rd)) {
+ RRDDIM *last;
// there is dimension to free
// upgrade our read lock to a write lock
pthread_rwlock_unlock(&st->rwlock);
@@ -1497,11 +1566,12 @@ unsigned long long rrdset_done(RRDSET *st)
}
}
}
+*/
pthread_rwlock_unlock(&st->rwlock);
- if(unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
- error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
+ if(unlikely(pthread_setcancelstate(pthreadoldcancelstate, NULL) != 0))
+ error("Cannot set pthread cancel state to RESTORE (%d).", pthreadoldcancelstate);
return(st->usec_since_last_update);
}
diff --git a/src/rrd.h b/src/rrd.h
index 37eb121e7..92a65fe8a 100644
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -277,7 +277,7 @@ struct rrdset {
// ------------------------------------------------------------------------
// the dimensions
- avl_tree_lock dimensions_index; // the root of the dimensions index
+ avl_tree_lock dimensions_index; // the root of the dimensions index
RRDDIM *dimensions; // the actual data for every dimension
};
@@ -310,6 +310,7 @@ struct rrdhost {
};
typedef struct rrdhost RRDHOST;
extern RRDHOST localhost;
+extern void rrdhost_init(char *hostname);
#ifdef NETDATA_INTERNAL_CHECKS
#define rrdhost_check_wrlock(host) rrdhost_check_wrlock_int(host, __FILE__, __FUNCTION__, __LINE__)
diff --git a/src/rrd2json.c b/src/rrd2json.c
index 9009a8b1d..474b5915d 100644
--- a/src/rrd2json.c
+++ b/src/rrd2json.c
@@ -1,8 +1,5 @@
#include "common.h"
-#define HOSTNAME_MAX 1024
-char *hostname = "unknown";
-
void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb)
{
pthread_rwlock_rdlock(&st->rwlock);
@@ -84,7 +81,7 @@ void rrd_stats_api_v1_charts(BUFFER *wb)
",\n\t\"update_every\": %d"
",\n\t\"history\": %d"
",\n\t\"charts\": {"
- , hostname
+ , localhost.hostname
, rrd_update_every
, rrd_default_history_entries
);
@@ -246,7 +243,7 @@ void rrd_stats_all_json(BUFFER *wb)
"\t\"history\": %d,\n"
"\t\"memory\": %lu\n"
"}\n"
- , hostname
+ , localhost.hostname
, rrd_update_every
, rrd_default_history_entries
, memory
@@ -1217,13 +1214,13 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
absolute_period_requested = 0;
}
- // allow relative for before and after
- if(((before < 0)?-before:before) <= (st->update_every * st->entries)) {
+ // allow relative for before and after (smaller than 3 years)
+ if(((before < 0)?-before:before) <= (3 * 365 * 86400)) {
before = last_entry_t + before;
absolute_period_requested = 0;
}
- if(((after < 0)?-after:after) <= (st->update_every * st->entries)) {
+ if(((after < 0)?-after:after) <= (3 * 365 * 86400)) {
if(after == 0) after = -st->update_every;
after = before + after;
absolute_period_requested = 0;
@@ -1567,9 +1564,9 @@ int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensio
}
if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
- wb->options |= WB_CONTENT_NO_CACHEABLE;
+ buffer_no_cacheable(wb);
else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
- wb->options |= WB_CONTENT_CACHEABLE;
+ buffer_cacheable(wb);
options = rrdr_check_options(r, options, dimensions);
@@ -1595,9 +1592,9 @@ int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long
}
if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
- wb->options |= WB_CONTENT_NO_CACHEABLE;
+ buffer_no_cacheable(wb);
else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
- wb->options |= WB_CONTENT_CACHEABLE;
+ buffer_cacheable(wb);
options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
diff --git a/src/sys_fs_cgroup.c b/src/sys_fs_cgroup.c
index 892f737c1..298f38a38 100644
--- a/src/sys_fs_cgroup.c
+++ b/src/sys_fs_cgroup.c
@@ -138,6 +138,18 @@ struct memory {
unsigned long long total_active_file;
unsigned long long total_unevictable;
*/
+
+ int usage_in_bytes_updated;
+ char *filename_usage_in_bytes;
+ unsigned long long usage_in_bytes;
+
+ int msw_usage_in_bytes_updated;
+ char *filename_msw_usage_in_bytes;
+ unsigned long long msw_usage_in_bytes;
+
+ int failcnt_updated;
+ char *filename_failcnt;
+ unsigned long long failcnt;
};
// https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
@@ -160,9 +172,13 @@ struct cpuacct_usage {
unsigned long long *cpu_percpu;
};
+#define CGROUP_OPTIONS_DISABLED_DUPLICATE 0x00000001
+
struct cgroup {
- int available; // found in the filesystem
- int enabled; // enabled in the config
+ uint32_t options;
+
+ char available; // found in the filesystem
+ char enabled; // enabled in the config
char *id;
uint32_t hash;
@@ -553,6 +569,24 @@ void cgroup_read_memory(struct memory *mem) {
mem->updated = 1;
}
+
+ mem->usage_in_bytes_updated = 0;
+ if(mem->filename_usage_in_bytes) {
+ if(likely(!read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes)))
+ mem->usage_in_bytes_updated = 1;
+ }
+
+ mem->msw_usage_in_bytes_updated = 0;
+ if(mem->filename_msw_usage_in_bytes) {
+ if(likely(!read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes)))
+ mem->msw_usage_in_bytes_updated = 1;
+ }
+
+ mem->failcnt_updated = 0;
+ if(mem->filename_failcnt) {
+ if(likely(!read_single_number_file(mem->filename_failcnt, &mem->failcnt)))
+ mem->failcnt_updated = 1;
+ }
}
void cgroup_read(struct cgroup *cg) {
@@ -714,11 +748,18 @@ struct cgroup *cgroup_add(const char *id) {
if (!strncmp(t->chart_id, "/system.slice/", 14) && !strncmp(cg->chart_id, "/init.scope/system.slice/", 25)) {
error("Control group with chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
cg->chart_id, t->id, cg->id, t->id);
+ debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
+ cg->chart_id, t->id, cg->id, t->id);
t->enabled = 0;
- } else {
- error("Control group with chart id '%s' already exists with id '%s' and is enabled. Disabling cgroup with id '%s'.",
+ t->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
+ }
+ else {
+ error("Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
+ cg->chart_id, t->id, cg->id);
+ debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
cg->chart_id, t->id, cg->id);
cg->enabled = 0;
+ cg->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
}
break;
@@ -865,8 +906,9 @@ void mark_all_cgroups_as_not_available() {
struct cgroup *cg;
// mark all as not available
- for(cg = cgroup_root; cg ; cg = cg->next)
+ for(cg = cgroup_root; cg ; cg = cg->next) {
cg->available = 0;
+ }
}
void cleanup_all_cgroups() {
@@ -874,6 +916,18 @@ void cleanup_all_cgroups() {
for(; cg ;) {
if(!cg->available) {
+ // enable the first duplicate cgroup
+ {
+ struct cgroup *t;
+ for(t = cgroup_root; t ; t = t->next) {
+ if(t != cg && t->available && !t->enabled && t->options & CGROUP_OPTIONS_DISABLED_DUPLICATE && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
+ debug(D_CGROUP, "Enabling duplicate of cgroup '%s' with id '%s', because the original with id '%s' stopped.", t->chart_id, t->id, cg->id);
+ t->enabled = 1;
+ t->options &= ~CGROUP_OPTIONS_DISABLED_DUPLICATE;
+ break;
+ }
+ }
+ }
if(!last)
cgroup_root = cg->next;
@@ -966,6 +1020,27 @@ void find_all_cgroups() {
debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
}
else debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+
+ snprintfz(filename, FILENAME_MAX, "%s%s/memory.usage_in_bytes", cgroup_memory_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->memory.filename_usage_in_bytes = strdupz(filename);
+ debug(D_CGROUP, "memory.usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_usage_in_bytes);
+ }
+ else debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+
+ snprintfz(filename, FILENAME_MAX, "%s%s/memory.msw_usage_in_bytes", cgroup_memory_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
+ debug(D_CGROUP, "memory.msw_usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_msw_usage_in_bytes);
+ }
+ else debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+
+ snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id);
+ if(stat(filename, &buf) != -1) {
+ cg->memory.filename_failcnt = strdupz(filename);
+ debug(D_CGROUP, "memory.failcnt filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_failcnt);
+ }
+ else debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename);
}
if(cgroup_enable_blkio) {
if(!cg->io_service_bytes.filename) {
@@ -1053,7 +1128,7 @@ void update_cgroup_charts(int update_every) {
if(cg->cpuacct_stat.updated) {
st = rrdset_find_bytype(type, "cpu");
if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "CPU Usage for cgroup %s", cg->chart_title);
+ snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors>1)?"s":"", cg->chart_title);
st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
@@ -1072,7 +1147,7 @@ void update_cgroup_charts(int update_every) {
st = rrdset_find_bytype(type, "cpu_per_core");
if(!st) {
- snprintfz(title, CHART_TITLE_MAX, "CPU Usage Per Core for cgroup %s", cg->chart_title);
+ snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) Per Core for cgroup %s", (processors * 100), processors, (processors>1)?"s":"", cg->chart_title);
st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
@@ -1094,7 +1169,7 @@ void update_cgroup_charts(int update_every) {
st = rrdset_find_bytype(type, "mem");
if(!st) {
snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
- st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40200, update_every,
+ st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40210, update_every,
RRDSET_TYPE_STACKED);
rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
@@ -1167,6 +1242,38 @@ void update_cgroup_charts(int update_every) {
}
}
+ if(cg->memory.usage_in_bytes_updated) {
+ st = rrdset_find_bytype(type, "mem_usage");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Total Memory for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title, "MB", 40200,
+ update_every, RRDSET_TYPE_STACKED);
+
+ rrddim_add(st, "ram", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ rrddim_add(st, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "ram", cg->memory.usage_in_bytes);
+ rrddim_set(st, "swap", (cg->memory.msw_usage_in_bytes > cg->memory.usage_in_bytes)?cg->memory.msw_usage_in_bytes - cg->memory.usage_in_bytes:0);
+ rrdset_done(st);
+ }
+
+ if(cg->memory.failcnt_updated && cg->memory.failcnt > 0) {
+ st = rrdset_find_bytype(type, "mem_failcnt");
+ if(!st) {
+ snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title);
+ st = rrdset_create(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt", title, "MB", 40250,
+ update_every, RRDSET_TYPE_LINE);
+
+ rrddim_add(st, "failures", NULL, 1, 1, RRDDIM_INCREMENTAL);
+ }
+ else rrdset_next(st);
+
+ rrddim_set(st, "failures", cg->memory.failcnt);
+ rrdset_done(st);
+ }
+
if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
st = rrdset_find_bytype(type, "io");
if(!st) {
@@ -1302,7 +1409,7 @@ int do_sys_fs_cgroup(int update_every, unsigned long long dt) {
void *cgroups_main(void *ptr)
{
- if(ptr) { ; }
+ (void)ptr;
info("CGROUP Plugin thread created with task id %d", gettid());
@@ -1327,7 +1434,7 @@ void *cgroups_main(void *ptr)
RRDSET *stcpu_thread = NULL;
- for(;1;) {
+ for(;;) {
if(unlikely(netdata_exit)) break;
// delay until it is our time to run
@@ -1372,6 +1479,8 @@ void *cgroups_main(void *ptr)
}
}
+ info("CGROUP thread exiting");
+
pthread_exit(NULL);
return NULL;
}
diff --git a/src/unit_test.c b/src/unit_test.c
index a77fdb134..d699707a4 100644
--- a/src/unit_test.c
+++ b/src/unit_test.c
@@ -379,7 +379,7 @@ struct feed_values test4_feed[] = {
};
calculated_number test4_results[] = {
- 5, 10, 10, 10, 10, 10, 10, 10, 10
+ 10, 10, 10, 10, 10, 10, 10, 10, 10
};
struct test test4 = {
@@ -414,7 +414,7 @@ struct feed_values test5_feed[] = {
};
calculated_number test5_results[] = {
- 500, 500, 0, 500, 500, 0, 0, 0, 0
+ 1000, 500, 0, 500, 500, 0, 0, 0, 0
};
struct test test5 = {
@@ -455,7 +455,7 @@ struct feed_values test6_feed[] = {
};
calculated_number test6_results[] = {
- 3000, 4000, 4000, 4000
+ 4000, 4000, 4000, 4000
};
struct test test6 = {
@@ -490,7 +490,7 @@ struct feed_values test7_feed[] = {
};
calculated_number test7_results[] = {
- 250, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500
+ 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500
};
struct test test7 = {
@@ -597,7 +597,7 @@ struct feed_values test10_feed[] = {
};
calculated_number test10_results[] = {
- 500, 1000, 1000, 1000, 1000, 1000, 1000
+ 1000, 1000, 1000, 1000, 1000, 1000, 1000
};
struct test test10 = {
@@ -737,6 +737,148 @@ struct test test13 = {
};
// --------------------------------------------------------------------------------------------------------------------
+// test14
+
+struct feed_values test14_feed[] = {
+ { 0, 0x015397dc42151c41ULL },
+ { 13573000, 0x015397e612e3ff5dULL },
+ { 29969000, 0x015397f905ecdaa8ULL },
+ { 29958000, 0x0153980c2a6cb5e4ULL },
+ { 30054000, 0x0153981f4032fb83ULL },
+ { 34952000, 0x015398355efadaccULL },
+ { 25046000, 0x01539845ba4b09f8ULL },
+ { 29947000, 0x0153985948bf381dULL },
+ { 30054000, 0x0153986c5b9c27e2ULL },
+ { 29942000, 0x0153987f888982d0ULL },
+};
+
+calculated_number test14_results[] = {
+ 23.1383300, 21.8515600, 21.8804600, 21.7788000, 22.0112200, 22.4386100, 22.0906100, 21.9150800
+};
+
+struct test test14 = {
+ "test14", // name
+ "issue #981 with real data",
+ 30, // update_every
+ 8, // multiplier
+ 1000000000, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 10, // feed entries
+ 8, // result entries
+ test14_feed, // feed
+ test14_results, // results
+ NULL, // feed2
+ NULL // results2
+};
+
+struct feed_values test14b_feed[] = {
+ { 0, 0 },
+ { 13573000, 13573000 },
+ { 29969000, 13573000 + 29969000 },
+ { 29958000, 13573000 + 29969000 + 29958000 },
+ { 30054000, 13573000 + 29969000 + 29958000 + 30054000 },
+ { 34952000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 },
+ { 25046000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 + 25046000 },
+ { 29947000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 + 25046000 + 29947000 },
+ { 30054000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 + 25046000 + 29947000 + 30054000 },
+ { 29942000, 13573000 + 29969000 + 29958000 + 30054000 + 34952000 + 25046000 + 29947000 + 30054000 + 29942000 },
+};
+
+calculated_number test14b_results[] = {
+ 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000
+};
+
+struct test test14b = {
+ "test14b", // name
+ "issue #981 with dummy data",
+ 30, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 10, // feed entries
+ 8, // result entries
+ test14b_feed, // feed
+ test14b_results, // results
+ NULL, // feed2
+ NULL // results2
+};
+
+struct feed_values test14c_feed[] = {
+ { 29000000, 29000000 },
+ { 1000000, 29000000 + 1000000 },
+ { 30000000, 29000000 + 1000000 + 30000000 },
+ { 30000000, 29000000 + 1000000 + 30000000 + 30000000 },
+ { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 },
+ { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 },
+ { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 },
+ { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 },
+ { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 },
+ { 30000000, 29000000 + 1000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 + 30000000 },
+};
+
+calculated_number test14c_results[] = {
+ 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000
+};
+
+struct test test14c = {
+ "test14c", // name
+ "issue #981 with dummy data, checking for late start",
+ 30, // update_every
+ 1, // multiplier
+ 1, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 10, // feed entries
+ 9, // result entries
+ test14c_feed, // feed
+ test14c_results, // results
+ NULL, // feed2
+ NULL // results2
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+// test15
+
+struct feed_values test15_feed[] = {
+ { 0, 1068066388 },
+ { 1008752, 1068822698 },
+ { 993809, 1069573072 },
+ { 995911, 1070324135 },
+ { 1014562, 1071078166 },
+ { 994684, 1071831349 },
+ { 993128, 1072235739 },
+ { 1010332, 1072958871 },
+ { 1003394, 1073707019 },
+ { 995201, 1074460255 },
+};
+
+collected_number test15_feed2[] = {
+ 178825286, 178825286, 178825286, 178825286, 178825498, 178825498, 179165652, 179202964, 179203282, 179204130
+};
+
+calculated_number test15_results[] = {
+ 5857.4080000, 5898.4540000, 5891.6590000, 5806.3160000, 5914.2640000, 3202.2630000, 5589.6560000, 5822.5260000, 5911.7520000
+};
+
+calculated_number test15_results2[] = {
+ 0.0000000, 0.0000000, 0.0024944, 1.6324779, 0.0212777, 2655.1890000, 290.5387000, 5.6733610, 6.5960220
+};
+
+struct test test15 = {
+ "test15", // name
+ "test incremental with 2 dimensions",
+ 1, // update_every
+ 8, // multiplier
+ 1024, // divisor
+ RRDDIM_INCREMENTAL, // algorithm
+ 10, // feed entries
+ 9, // result entries
+ test15_feed, // feed
+ test15_results, // results
+ test15_feed2, // feed2
+ test15_results2 // results2
+};
+
+// --------------------------------------------------------------------------------------------------------------------
int run_test(struct test *test)
{
@@ -749,7 +891,7 @@ int run_test(struct test *test)
snprintfz(name, 100, "unittest-%s", test->name);
// create the chart
- RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
+ RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, test->update_every, RRDSET_TYPE_LINE);
RRDDIM *rd = rrddim_add(st, "dim1", NULL, test->multiplier, test->divisor, test->algorithm);
RRDDIM *rd2 = NULL;
@@ -759,12 +901,20 @@ int run_test(struct test *test)
st->debug = 1;
// feed it with the test data
+ time_t time_now = 0, time_start = time(NULL);
unsigned long c;
+ collected_number last = 0;
for(c = 0; c < test->feed_entries; c++) {
if(debug_flags) fprintf(stderr, "\n\n");
if(c) {
- fprintf(stderr, " > %s: feeding position %lu, after %llu microseconds\n", test->name, c+1, test->feed[c].microseconds);
+ time_now += test->feed[c].microseconds;
+ fprintf(stderr, " > %s: feeding position %lu, after %0.3f seconds (%0.3f seconds from start), delta " CALCULATED_NUMBER_FORMAT ", rate " CALCULATED_NUMBER_FORMAT "\n",
+ test->name, c+1,
+ (float)test->feed[c].microseconds / 1000000.0,
+ (float)time_now / 1000000.0,
+ ((calculated_number)test->feed[c].value - (calculated_number)last) * (calculated_number)test->multiplier / (calculated_number)test->divisor,
+ (((calculated_number)test->feed[c].value - (calculated_number)last) * (calculated_number)test->multiplier / (calculated_number)test->divisor) / (calculated_number)test->feed[c].microseconds * (calculated_number)1000000);
rrdset_next_usec(st, test->feed[c].microseconds);
}
else {
@@ -773,6 +923,7 @@ int run_test(struct test *test)
fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd->name, test->feed[c].value);
rrddim_set(st, "dim1", test->feed[c].value);
+ last = test->feed[c].value;
if(rd2) {
fprintf(stderr, " >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd2->name, test->feed2[c]);
@@ -785,6 +936,7 @@ int run_test(struct test *test)
if(!c) {
fprintf(stderr, " > %s: fixing first collection time to be %llu microseconds to second boundary\n", test->name, test->feed[c].microseconds);
rd->last_collected_time.tv_usec = st->last_collected_time.tv_usec = st->last_updated.tv_usec = test->feed[c].microseconds;
+ // time_start = st->last_collected_time.tv_sec;
}
}
@@ -801,14 +953,21 @@ int run_test(struct test *test)
calculated_number v = unpack_storage_number(rd->values[c]);
calculated_number n = test->results[c];
int same = (roundl(v * 10000000.0) == roundl(n * 10000000.0))?1:0;
- fprintf(stderr, " %s/%s: checking position %lu, expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n", test->name, rd->name, c+1, n, v, (same)?"OK":"### E R R O R ###");
+ fprintf(stderr, " %s/%s: checking position %lu (at %lu secs), expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n",
+ test->name, rd->name, c+1,
+ (rrdset_first_entry_t(st) + c * st->update_every) - time_start,
+ n, v, (same)?"OK":"### E R R O R ###");
+
if(!same) errors++;
if(rd2) {
v = unpack_storage_number(rd2->values[c]);
n = test->results2[c];
same = (roundl(v * 10000000.0) == roundl(n * 10000000.0))?1:0;
- fprintf(stderr, " %s/%s: checking position %lu, expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n", test->name, rd2->name, c+1, n, v, (same)?"OK":"### E R R O R ###");
+ fprintf(stderr, " %s/%s: checking position %lu (at %lu secs), expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n",
+ test->name, rd2->name, c+1,
+ (rrdset_first_entry_t(st) + c * st->update_every) - time_start,
+ n, v, (same)?"OK":"### E R R O R ###");
if(!same) errors++;
}
}
@@ -857,6 +1016,18 @@ int run_all_mockup_tests(void)
if(run_test(&test13))
return 1;
+ if(run_test(&test14))
+ return 1;
+
+ if(run_test(&test14b))
+ return 1;
+
+ if(run_test(&test14c))
+ return 1;
+
+ if(run_test(&test15))
+ return 1;
+
return 0;
}
diff --git a/src/web_buffer.c b/src/web_buffer.c
index 01a97ddc9..93ba782af 100644
--- a/src/web_buffer.c
+++ b/src/web_buffer.c
@@ -35,6 +35,7 @@ void buffer_reset(BUFFER *wb)
wb->contenttype = CT_TEXT_PLAIN;
wb->options = 0;
wb->date = 0;
+ wb->expires = 0;
buffer_overflow_check(wb);
}
diff --git a/src/web_buffer.h b/src/web_buffer.h
index c4cd05632..ee611209b 100644
--- a/src/web_buffer.h
+++ b/src/web_buffer.h
@@ -4,12 +4,13 @@
#define WEB_DATA_LENGTH_INCREASE_STEP 1024
typedef struct web_buffer {
- size_t size; // allocation size of buffer
- size_t len; // current data length in buffer
- char *buffer; // the buffer
- uint8_t contenttype;
- uint8_t options;
- time_t date; // the date this content has been generated
+ size_t size; // allocation size of buffer, in bytes
+ size_t len; // current data length in buffer, in bytes
+ char *buffer; // the buffer itself
+ uint8_t contenttype; // the content type of the data in the buffer
+ uint8_t options; // options related to the content
+ time_t date; // the timestamp this content has been generated
+ time_t expires; // the timestamp this content expires
} BUFFER;
// options
@@ -39,6 +40,9 @@ typedef struct web_buffer {
#define CT_IMAGE_ICNS 20
#define CT_IMAGE_BMP 21
+#define buffer_cacheable(wb) do { (wb)->options |= WB_CONTENT_CACHEABLE; if((wb)->options & WB_CONTENT_NO_CACHEABLE) (wb)->options &= ~WB_CONTENT_NO_CACHEABLE; } while(0)
+#define buffer_no_cacheable(wb) do { (wb)->options |= WB_CONTENT_NO_CACHEABLE; if((wb)->options & WB_CONTENT_CACHEABLE) (wb)->options &= ~WB_CONTENT_CACHEABLE; (wb)->expires = 0; } while(0)
+
#define buffer_strlen(wb) ((wb)->len)
extern const char *buffer_tostring(BUFFER *wb);
diff --git a/src/web_buffer_svg.c b/src/web_buffer_svg.c
index 2f7627cc5..3e847b5d9 100644
--- a/src/web_buffer_svg.c
+++ b/src/web_buffer_svg.c
@@ -529,7 +529,7 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
strcpy(value_string, "-");
else if(precision < 0) {
- int len, l, lstop = 0;
+ int len, lstop = 0, trim_zeros = 1;
calculated_number abs = value;
if(isless(value, 0)) {
@@ -537,27 +537,30 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
abs = -value;
}
- if(isgreaterequal(abs, 1000)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.0Lf", (long double)value);
- else if(isgreaterequal(abs, 100)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.1Lf", (long double)value);
- else if(isgreaterequal(abs, 1)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.2Lf", (long double)value);
- else if(isgreaterequal(abs, 0.1)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.3Lf", (long double)value);
- else len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.4Lf", (long double)value);
-
- // remove trailing zeros
- for(l = len - 1; l > lstop ; l--) {
- if(likely(value_string[l] == '0')) {
- value_string[l] = '\0';
- len--;
- }
+ if(isgreaterequal(abs, 1000)) { len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.0Lf", (long double)value); trim_zeros = 0; }
+ else if(isgreaterequal(abs, 100)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.1Lf", (long double)value);
+ else if(isgreaterequal(abs, 1)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.2Lf", (long double)value);
+ else if(isgreaterequal(abs, 0.1)) len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.3Lf", (long double)value);
+ else len = snprintfz(value_string, VALUE_STRING_SIZE, "%0.4Lf", (long double)value);
+
+ if(unlikely(trim_zeros)) {
+ int l;
+ // remove trailing zeros from the decimal part
+ for(l = len - 1; l > lstop ; l--) {
+ if(likely(value_string[l] == '0')) {
+ value_string[l] = '\0';
+ len--;
+ }
- else if(unlikely(value_string[l] == '.')) {
- value_string[l] = '\0';
- len--;
- break;
- }
+ else if(unlikely(value_string[l] == '.')) {
+ value_string[l] = '\0';
+ len--;
+ break;
+ }
- else
- break;
+ else
+ break;
+ }
}
if(len >= 0)
diff --git a/src/web_client.c b/src/web_client.c
index 4036d4c81..0cf9eeb6a 100644
--- a/src/web_client.c
+++ b/src/web_client.c
@@ -407,6 +407,7 @@ int mysendfile(struct web_client *w, char *filename)
buffer_flush(w->response.data);
w->response.rlen = stat.st_size;
w->response.data->date = stat.st_mtim.tv_sec;
+ buffer_cacheable(w->response.data);
return 200;
}
@@ -665,7 +666,7 @@ int web_client_api_request_v1_alarms(struct web_client *w, char *url)
int all = 0;
while(url) {
- char *value = mystrsep(&url, "?&[]");
+ char *value = mystrsep(&url, "?&");
if (!value || !*value) continue;
if(!strcmp(value, "all")) all = 1;
@@ -680,17 +681,28 @@ int web_client_api_request_v1_alarms(struct web_client *w, char *url)
int web_client_api_request_v1_alarm_log(struct web_client *w, char *url)
{
- (void)url;
+ uint32_t after = 0;
+
+ while(url) {
+ char *value = mystrsep(&url, "?&");
+ if (!value || !*value) continue;
+
+ char *name = mystrsep(&value, "=");
+ if(!name || !*name) continue;
+ if(!value || !*value) continue;
+
+ if(!strcmp(name, "after")) after = strtoul(value, NULL, 0);
+ }
buffer_flush(w->response.data);
w->response.data->contenttype = CT_APPLICATION_JSON;
- health_alarm_log2json(&localhost, w->response.data);
+ health_alarm_log2json(&localhost, w->response.data, after);
return 200;
}
int web_client_api_request_v1_charts(struct web_client *w, char *url)
{
- if(url) { ; }
+ (void)url;
buffer_flush(w->response.data);
w->response.data->contenttype = CT_APPLICATION_JSON;
@@ -706,7 +718,7 @@ int web_client_api_request_v1_chart(struct web_client *w, char *url)
buffer_flush(w->response.data);
while(url) {
- char *value = mystrsep(&url, "?&[]");
+ char *value = mystrsep(&url, "?&");
if(!value || !*value) continue;
char *name = mystrsep(&value, "=");
@@ -768,7 +780,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
uint32_t options = 0x00000000;
while(url) {
- char *value = mystrsep(&url, "/?&[]");
+ char *value = mystrsep(&url, "/?&");
if(!value || !*value) continue;
char *name = mystrsep(&value, "=");
@@ -809,6 +821,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
}
if(!chart || !*chart) {
+ buffer_no_cacheable(w->response.data);
buffer_sprintf(w->response.data, "No chart id is given at the request.");
goto cleanup;
}
@@ -816,6 +829,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
RRDSET *st = rrdset_find(chart);
if(!st) st = rrdset_find_byname(chart);
if(!st) {
+ buffer_no_cacheable(w->response.data);
buffer_svg(w->response.data, "chart not found", 0, "", NULL, NULL, 1, -1);
ret = 200;
goto cleanup;
@@ -825,6 +839,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
if(alarm) {
rc = rrdcalc_find(st, alarm);
if (!rc) {
+ buffer_no_cacheable(w->response.data);
buffer_svg(w->response.data, "alarm not found", 0, "", NULL, NULL, 1, -1);
ret = 200;
goto cleanup;
@@ -848,7 +863,7 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
else if(options & RRDR_OPTION_NOT_ALIGNED)
refresh = st->update_every;
else {
- refresh = (before - after);
+ refresh = (int)(before - after);
if(refresh < 0) refresh = -refresh;
}
}
@@ -904,8 +919,11 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
calculated_number n = rc->value;
if(isnan(n) || isinf(n)) n = 0;
- if (refresh > 0)
+ if (refresh > 0) {
buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
+ w->response.data->expires = time(NULL) + refresh;
+ }
+ else buffer_no_cacheable(w->response.data);
if(!value_color) {
switch(rc->status) {
@@ -935,7 +953,14 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
}
}
- buffer_svg(w->response.data, label, rc->value * multiply / divide, units, label_color, value_color, 0, precision);
+ buffer_svg(w->response.data,
+ label,
+ rc->value * multiply / divide,
+ units,
+ label_color,
+ value_color,
+ 0,
+ precision);
ret = 200;
}
else {
@@ -946,20 +971,40 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
// if the collected value is too old, don't calculate its value
if (rrdset_last_entry_t(st) >= (time(NULL) - (st->update_every * st->gap_when_lost_iterations_above)))
- ret = rrd2value(st, w->response.data, &n, (dimensions) ? buffer_tostring(dimensions) : NULL, points, after,
- before, group, options, NULL, &latest_timestamp, &value_is_null);
+ ret = rrd2value(st,
+ w->response.data,
+ &n,
+ (dimensions) ? buffer_tostring(dimensions) : NULL,
+ points,
+ after,
+ before,
+ group,
+ options,
+ NULL,
+ &latest_timestamp,
+ &value_is_null);
// if the value cannot be calculated, show empty badge
if (ret != 200) {
+ buffer_no_cacheable(w->response.data);
value_is_null = 1;
n = 0;
ret = 200;
}
- else if (refresh > 0)
+ else if (refresh > 0) {
buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
+ w->response.data->expires = time(NULL) + refresh;
+ }
+ else buffer_no_cacheable(w->response.data);
// render the badge
- buffer_svg(w->response.data, label, n * multiply / divide, units, label_color, value_color, value_is_null,
+ buffer_svg(w->response.data,
+ label,
+ n * multiply / divide,
+ units,
+ label_color,
+ value_color,
+ value_is_null,
precision);
}
@@ -998,7 +1043,7 @@ int web_client_api_request_v1_data(struct web_client *w, char *url)
uint32_t options = 0x00000000;
while(url) {
- char *value = mystrsep(&url, "?&[]");
+ char *value = mystrsep(&url, "?&");
if(!value || !*value) continue;
char *name = mystrsep(&value, "=");
@@ -1143,6 +1188,9 @@ cleanup:
return ret;
}
+
+#define REGISTRY_VERIFY_COOKIES_GUID "give-me-back-this-cookie-now--please"
+
int web_client_api_request_v1_registry(struct web_client *w, char *url)
{
static uint32_t hash_action = 0, hash_access = 0, hash_hello = 0, hash_delete = 0, hash_search = 0,
@@ -1190,7 +1238,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
*/
while(url) {
- char *value = mystrsep(&url, "?&[]");
+ char *value = mystrsep(&url, "?&");
if (!value || !*value) continue;
char *name = mystrsep(&value, "=");
@@ -1280,8 +1328,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
w->tracking_required = 1;
if(registry_verify_cookies_redirects() > 0 && (!cookie || !person_guid[0])) {
buffer_flush(w->response.data);
-
- registry_set_cookie(w, "give-me-back-this-cookie-please");
+ registry_set_cookie(w, REGISTRY_VERIFY_COOKIES_GUID);
w->response.data->contenttype = CT_APPLICATION_JSON;
buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry_to_announce());
return 200;
@@ -1329,6 +1376,10 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
return 307
*/
}
+
+ if(unlikely(cookie && person_guid[0] && !strcmp(person_guid, REGISTRY_VERIFY_COOKIES_GUID)))
+ person_guid[0] = '\0';
+
return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, time(NULL));
case 'D':
@@ -1852,8 +1903,18 @@ static inline int http_request_validate(struct web_client *w) {
}
void web_client_process(struct web_client *w) {
- static uint32_t hash_api = 0, hash_netdata_conf = 0, hash_data = 0, hash_datasource = 0, hash_graph = 0,
- hash_list = 0, hash_all_json = 0, hash_exit = 0, hash_debug = 0, hash_mirror = 0;
+ static uint32_t
+ hash_api = 0,
+ hash_netdata_conf = 0,
+ hash_data = 0,
+ hash_datasource = 0,
+ hash_graph = 0,
+ hash_list = 0,
+ hash_all_json = 0;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ static uint32_t hash_exit = 0, hash_debug = 0, hash_mirror = 0;
+#endif
// start timing us
gettimeofday(&w->tv_in, NULL);
@@ -1866,9 +1927,11 @@ void web_client_process(struct web_client *w) {
hash_graph = simple_hash(WEB_PATH_GRAPH);
hash_list = simple_hash("list");
hash_all_json = simple_hash("all.json");
+#ifdef NETDATA_INTERNAL_CHECKS
hash_exit = simple_hash("exit");
hash_debug = simple_hash("debug");
hash_mirror = simple_hash("mirror");
+#endif
}
int code = 500;
@@ -2065,19 +2128,41 @@ void web_client_process(struct web_client *w) {
}
gettimeofday(&w->tv_ready, NULL);
- w->response.data->date = time(NULL);
w->response.sent = 0;
w->response.code = code;
+ // set a proper last modified date
+ if(unlikely(!w->response.data->date))
+ w->response.data->date = w->tv_ready.tv_sec;
+
+ if(unlikely(code != 200))
+ buffer_no_cacheable(w->response.data);
+
+ // set a proper expiration date, if not already set
+ if(unlikely(!w->response.data->expires)) {
+ if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
+ w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
+ else
+ w->response.data->expires = w->tv_ready.tv_sec + 86400;
+ }
+
// prepare the HTTP response header
debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
const char *code_msg = web_response_code_to_string(code);
- char date[32];
- struct tm tmbuf, *tm = gmtime_r(&w->response.data->date, &tmbuf);
- strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
+ // prepare the last modified and expiration dates
+ char date[32], edate[32];
+ {
+ struct tm tmbuf, *tm;
+
+ tm = gmtime_r(&w->response.data->date, &tmbuf);
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
+
+ tm = gmtime_r(&w->response.data->expires, &tmbuf);
+ strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
+ }
buffer_sprintf(w->response.header_output,
"HTTP/1.1 %d %s\r\n"
@@ -2129,44 +2214,37 @@ void web_client_process(struct web_client *w) {
"Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
);
}
-
- if(buffer_strlen(w->response.header))
- buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
-
- if(w->mode == WEB_CLIENT_MODE_NORMAL && (w->response.data->options & WB_CONTENT_NO_CACHEABLE)) {
- buffer_sprintf(w->response.header_output,
- "Expires: %s\r\n"
- "Cache-Control: no-cache\r\n"
- , date);
- }
- else if(w->mode != WEB_CLIENT_MODE_OPTIONS) {
- char edate[32];
- time_t et = w->response.data->date + (86400 * 14);
- struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
- strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
-
+ else {
buffer_sprintf(w->response.header_output,
- "Expires: %s\r\n"
- "Cache-Control: public\r\n"
- , edate);
+ "Cache-Control: %s\r\n"
+ "Expires: %s\r\n",
+ (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
+ edate);
}
- // if we know the content length, put it
- if(!w->response.zoutput && (w->response.data->len || w->response.rlen))
- buffer_sprintf(w->response.header_output,
- "Content-Length: %zu\r\n"
- , w->response.data->len? w->response.data->len: w->response.rlen
- );
- else if(!w->response.zoutput)
- w->keepalive = 0; // content-length is required for keep-alive
+ // copy a possibly available custom header
+ if(unlikely(buffer_strlen(w->response.header)))
+ buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
- if(w->response.zoutput) {
+ // headers related to the transfer method
+ if(likely(w->response.zoutput)) {
buffer_strcat(w->response.header_output,
"Content-Encoding: gzip\r\n"
"Transfer-Encoding: chunked\r\n"
);
}
+ else {
+ if(likely((w->response.data->len || w->response.rlen))) {
+ // we know the content length, put it
+ buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
+ }
+ else {
+ // we don't know the content length, disable keep-alive
+ w->keepalive = 0;
+ }
+ }
+ // end of HTTP header
buffer_strcat(w->response.header_output, "\r\n");
// sent the HTTP header
diff --git a/src/web_server.c b/src/web_server.c
index cf3687f3e..cbbe6bb40 100644
--- a/src/web_server.c
+++ b/src/web_server.c
@@ -10,21 +10,39 @@ int web_server_mode = WEB_SERVER_MODE_MULTI_THREADED;
#ifdef NETDATA_INTERNAL_CHECKS
static void log_allocations(void)
{
- static int mem = 0;
+#ifdef HAVE_C_MALLINFO
+ static int heap = 0, used = 0, mmap = 0;
struct mallinfo mi;
mi = mallinfo();
- if(mi.uordblks > mem) {
+ if(mi.uordblks > used) {
int clients = 0;
struct web_client *w;
for(w = web_clients; w ; w = w->next) clients++;
- info("Allocated memory increased from %d to %d (increased by %d bytes). There are %d web clients connected.", mem, mi.uordblks, mi.uordblks - mem, clients);
- mem = mi.uordblks;
+ info("Allocated memory: used %d KB (+%d B), mmap %d KB (+%d B), heap %d KB (+%d B). %d web clients connected.",
+ mi.uordblks / 1024,
+ mi.uordblks - used,
+ mi.hblkhd / 1024,
+ mi.hblkhd - mmap,
+ mi.arena / 1024,
+ mi.arena - heap,
+ clients);
+
+ used = mi.uordblks;
+ heap = mi.arena;
+ mmap = mi.hblkhd;
}
-}
+#else /* ! HAVE_C_MALLINFO */
+ ;
+#endif /* ! HAVE_C_MALLINFO */
+
+#ifdef has_jemalloc
+ malloc_stats_print(NULL, NULL, NULL);
#endif
+}
+#endif /* NETDATA_INTERNAL_CHECKS */
#ifndef HAVE_ACCEPT4
int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags) {
@@ -38,10 +56,14 @@ int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags) {
flags &= ~SOCK_NONBLOCK;
}
+#ifdef SOCK_CLOEXEC
+#ifdef O_CLOEXEC
if (flags & SOCK_CLOEXEC) {
newflags |= O_CLOEXEC;
flags &= ~SOCK_CLOEXEC;
}
+#endif
+#endif
if (flags) {
errno = -EINVAL;