master
c 256 lines 10.2 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "plugin_proc.h"
4
5 #define PLUGIN_PROC_MODULE_PRESSURE_NAME "/proc/pressure"
6 #define CONFIG_SECTION_PLUGIN_PROC_PRESSURE "plugin:" PLUGIN_PROC_CONFIG_NAME ":" PLUGIN_PROC_MODULE_PRESSURE_NAME
7
8 // linux calculates this every 2 seconds, see kernel/sched/psi.c PSI_FREQ
9 #define MIN_PRESSURE_UPDATE_EVERY 2
10
11 static int pressure_update_every = 0;
12
13 static struct pressure resources[PRESSURE_NUM_RESOURCES] = {
14 {
15 .some = {
16 .available = true,
17 .share_time = {.id = "cpu_some_pressure", .title = "CPU some pressure"},
18 .total_time = {.id = "cpu_some_pressure_stall_time", .title = "CPU some pressure stall time"}
19 },
20 .full = {
21 // Disable CPU full pressure.
22 // See https://github.com/torvalds/linux/commit/890d550d7dbac7a31ecaa78732aa22be282bb6b8
23 .available = false,
24 .share_time = {.id = "cpu_full_pressure", .title = "CPU full pressure"},
25 .total_time = {.id = "cpu_full_pressure_stall_time", .title = "CPU full pressure stall time"}
26 },
27 },
28 {
29 .some = {
30 .available = true,
31 .share_time = {.id = "memory_some_pressure", .title = "Memory some pressure"},
32 .total_time = {.id = "memory_some_pressure_stall_time", .title = "Memory some pressure stall time"}
33 },
34 .full = {
35 .available = true,
36 .share_time = {.id = "memory_full_pressure", .title = "Memory full pressure"},
37 .total_time = {.id = "memory_full_pressure_stall_time", .title = "Memory full pressure stall time"}
38 },
39 },
40 {
41 .some = {
42 .available = true,
43 .share_time = {.id = "io_some_pressure", .title = "I/O some pressure"},
44 .total_time = {.id = "io_some_pressure_stall_time", .title = "I/O some pressure stall time"}
45 },
46 .full = {
47 .available = true,
48 .share_time = {.id = "io_full_pressure", .title = "I/O full pressure"},
49 .total_time = {.id = "io_full_pressure_stall_time", .title = "I/O full pressure stall time"}
50 },
51 },
52 {
53 .some = {
54 // this is not available
55 .available = false,
56 .share_time = {.id = "irq_some_pressure", .title = "IRQ some pressure"},
57 .total_time = {.id = "irq_some_pressure_stall_time", .title = "IRQ some pressure stall time"}
58 },
59 .full = {
60 .available = true,
61 .share_time = {.id = "irq_full_pressure", .title = "IRQ full pressure"},
62 .total_time = {.id = "irq_full_pressure_stall_time", .title = "IRQ full pressure stall time"}
63 },
64 },
65 };
66
67 static struct resource_info {
68 procfile *pf;
69 const char *name; // metric file name
70 const char *family; // webui section name
71 int section_priority;
72 } resource_info[PRESSURE_NUM_RESOURCES] = {
73 { .name = "cpu", .family = "cpu", .section_priority = NETDATA_CHART_PRIO_SYSTEM_CPU },
74 { .name = "memory", .family = "ram", .section_priority = NETDATA_CHART_PRIO_SYSTEM_RAM },
75 { .name = "io", .family = "disk", .section_priority = NETDATA_CHART_PRIO_SYSTEM_IO },
76 { .name = "irq", .family = "interrupts", .section_priority = NETDATA_CHART_PRIO_SYSTEM_INTERRUPTS },
77 };
78
79 void update_pressure_charts(struct pressure_charts *pcs) {
80 if (pcs->share_time.st) {
81 rrddim_set_by_pointer(
82 pcs->share_time.st, pcs->share_time.rd10, (collected_number)(pcs->share_time.value10 * 100));
83 rrddim_set_by_pointer(
84 pcs->share_time.st, pcs->share_time.rd60, (collected_number)(pcs->share_time.value60 * 100));
85 rrddim_set_by_pointer(
86 pcs->share_time.st, pcs->share_time.rd300, (collected_number)(pcs->share_time.value300 * 100));
87 rrdset_done(pcs->share_time.st);
88 }
89 if (pcs->total_time.st) {
90 rrddim_set_by_pointer(
91 pcs->total_time.st, pcs->total_time.rdtotal, (collected_number)(pcs->total_time.value_total));
92 rrdset_done(pcs->total_time.st);
93 }
94 }
95
96 static void proc_pressure_do_resource(procfile *ff, int res_idx, size_t line, bool some) {
97 struct pressure_charts *pcs;
98 struct resource_info ri;
99 pcs = some ? &resources[res_idx].some : &resources[res_idx].full;
100 ri = resource_info[res_idx];
101
102 if (unlikely(!pcs->share_time.st)) {
103 pcs->share_time.st = rrdset_create_localhost(
104 "system",
105 pcs->share_time.id,
106 NULL,
107 ri.family,
108 NULL,
109 pcs->share_time.title,
110 "percentage",
111 PLUGIN_PROC_NAME,
112 PLUGIN_PROC_MODULE_PRESSURE_NAME,
113 ri.section_priority + (some ? 40 : 50),
114 pressure_update_every,
115 RRDSET_TYPE_LINE);
116 pcs->share_time.rd10 =
117 rrddim_add(pcs->share_time.st, some ? "some 10" : "full 10", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
118 pcs->share_time.rd60 =
119 rrddim_add(pcs->share_time.st, some ? "some 60" : "full 60", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
120 pcs->share_time.rd300 =
121 rrddim_add(pcs->share_time.st, some ? "some 300" : "full 300", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
122 }
123
124 pcs->share_time.value10 = strtod(procfile_lineword(ff, line, 2), NULL);
125 pcs->share_time.value60 = strtod(procfile_lineword(ff, line, 4), NULL);
126 pcs->share_time.value300 = strtod(procfile_lineword(ff, line, 6), NULL);
127
128 if (unlikely(!pcs->total_time.st)) {
129 pcs->total_time.st = rrdset_create_localhost(
130 "system",
131 pcs->total_time.id,
132 NULL,
133 ri.family,
134 NULL,
135 pcs->total_time.title,
136 "ms",
137 PLUGIN_PROC_NAME,
138 PLUGIN_PROC_MODULE_PRESSURE_NAME,
139 ri.section_priority + (some ? 45 : 55),
140 pressure_update_every,
141 RRDSET_TYPE_LINE);
142 pcs->total_time.rdtotal = rrddim_add(pcs->total_time.st, "time", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
143 }
144
145 pcs->total_time.value_total = str2ull(procfile_lineword(ff, line, 8), NULL) / 1000;
146 }
147
148 static void proc_pressure_do_resource_some(procfile *ff, int res_idx, size_t line) {
149 proc_pressure_do_resource(ff, res_idx, line, true);
150 }
151
152 static void proc_pressure_do_resource_full(procfile *ff, int res_idx, size_t line) {
153 proc_pressure_do_resource(ff, res_idx, line, false);
154 }
155
156 int do_proc_pressure(int update_every, usec_t dt) {
157 int ok_count = 0;
158 int i;
159
160 static usec_t next_pressure_dt = 0;
161 static const char *base_path = NULL;
162
163 update_every = (update_every < MIN_PRESSURE_UPDATE_EVERY) ? MIN_PRESSURE_UPDATE_EVERY : update_every;
164 pressure_update_every = update_every;
165
166 if (next_pressure_dt <= dt) {
167 next_pressure_dt = update_every * USEC_PER_SEC;
168 } else {
169 next_pressure_dt -= dt;
170 return 0;
171 }
172
173 if (unlikely(!base_path))
174 base_path = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_PRESSURE, "base path of pressure metrics", "/proc/pressure");
175
176 for (i = 0; i < PRESSURE_NUM_RESOURCES; i++) {
177 procfile *ff = resource_info[i].pf;
178 int do_some = resources[i].some.enabled, do_full = resources[i].full.enabled;
179
180 if (!resources[i].some.available && !resources[i].full.available)
181 continue;
182
183 if (unlikely(!ff)) {
184 char filename[FILENAME_MAX + 1];
185 char config_key[CONFIG_MAX_NAME + 1];
186
187 snprintfz(filename
188 , FILENAME_MAX
189 , "%s%s/%s"
190 , netdata_configured_host_prefix
191 , base_path
192 , resource_info[i].name);
193
194 do_some = resources[i].some.available ? CONFIG_BOOLEAN_YES : CONFIG_BOOLEAN_NO;
195 do_full = resources[i].full.available ? CONFIG_BOOLEAN_YES : CONFIG_BOOLEAN_NO;
196
197 snprintfz(config_key, CONFIG_MAX_NAME, "enable %s some pressure", resource_info[i].name);
198 do_some = inicfg_get_boolean(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_PRESSURE, config_key, do_some);
199 resources[i].some.enabled = do_some;
200
201 snprintfz(config_key, CONFIG_MAX_NAME, "enable %s full pressure", resource_info[i].name);
202 do_full = inicfg_get_boolean(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_PRESSURE, config_key, do_full);
203 resources[i].full.enabled = do_full;
204
205 if (!do_full && !do_some) {
206 resources[i].some.available = false;
207 resources[i].full.available = false;
208 continue;
209 }
210
211 ff = procfile_open(filename, " =", PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
212 if (unlikely(!ff)) {
213 // PSI IRQ was added recently (https://github.com/torvalds/linux/commit/52b1364ba0b105122d6de0e719b36db705011ac1)
214 if (strcmp(resource_info[i].name, "irq") != 0)
215 collector_error("Cannot read pressure information from %s.", filename);
216 resources[i].some.available = false;
217 resources[i].full.available = false;
218 continue;
219 }
220 }
221
222 ff = procfile_readall(ff);
223 resource_info[i].pf = ff;
224 if (unlikely(!ff))
225 continue;
226
227 size_t lines = procfile_lines(ff);
228 if (unlikely(lines < 1)) {
229 collector_error("%s has no lines.", procfile_filename(ff));
230 continue;
231 }
232
233 for(size_t l = 0; l < lines ;l++) {
234 const char *key = procfile_lineword(ff, l, 0);
235 if(strcmp(key, "some") == 0) {
236 if(do_some) {
237 proc_pressure_do_resource_some(ff, i, l);
238 update_pressure_charts(&resources[i].some);
239 ok_count++;
240 }
241 }
242 else if(strcmp(key, "full") == 0) {
243 if(do_full) {
244 proc_pressure_do_resource_full(ff, i, l);
245 update_pressure_charts(&resources[i].full);
246 ok_count++;
247 }
248 }
249 }
250 }
251
252 if(!ok_count)
253 return 1;
254
255 return 0;
256 }