plugin_timex.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "daemon/common.h"
  3. #include "libnetdata/os.h"
  4. #define PLUGIN_TIMEX_NAME "timex.plugin"
  5. #define CONFIG_SECTION_TIMEX "plugin:timex"
  6. struct status_codes {
  7. char *name;
  8. int code;
  9. RRDDIM *rd;
  10. } sta_codes[] = {
  11. // {"pll", STA_PLL, NULL},
  12. // {"ppsfreq", STA_PPSFREQ, NULL},
  13. // {"ppstime", STA_PPSTIME, NULL},
  14. // {"fll", STA_FLL, NULL},
  15. // {"ins", STA_INS, NULL},
  16. // {"del", STA_DEL, NULL},
  17. {"unsync", STA_UNSYNC, NULL},
  18. // {"freqhold", STA_FREQHOLD, NULL},
  19. // {"ppssignal", STA_PPSSIGNAL, NULL},
  20. // {"ppsjitter", STA_PPSJITTER, NULL},
  21. // {"ppswander", STA_PPSWANDER, NULL},
  22. // {"ppserror", STA_PPSERROR, NULL},
  23. {"clockerr", STA_CLOCKERR, NULL},
  24. // {"nano", STA_NANO, NULL},
  25. // {"clk", STA_CLK, NULL},
  26. {NULL, 0, NULL},
  27. };
  28. static void timex_main_cleanup(void *ptr)
  29. {
  30. worker_unregister();
  31. struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
  32. static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
  33. info("cleaning up...");
  34. static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
  35. }
  36. void *timex_main(void *ptr)
  37. {
  38. worker_register("TIMEX");
  39. worker_register_job_name(0, "clock check");
  40. netdata_thread_cleanup_push(timex_main_cleanup, ptr);
  41. int update_every = (int)config_get_number(CONFIG_SECTION_TIMEX, "update every", 10);
  42. if (update_every < localhost->rrd_update_every)
  43. update_every = localhost->rrd_update_every;
  44. int do_sync = config_get_boolean(CONFIG_SECTION_TIMEX, "clock synchronization state", CONFIG_BOOLEAN_YES);
  45. int do_offset = config_get_boolean(CONFIG_SECTION_TIMEX, "time offset", CONFIG_BOOLEAN_YES);
  46. if (unlikely(do_sync == CONFIG_BOOLEAN_NO && do_offset == CONFIG_BOOLEAN_NO)) {
  47. info("No charts to show");
  48. goto exit;
  49. }
  50. usec_t step = update_every * USEC_PER_SEC;
  51. heartbeat_t hb;
  52. heartbeat_init(&hb);
  53. while (!netdata_exit) {
  54. worker_is_idle();
  55. heartbeat_next(&hb, step);
  56. worker_is_busy(0);
  57. struct timex timex_buf = {};
  58. int sync_state = 0;
  59. static int prev_sync_state = 0;
  60. sync_state = ADJUST_TIMEX(&timex_buf);
  61. int non_seq_failure = (sync_state == -1 && prev_sync_state != -1);
  62. prev_sync_state = sync_state;
  63. if (non_seq_failure) {
  64. error("Cannot get clock synchronization state");
  65. continue;
  66. }
  67. collected_number divisor = USEC_PER_MS;
  68. if (timex_buf.status & STA_NANO)
  69. divisor = NSEC_PER_MSEC;
  70. // ----------------------------------------------------------------
  71. if (do_sync) {
  72. static RRDSET *st_sync_state = NULL;
  73. static RRDDIM *rd_sync_state;
  74. if (unlikely(!st_sync_state)) {
  75. st_sync_state = rrdset_create_localhost(
  76. "system",
  77. "clock_sync_state",
  78. NULL,
  79. "clock synchronization",
  80. NULL,
  81. "System Clock Synchronization State",
  82. "state",
  83. PLUGIN_TIMEX_NAME,
  84. NULL,
  85. NETDATA_CHART_PRIO_CLOCK_SYNC_STATE,
  86. update_every,
  87. RRDSET_TYPE_LINE);
  88. rd_sync_state = rrddim_add(st_sync_state, "state", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  89. } else {
  90. rrdset_next(st_sync_state);
  91. }
  92. rrddim_set_by_pointer(st_sync_state, rd_sync_state, sync_state != TIME_ERROR ? 1 : 0);
  93. rrdset_done(st_sync_state);
  94. static RRDSET *st_clock_status = NULL;
  95. if (unlikely(!st_clock_status)) {
  96. st_clock_status = rrdset_create_localhost(
  97. "system",
  98. "clock_status",
  99. NULL,
  100. "clock synchronization",
  101. NULL,
  102. "System Clock Status",
  103. "status",
  104. PLUGIN_TIMEX_NAME,
  105. NULL,
  106. NETDATA_CHART_PRIO_CLOCK_STATUS,
  107. update_every,
  108. RRDSET_TYPE_LINE);
  109. for (int i = 0; sta_codes[i].name != NULL; i++) {
  110. sta_codes[i].rd =
  111. rrddim_add(st_clock_status, sta_codes[i].name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  112. }
  113. } else {
  114. rrdset_next(st_clock_status);
  115. }
  116. for (int i = 0; sta_codes[i].name != NULL; i++) {
  117. rrddim_set_by_pointer(st_clock_status, sta_codes[i].rd, timex_buf.status & sta_codes[i].code ? 1 : 0);
  118. }
  119. rrdset_done(st_clock_status);
  120. }
  121. if (do_offset) {
  122. static RRDSET *st_offset = NULL;
  123. static RRDDIM *rd_offset;
  124. if (unlikely(!st_offset)) {
  125. st_offset = rrdset_create_localhost(
  126. "system",
  127. "clock_sync_offset",
  128. NULL,
  129. "clock synchronization",
  130. NULL,
  131. "Computed Time Offset Between Local System and Reference Clock",
  132. "milliseconds",
  133. PLUGIN_TIMEX_NAME,
  134. NULL,
  135. NETDATA_CHART_PRIO_CLOCK_SYNC_OFFSET,
  136. update_every,
  137. RRDSET_TYPE_LINE);
  138. rd_offset = rrddim_add(st_offset, "offset", NULL, 1, divisor, RRD_ALGORITHM_ABSOLUTE);
  139. } else {
  140. rrdset_next(st_offset);
  141. }
  142. rrddim_set_by_pointer(st_offset, rd_offset, timex_buf.offset);
  143. rrdset_done(st_offset);
  144. }
  145. }
  146. exit:
  147. netdata_thread_cleanup_pop(1);
  148. return NULL;
  149. }