proc_softirqs.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "plugin_proc.h"
  3. #define PLUGIN_PROC_MODULE_SOFTIRQS_NAME "/proc/softirqs"
  4. #define MAX_INTERRUPT_NAME 50
  5. struct cpu_interrupt {
  6. unsigned long long value;
  7. RRDDIM *rd;
  8. };
  9. struct interrupt {
  10. int used;
  11. char *id;
  12. char name[MAX_INTERRUPT_NAME + 1];
  13. RRDDIM *rd;
  14. unsigned long long total;
  15. struct cpu_interrupt cpu[];
  16. };
  17. // since each interrupt is variable in size
  18. // we use this to calculate its record size
  19. #define recordsize(cpus) (sizeof(struct interrupt) + ((cpus) * sizeof(struct cpu_interrupt)))
  20. // given a base, get a pointer to each record
  21. #define irrindex(base, line, cpus) ((struct interrupt *)&((char *)(base))[(line) * recordsize(cpus)])
  22. static inline struct interrupt *get_interrupts_array(size_t lines, int cpus) {
  23. static struct interrupt *irrs = NULL;
  24. static size_t allocated = 0;
  25. if(unlikely(lines != allocated)) {
  26. uint32_t l;
  27. int c;
  28. irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus));
  29. // reset all interrupt RRDDIM pointers as any line could have shifted
  30. for(l = 0; l < lines ;l++) {
  31. struct interrupt *irr = irrindex(irrs, l, cpus);
  32. irr->rd = NULL;
  33. irr->name[0] = '\0';
  34. for(c = 0; c < cpus ;c++)
  35. irr->cpu[c].rd = NULL;
  36. }
  37. allocated = lines;
  38. }
  39. return irrs;
  40. }
  41. int do_proc_softirqs(int update_every, usec_t dt) {
  42. (void)dt;
  43. static procfile *ff = NULL;
  44. static int cpus = -1, do_per_core = CONFIG_BOOLEAN_INVALID;
  45. struct interrupt *irrs = NULL;
  46. if(unlikely(do_per_core == CONFIG_BOOLEAN_INVALID))
  47. do_per_core = config_get_boolean_ondemand("plugin:proc:/proc/softirqs", "interrupts per core", CONFIG_BOOLEAN_AUTO);
  48. if(unlikely(!ff)) {
  49. char filename[FILENAME_MAX + 1];
  50. snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/softirqs");
  51. ff = procfile_open(config_get("plugin:proc:/proc/softirqs", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
  52. if(unlikely(!ff)) return 1;
  53. }
  54. ff = procfile_readall(ff);
  55. if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time
  56. size_t lines = procfile_lines(ff), l;
  57. size_t words = procfile_linewords(ff, 0);
  58. if(unlikely(!lines)) {
  59. collector_error("Cannot read /proc/softirqs, zero lines reported.");
  60. return 1;
  61. }
  62. // find how many CPUs are there
  63. if(unlikely(cpus == -1)) {
  64. uint32_t w;
  65. cpus = 0;
  66. for(w = 0; w < words ; w++) {
  67. if(likely(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0))
  68. cpus++;
  69. }
  70. }
  71. if(unlikely(!cpus)) {
  72. collector_error("PLUGIN: PROC_SOFTIRQS: Cannot find the number of CPUs in /proc/softirqs");
  73. return 1;
  74. }
  75. // allocate the size we need;
  76. irrs = get_interrupts_array(lines, cpus);
  77. irrs[0].used = 0;
  78. // loop through all lines
  79. for(l = 1; l < lines ;l++) {
  80. struct interrupt *irr = irrindex(irrs, l, cpus);
  81. irr->used = 0;
  82. irr->total = 0;
  83. words = procfile_linewords(ff, l);
  84. if(unlikely(!words)) continue;
  85. irr->id = procfile_lineword(ff, l, 0);
  86. if(unlikely(!irr->id || !irr->id[0])) continue;
  87. int c;
  88. for(c = 0; c < cpus ;c++) {
  89. if(likely((c + 1) < (int)words))
  90. irr->cpu[c].value = str2ull(procfile_lineword(ff, l, (uint32_t) (c + 1)), NULL);
  91. else
  92. irr->cpu[c].value = 0;
  93. irr->total += irr->cpu[c].value;
  94. }
  95. strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME);
  96. irr->used = 1;
  97. }
  98. // --------------------------------------------------------------------
  99. static RRDSET *st_system_softirqs = NULL;
  100. if(unlikely(!st_system_softirqs)) {
  101. st_system_softirqs = rrdset_create_localhost(
  102. "system"
  103. , "softirqs"
  104. , NULL
  105. , "softirqs"
  106. , NULL
  107. , "System softirqs"
  108. , "softirqs/s"
  109. , PLUGIN_PROC_NAME
  110. , PLUGIN_PROC_MODULE_SOFTIRQS_NAME
  111. , NETDATA_CHART_PRIO_SYSTEM_SOFTIRQS
  112. , update_every
  113. , RRDSET_TYPE_STACKED
  114. );
  115. }
  116. for(l = 0; l < lines ;l++) {
  117. struct interrupt *irr = irrindex(irrs, l, cpus);
  118. if(irr->used && irr->total) {
  119. // some interrupt may have changed without changing the total number of lines
  120. // if the same number of interrupts have been added and removed between two
  121. // calls of this function.
  122. if(unlikely(!irr->rd || strncmp(irr->name, rrddim_name(irr->rd), MAX_INTERRUPT_NAME) != 0)) {
  123. irr->rd = rrddim_add(st_system_softirqs, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL);
  124. rrddim_reset_name(st_system_softirqs, irr->rd, irr->name);
  125. // also reset per cpu RRDDIMs to avoid repeating strncmp() in the per core loop
  126. if(likely(do_per_core != CONFIG_BOOLEAN_NO)) {
  127. int c;
  128. for(c = 0; c < cpus; c++) irr->cpu[c].rd = NULL;
  129. }
  130. }
  131. rrddim_set_by_pointer(st_system_softirqs, irr->rd, irr->total);
  132. }
  133. }
  134. rrdset_done(st_system_softirqs);
  135. // --------------------------------------------------------------------
  136. if(do_per_core != CONFIG_BOOLEAN_NO) {
  137. static RRDSET **core_st = NULL;
  138. static int old_cpus = 0;
  139. if(old_cpus < cpus) {
  140. core_st = reallocz(core_st, sizeof(RRDSET *) * cpus);
  141. memset(&core_st[old_cpus], 0, sizeof(RRDSET *) * (cpus - old_cpus));
  142. old_cpus = cpus;
  143. }
  144. int c;
  145. for(c = 0; c < cpus ; c++) {
  146. if(unlikely(!core_st[c])) {
  147. // find if everything is just zero
  148. unsigned long long core_sum = 0;
  149. for (l = 0; l < lines; l++) {
  150. struct interrupt *irr = irrindex(irrs, l, cpus);
  151. if (unlikely(!irr->used)) continue;
  152. core_sum += irr->cpu[c].value;
  153. }
  154. if (unlikely(core_sum == 0)) continue; // try next core
  155. char id[50 + 1];
  156. snprintfz(id, 50, "cpu%d_softirqs", c);
  157. char title[100 + 1];
  158. snprintfz(title, 100, "CPU softirqs");
  159. core_st[c] = rrdset_create_localhost(
  160. "cpu"
  161. , id
  162. , NULL
  163. , "softirqs"
  164. , "cpu.softirqs"
  165. , title
  166. , "softirqs/s"
  167. , PLUGIN_PROC_NAME
  168. , PLUGIN_PROC_MODULE_SOFTIRQS_NAME
  169. , NETDATA_CHART_PRIO_SOFTIRQS_PER_CORE + c
  170. , update_every
  171. , RRDSET_TYPE_STACKED
  172. );
  173. char core[50+1];
  174. snprintfz(core, 50, "cpu%d", c);
  175. rrdlabels_add(core_st[c]->rrdlabels, "cpu", core, RRDLABEL_SRC_AUTO);
  176. }
  177. for(l = 0; l < lines ;l++) {
  178. struct interrupt *irr = irrindex(irrs, l, cpus);
  179. if(irr->used && (do_per_core == CONFIG_BOOLEAN_YES || irr->cpu[c].value)) {
  180. if(unlikely(!irr->cpu[c].rd)) {
  181. irr->cpu[c].rd = rrddim_add(core_st[c], irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL);
  182. rrddim_reset_name(core_st[c], irr->cpu[c].rd, irr->name);
  183. }
  184. rrddim_set_by_pointer(core_st[c], irr->cpu[c].rd, irr->cpu[c].value);
  185. }
  186. }
  187. rrdset_done(core_st[c]);
  188. }
  189. }
  190. return 0;
  191. }