plugin_timex.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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. struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
  31. static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
  32. info("cleaning up...");
  33. static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
  34. }
  35. void *timex_main(void *ptr)
  36. {
  37. netdata_thread_cleanup_push(timex_main_cleanup, ptr);
  38. int vdo_cpu_netdata = config_get_boolean(CONFIG_SECTION_TIMEX, "timex plugin resource charts", CONFIG_BOOLEAN_YES);
  39. int update_every = (int)config_get_number(CONFIG_SECTION_TIMEX, "update every", 10);
  40. if (update_every < localhost->rrd_update_every)
  41. update_every = localhost->rrd_update_every;
  42. int do_sync = config_get_boolean(CONFIG_SECTION_TIMEX, "clock synchronization state", CONFIG_BOOLEAN_YES);
  43. int do_offset = config_get_boolean(CONFIG_SECTION_TIMEX, "time offset", CONFIG_BOOLEAN_YES);
  44. if (unlikely(do_sync == CONFIG_BOOLEAN_NO && do_offset == CONFIG_BOOLEAN_NO)) {
  45. info("No charts to show");
  46. goto exit;
  47. }
  48. usec_t step = update_every * USEC_PER_SEC;
  49. heartbeat_t hb;
  50. heartbeat_init(&hb);
  51. while (!netdata_exit) {
  52. usec_t duration = heartbeat_monotonic_dt_to_now_usec(&hb);
  53. heartbeat_next(&hb, step);
  54. struct timex timex_buf = {};
  55. int sync_state = 0;
  56. static int prev_sync_state = 0;
  57. sync_state = ADJUST_TIMEX(&timex_buf);
  58. int non_seq_failure = (sync_state == -1 && prev_sync_state != -1);
  59. prev_sync_state = sync_state;
  60. if (non_seq_failure) {
  61. error("Cannot get clock synchronization state");
  62. continue;
  63. }
  64. collected_number divisor = USEC_PER_MS;
  65. if (timex_buf.status & STA_NANO)
  66. divisor = NSEC_PER_MSEC;
  67. // ----------------------------------------------------------------
  68. if (do_sync) {
  69. static RRDSET *st_sync_state = NULL;
  70. static RRDDIM *rd_sync_state;
  71. if (unlikely(!st_sync_state)) {
  72. st_sync_state = rrdset_create_localhost(
  73. "system",
  74. "clock_sync_state",
  75. NULL,
  76. "clock synchronization",
  77. NULL,
  78. "System Clock Synchronization State",
  79. "state",
  80. PLUGIN_TIMEX_NAME,
  81. NULL,
  82. NETDATA_CHART_PRIO_CLOCK_SYNC_STATE,
  83. update_every,
  84. RRDSET_TYPE_LINE);
  85. rd_sync_state = rrddim_add(st_sync_state, "state", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  86. } else {
  87. rrdset_next(st_sync_state);
  88. }
  89. rrddim_set_by_pointer(st_sync_state, rd_sync_state, sync_state != TIME_ERROR ? 1 : 0);
  90. rrdset_done(st_sync_state);
  91. static RRDSET *st_clock_status = NULL;
  92. if (unlikely(!st_clock_status)) {
  93. st_clock_status = rrdset_create_localhost(
  94. "system",
  95. "clock_status",
  96. NULL,
  97. "clock synchronization",
  98. NULL,
  99. "System Clock Status",
  100. "status",
  101. PLUGIN_TIMEX_NAME,
  102. NULL,
  103. NETDATA_CHART_PRIO_CLOCK_STATUS,
  104. update_every,
  105. RRDSET_TYPE_LINE);
  106. for (int i = 0; sta_codes[i].name != NULL; i++) {
  107. sta_codes[i].rd =
  108. rrddim_add(st_clock_status, sta_codes[i].name, NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  109. }
  110. } else {
  111. rrdset_next(st_clock_status);
  112. }
  113. for (int i = 0; sta_codes[i].name != NULL; i++) {
  114. rrddim_set_by_pointer(st_clock_status, sta_codes[i].rd, timex_buf.status & sta_codes[i].code ? 1 : 0);
  115. }
  116. rrdset_done(st_clock_status);
  117. }
  118. if (do_offset) {
  119. static RRDSET *st_offset = NULL;
  120. static RRDDIM *rd_offset;
  121. if (unlikely(!st_offset)) {
  122. st_offset = rrdset_create_localhost(
  123. "system",
  124. "clock_sync_offset",
  125. NULL,
  126. "clock synchronization",
  127. NULL,
  128. "Computed Time Offset Between Local System and Reference Clock",
  129. "milliseconds",
  130. PLUGIN_TIMEX_NAME,
  131. NULL,
  132. NETDATA_CHART_PRIO_CLOCK_SYNC_OFFSET,
  133. update_every,
  134. RRDSET_TYPE_LINE);
  135. rd_offset = rrddim_add(st_offset, "offset", NULL, 1, divisor, RRD_ALGORITHM_ABSOLUTE);
  136. } else {
  137. rrdset_next(st_offset);
  138. }
  139. rrddim_set_by_pointer(st_offset, rd_offset, timex_buf.offset);
  140. rrdset_done(st_offset);
  141. }
  142. if (vdo_cpu_netdata) {
  143. static RRDSET *stcpu_thread = NULL, *st_duration = NULL;
  144. static RRDDIM *rd_user = NULL, *rd_system = NULL, *rd_duration = NULL;
  145. // ----------------------------------------------------------------
  146. struct rusage thread;
  147. getrusage(RUSAGE_THREAD, &thread);
  148. if (unlikely(!stcpu_thread)) {
  149. stcpu_thread = rrdset_create_localhost(
  150. "netdata",
  151. "plugin_timex",
  152. NULL,
  153. "timex",
  154. NULL,
  155. "Netdata Timex Plugin CPU usage",
  156. "milliseconds/s",
  157. PLUGIN_TIMEX_NAME,
  158. NULL,
  159. NETDATA_CHART_PRIO_NETDATA_TIMEX,
  160. update_every,
  161. RRDSET_TYPE_STACKED);
  162. rd_user = rrddim_add(stcpu_thread, "user", NULL, 1, USEC_PER_MS, RRD_ALGORITHM_INCREMENTAL);
  163. rd_system = rrddim_add(stcpu_thread, "system", NULL, 1, USEC_PER_MS, RRD_ALGORITHM_INCREMENTAL);
  164. } else {
  165. rrdset_next(stcpu_thread);
  166. }
  167. rrddim_set_by_pointer(
  168. stcpu_thread, rd_user, thread.ru_utime.tv_sec * USEC_PER_SEC + thread.ru_utime.tv_usec);
  169. rrddim_set_by_pointer(
  170. stcpu_thread, rd_system, thread.ru_stime.tv_sec * USEC_PER_SEC + thread.ru_stime.tv_usec);
  171. rrdset_done(stcpu_thread);
  172. // ----------------------------------------------------------------
  173. if (unlikely(!st_duration)) {
  174. st_duration = rrdset_create_localhost(
  175. "netdata",
  176. "plugin_timex_dt",
  177. NULL,
  178. "timex",
  179. NULL,
  180. "Netdata Timex Plugin Duration",
  181. "milliseconds/run",
  182. PLUGIN_TIMEX_NAME,
  183. NULL,
  184. NETDATA_CHART_PRIO_NETDATA_TIMEX + 1,
  185. update_every,
  186. RRDSET_TYPE_AREA);
  187. rd_duration = rrddim_add(st_duration, "duration", NULL, 1, USEC_PER_MS, RRD_ALGORITHM_ABSOLUTE);
  188. } else {
  189. rrdset_next(st_duration);
  190. }
  191. rrddim_set_by_pointer(st_duration, rd_duration, duration);
  192. rrdset_done(st_duration);
  193. }
  194. }
  195. exit:
  196. netdata_thread_cleanup_pop(1);
  197. return NULL;
  198. }