summaryrefslogtreecommitdiffstats
path: root/collectors/timex.plugin
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 14:31:17 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 14:31:17 +0000
commit8020f71afd34d7696d7933659df2d763ab05542f (patch)
tree2fdf1b5447ffd8bdd61e702ca183e814afdcb4fc /collectors/timex.plugin
parentInitial commit. (diff)
downloadnetdata-upstream.tar.xz
netdata-upstream.zip
Adding upstream version 1.37.1.upstream/1.37.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'collectors/timex.plugin')
-rw-r--r--collectors/timex.plugin/Makefile.am8
-rw-r--r--collectors/timex.plugin/README.md31
-rw-r--r--collectors/timex.plugin/plugin_timex.c176
3 files changed, 215 insertions, 0 deletions
diff --git a/collectors/timex.plugin/Makefile.am b/collectors/timex.plugin/Makefile.am
new file mode 100644
index 0000000..161784b
--- /dev/null
+++ b/collectors/timex.plugin/Makefile.am
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+AUTOMAKE_OPTIONS = subdir-objects
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+dist_noinst_DATA = \
+ README.md \
+ $(NULL)
diff --git a/collectors/timex.plugin/README.md b/collectors/timex.plugin/README.md
new file mode 100644
index 0000000..18665f8
--- /dev/null
+++ b/collectors/timex.plugin/README.md
@@ -0,0 +1,31 @@
+<!--
+title: "timex.plugin"
+description: "Monitor the system clock synchronization state."
+custom_edit_url: https://github.com/netdata/netdata/edit/master/collectors/timex.plugin/README.md
+-->
+
+# timex.plugin
+
+This plugin monitors the system kernel clock synchronization state.
+
+This plugin creates the following charts:
+
+- System clock synchronization state according to the system kernel
+- System clock status which gives the value of the `time_status` variable in the kernel
+- Computed time offset between local system and reference clock
+
+This is obtained from the information provided by the [ntp_adjtime()](https://man7.org/linux/man-pages/man2/adjtimex.2.html) system call.
+An unsynchronized clock may indicate a hardware clock error, or an issue with UTC synchronization.
+
+## Configuration
+
+Edit the `netdata.conf` configuration file using [`edit-config`](/docs/configure/nodes.md#use-edit-config-to-edit-configuration-files) from the [Netdata config directory](/docs/configure/nodes.md#the-netdata-config-directory), which is typically at `/etc/netdata`.
+
+Scroll down to the `[plugin:timex]` section to find the available options:
+
+```ini
+[plugin:timex]
+ # update every = 1
+ # clock synchronization state = yes
+ # time offset = yes
+```
diff --git a/collectors/timex.plugin/plugin_timex.c b/collectors/timex.plugin/plugin_timex.c
new file mode 100644
index 0000000..46cfc57
--- /dev/null
+++ b/collectors/timex.plugin/plugin_timex.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "daemon/common.h"
+#include "libnetdata/os.h"
+
+#define PLUGIN_TIMEX_NAME "timex.plugin"
+
+#define CONFIG_SECTION_TIMEX "plugin:timex"
+
+struct status_codes {
+ char *name;
+ int code;
+ RRDDIM *rd;
+} sta_codes[] = {
+ // {"pll", STA_PLL, NULL},
+ // {"ppsfreq", STA_PPSFREQ, NULL},
+ // {"ppstime", STA_PPSTIME, NULL},
+ // {"fll", STA_FLL, NULL},
+ // {"ins", STA_INS, NULL},
+ // {"del", STA_DEL, NULL},
+ {"unsync", STA_UNSYNC, NULL},
+ // {"freqhold", STA_FREQHOLD, NULL},
+ // {"ppssignal", STA_PPSSIGNAL, NULL},
+ // {"ppsjitter", STA_PPSJITTER, NULL},
+ // {"ppswander", STA_PPSWANDER, NULL},
+ // {"ppserror", STA_PPSERROR, NULL},
+ {"clockerr", STA_CLOCKERR, NULL},
+ // {"nano", STA_NANO, NULL},
+ // {"clk", STA_CLK, NULL},
+ {NULL, 0, NULL},
+};
+
+static void timex_main_cleanup(void *ptr)
+{
+ worker_unregister();
+
+ struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
+
+ info("cleaning up...");
+
+ static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
+}
+
+void *timex_main(void *ptr)
+{
+ worker_register("TIMEX");
+ worker_register_job_name(0, "clock check");
+
+ netdata_thread_cleanup_push(timex_main_cleanup, ptr);
+
+ int update_every = (int)config_get_number(CONFIG_SECTION_TIMEX, "update every", 10);
+ if (update_every < localhost->rrd_update_every)
+ update_every = localhost->rrd_update_every;
+
+ int do_sync = config_get_boolean(CONFIG_SECTION_TIMEX, "clock synchronization state", CONFIG_BOOLEAN_YES);
+ int do_offset = config_get_boolean(CONFIG_SECTION_TIMEX, "time offset", CONFIG_BOOLEAN_YES);
+
+ if (unlikely(do_sync == CONFIG_BOOLEAN_NO && do_offset == CONFIG_BOOLEAN_NO)) {
+ info("No charts to show");
+ goto exit;
+ }
+
+ usec_t step = update_every * USEC_PER_SEC;
+ heartbeat_t hb;
+ heartbeat_init(&hb);
+ while (!netdata_exit) {
+ worker_is_idle();
+ heartbeat_next(&hb, step);
+ worker_is_busy(0);
+
+ struct timex timex_buf = {};
+ int sync_state = 0;
+ static int prev_sync_state = 0;
+
+ sync_state = ADJUST_TIMEX(&timex_buf);
+
+ int non_seq_failure = (sync_state == -1 && prev_sync_state != -1);
+ prev_sync_state = sync_state;
+
+ if (non_seq_failure) {
+ error("Cannot get clock synchronization state");
+ continue;
+ }
+
+ collected_number divisor = USEC_PER_MS;
+ if (timex_buf.status & STA_NANO)
+ divisor = NSEC_PER_MSEC;
+
+ // ----------------------------------------------------------------
+
+ if (do_sync) {
+ static RRDSET *st_sync_state = NULL;
+ static RRDDIM *rd_sync_state;
+
+ if (unlikely(!st_sync_state)) {
+ st_sync_state = rrdset_create_localhost(
+ "system",
+ "clock_sync_state",
+ NULL,
+ "clock synchronization",
+ NULL,
+ "System Clock Synchronization State",
+ "state",
+ PLUGIN_TIMEX_NAME,
+ NULL,
+ NETDATA_CHART_PRIO_CLOCK_SYNC_STATE,
+ update_every,
+ RRDSET_TYPE_LINE);
+
+ rd_sync_state = rrddim_add(st_sync_state, "state", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ }
+
+ rrddim_set_by_pointer(st_sync_state, rd_sync_state, sync_state != TIME_ERROR ? 1 : 0);
+ rrdset_done(st_sync_state);
+
+ static RRDSET *st_clock_status = NULL;
+
+ if (unlikely(!st_clock_status)) {
+ st_clock_status = rrdset_create_localhost(
+ "system",
+ "clock_status",
+ NULL,
+ "clock synchronization",
+ NULL,
+ "System Clock Status",
+ "status",
+ PLUGIN_TIMEX_NAME,
+ NULL,
+ NETDATA_CHART_PRIO_CLOCK_STATUS,
+ update_every,
+ RRDSET_TYPE_LINE);
+
+ for (int i = 0; sta_codes[i].name != NULL; i++) {
+ sta_codes[i].rd =
+ rrddim_add(st_clock_status, sta_codes[i].name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
+ }
+ }
+
+ for (int i = 0; sta_codes[i].name != NULL; i++)
+ rrddim_set_by_pointer(st_clock_status, sta_codes[i].rd, timex_buf.status & sta_codes[i].code ? 1 : 0);
+
+ rrdset_done(st_clock_status);
+ }
+
+ if (do_offset) {
+ static RRDSET *st_offset = NULL;
+ static RRDDIM *rd_offset;
+
+ if (unlikely(!st_offset)) {
+ st_offset = rrdset_create_localhost(
+ "system",
+ "clock_sync_offset",
+ NULL,
+ "clock synchronization",
+ NULL,
+ "Computed Time Offset Between Local System and Reference Clock",
+ "milliseconds",
+ PLUGIN_TIMEX_NAME,
+ NULL,
+ NETDATA_CHART_PRIO_CLOCK_SYNC_OFFSET,
+ update_every,
+ RRDSET_TYPE_LINE);
+
+ rd_offset = rrddim_add(st_offset, "offset", NULL, 1, divisor, RRD_ALGORITHM_ABSOLUTE);
+ }
+
+ rrddim_set_by_pointer(st_offset, rd_offset, timex_buf.offset);
+ rrdset_done(st_offset);
+ }
+ }
+
+exit:
+ netdata_thread_cleanup_pop(1);
+ return NULL;
+}