summaryrefslogtreecommitdiffstats
path: root/collectors/timex.plugin
diff options
context:
space:
mode:
Diffstat (limited to 'collectors/timex.plugin')
-rw-r--r--collectors/timex.plugin/Makefile.am8
-rw-r--r--collectors/timex.plugin/README.md29
-rw-r--r--collectors/timex.plugin/plugin_timex.c176
-rw-r--r--collectors/timex.plugin/plugin_timex.h29
4 files changed, 242 insertions, 0 deletions
diff --git a/collectors/timex.plugin/Makefile.am b/collectors/timex.plugin/Makefile.am
new file mode 100644
index 00000000..161784b8
--- /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 00000000..79947441
--- /dev/null
+++ b/collectors/timex.plugin/README.md
@@ -0,0 +1,29 @@
+<!--
+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 clock synchronization state on Linux nodes.
+
+This plugin creates two charts:
+
+- System clock synchronization state
+- Computed time offset between local system and reference clock
+
+## 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:
+
+```
+[plugin:timex]
+ # update every = 1
+ # clock synchronization state = yes
+ # time offset = yes
+```
+
+[![analytics](https://www.google-analytics.com/collect?v=1&aip=1&t=pageview&_s=1&ds=github&dr=https%3A%2F%2Fgithub.com%2Fnetdata%2Fnetdata&dl=https%3A%2F%2Fmy-netdata.io%2Fgithub%2Fcollectors%2Ftimex.plugin%2FREADME&_u=MAC~&cid=5792dfd7-8dc4-476b-af31-da2fdb9f93d2&tid=UA-64295674-3)](<>)
diff --git a/collectors/timex.plugin/plugin_timex.c b/collectors/timex.plugin/plugin_timex.c
new file mode 100644
index 00000000..b3e722a4
--- /dev/null
+++ b/collectors/timex.plugin/plugin_timex.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "plugin_timex.h"
+#include "sys/timex.h"
+
+#define PLUGIN_TIMEX_NAME "timex.plugin"
+
+#define CONFIG_SECTION_TIMEX "plugin:timex"
+
+static void timex_main_cleanup(void *ptr)
+{
+ 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)
+{
+ netdata_thread_cleanup_push(timex_main_cleanup, ptr);
+
+ int vdo_cpu_netdata = config_get_boolean(CONFIG_SECTION_TIMEX, "timex plugin resource charts", CONFIG_BOOLEAN_YES);
+
+ 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) {
+ usec_t duration = heartbeat_monotonic_dt_to_now_usec(&hb);
+ heartbeat_next(&hb, step);
+
+ struct timex timex_buf = {};
+ int sync_state = 0;
+ sync_state = adjtimex(&timex_buf);
+
+ 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);
+ } else {
+ rrdset_next(st_sync_state);
+ }
+
+ rrddim_set_by_pointer(st_sync_state, rd_sync_state, sync_state != TIME_ERROR ? 1 : 0);
+ rrdset_done(st_sync_state);
+ }
+
+ if (do_sync) {
+ 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);
+ } else {
+ rrdset_next(st_offset);
+ }
+
+ rrddim_set_by_pointer(st_offset, rd_offset, timex_buf.offset);
+ rrdset_done(st_offset);
+ }
+
+ if (vdo_cpu_netdata) {
+ static RRDSET *stcpu_thread = NULL, *st_duration = NULL;
+ static RRDDIM *rd_user = NULL, *rd_system = NULL, *rd_duration = NULL;
+
+ // ----------------------------------------------------------------
+
+ struct rusage thread;
+ getrusage(RUSAGE_THREAD, &thread);
+
+ if (unlikely(!stcpu_thread)) {
+ stcpu_thread = rrdset_create_localhost(
+ "netdata",
+ "plugin_timex",
+ NULL,
+ "timex",
+ NULL,
+ "Netdata Timex Plugin CPU usage",
+ "milliseconds/s",
+ PLUGIN_TIMEX_NAME,
+ NULL,
+ NETDATA_CHART_PRIO_NETDATA_TIMEX,
+ update_every,
+ RRDSET_TYPE_STACKED);
+
+ rd_user = rrddim_add(stcpu_thread, "user", NULL, 1, USEC_PER_MS, RRD_ALGORITHM_INCREMENTAL);
+ rd_system = rrddim_add(stcpu_thread, "system", NULL, 1, USEC_PER_MS, RRD_ALGORITHM_INCREMENTAL);
+ } else {
+ rrdset_next(stcpu_thread);
+ }
+
+ rrddim_set_by_pointer(stcpu_thread, rd_user, thread.ru_utime.tv_sec * USEC_PER_SEC + thread.ru_utime.tv_usec);
+ rrddim_set_by_pointer(
+ stcpu_thread, rd_system, thread.ru_stime.tv_sec * USEC_PER_SEC + thread.ru_stime.tv_usec);
+ rrdset_done(stcpu_thread);
+
+ // ----------------------------------------------------------------
+
+ if (unlikely(!st_duration)) {
+ st_duration = rrdset_create_localhost(
+ "netdata",
+ "plugin_timex_dt",
+ NULL,
+ "timex",
+ NULL,
+ "Netdata Timex Plugin Duration",
+ "milliseconds/run",
+ PLUGIN_TIMEX_NAME,
+ NULL,
+ NETDATA_CHART_PRIO_NETDATA_TIMEX + 1,
+ update_every,
+ RRDSET_TYPE_AREA);
+
+ rd_duration = rrddim_add(st_duration, "duration", NULL, 1, USEC_PER_MS, RRD_ALGORITHM_ABSOLUTE);
+ } else {
+ rrdset_next(st_duration);
+ }
+
+ rrddim_set_by_pointer(st_duration, rd_duration, duration);
+ rrdset_done(st_duration);
+ }
+ }
+
+exit:
+ netdata_thread_cleanup_pop(1);
+ return NULL;
+}
diff --git a/collectors/timex.plugin/plugin_timex.h b/collectors/timex.plugin/plugin_timex.h
new file mode 100644
index 00000000..6025641a
--- /dev/null
+++ b/collectors/timex.plugin/plugin_timex.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_PLUGIN_TIMEX_H
+#define NETDATA_PLUGIN_TIMEX_H
+
+#include "../../daemon/common.h"
+
+#if (TARGET_OS == OS_LINUX)
+
+#define NETDATA_PLUGIN_HOOK_LINUX_TIMEX \
+ { \
+ .name = "PLUGIN[timex]", \
+ .config_section = CONFIG_SECTION_PLUGINS, \
+ .config_name = "timex", \
+ .enabled = 1, \
+ .thread = NULL, \
+ .init_routine = NULL, \
+ .start_routine = timex_main \
+ },
+
+extern void *timex_main(void *ptr);
+
+#else // (TARGET_OS == OS_LINUX)
+
+#define NETDATA_PLUGIN_HOOK_LINUX_TIMEX
+
+#endif // (TARGET_OS == OS_LINUX)
+
+#endif //NETDATA_PLUGIN_TIMEX_H