@cryptotaxi247 / netdata-1 / commits / d7a2499a4

split apps.plugin into multiple files and support MacOS (#17180)

* split apps.plugin into multiple files * move everything about pids to apps_proc_pids.c * code cleanup and reorg for supporting MacOS * fixed SIGFPE * more cleanup * completed split; added untested macos support * enable apps everywhere * macos fixes * disable macos for the moment * extern pagesize * fix missing function * added comments * restored function * working macos apps - not verified yet * enable apps.plugin in MacOS * added ppid * parse cmdline * fix scaling of cpu usage * fixed cmdline parsing * codacy fixes * support uptime for FreeBSD and MacOS * uptime in sec * enable uptime charts on macos and freebsd * disable vmsize on macos

Costa Tsaousis committed Mar 18, 2024 at 13:34 UTC d7a2499a4081cede800a3d66e9bc51842a989e2c
17 files changed +5147 -4505
CMakeLists.txt
+17 -1
@@ -1717,7 +1717,23 @@ endif()
1717 if(ENABLE_PLUGIN_APPS)
1718 pkg_check_modules(CAP QUIET libcap)
1719
1720 - set(APPS_PLUGIN_FILES src/collectors/apps.plugin/apps_plugin.c)
1720 + set(APPS_PLUGIN_FILES
1721 + src/collectors/apps.plugin/apps_plugin.c
1722 + src/collectors/apps.plugin/apps_plugin.h
1723 + src/collectors/apps.plugin/apps_functions.c
1724 + src/collectors/apps.plugin/apps_targets.c
1725 + src/collectors/apps.plugin/apps_users_and_groups.c
1726 + src/collectors/apps.plugin/apps_output.c
1727 + src/collectors/apps.plugin/apps_proc_pid_status.c
1728 + src/collectors/apps.plugin/apps_proc_pid_limits.c
1729 + src/collectors/apps.plugin/apps_proc_pid_stat.c
1730 + src/collectors/apps.plugin/apps_proc_pid_cmdline.c
1731 + src/collectors/apps.plugin/apps_proc_pid_io.c
1732 + src/collectors/apps.plugin/apps_proc_stat.c
1733 + src/collectors/apps.plugin/apps_proc_pid_fd.c
1734 + src/collectors/apps.plugin/apps_proc_pids.c
1735 + src/collectors/apps.plugin/apps_proc_meminfo.c
1736 + )
1737
1738 add_executable(apps.plugin ${APPS_PLUGIN_FILES})
1739 target_link_libraries(apps.plugin libnetdata ${CAP_LIBRARIES})
packaging/installer/functions.sh
+1 -8
@@ -310,14 +310,7 @@ prepare_cmake_options() {
310 enable_feature DBENGINE "${ENABLE_DBENGINE:-1}"
311 enable_feature H2O "${ENABLE_H2O:-1}"
312 enable_feature ML "${NETDATA_ENABLE_ML:-1}"
313 -
314 - ENABLE_APPS=0
315 -
316 - if [ "${IS_LINUX}" = 1 ] || [ "$(uname -s)" = "FreeBSD" ]; then
317 - ENABLE_APPS=1
318 - fi
319 -
320 - enable_feature PLUGIN_APPS "${ENABLE_APPS}"
313 + enable_feature PLUGIN_APPS "${ENABLE_APPS:-1}"
314
315 check_for_feature EXPORTER_PROMETHEUS_REMOTE_WRITE "${EXPORTER_PROMETHEUS}" snappy
316 check_for_feature EXPORTER_MONGODB "${EXPORTER_MONGODB}" libmongoc-1.0
src/collectors/apps.plugin/apps_functions.c new
+928
@@ -0,0 +1,928 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +bool enable_function_cmdline = false;
6 +
7 +#define PROCESS_FILTER_CATEGORY "category:"
8 +#define PROCESS_FILTER_USER "user:"
9 +#define PROCESS_FILTER_GROUP "group:"
10 +#define PROCESS_FILTER_PROCESS "process:"
11 +#define PROCESS_FILTER_PID "pid:"
12 +#define PROCESS_FILTER_UID "uid:"
13 +#define PROCESS_FILTER_GID "gid:"
14 +
15 +static void apps_plugin_function_processes_help(const char *transaction) {
16 + BUFFER *wb = buffer_create(0, NULL);
17 + buffer_sprintf(wb, "%s",
18 + "apps.plugin / processes\n"
19 + "\n"
20 + "Function `processes` presents all the currently running processes of the system.\n"
21 + "\n"
22 + "The following filters are supported:\n"
23 + "\n"
24 + " category:NAME\n"
25 + " Shows only processes that are assigned the category `NAME` in apps_groups.conf\n"
26 + "\n"
27 + " user:NAME\n"
28 + " Shows only processes that are running as user name `NAME`.\n"
29 + "\n"
30 + " group:NAME\n"
31 + " Shows only processes that are running as group name `NAME`.\n"
32 + "\n"
33 + " process:NAME\n"
34 + " Shows only processes that their Command is `NAME` or their parent's Command is `NAME`.\n"
35 + "\n"
36 + " pid:NUMBER\n"
37 + " Shows only processes that their PID is `NUMBER` or their parent's PID is `NUMBER`\n"
38 + "\n"
39 + " uid:NUMBER\n"
40 + " Shows only processes that their UID is `NUMBER`\n"
41 + "\n"
42 + " gid:NUMBER\n"
43 + " Shows only processes that their GID is `NUMBER`\n"
44 + "\n"
45 + "Filters can be combined. Each filter can be given only one time.\n"
46 + );
47 +
48 + pluginsd_function_result_to_stdout(transaction, HTTP_RESP_OK, "text/plain", now_realtime_sec() + 3600, wb);
49 + buffer_free(wb);
50 +}
51 +
52 +#define add_value_field_llu_with_max(wb, key, value) do { \
53 + unsigned long long _tmp = (value); \
54 + key ## _max = (rows == 0) ? (_tmp) : MAX(key ## _max, _tmp); \
55 + buffer_json_add_array_item_uint64(wb, _tmp); \
56 +} while(0)
57 +
58 +#define add_value_field_ndd_with_max(wb, key, value) do { \
59 + NETDATA_DOUBLE _tmp = (value); \
60 + key ## _max = (rows == 0) ? (_tmp) : MAX(key ## _max, _tmp); \
61 + buffer_json_add_array_item_double(wb, _tmp); \
62 +} while(0)
63 +
64 +void function_processes(const char *transaction, char *function,
65 + usec_t *stop_monotonic_ut __maybe_unused, bool *cancelled __maybe_unused,
66 + BUFFER *payload __maybe_unused, HTTP_ACCESS access,
67 + const char *source __maybe_unused, void *data __maybe_unused) {
68 + time_t now_s = now_realtime_sec();
69 + struct pid_stat *p;
70 +
71 + bool show_cmdline = http_access_user_has_enough_access_level_for_endpoint(
72 + access,
73 + HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA |
74 + HTTP_ACCESS_VIEW_AGENT_CONFIG) || enable_function_cmdline;
75 +
76 + char *words[PLUGINSD_MAX_WORDS] = { NULL };
77 + size_t num_words = quoted_strings_splitter_pluginsd(function, words, PLUGINSD_MAX_WORDS);
78 +
79 + struct target *category = NULL, *user = NULL, *group = NULL;
80 + const char *process_name = NULL;
81 + pid_t pid = 0;
82 + uid_t uid = 0;
83 + gid_t gid = 0;
84 + bool info = false;
85 +
86 + bool filter_pid = false, filter_uid = false, filter_gid = false;
87 +
88 + for(int i = 1; i < PLUGINSD_MAX_WORDS ;i++) {
89 + const char *keyword = get_word(words, num_words, i);
90 + if(!keyword) break;
91 +
92 + if(!category && strncmp(keyword, PROCESS_FILTER_CATEGORY, strlen(PROCESS_FILTER_CATEGORY)) == 0) {
93 + category = find_target_by_name(apps_groups_root_target, &keyword[strlen(PROCESS_FILTER_CATEGORY)]);
94 + if(!category) {
95 + pluginsd_function_json_error_to_stdout(transaction, HTTP_RESP_BAD_REQUEST,
96 + "No category with that name found.");
97 + return;
98 + }
99 + }
100 + else if(!user && strncmp(keyword, PROCESS_FILTER_USER, strlen(PROCESS_FILTER_USER)) == 0) {
101 + user = find_target_by_name(users_root_target, &keyword[strlen(PROCESS_FILTER_USER)]);
102 + if(!user) {
103 + pluginsd_function_json_error_to_stdout(transaction, HTTP_RESP_BAD_REQUEST,
104 + "No user with that name found.");
105 + return;
106 + }
107 + }
108 + else if(strncmp(keyword, PROCESS_FILTER_GROUP, strlen(PROCESS_FILTER_GROUP)) == 0) {
109 + group = find_target_by_name(groups_root_target, &keyword[strlen(PROCESS_FILTER_GROUP)]);
110 + if(!group) {
111 + pluginsd_function_json_error_to_stdout(transaction, HTTP_RESP_BAD_REQUEST,
112 + "No group with that name found.");
113 + return;
114 + }
115 + }
116 + else if(!process_name && strncmp(keyword, PROCESS_FILTER_PROCESS, strlen(PROCESS_FILTER_PROCESS)) == 0) {
117 + process_name = &keyword[strlen(PROCESS_FILTER_PROCESS)];
118 + }
119 + else if(!pid && strncmp(keyword, PROCESS_FILTER_PID, strlen(PROCESS_FILTER_PID)) == 0) {
120 + pid = str2i(&keyword[strlen(PROCESS_FILTER_PID)]);
121 + filter_pid = true;
122 + }
123 + else if(!uid && strncmp(keyword, PROCESS_FILTER_UID, strlen(PROCESS_FILTER_UID)) == 0) {
124 + uid = str2i(&keyword[strlen(PROCESS_FILTER_UID)]);
125 + filter_uid = true;
126 + }
127 + else if(!gid && strncmp(keyword, PROCESS_FILTER_GID, strlen(PROCESS_FILTER_GID)) == 0) {
128 + gid = str2i(&keyword[strlen(PROCESS_FILTER_GID)]);
129 + filter_gid = true;
130 + }
131 + else if(strcmp(keyword, "help") == 0) {
132 + apps_plugin_function_processes_help(transaction);
133 + return;
134 + }
135 + else if(strcmp(keyword, "info") == 0) {
136 + info = true;
137 + }
138 + }
139 +
140 + unsigned int cpu_divisor = time_factor * RATES_DETAIL / 100;
141 + unsigned int memory_divisor = 1024;
142 + unsigned int io_divisor = 1024 * RATES_DETAIL;
143 +
144 + BUFFER *wb = buffer_create(4096, NULL);
145 + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
146 + buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
147 + buffer_json_member_add_string(wb, "type", "table");
148 + buffer_json_member_add_time_t(wb, "update_every", update_every);
149 + buffer_json_member_add_boolean(wb, "has_history", false);
150 + buffer_json_member_add_string(wb, "help", APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION);
151 + buffer_json_member_add_array(wb, "data");
152 +
153 + if(info)
154 + goto close_and_send;
155 +
156 + NETDATA_DOUBLE
157 + UserCPU_max = 0.0
158 + , SysCPU_max = 0.0
159 + , GuestCPU_max = 0.0
160 + , CUserCPU_max = 0.0
161 + , CSysCPU_max = 0.0
162 + , CGuestCPU_max = 0.0
163 + , CPU_max = 0.0
164 + , VMSize_max = 0.0
165 + , RSS_max = 0.0
166 + , Shared_max = 0.0
167 + , Swap_max = 0.0
168 + , Memory_max = 0.0
169 + , FDsLimitPercent_max = 0.0
170 + ;
171 +
172 + unsigned long long
173 + Processes_max = 0
174 + , Threads_max = 0
175 + , VoluntaryCtxtSwitches_max = 0
176 + , NonVoluntaryCtxtSwitches_max = 0
177 + , Uptime_max = 0
178 + , MinFlt_max = 0
179 + , CMinFlt_max = 0
180 + , TMinFlt_max = 0
181 + , MajFlt_max = 0
182 + , CMajFlt_max = 0
183 + , TMajFlt_max = 0
184 + , PReads_max = 0
185 + , PWrites_max = 0
186 + , RCalls_max = 0
187 + , WCalls_max = 0
188 + , Files_max = 0
189 + , Pipes_max = 0
190 + , Sockets_max = 0
191 + , iNotiFDs_max = 0
192 + , EventFDs_max = 0
193 + , TimerFDs_max = 0
194 + , SigFDs_max = 0
195 + , EvPollFDs_max = 0
196 + , OtherFDs_max = 0
197 + , FDs_max = 0
198 + ;
199 +
200 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
201 + unsigned long long
202 + LReads_max = 0
203 + , LWrites_max = 0
204 + ;
205 +#endif // !__FreeBSD__ !__APPLE_
206 +
207 + int rows= 0;
208 + for(p = root_of_pids; p ; p = p->next) {
209 + if(!p->updated)
210 + continue;
211 +
212 + if(category && p->target != category)
213 + continue;
214 +
215 + if(user && p->user_target != user)
216 + continue;
217 +
218 + if(group && p->group_target != group)
219 + continue;
220 +
221 + if(process_name && ((strcmp(p->comm, process_name) != 0 && !p->parent) || (p->parent && strcmp(p->comm, process_name) != 0 && strcmp(p->parent->comm, process_name) != 0)))
222 + continue;
223 +
224 + if(filter_pid && p->pid != pid && p->ppid != pid)
225 + continue;
226 +
227 + if(filter_uid && p->uid != uid)
228 + continue;
229 +
230 + if(filter_gid && p->gid != gid)
231 + continue;
232 +
233 + rows++;
234 +
235 + buffer_json_add_array_item_array(wb); // for each pid
236 +
237 + // IMPORTANT!
238 + // THE ORDER SHOULD BE THE SAME WITH THE FIELDS!
239 +
240 + // pid
241 + buffer_json_add_array_item_uint64(wb, p->pid);
242 +
243 + // cmd
244 + buffer_json_add_array_item_string(wb, p->comm);
245 +
246 + // cmdline
247 + if (show_cmdline) {
248 + buffer_json_add_array_item_string(wb, (p->cmdline && *p->cmdline) ? p->cmdline : p->comm);
249 + }
250 +
251 + // ppid
252 + buffer_json_add_array_item_uint64(wb, p->ppid);
253 +
254 + // category
255 + buffer_json_add_array_item_string(wb, p->target ? p->target->name : "-");
256 +
257 + // user
258 + buffer_json_add_array_item_string(wb, p->user_target ? p->user_target->name : "-");
259 +
260 + // uid
261 + buffer_json_add_array_item_uint64(wb, p->uid);
262 +
263 + // group
264 + buffer_json_add_array_item_string(wb, p->group_target ? p->group_target->name : "-");
265 +
266 + // gid
267 + buffer_json_add_array_item_uint64(wb, p->gid);
268 +
269 + // CPU utilization %
270 + add_value_field_ndd_with_max(wb, CPU, (NETDATA_DOUBLE)(p->utime + p->stime + p->gtime + p->cutime + p->cstime + p->cgtime) / cpu_divisor);
271 + add_value_field_ndd_with_max(wb, UserCPU, (NETDATA_DOUBLE)(p->utime) / cpu_divisor);
272 + add_value_field_ndd_with_max(wb, SysCPU, (NETDATA_DOUBLE)(p->stime) / cpu_divisor);
273 + add_value_field_ndd_with_max(wb, GuestCPU, (NETDATA_DOUBLE)(p->gtime) / cpu_divisor);
274 + add_value_field_ndd_with_max(wb, CUserCPU, (NETDATA_DOUBLE)(p->cutime) / cpu_divisor);
275 + add_value_field_ndd_with_max(wb, CSysCPU, (NETDATA_DOUBLE)(p->cstime) / cpu_divisor);
276 + add_value_field_ndd_with_max(wb, CGuestCPU, (NETDATA_DOUBLE)(p->cgtime) / cpu_divisor);
277 +
278 + add_value_field_llu_with_max(wb, VoluntaryCtxtSwitches, p->status_voluntary_ctxt_switches / RATES_DETAIL);
279 + add_value_field_llu_with_max(wb, NonVoluntaryCtxtSwitches, p->status_nonvoluntary_ctxt_switches / RATES_DETAIL);
280 +
281 + // memory MiB
282 + if(MemTotal)
283 + add_value_field_ndd_with_max(wb, Memory, (NETDATA_DOUBLE)p->status_vmrss * 100.0 / (NETDATA_DOUBLE)MemTotal);
284 +
285 + add_value_field_ndd_with_max(wb, RSS, (NETDATA_DOUBLE)p->status_vmrss / memory_divisor);
286 + add_value_field_ndd_with_max(wb, Shared, (NETDATA_DOUBLE)p->status_vmshared / memory_divisor);
287 +#if !defined(__APPLE__)
288 + add_value_field_ndd_with_max(wb, VMSize, (NETDATA_DOUBLE)p->status_vmsize / memory_divisor);
289 +#endif
290 + add_value_field_ndd_with_max(wb, Swap, (NETDATA_DOUBLE)p->status_vmswap / memory_divisor);
291 +
292 + // Physical I/O
293 + add_value_field_llu_with_max(wb, PReads, p->io_storage_bytes_read / io_divisor);
294 + add_value_field_llu_with_max(wb, PWrites, p->io_storage_bytes_written / io_divisor);
295 +
296 + // Logical I/O
297 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
298 + add_value_field_llu_with_max(wb, LReads, p->io_logical_bytes_read / io_divisor);
299 + add_value_field_llu_with_max(wb, LWrites, p->io_logical_bytes_written / io_divisor);
300 +#endif
301 +
302 + // I/O calls
303 + add_value_field_llu_with_max(wb, RCalls, p->io_read_calls / RATES_DETAIL);
304 + add_value_field_llu_with_max(wb, WCalls, p->io_write_calls / RATES_DETAIL);
305 +
306 + // minor page faults
307 + add_value_field_llu_with_max(wb, MinFlt, p->minflt / RATES_DETAIL);
308 + add_value_field_llu_with_max(wb, CMinFlt, p->cminflt / RATES_DETAIL);
309 + add_value_field_llu_with_max(wb, TMinFlt, (p->minflt + p->cminflt) / RATES_DETAIL);
310 +
311 + // major page faults
312 + add_value_field_llu_with_max(wb, MajFlt, p->majflt / RATES_DETAIL);
313 + add_value_field_llu_with_max(wb, CMajFlt, p->cmajflt / RATES_DETAIL);
314 + add_value_field_llu_with_max(wb, TMajFlt, (p->majflt + p->cmajflt) / RATES_DETAIL);
315 +
316 + // open file descriptors
317 + add_value_field_ndd_with_max(wb, FDsLimitPercent, p->openfds_limits_percent);
318 + add_value_field_llu_with_max(wb, FDs, pid_openfds_sum(p));
319 + add_value_field_llu_with_max(wb, Files, p->openfds.files);
320 + add_value_field_llu_with_max(wb, Pipes, p->openfds.pipes);
321 + add_value_field_llu_with_max(wb, Sockets, p->openfds.sockets);
322 + add_value_field_llu_with_max(wb, iNotiFDs, p->openfds.inotifies);
323 + add_value_field_llu_with_max(wb, EventFDs, p->openfds.eventfds);
324 + add_value_field_llu_with_max(wb, TimerFDs, p->openfds.timerfds);
325 + add_value_field_llu_with_max(wb, SigFDs, p->openfds.signalfds);
326 + add_value_field_llu_with_max(wb, EvPollFDs, p->openfds.eventpolls);
327 + add_value_field_llu_with_max(wb, OtherFDs, p->openfds.other);
328 +
329 +
330 + // processes, threads, uptime
331 + add_value_field_llu_with_max(wb, Processes, p->children_count);
332 + add_value_field_llu_with_max(wb, Threads, p->num_threads);
333 + add_value_field_llu_with_max(wb, Uptime, p->uptime);
334 +
335 + buffer_json_array_close(wb); // for each pid
336 + }
337 +
338 + buffer_json_array_close(wb); // data
339 + buffer_json_member_add_object(wb, "columns");
340 +
341 + {
342 + int field_id = 0;
343 +
344 + // IMPORTANT!
345 + // THE ORDER SHOULD BE THE SAME WITH THE VALUES!
346 + // wb, key, name, visible, type, visualization, transform, decimal_points, units, max, sort, sortable, sticky, unique_key, pointer_to, summary, range
347 + buffer_rrdf_table_add_field(wb, field_id++, "PID", "Process ID", RRDF_FIELD_TYPE_INTEGER,
348 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
349 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
350 + RRDF_FIELD_FILTER_MULTISELECT,
351 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY |
352 + RRDF_FIELD_OPTS_UNIQUE_KEY, NULL);
353 +
354 + buffer_rrdf_table_add_field(wb, field_id++, "Cmd", "Process Name", RRDF_FIELD_TYPE_STRING,
355 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
356 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
357 + RRDF_FIELD_FILTER_MULTISELECT,
358 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
359 +
360 + if (show_cmdline) {
361 + buffer_rrdf_table_add_field(wb, field_id++, "CmdLine", "Command Line", RRDF_FIELD_TYPE_STRING,
362 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0,
363 + NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
364 + RRDF_FIELD_FILTER_MULTISELECT,
365 + RRDF_FIELD_OPTS_NONE, NULL);
366 + }
367 +
368 + buffer_rrdf_table_add_field(wb, field_id++, "PPID", "Parent Process ID", RRDF_FIELD_TYPE_INTEGER,
369 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL,
370 + NAN, RRDF_FIELD_SORT_ASCENDING, "PID", RRDF_FIELD_SUMMARY_COUNT,
371 + RRDF_FIELD_FILTER_MULTISELECT,
372 + RRDF_FIELD_OPTS_NONE, NULL);
373 + buffer_rrdf_table_add_field(wb, field_id++, "Category", "Category (apps_groups.conf)", RRDF_FIELD_TYPE_STRING,
374 + RRDF_FIELD_VISUAL_VALUE,
375 + RRDF_FIELD_TRANSFORM_NONE,
376 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
377 + RRDF_FIELD_FILTER_MULTISELECT,
378 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
379 + buffer_rrdf_table_add_field(wb, field_id++, "User", "User Owner", RRDF_FIELD_TYPE_STRING,
380 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
381 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
382 + RRDF_FIELD_FILTER_MULTISELECT,
383 + RRDF_FIELD_OPTS_VISIBLE, NULL);
384 + buffer_rrdf_table_add_field(wb, field_id++, "Uid", "User ID", RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE,
385 + RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
386 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
387 + RRDF_FIELD_FILTER_MULTISELECT,
388 + RRDF_FIELD_OPTS_NONE, NULL);
389 + buffer_rrdf_table_add_field(wb, field_id++, "Group", "Group Owner", RRDF_FIELD_TYPE_STRING,
390 + RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
391 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
392 + RRDF_FIELD_FILTER_MULTISELECT,
393 + RRDF_FIELD_OPTS_NONE, NULL);
394 + buffer_rrdf_table_add_field(wb, field_id++, "Gid", "Group ID", RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE,
395 + RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
396 + RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
397 + RRDF_FIELD_FILTER_MULTISELECT,
398 + RRDF_FIELD_OPTS_NONE, NULL);
399 +
400 + // CPU utilization
401 + buffer_rrdf_table_add_field(wb, field_id++, "CPU", "Total CPU Time (100% = 1 core)",
402 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
403 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
404 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
405 + RRDF_FIELD_OPTS_VISIBLE, NULL);
406 + buffer_rrdf_table_add_field(wb, field_id++, "UserCPU", "User CPU time (100% = 1 core)",
407 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
408 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", UserCPU_max,
409 + RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
410 + RRDF_FIELD_OPTS_NONE, NULL);
411 + buffer_rrdf_table_add_field(wb, field_id++, "SysCPU", "System CPU Time (100% = 1 core)",
412 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
413 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", SysCPU_max,
414 + RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
415 + RRDF_FIELD_OPTS_NONE, NULL);
416 + buffer_rrdf_table_add_field(wb, field_id++, "GuestCPU", "Guest CPU Time (100% = 1 core)",
417 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
418 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", GuestCPU_max,
419 + RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
420 + RRDF_FIELD_OPTS_NONE, NULL);
421 + buffer_rrdf_table_add_field(wb, field_id++, "CUserCPU", "Children User CPU Time (100% = 1 core)",
422 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
423 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CUserCPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
424 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
425 + RRDF_FIELD_OPTS_NONE, NULL);
426 + buffer_rrdf_table_add_field(wb, field_id++, "CSysCPU", "Children System CPU Time (100% = 1 core)",
427 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
428 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CSysCPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
429 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
430 + RRDF_FIELD_OPTS_NONE, NULL);
431 + buffer_rrdf_table_add_field(wb, field_id++, "CGuestCPU", "Children Guest CPU Time (100% = 1 core)",
432 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
433 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CGuestCPU_max, RRDF_FIELD_SORT_DESCENDING,
434 + NULL,
435 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
436 +
437 + // CPU context switches
438 + buffer_rrdf_table_add_field(wb, field_id++, "vCtxSwitch", "Voluntary Context Switches",
439 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
440 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "switches/s",
441 + VoluntaryCtxtSwitches_max, RRDF_FIELD_SORT_DESCENDING, NULL,
442 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
443 + buffer_rrdf_table_add_field(wb, field_id++, "iCtxSwitch", "Involuntary Context Switches",
444 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
445 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "switches/s",
446 + NonVoluntaryCtxtSwitches_max, RRDF_FIELD_SORT_DESCENDING, NULL,
447 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
448 +
449 + // memory
450 + if (MemTotal)
451 + buffer_rrdf_table_add_field(wb, field_id++, "Memory", "Memory Percentage", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
452 + RRDF_FIELD_VISUAL_BAR,
453 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL,
454 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
455 + RRDF_FIELD_OPTS_VISIBLE, NULL);
456 +
457 + buffer_rrdf_table_add_field(wb, field_id++, "Resident", "Resident Set Size", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
458 + RRDF_FIELD_VISUAL_BAR,
459 + RRDF_FIELD_TRANSFORM_NUMBER,
460 + 2, "MiB", RSS_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
461 + RRDF_FIELD_FILTER_RANGE,
462 + RRDF_FIELD_OPTS_VISIBLE, NULL);
463 + buffer_rrdf_table_add_field(wb, field_id++, "Shared", "Shared Pages", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
464 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
465 + "MiB", Shared_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
466 + RRDF_FIELD_FILTER_RANGE,
467 + RRDF_FIELD_OPTS_VISIBLE, NULL);
468 +#if !defined(__APPLE__)
469 + buffer_rrdf_table_add_field(wb, field_id++, "Virtual", "Virtual Memory Size", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
470 + RRDF_FIELD_VISUAL_BAR,
471 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "MiB", VMSize_max, RRDF_FIELD_SORT_DESCENDING, NULL,
472 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
473 + RRDF_FIELD_OPTS_VISIBLE, NULL);
474 +#endif
475 + buffer_rrdf_table_add_field(wb, field_id++, "Swap", "Swap Memory", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
476 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
477 + "MiB",
478 + Swap_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
479 + RRDF_FIELD_FILTER_RANGE,
480 + RRDF_FIELD_OPTS_NONE, NULL);
481 +
482 + // Physical I/O
483 + buffer_rrdf_table_add_field(wb, field_id++, "PReads", "Physical I/O Reads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
484 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
485 + 2, "KiB/s", PReads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
486 + RRDF_FIELD_FILTER_RANGE,
487 + RRDF_FIELD_OPTS_VISIBLE, NULL);
488 + buffer_rrdf_table_add_field(wb, field_id++, "PWrites", "Physical I/O Writes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
489 + RRDF_FIELD_VISUAL_BAR,
490 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "KiB/s", PWrites_max, RRDF_FIELD_SORT_DESCENDING,
491 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
492 + RRDF_FIELD_OPTS_VISIBLE, NULL);
493 +
494 + // Logical I/O
495 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
496 + buffer_rrdf_table_add_field(wb, field_id++, "LReads", "Logical I/O Reads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
497 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
498 + 2, "KiB/s", LReads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
499 + RRDF_FIELD_FILTER_RANGE,
500 + RRDF_FIELD_OPTS_NONE, NULL);
501 + buffer_rrdf_table_add_field(wb, field_id++, "LWrites", "Logical I/O Writes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
502 + RRDF_FIELD_VISUAL_BAR,
503 + RRDF_FIELD_TRANSFORM_NUMBER,
504 + 2, "KiB/s", LWrites_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
505 + RRDF_FIELD_FILTER_RANGE,
506 + RRDF_FIELD_OPTS_NONE, NULL);
507 +#endif
508 +
509 + // I/O calls
510 + buffer_rrdf_table_add_field(wb, field_id++, "RCalls", "I/O Read Calls", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
511 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
512 + "calls/s", RCalls_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
513 + RRDF_FIELD_FILTER_RANGE,
514 + RRDF_FIELD_OPTS_NONE, NULL);
515 + buffer_rrdf_table_add_field(wb, field_id++, "WCalls", "I/O Write Calls", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
516 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
517 + "calls/s", WCalls_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
518 + RRDF_FIELD_FILTER_RANGE,
519 + RRDF_FIELD_OPTS_NONE, NULL);
520 +
521 + // minor page faults
522 + buffer_rrdf_table_add_field(wb, field_id++, "MinFlt", "Minor Page Faults/s", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
523 + RRDF_FIELD_VISUAL_BAR,
524 + RRDF_FIELD_TRANSFORM_NUMBER,
525 + 2, "pgflts/s", MinFlt_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
526 + RRDF_FIELD_FILTER_RANGE,
527 + RRDF_FIELD_OPTS_NONE, NULL);
528 + buffer_rrdf_table_add_field(wb, field_id++, "CMinFlt", "Children Minor Page Faults/s",
529 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
530 + RRDF_FIELD_VISUAL_BAR,
531 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", CMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
532 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
533 + RRDF_FIELD_OPTS_NONE, NULL);
534 + buffer_rrdf_table_add_field(wb, field_id++, "TMinFlt", "Total Minor Page Faults/s",
535 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
536 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", TMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
537 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
538 + RRDF_FIELD_OPTS_NONE, NULL);
539 +
540 + // major page faults
541 + buffer_rrdf_table_add_field(wb, field_id++, "MajFlt", "Major Page Faults/s", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
542 + RRDF_FIELD_VISUAL_BAR,
543 + RRDF_FIELD_TRANSFORM_NUMBER,
544 + 2, "pgflts/s", MajFlt_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
545 + RRDF_FIELD_FILTER_RANGE,
546 + RRDF_FIELD_OPTS_NONE, NULL);
547 + buffer_rrdf_table_add_field(wb, field_id++, "CMajFlt", "Children Major Page Faults/s",
548 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
549 + RRDF_FIELD_VISUAL_BAR,
550 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", CMajFlt_max, RRDF_FIELD_SORT_DESCENDING,
551 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
552 + RRDF_FIELD_OPTS_NONE, NULL);
553 + buffer_rrdf_table_add_field(wb, field_id++, "TMajFlt", "Total Major Page Faults/s",
554 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
555 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", TMajFlt_max, RRDF_FIELD_SORT_DESCENDING,
556 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
557 + RRDF_FIELD_OPTS_NONE, NULL);
558 +
559 + // open file descriptors
560 + buffer_rrdf_table_add_field(wb, field_id++, "FDsLimitPercent", "Percentage of Open Descriptors vs Limits",
561 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
562 + RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", FDsLimitPercent_max, RRDF_FIELD_SORT_DESCENDING, NULL,
563 + RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
564 + RRDF_FIELD_OPTS_NONE, NULL);
565 + buffer_rrdf_table_add_field(wb, field_id++, "FDs", "All Open File Descriptors",
566 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
567 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", FDs_max, RRDF_FIELD_SORT_DESCENDING, NULL,
568 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
569 + RRDF_FIELD_OPTS_NONE, NULL);
570 + buffer_rrdf_table_add_field(wb, field_id++, "Files", "Open Files", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
571 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
572 + "fds",
573 + Files_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
574 + RRDF_FIELD_FILTER_RANGE,
575 + RRDF_FIELD_OPTS_NONE, NULL);
576 + buffer_rrdf_table_add_field(wb, field_id++, "Pipes", "Open Pipes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
577 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
578 + "fds",
579 + Pipes_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
580 + RRDF_FIELD_FILTER_RANGE,
581 + RRDF_FIELD_OPTS_NONE, NULL);
582 + buffer_rrdf_table_add_field(wb, field_id++, "Sockets", "Open Sockets", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
583 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
584 + "fds", Sockets_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
585 + RRDF_FIELD_FILTER_RANGE,
586 + RRDF_FIELD_OPTS_NONE, NULL);
587 + buffer_rrdf_table_add_field(wb, field_id++, "iNotiFDs", "Open iNotify Descriptors",
588 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
589 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", iNotiFDs_max, RRDF_FIELD_SORT_DESCENDING,
590 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
591 + RRDF_FIELD_OPTS_NONE, NULL);
592 + buffer_rrdf_table_add_field(wb, field_id++, "EventFDs", "Open Event Descriptors",
593 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
594 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", EventFDs_max, RRDF_FIELD_SORT_DESCENDING,
595 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
596 + RRDF_FIELD_OPTS_NONE, NULL);
597 + buffer_rrdf_table_add_field(wb, field_id++, "TimerFDs", "Open Timer Descriptors",
598 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
599 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", TimerFDs_max, RRDF_FIELD_SORT_DESCENDING,
600 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
601 + RRDF_FIELD_OPTS_NONE, NULL);
602 + buffer_rrdf_table_add_field(wb, field_id++, "SigFDs", "Open Signal Descriptors",
603 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
604 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", SigFDs_max, RRDF_FIELD_SORT_DESCENDING, NULL,
605 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
606 + RRDF_FIELD_OPTS_NONE, NULL);
607 + buffer_rrdf_table_add_field(wb, field_id++, "EvPollFDs", "Open Event Poll Descriptors",
608 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
609 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", EvPollFDs_max,
610 + RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
611 + RRDF_FIELD_OPTS_NONE, NULL);
612 + buffer_rrdf_table_add_field(wb, field_id++, "OtherFDs", "Other Open Descriptors",
613 + RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
614 + RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", OtherFDs_max, RRDF_FIELD_SORT_DESCENDING,
615 + NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
616 + RRDF_FIELD_OPTS_NONE, NULL);
617 +
618 + // processes, threads, uptime
619 + buffer_rrdf_table_add_field(wb, field_id++, "Processes", "Processes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
620 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
621 + "processes", Processes_max, RRDF_FIELD_SORT_DESCENDING, NULL,
622 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
623 + RRDF_FIELD_OPTS_NONE, NULL);
624 + buffer_rrdf_table_add_field(wb, field_id++, "Threads", "Threads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
625 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
626 + "threads", Threads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
627 + RRDF_FIELD_FILTER_RANGE,
628 + RRDF_FIELD_OPTS_NONE, NULL);
629 + buffer_rrdf_table_add_field(wb, field_id++, "Uptime", "Uptime in seconds", RRDF_FIELD_TYPE_DURATION,
630 + RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_DURATION_S, 2,
631 + "seconds", Uptime_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_MAX,
632 + RRDF_FIELD_FILTER_RANGE,
633 + RRDF_FIELD_OPTS_VISIBLE, NULL);
634 + }
635 + buffer_json_object_close(wb); // columns
636 +
637 + buffer_json_member_add_string(wb, "default_sort_column", "CPU");
638 +
639 + buffer_json_member_add_object(wb, "charts");
640 + {
641 + // CPU chart
642 + buffer_json_member_add_object(wb, "CPU");
643 + {
644 + buffer_json_member_add_string(wb, "name", "CPU Utilization");
645 + buffer_json_member_add_string(wb, "type", "stacked-bar");
646 + buffer_json_member_add_array(wb, "columns");
647 + {
648 + buffer_json_add_array_item_string(wb, "UserCPU");
649 + buffer_json_add_array_item_string(wb, "SysCPU");
650 + buffer_json_add_array_item_string(wb, "GuestCPU");
651 + buffer_json_add_array_item_string(wb, "CUserCPU");
652 + buffer_json_add_array_item_string(wb, "CSysCPU");
653 + buffer_json_add_array_item_string(wb, "CGuestCPU");
654 + }
655 + buffer_json_array_close(wb);
656 + }
657 + buffer_json_object_close(wb);
658 +
659 + buffer_json_member_add_object(wb, "CPUCtxSwitches");
660 + {
661 + buffer_json_member_add_string(wb, "name", "CPU Context Switches");
662 + buffer_json_member_add_string(wb, "type", "stacked-bar");
663 + buffer_json_member_add_array(wb, "columns");
664 + {
665 + buffer_json_add_array_item_string(wb, "vCtxSwitch");
666 + buffer_json_add_array_item_string(wb, "iCtxSwitch");
667 + }
668 + buffer_json_array_close(wb);
669 + }
670 + buffer_json_object_close(wb);
671 +
672 + // Memory chart
673 + buffer_json_member_add_object(wb, "Memory");
674 + {
675 + buffer_json_member_add_string(wb, "name", "Memory");
676 + buffer_json_member_add_string(wb, "type", "stacked-bar");
677 + buffer_json_member_add_array(wb, "columns");
678 + {
679 + buffer_json_add_array_item_string(wb, "Virtual");
680 + buffer_json_add_array_item_string(wb, "Resident");
681 + buffer_json_add_array_item_string(wb, "Shared");
682 + buffer_json_add_array_item_string(wb, "Swap");
683 + }
684 + buffer_json_array_close(wb);
685 + }
686 + buffer_json_object_close(wb);
687 +
688 + if(MemTotal) {
689 + // Memory chart
690 + buffer_json_member_add_object(wb, "MemoryPercent");
691 + {
692 + buffer_json_member_add_string(wb, "name", "Memory Percentage");
693 + buffer_json_member_add_string(wb, "type", "stacked-bar");
694 + buffer_json_member_add_array(wb, "columns");
695 + {
696 + buffer_json_add_array_item_string(wb, "Memory");
697 + }
698 + buffer_json_array_close(wb);
699 + }
700 + buffer_json_object_close(wb);
701 + }
702 +
703 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
704 + // I/O Reads chart
705 + buffer_json_member_add_object(wb, "Reads");
706 + {
707 + buffer_json_member_add_string(wb, "name", "I/O Reads");
708 + buffer_json_member_add_string(wb, "type", "stacked-bar");
709 + buffer_json_member_add_array(wb, "columns");
710 + {
711 + buffer_json_add_array_item_string(wb, "LReads");
712 + buffer_json_add_array_item_string(wb, "PReads");
713 + }
714 + buffer_json_array_close(wb);
715 + }
716 + buffer_json_object_close(wb);
717 +
718 + // I/O Writes chart
719 + buffer_json_member_add_object(wb, "Writes");
720 + {
721 + buffer_json_member_add_string(wb, "name", "I/O Writes");
722 + buffer_json_member_add_string(wb, "type", "stacked-bar");
723 + buffer_json_member_add_array(wb, "columns");
724 + {
725 + buffer_json_add_array_item_string(wb, "LWrites");
726 + buffer_json_add_array_item_string(wb, "PWrites");
727 + }
728 + buffer_json_array_close(wb);
729 + }
730 + buffer_json_object_close(wb);
731 +
732 + // Logical I/O chart
733 + buffer_json_member_add_object(wb, "LogicalIO");
734 + {
735 + buffer_json_member_add_string(wb, "name", "Logical I/O");
736 + buffer_json_member_add_string(wb, "type", "stacked-bar");
737 + buffer_json_member_add_array(wb, "columns");
738 + {
739 + buffer_json_add_array_item_string(wb, "LReads");
740 + buffer_json_add_array_item_string(wb, "LWrites");
741 + }
742 + buffer_json_array_close(wb);
743 + }
744 + buffer_json_object_close(wb);
745 +#endif
746 +
747 + // Physical I/O chart
748 + buffer_json_member_add_object(wb, "PhysicalIO");
749 + {
750 + buffer_json_member_add_string(wb, "name", "Physical I/O");
751 + buffer_json_member_add_string(wb, "type", "stacked-bar");
752 + buffer_json_member_add_array(wb, "columns");
753 + {
754 + buffer_json_add_array_item_string(wb, "PReads");
755 + buffer_json_add_array_item_string(wb, "PWrites");
756 + }
757 + buffer_json_array_close(wb);
758 + }
759 + buffer_json_object_close(wb);
760 +
761 + // I/O Calls chart
762 + buffer_json_member_add_object(wb, "IOCalls");
763 + {
764 + buffer_json_member_add_string(wb, "name", "I/O Calls");
765 + buffer_json_member_add_string(wb, "type", "stacked-bar");
766 + buffer_json_member_add_array(wb, "columns");
767 + {
768 + buffer_json_add_array_item_string(wb, "RCalls");
769 + buffer_json_add_array_item_string(wb, "WCalls");
770 + }
771 + buffer_json_array_close(wb);
772 + }
773 + buffer_json_object_close(wb);
774 +
775 + // Minor Page Faults chart
776 + buffer_json_member_add_object(wb, "MinFlt");
777 + {
778 + buffer_json_member_add_string(wb, "name", "Minor Page Faults");
779 + buffer_json_member_add_string(wb, "type", "stacked-bar");
780 + buffer_json_member_add_array(wb, "columns");
781 + {
782 + buffer_json_add_array_item_string(wb, "MinFlt");
783 + buffer_json_add_array_item_string(wb, "CMinFlt");
784 + }
785 + buffer_json_array_close(wb);
786 + }
787 + buffer_json_object_close(wb);
788 +
789 + // Major Page Faults chart
790 + buffer_json_member_add_object(wb, "MajFlt");
791 + {
792 + buffer_json_member_add_string(wb, "name", "Major Page Faults");
793 + buffer_json_member_add_string(wb, "type", "stacked-bar");
794 + buffer_json_member_add_array(wb, "columns");
795 + {
796 + buffer_json_add_array_item_string(wb, "MajFlt");
797 + buffer_json_add_array_item_string(wb, "CMajFlt");
798 + }
799 + buffer_json_array_close(wb);
800 + }
801 + buffer_json_object_close(wb);
802 +
803 + // Threads chart
804 + buffer_json_member_add_object(wb, "Threads");
805 + {
806 + buffer_json_member_add_string(wb, "name", "Threads");
807 + buffer_json_member_add_string(wb, "type", "stacked-bar");
808 + buffer_json_member_add_array(wb, "columns");
809 + {
810 + buffer_json_add_array_item_string(wb, "Threads");
811 + }
812 + buffer_json_array_close(wb);
813 + }
814 + buffer_json_object_close(wb);
815 +
816 + // Processes chart
817 + buffer_json_member_add_object(wb, "Processes");
818 + {
819 + buffer_json_member_add_string(wb, "name", "Processes");
820 + buffer_json_member_add_string(wb, "type", "stacked-bar");
821 + buffer_json_member_add_array(wb, "columns");
822 + {
823 + buffer_json_add_array_item_string(wb, "Processes");
824 + }
825 + buffer_json_array_close(wb);
826 + }
827 + buffer_json_object_close(wb);
828 +
829 + // FDs chart
830 + buffer_json_member_add_object(wb, "FDs");
831 + {
832 + buffer_json_member_add_string(wb, "name", "File Descriptors");
833 + buffer_json_member_add_string(wb, "type", "stacked-bar");
834 + buffer_json_member_add_array(wb, "columns");
835 + {
836 + buffer_json_add_array_item_string(wb, "Files");
837 + buffer_json_add_array_item_string(wb, "Pipes");
838 + buffer_json_add_array_item_string(wb, "Sockets");
839 + buffer_json_add_array_item_string(wb, "iNotiFDs");
840 + buffer_json_add_array_item_string(wb, "EventFDs");
841 + buffer_json_add_array_item_string(wb, "TimerFDs");
842 + buffer_json_add_array_item_string(wb, "SigFDs");
843 + buffer_json_add_array_item_string(wb, "EvPollFDs");
844 + buffer_json_add_array_item_string(wb, "OtherFDs");
845 + }
846 + buffer_json_array_close(wb);
847 + }
848 + buffer_json_object_close(wb);
849 + }
850 + buffer_json_object_close(wb); // charts
851 +
852 + buffer_json_member_add_array(wb, "default_charts");
853 + {
854 + buffer_json_add_array_item_array(wb);
855 + buffer_json_add_array_item_string(wb, "CPU");
856 + buffer_json_add_array_item_string(wb, "Category");
857 + buffer_json_array_close(wb);
858 +
859 + buffer_json_add_array_item_array(wb);
860 + buffer_json_add_array_item_string(wb, "Memory");
861 + buffer_json_add_array_item_string(wb, "Category");
862 + buffer_json_array_close(wb);
863 + }
864 + buffer_json_array_close(wb);
865 +
866 + buffer_json_member_add_object(wb, "group_by");
867 + {
868 + // group by PID
869 + buffer_json_member_add_object(wb, "PID");
870 + {
871 + buffer_json_member_add_string(wb, "name", "Process Tree by PID");
872 + buffer_json_member_add_array(wb, "columns");
873 + {
874 + buffer_json_add_array_item_string(wb, "PPID");
875 + }
876 + buffer_json_array_close(wb);
877 + }
878 + buffer_json_object_close(wb);
879 +
880 + // group by Category
881 + buffer_json_member_add_object(wb, "Category");
882 + {
883 + buffer_json_member_add_string(wb, "name", "Process Tree by Category");
884 + buffer_json_member_add_array(wb, "columns");
885 + {
886 + buffer_json_add_array_item_string(wb, "Category");
887 + buffer_json_add_array_item_string(wb, "PPID");
888 + }
889 + buffer_json_array_close(wb);
890 + }
891 + buffer_json_object_close(wb);
892 +
893 + // group by User
894 + buffer_json_member_add_object(wb, "User");
895 + {
896 + buffer_json_member_add_string(wb, "name", "Process Tree by User");
897 + buffer_json_member_add_array(wb, "columns");
898 + {
899 + buffer_json_add_array_item_string(wb, "User");
900 + buffer_json_add_array_item_string(wb, "PPID");
901 + }
902 + buffer_json_array_close(wb);
903 + }
904 + buffer_json_object_close(wb);
905 +
906 + // group by Group
907 + buffer_json_member_add_object(wb, "Group");
908 + {
909 + buffer_json_member_add_string(wb, "name", "Process Tree by Group");
910 + buffer_json_member_add_array(wb, "columns");
911 + {
912 + buffer_json_add_array_item_string(wb, "Group");
913 + buffer_json_add_array_item_string(wb, "PPID");
914 + }
915 + buffer_json_array_close(wb);
916 + }
917 + buffer_json_object_close(wb);
918 + }
919 + buffer_json_object_close(wb); // group_by
920 +
921 +close_and_send:
922 + buffer_json_member_add_time_t(wb, "expires", now_s + update_every);
923 + buffer_json_finalize(wb);
924 +
925 + pluginsd_function_result_to_stdout(transaction, HTTP_RESP_OK, "application/json", now_s + update_every, wb);
926 +
927 + buffer_free(wb);
928 +}
src/collectors/apps.plugin/apps_output.c new
+441
@@ -0,0 +1,441 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +static inline void send_BEGIN(const char *type, const char *name,const char *metric, usec_t usec) {
6 + fprintf(stdout, "BEGIN %s.%s_%s %" PRIu64 "\n", type, name, metric, usec);
7 +}
8 +
9 +static inline void send_SET(const char *name, kernel_uint_t value) {
10 + fprintf(stdout, "SET %s = " KERNEL_UINT_FORMAT "\n", name, value);
11 +}
12 +
13 +static inline void send_END(void) {
14 + fprintf(stdout, "END\n\n");
15 +}
16 +
17 +void send_resource_usage_to_netdata(usec_t dt) {
18 + static struct timeval last = { 0, 0 };
19 + static struct rusage me_last;
20 +
21 + struct timeval now;
22 + struct rusage me;
23 +
24 + usec_t cpuuser;
25 + usec_t cpusyst;
26 +
27 + if(!last.tv_sec) {
28 + now_monotonic_timeval(&last);
29 + getrusage(RUSAGE_SELF, &me_last);
30 +
31 + cpuuser = 0;
32 + cpusyst = 0;
33 + }
34 + else {
35 + now_monotonic_timeval(&now);
36 + getrusage(RUSAGE_SELF, &me);
37 +
38 + cpuuser = me.ru_utime.tv_sec * USEC_PER_SEC + me.ru_utime.tv_usec;
39 + cpusyst = me.ru_stime.tv_sec * USEC_PER_SEC + me.ru_stime.tv_usec;
40 +
41 + memmove(&last, &now, sizeof(struct timeval));
42 + memmove(&me_last, &me, sizeof(struct rusage));
43 + }
44 +
45 + static char created_charts = 0;
46 + if(unlikely(!created_charts)) {
47 + created_charts = 1;
48 +
49 + fprintf(stdout,
50 + "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n"
51 + "DIMENSION user '' incremental 1 1000\n"
52 + "DIMENSION system '' incremental 1 1000\n"
53 + "CHART netdata.apps_sizes '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_sizes line 140001 %1$d\n"
54 + "DIMENSION calls '' incremental 1 1\n"
55 + "DIMENSION files '' incremental 1 1\n"
56 + "DIMENSION filenames '' incremental 1 1\n"
57 + "DIMENSION inode_changes '' incremental 1 1\n"
58 + "DIMENSION link_changes '' incremental 1 1\n"
59 + "DIMENSION pids '' absolute 1 1\n"
60 + "DIMENSION fds '' absolute 1 1\n"
61 + "DIMENSION targets '' absolute 1 1\n"
62 + "DIMENSION new_pids 'new pids' incremental 1 1\n"
63 + , update_every
64 + );
65 +
66 + fprintf(stdout,
67 + "CHART netdata.apps_fix '' 'Apps Plugin Normalization Ratios' 'percentage' apps.plugin netdata.apps_fix line 140002 %1$d\n"
68 + "DIMENSION utime '' absolute 1 %2$llu\n"
69 + "DIMENSION stime '' absolute 1 %2$llu\n"
70 + "DIMENSION gtime '' absolute 1 %2$llu\n"
71 + "DIMENSION minflt '' absolute 1 %2$llu\n"
72 + "DIMENSION majflt '' absolute 1 %2$llu\n"
73 + , update_every
74 + , RATES_DETAIL
75 + );
76 +
77 + if(include_exited_childs)
78 + fprintf(stdout,
79 + "CHART netdata.apps_children_fix '' 'Apps Plugin Exited Children Normalization Ratios' 'percentage' apps.plugin netdata.apps_children_fix line 140003 %1$d\n"
80 + "DIMENSION cutime '' absolute 1 %2$llu\n"
81 + "DIMENSION cstime '' absolute 1 %2$llu\n"
82 + "DIMENSION cgtime '' absolute 1 %2$llu\n"
83 + "DIMENSION cminflt '' absolute 1 %2$llu\n"
84 + "DIMENSION cmajflt '' absolute 1 %2$llu\n"
85 + , update_every
86 + , RATES_DETAIL
87 + );
88 +
89 + }
90 +
91 + fprintf(stdout,
92 + "BEGIN netdata.apps_cpu %"PRIu64"\n"
93 + "SET user = %"PRIu64"\n"
94 + "SET system = %"PRIu64"\n"
95 + "END\n"
96 + "BEGIN netdata.apps_sizes %"PRIu64"\n"
97 + "SET calls = %zu\n"
98 + "SET files = %zu\n"
99 + "SET filenames = %zu\n"
100 + "SET inode_changes = %zu\n"
101 + "SET link_changes = %zu\n"
102 + "SET pids = %zu\n"
103 + "SET fds = %d\n"
104 + "SET targets = %zu\n"
105 + "SET new_pids = %zu\n"
106 + "END\n"
107 + , dt
108 + , cpuuser
109 + , cpusyst
110 + , dt
111 + , calls_counter
112 + , file_counter
113 + , filenames_allocated_counter
114 + , inodes_changed_counter
115 + , links_changed_counter
116 + , all_pids_count
117 + , all_files_len
118 + , apps_groups_targets_count
119 + , targets_assignment_counter
120 + );
121 +
122 + fprintf(stdout,
123 + "BEGIN netdata.apps_fix %"PRIu64"\n"
124 + "SET utime = %u\n"
125 + "SET stime = %u\n"
126 + "SET gtime = %u\n"
127 + "SET minflt = %u\n"
128 + "SET majflt = %u\n"
129 + "END\n"
130 + , dt
131 + , (unsigned int)(utime_fix_ratio * 100 * RATES_DETAIL)
132 + , (unsigned int)(stime_fix_ratio * 100 * RATES_DETAIL)
133 + , (unsigned int)(gtime_fix_ratio * 100 * RATES_DETAIL)
134 + , (unsigned int)(minflt_fix_ratio * 100 * RATES_DETAIL)
135 + , (unsigned int)(majflt_fix_ratio * 100 * RATES_DETAIL)
136 + );
137 +
138 + if(include_exited_childs)
139 + fprintf(stdout,
140 + "BEGIN netdata.apps_children_fix %"PRIu64"\n"
141 + "SET cutime = %u\n"
142 + "SET cstime = %u\n"
143 + "SET cgtime = %u\n"
144 + "SET cminflt = %u\n"
145 + "SET cmajflt = %u\n"
146 + "END\n"
147 + , dt
148 + , (unsigned int)(cutime_fix_ratio * 100 * RATES_DETAIL)
149 + , (unsigned int)(cstime_fix_ratio * 100 * RATES_DETAIL)
150 + , (unsigned int)(cgtime_fix_ratio * 100 * RATES_DETAIL)
151 + , (unsigned int)(cminflt_fix_ratio * 100 * RATES_DETAIL)
152 + , (unsigned int)(cmajflt_fix_ratio * 100 * RATES_DETAIL)
153 + );
154 +}
155 +
156 +void send_collected_data_to_netdata(struct target *root, const char *type, usec_t dt) {
157 + struct target *w;
158 +
159 + for (w = root; w ; w = w->next) {
160 + if (unlikely(!w->exposed))
161 + continue;
162 +
163 + send_BEGIN(type, w->clean_name, "processes", dt);
164 + send_SET("processes", w->processes);
165 + send_END();
166 +
167 + send_BEGIN(type, w->clean_name, "threads", dt);
168 + send_SET("threads", w->num_threads);
169 + send_END();
170 +
171 + if (unlikely(!w->processes && !w->is_other))
172 + continue;
173 +
174 + send_BEGIN(type, w->clean_name, "cpu_utilization", dt);
175 + send_SET("user", (kernel_uint_t)(w->utime * utime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cutime * cutime_fix_ratio)) : 0ULL));
176 + send_SET("system", (kernel_uint_t)(w->stime * stime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cstime * cstime_fix_ratio)) : 0ULL));
177 + send_END();
178 +
179 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
180 + if (enable_guest_charts) {
181 + send_BEGIN(type, w->clean_name, "cpu_guest_utilization", dt);
182 + send_SET("guest", (kernel_uint_t)(w->gtime * gtime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cgtime * cgtime_fix_ratio)) : 0ULL));
183 + send_END();
184 + }
185 +
186 + send_BEGIN(type, w->clean_name, "cpu_context_switches", dt);
187 + send_SET("voluntary", w->status_voluntary_ctxt_switches);
188 + send_SET("involuntary", w->status_nonvoluntary_ctxt_switches);
189 + send_END();
190 +
191 + send_BEGIN(type, w->clean_name, "mem_private_usage", dt);
192 + send_SET("mem", (w->status_vmrss > w->status_vmshared)?(w->status_vmrss - w->status_vmshared) : 0ULL);
193 + send_END();
194 +#endif
195 +
196 + send_BEGIN(type, w->clean_name, "mem_usage", dt);
197 + send_SET("rss", w->status_vmrss);
198 + send_END();
199 +
200 +#if !defined(__APPLE__)
201 + send_BEGIN(type, w->clean_name, "vmem_usage", dt);
202 + send_SET("vmem", w->status_vmsize);
203 + send_END();
204 +#endif
205 +
206 + send_BEGIN(type, w->clean_name, "mem_page_faults", dt);
207 + send_SET("minor", (kernel_uint_t)(w->minflt * minflt_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cminflt * cminflt_fix_ratio)) : 0ULL));
208 + send_SET("major", (kernel_uint_t)(w->majflt * majflt_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cmajflt * cmajflt_fix_ratio)) : 0ULL));
209 + send_END();
210 +
211 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
212 + send_BEGIN(type, w->clean_name, "swap_usage", dt);
213 + send_SET("swap", w->status_vmswap);
214 + send_END();
215 +#endif
216 +
217 + if (w->processes == 0) {
218 + send_BEGIN(type, w->clean_name, "uptime", dt);
219 + send_SET("uptime", 0);
220 + send_END();
221 +
222 + if (enable_detailed_uptime_charts) {
223 + send_BEGIN(type, w->clean_name, "uptime_summary", dt);
224 + send_SET("min", 0);
225 + send_SET("avg", 0);
226 + send_SET("max", 0);
227 + send_END();
228 + }
229 + } else {
230 + send_BEGIN(type, w->clean_name, "uptime", dt);
231 + send_SET("uptime", w->uptime_max);
232 + send_END();
233 +
234 + if (enable_detailed_uptime_charts) {
235 + send_BEGIN(type, w->clean_name, "uptime_summary", dt);
236 + send_SET("min", w->uptime_min);
237 + send_SET("avg", w->processes > 0 ? w->uptime_sum / w->processes : 0);
238 + send_SET("max", w->uptime_max);
239 + send_END();
240 + }
241 + }
242 +
243 + send_BEGIN(type, w->clean_name, "disk_physical_io", dt);
244 + send_SET("reads", w->io_storage_bytes_read);
245 + send_SET("writes", w->io_storage_bytes_written);
246 + send_END();
247 +
248 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
249 + send_BEGIN(type, w->clean_name, "disk_logical_io", dt);
250 + send_SET("reads", w->io_logical_bytes_read);
251 + send_SET("writes", w->io_logical_bytes_written);
252 + send_END();
253 +#endif
254 + if (enable_file_charts) {
255 + send_BEGIN(type, w->clean_name, "fds_open_limit", dt);
256 + send_SET("limit", w->max_open_files_percent * 100.0);
257 + send_END();
258 +
259 + send_BEGIN(type, w->clean_name, "fds_open", dt);
260 + send_SET("files", w->openfds.files);
261 + send_SET("sockets", w->openfds.sockets);
262 + send_SET("pipes", w->openfds.sockets);
263 + send_SET("inotifies", w->openfds.inotifies);
264 + send_SET("event", w->openfds.eventfds);
265 + send_SET("timer", w->openfds.timerfds);
266 + send_SET("signal", w->openfds.signalfds);
267 + send_SET("eventpolls", w->openfds.eventpolls);
268 + send_SET("other", w->openfds.other);
269 + send_END();
270 + }
271 + }
272 +}
273 +
274 +
275 +// ----------------------------------------------------------------------------
276 +// generate the charts
277 +
278 +void send_charts_updates_to_netdata(struct target *root, const char *type, const char *lbl_name, const char *title) {
279 + struct target *w;
280 +
281 + if (debug_enabled) {
282 + for (w = root; w; w = w->next) {
283 + if (unlikely(!w->target && w->processes)) {
284 + struct pid_on_target *pid_on_target;
285 + fprintf(stderr, "apps.plugin: target '%s' has aggregated %u process(es):", w->name, w->processes);
286 + for (pid_on_target = w->root_pid; pid_on_target; pid_on_target = pid_on_target->next) {
287 + fprintf(stderr, " %d", pid_on_target->pid);
288 + }
289 + fputc('\n', stderr);
290 + }
291 + }
292 + }
293 +
294 + for (w = root; w; w = w->next) {
295 + if (likely(w->exposed || (!w->processes && !w->is_other)))
296 + continue;
297 +
298 + w->exposed = 1;
299 +
300 + fprintf(stdout, "CHART %s.%s_cpu_utilization '' '%s CPU utilization (100%% = 1 core)' 'percentage' cpu %s.cpu_utilization stacked 20001 %d\n", type, w->clean_name, title, type, update_every);
301 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
302 + fprintf(stdout, "CLABEL_COMMIT\n");
303 + fprintf(stdout, "DIMENSION user '' absolute 1 %llu\n", time_factor * RATES_DETAIL / 100LLU);
304 + fprintf(stdout, "DIMENSION system '' absolute 1 %llu\n", time_factor * RATES_DETAIL / 100LLU);
305 +
306 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
307 + if (enable_guest_charts) {
308 + fprintf(stdout, "CHART %s.%s_cpu_guest_utilization '' '%s CPU guest utlization (100%% = 1 core)' 'percentage' cpu %s.cpu_guest_utilization line 20005 %d\n", type, w->clean_name, title, type, update_every);
309 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
310 + fprintf(stdout, "CLABEL_COMMIT\n");
311 + fprintf(stdout, "DIMENSION guest '' absolute 1 %llu\n", time_factor * RATES_DETAIL / 100LLU);
312 + }
313 +
314 + fprintf(stdout, "CHART %s.%s_cpu_context_switches '' '%s CPU context switches' 'switches/s' cpu %s.cpu_context_switches stacked 20010 %d\n", type, w->clean_name, title, type, update_every);
315 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
316 + fprintf(stdout, "CLABEL_COMMIT\n");
317 + fprintf(stdout, "DIMENSION voluntary '' absolute 1 %llu\n", RATES_DETAIL);
318 + fprintf(stdout, "DIMENSION involuntary '' absolute 1 %llu\n", RATES_DETAIL);
319 +
320 + fprintf(stdout, "CHART %s.%s_mem_private_usage '' '%s memory usage without shared' 'MiB' mem %s.mem_private_usage area 20050 %d\n", type, w->clean_name, title, type, update_every);
321 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
322 + fprintf(stdout, "CLABEL_COMMIT\n");
323 + fprintf(stdout, "DIMENSION mem '' absolute %ld %ld\n", 1L, 1024L);
324 +#endif
325 +
326 + fprintf(stdout, "CHART %s.%s_mem_usage '' '%s memory RSS usage' 'MiB' mem %s.mem_usage area 20055 %d\n", type, w->clean_name, title, type, update_every);
327 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
328 + fprintf(stdout, "CLABEL_COMMIT\n");
329 + fprintf(stdout, "DIMENSION rss '' absolute %ld %ld\n", 1L, 1024L);
330 +
331 +#if !defined(__APPLE__)
332 + fprintf(stdout, "CHART %s.%s_vmem_usage '' '%s virtual memory size' 'MiB' mem %s.vmem_usage line 20065 %d\n", type, w->clean_name, title, type, update_every);
333 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
334 + fprintf(stdout, "CLABEL_COMMIT\n");
335 + fprintf(stdout, "DIMENSION vmem '' absolute %ld %ld\n", 1L, 1024L);
336 +#endif
337 +
338 + fprintf(stdout, "CHART %s.%s_mem_page_faults '' '%s memory page faults' 'pgfaults/s' mem %s.mem_page_faults stacked 20060 %d\n", type, w->clean_name, title, type, update_every);
339 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
340 + fprintf(stdout, "CLABEL_COMMIT\n");
341 + fprintf(stdout, "DIMENSION major '' absolute 1 %llu\n", RATES_DETAIL);
342 + fprintf(stdout, "DIMENSION minor '' absolute 1 %llu\n", RATES_DETAIL);
343 +
344 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
345 + fprintf(stdout, "CHART %s.%s_swap_usage '' '%s swap usage' 'MiB' mem %s.swap_usage area 20065 %d\n", type, w->clean_name, title, type, update_every);
346 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
347 + fprintf(stdout, "CLABEL_COMMIT\n");
348 + fprintf(stdout, "DIMENSION swap '' absolute %ld %ld\n", 1L, 1024L);
349 +#endif
350 +
351 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
352 + fprintf(stdout, "CHART %s.%s_disk_physical_io '' '%s disk physical IO' 'KiB/s' disk %s.disk_physical_io area 20100 %d\n", type, w->clean_name, title, type, update_every);
353 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
354 + fprintf(stdout, "CLABEL_COMMIT\n");
355 + fprintf(stdout, "DIMENSION reads '' absolute 1 %llu\n", 1024LLU * RATES_DETAIL);
356 + fprintf(stdout, "DIMENSION writes '' absolute -1 %llu\n", 1024LLU * RATES_DETAIL);
357 +
358 + fprintf(stdout, "CHART %s.%s_disk_logical_io '' '%s disk logical IO' 'KiB/s' disk %s.disk_logical_io area 20105 %d\n", type, w->clean_name, title, type, update_every);
359 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
360 + fprintf(stdout, "CLABEL_COMMIT\n");
361 + fprintf(stdout, "DIMENSION reads '' absolute 1 %llu\n", 1024LLU * RATES_DETAIL);
362 + fprintf(stdout, "DIMENSION writes '' absolute -1 %llu\n", 1024LLU * RATES_DETAIL);
363 +#else
364 + fprintf(stdout, "CHART %s.%s_disk_physical_io '' '%s disk physical IO' 'blocks/s' disk %s.disk_physical_block_io area 20100 %d\n", type, w->clean_name, title, type, update_every);
365 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
366 + fprintf(stdout, "CLABEL_COMMIT\n");
367 + fprintf(stdout, "DIMENSION reads '' absolute 1 %llu\n", RATES_DETAIL);
368 + fprintf(stdout, "DIMENSION writes '' absolute -1 %llu\n", RATES_DETAIL);
369 +#endif
370 +
371 + fprintf(stdout, "CHART %s.%s_processes '' '%s processes' 'processes' processes %s.processes line 20150 %d\n", type, w->clean_name, title, type, update_every);
372 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
373 + fprintf(stdout, "CLABEL_COMMIT\n");
374 + fprintf(stdout, "DIMENSION processes '' absolute 1 1\n");
375 +
376 + fprintf(stdout, "CHART %s.%s_threads '' '%s threads' 'threads' processes %s.threads line 20155 %d\n", type, w->clean_name, title, type, update_every);
377 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
378 + fprintf(stdout, "CLABEL_COMMIT\n");
379 + fprintf(stdout, "DIMENSION threads '' absolute 1 1\n");
380 +
381 + if (enable_file_charts) {
382 + fprintf(stdout, "CHART %s.%s_fds_open_limit '' '%s open file descriptors limit' '%%' fds %s.fds_open_limit line 20200 %d\n", type, w->clean_name, title, type, update_every);
383 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
384 + fprintf(stdout, "CLABEL_COMMIT\n");
385 + fprintf(stdout, "DIMENSION limit '' absolute 1 100\n");
386 +
387 + fprintf(stdout, "CHART %s.%s_fds_open '' '%s open files descriptors' 'fds' fds %s.fds_open stacked 20210 %d\n", type, w->clean_name, title, type, update_every);
388 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
389 + fprintf(stdout, "CLABEL_COMMIT\n");
390 + fprintf(stdout, "DIMENSION files '' absolute 1 1\n");
391 + fprintf(stdout, "DIMENSION sockets '' absolute 1 1\n");
392 + fprintf(stdout, "DIMENSION pipes '' absolute 1 1\n");
393 + fprintf(stdout, "DIMENSION inotifies '' absolute 1 1\n");
394 + fprintf(stdout, "DIMENSION event '' absolute 1 1\n");
395 + fprintf(stdout, "DIMENSION timer '' absolute 1 1\n");
396 + fprintf(stdout, "DIMENSION signal '' absolute 1 1\n");
397 + fprintf(stdout, "DIMENSION eventpolls '' absolute 1 1\n");
398 + fprintf(stdout, "DIMENSION other '' absolute 1 1\n");
399 + }
400 +
401 + fprintf(stdout, "CHART %s.%s_uptime '' '%s uptime' 'seconds' uptime %s.uptime line 20250 %d\n", type, w->clean_name, title, type, update_every);
402 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
403 + fprintf(stdout, "CLABEL_COMMIT\n");
404 + fprintf(stdout, "DIMENSION uptime '' absolute 1 1\n");
405 +
406 + if (enable_detailed_uptime_charts) {
407 + fprintf(stdout, "CHART %s.%s_uptime_summary '' '%s uptime summary' 'seconds' uptime %s.uptime_summary area 20255 %d\n", type, w->clean_name, title, type, update_every);
408 + fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
409 + fprintf(stdout, "CLABEL_COMMIT\n");
410 + fprintf(stdout, "DIMENSION min '' absolute 1 1\n");
411 + fprintf(stdout, "DIMENSION avg '' absolute 1 1\n");
412 + fprintf(stdout, "DIMENSION max '' absolute 1 1\n");
413 + }
414 + }
415 +}
416 +
417 +void send_proc_states_count(usec_t dt __maybe_unused) {
418 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
419 + static bool chart_added = false;
420 + // create chart for count of processes in different states
421 + if (!chart_added) {
422 + fprintf(
423 + stdout,
424 + "CHART system.processes_state '' 'System Processes State' 'processes' processes system.processes_state line %d %d\n",
425 + NETDATA_CHART_PRIO_SYSTEM_PROCESS_STATES,
426 + update_every);
427 + for (proc_state i = PROC_STATUS_RUNNING; i < PROC_STATUS_END; i++) {
428 + fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", proc_states[i]);
429 + }
430 + chart_added = true;
431 + }
432 +
433 + // send process state count
434 + fprintf(stdout, "BEGIN system.processes_state %" PRIu64 "\n", dt);
435 + for (proc_state i = PROC_STATUS_RUNNING; i < PROC_STATUS_END; i++) {
436 + send_SET(proc_states[i], proc_state_count[i]);
437 + }
438 + send_END();
439 +#endif
440 +}
441 +
src/collectors/apps.plugin/apps_plugin.c
+198 -4496
@@ -6,2975 +6,227 @@
6 * Released under GPL v3+
7 */
8
9 -#include "collectors/all.h"
10 -#include "libnetdata/libnetdata.h"
9 +#include "apps_plugin.h"
10 #include "libnetdata/required_dummies.h"
11
13 -#define APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION "Detailed information on the currently running processes."
14 -
15 -#define APPS_PLUGIN_FUNCTIONS() do { \
16 - fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " \"processes\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n", \
17 - PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION, \
18 - (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID|HTTP_ACCESS_SAME_SPACE|HTTP_ACCESS_SENSITIVE_DATA), \
19 - RRDFUNCTIONS_PRIORITY_DEFAULT / 10); \
20 -} while(0)
21 -
22 -#define APPS_PLUGIN_GLOBAL_FUNCTIONS() do { \
23 - fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"processes\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n", \
24 - PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION, \
25 - (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID|HTTP_ACCESS_SAME_SPACE|HTTP_ACCESS_SENSITIVE_DATA), \
26 - RRDFUNCTIONS_PRIORITY_DEFAULT / 10); \
27 -} while(0)
28 -
29 -// ----------------------------------------------------------------------------
30 -// debugging
31 -
32 -static int debug_enabled = 0;
33 -static inline void debug_log_int(const char *fmt, ... ) {
34 - va_list args;
35 -
36 - fprintf( stderr, "apps.plugin: ");
37 - va_start( args, fmt );
38 - vfprintf( stderr, fmt, args );
39 - va_end( args );
40 -
41 - fputc('\n', stderr);
42 -}
43 -
44 -#ifdef NETDATA_INTERNAL_CHECKS
45 -
46 -#define debug_log(fmt, args...) do { if(unlikely(debug_enabled)) debug_log_int(fmt, ##args); } while(0)
47 -
48 -#else
49 -
50 -static inline void debug_log_dummy(void) {}
51 -#define debug_log(fmt, args...) debug_log_dummy()
52 -
53 -#endif
54 -
55 -
56 -// ----------------------------------------------------------------------------
57 -
58 -#ifdef __FreeBSD__
59 -#include <sys/user.h>
60 -#endif
61 -
62 -// ----------------------------------------------------------------------------
63 -// per O/S configuration
64 -
65 -// the minimum PID of the system
66 -// this is also the pid of the init process
67 -#define INIT_PID 1
68 -
69 -// if the way apps.plugin will work, will read the entire process list,
70 -// including the resource utilization of each process, instantly
71 -// set this to 1
72 -// when set to 0, apps.plugin builds a sort list of processes, in order
73 -// to process children processes, before parent processes
74 -#ifdef __FreeBSD__
75 -#define ALL_PIDS_ARE_READ_INSTANTLY 1
76 -#else
77 -#define ALL_PIDS_ARE_READ_INSTANTLY 0
78 -#endif
79 -
80 -// ----------------------------------------------------------------------------
81 -// string lengths
82 -
83 -#define MAX_COMPARE_NAME 100
84 -#define MAX_NAME 100
85 -#define MAX_CMDLINE 16384
86 -
87 -// ----------------------------------------------------------------------------
88 -// the rates we are going to send to netdata will have this detail a value of:
89 -// - 1 will send just integer parts to netdata
90 -// - 100 will send 2 decimal points
91 -// - 1000 will send 3 decimal points
92 -// etc.
93 -#define RATES_DETAIL 10000ULL
94 -
95 -// ----------------------------------------------------------------------------
96 -// factor for calculating correct CPU time values depending on units of raw data
97 -static unsigned int time_factor = 0;
98 -
99 -// ----------------------------------------------------------------------------
100 -// to avoid reallocating too frequently, we can increase the number of spare
101 -// file descriptors used by processes.
102 -// IMPORTANT:
103 -// having a lot of spares, increases the CPU utilization of the plugin.
104 -#define MAX_SPARE_FDS 1
105 -
106 -// ----------------------------------------------------------------------------
107 -// command line options
108 -
109 -static int
110 - update_every = 1,
111 - enable_guest_charts = 0,
112 -#ifdef __FreeBSD__
113 - enable_file_charts = 0,
114 -#else
115 - enable_file_charts = 1,
116 - max_fds_cache_seconds = 60,
117 -#endif
118 - enable_function_cmdline = 0,
119 - enable_detailed_uptime_charts = 0,
120 - enable_users_charts = 1,
121 - enable_groups_charts = 1,
122 - include_exited_childs = 1;
123 -
124 -// will be changed to getenv(NETDATA_USER_CONFIG_DIR) if it exists
125 -static char *user_config_dir = CONFIG_DIR;
126 -static char *stock_config_dir = LIBCONFIG_DIR;
127 -
128 -// some variables for keeping track of processes count by states
129 -typedef enum {
130 - PROC_STATUS_RUNNING = 0,
131 - PROC_STATUS_SLEEPING_D, // uninterruptible sleep
132 - PROC_STATUS_SLEEPING, // interruptible sleep
133 - PROC_STATUS_ZOMBIE,
134 - PROC_STATUS_STOPPED,
135 - PROC_STATUS_END, //place holder for ending enum fields
136 -} proc_state;
137 -
138 -#ifndef __FreeBSD__
139 -static proc_state proc_state_count[PROC_STATUS_END];
140 -static const char *proc_states[] = {
141 - [PROC_STATUS_RUNNING] = "running",
142 - [PROC_STATUS_SLEEPING] = "sleeping_interruptible",
143 - [PROC_STATUS_SLEEPING_D] = "sleeping_uninterruptible",
144 - [PROC_STATUS_ZOMBIE] = "zombie",
145 - [PROC_STATUS_STOPPED] = "stopped",
146 - };
147 -#endif
148 -
149 -// ----------------------------------------------------------------------------
150 -// internal flags
151 -// handled in code (automatically set)
152 -
153 -// log each problem once per process
154 -// log flood protection flags (log_thrown)
155 -typedef enum __attribute__((packed)) {
156 - PID_LOG_IO = (1 << 0),
157 - PID_LOG_STATUS = (1 << 1),
158 - PID_LOG_CMDLINE = (1 << 2),
159 - PID_LOG_FDS = (1 << 3),
160 - PID_LOG_STAT = (1 << 4),
161 - PID_LOG_LIMITS = (1 << 5),
162 - PID_LOG_LIMITS_DETAIL = (1 << 6),
163 -} PID_LOG;
164 -
165 -static int
166 - show_guest_time = 0, // 1 when guest values are collected
167 - show_guest_time_old = 0,
168 - proc_pid_cmdline_is_needed = 0; // 1 when we need to read /proc/cmdline
169 -
170 -
171 -// ----------------------------------------------------------------------------
172 -// internal counters
173 -
174 -static size_t
175 - global_iterations_counter = 1,
176 - calls_counter = 0,
177 - file_counter = 0,
178 - filenames_allocated_counter = 0,
179 - inodes_changed_counter = 0,
180 - links_changed_counter = 0,
181 - targets_assignment_counter = 0;
182 -
183 -
184 -// ----------------------------------------------------------------------------
185 -// Normalization
186 -//
187 -// With normalization we lower the collected metrics by a factor to make them
188 -// match the total utilization of the system.
189 -// The discrepancy exists because apps.plugin needs some time to collect all
190 -// the metrics. This results in utilization that exceeds the total utilization
191 -// of the system.
192 -//
193 -// During normalization, we align the per-process utilization, to the total of
194 -// the system. We first consume the exited children utilization and it the
195 -// collected values is above the total, we proportionally scale each reported
196 -// metric.
197 -
198 -// the total system time, as reported by /proc/stat
199 -static kernel_uint_t
200 - global_utime = 0,
201 - global_stime = 0,
202 - global_gtime = 0;
203 -
204 -// the normalization ratios, as calculated by normalize_utilization()
205 -NETDATA_DOUBLE
206 - utime_fix_ratio = 1.0,
207 - stime_fix_ratio = 1.0,
208 - gtime_fix_ratio = 1.0,
209 - minflt_fix_ratio = 1.0,
210 - majflt_fix_ratio = 1.0,
211 - cutime_fix_ratio = 1.0,
212 - cstime_fix_ratio = 1.0,
213 - cgtime_fix_ratio = 1.0,
214 - cminflt_fix_ratio = 1.0,
215 - cmajflt_fix_ratio = 1.0;
216 -
217 -
218 -struct pid_on_target {
219 - int32_t pid;
220 - struct pid_on_target *next;
221 -};
222 -
223 -struct openfds {
224 - kernel_uint_t files;
225 - kernel_uint_t pipes;
226 - kernel_uint_t sockets;
227 - kernel_uint_t inotifies;
228 - kernel_uint_t eventfds;
229 - kernel_uint_t timerfds;
230 - kernel_uint_t signalfds;
231 - kernel_uint_t eventpolls;
232 - kernel_uint_t other;
233 -};
234 -
235 -#define pid_openfds_sum(p) ((p)->openfds.files + (p)->openfds.pipes + (p)->openfds.sockets + (p)->openfds.inotifies + (p)->openfds.eventfds + (p)->openfds.timerfds + (p)->openfds.signalfds + (p)->openfds.eventpolls + (p)->openfds.other)
236 -
237 -struct pid_limits {
238 -// kernel_uint_t max_cpu_time;
239 -// kernel_uint_t max_file_size;
240 -// kernel_uint_t max_data_size;
241 -// kernel_uint_t max_stack_size;
242 -// kernel_uint_t max_core_file_size;
243 -// kernel_uint_t max_resident_set;
244 -// kernel_uint_t max_processes;
245 - kernel_uint_t max_open_files;
246 -// kernel_uint_t max_locked_memory;
247 -// kernel_uint_t max_address_space;
248 -// kernel_uint_t max_file_locks;
249 -// kernel_uint_t max_pending_signals;
250 -// kernel_uint_t max_msgqueue_size;
251 -// kernel_uint_t max_nice_priority;
252 -// kernel_uint_t max_realtime_priority;
253 -// kernel_uint_t max_realtime_timeout;
254 -};
255 -
256 -// ----------------------------------------------------------------------------
257 -// target
258 -//
259 -// target is the structure that processes are aggregated to be reported
260 -// to netdata.
261 -//
262 -// - Each entry in /etc/apps_groups.conf creates a target.
263 -// - Each user and group used by a process in the system, creates a target.
264 -
265 -struct target {
266 - char compare[MAX_COMPARE_NAME + 1];
267 - uint32_t comparehash;
268 - size_t comparelen;
269 -
270 - char id[MAX_NAME + 1];
271 - uint32_t idhash;
272 -
273 - char name[MAX_NAME + 1];
274 - char clean_name[MAX_NAME + 1]; // sanitized name used in chart id (need to replace at least dots)
275 - uid_t uid;
276 - gid_t gid;
277 -
278 - bool is_other;
279 -
280 - kernel_uint_t minflt;
281 - kernel_uint_t cminflt;
282 - kernel_uint_t majflt;
283 - kernel_uint_t cmajflt;
284 - kernel_uint_t utime;
285 - kernel_uint_t stime;
286 - kernel_uint_t gtime;
287 - kernel_uint_t cutime;
288 - kernel_uint_t cstime;
289 - kernel_uint_t cgtime;
290 - kernel_uint_t num_threads;
291 - // kernel_uint_t rss;
292 -
293 - kernel_uint_t status_vmsize;
294 - kernel_uint_t status_vmrss;
295 - kernel_uint_t status_vmshared;
296 - kernel_uint_t status_rssfile;
297 - kernel_uint_t status_rssshmem;
298 - kernel_uint_t status_vmswap;
299 - kernel_uint_t status_voluntary_ctxt_switches;
300 - kernel_uint_t status_nonvoluntary_ctxt_switches;
301 -
302 - kernel_uint_t io_logical_bytes_read;
303 - kernel_uint_t io_logical_bytes_written;
304 - kernel_uint_t io_read_calls;
305 - kernel_uint_t io_write_calls;
306 - kernel_uint_t io_storage_bytes_read;
307 - kernel_uint_t io_storage_bytes_written;
308 - kernel_uint_t io_cancelled_write_bytes;
309 -
310 - int *target_fds;
311 - int target_fds_size;
312 -
313 - struct openfds openfds;
314 -
315 - NETDATA_DOUBLE max_open_files_percent;
316 -
317 - kernel_uint_t starttime;
318 - kernel_uint_t collected_starttime;
319 - kernel_uint_t uptime_min;
320 - kernel_uint_t uptime_sum;
321 - kernel_uint_t uptime_max;
322 -
323 - unsigned int processes; // how many processes have been merged to this
324 - int exposed; // if set, we have sent this to netdata
325 - int hidden; // if set, we set the hidden flag on the dimension
326 - int debug_enabled;
327 - int ends_with;
328 - int starts_with; // if set, the compare string matches only the
329 - // beginning of the command
330 -
331 - struct pid_on_target *root_pid; // list of aggregated pids for target debugging
332 -
333 - struct target *target; // the one that will be reported to netdata
334 - struct target *next;
335 -};
336 -
337 -struct target
338 - *apps_groups_default_target = NULL, // the default target
339 - *apps_groups_root_target = NULL, // apps_groups.conf defined
340 - *users_root_target = NULL, // users
341 - *groups_root_target = NULL; // user groups
342 -
343 -size_t
344 - apps_groups_targets_count = 0; // # of apps_groups.conf targets
345 -
346 -
347 -// ----------------------------------------------------------------------------
348 -// pid_stat
349 -//
350 -// structure to store data for each process running
351 -// see: man proc for the description of the fields
352 -
353 -struct pid_fd {
354 - int fd;
355 -
356 -#ifndef __FreeBSD__
357 - ino_t inode;
358 - char *filename;
359 - uint32_t link_hash;
360 - size_t cache_iterations_counter;
361 - size_t cache_iterations_reset;
362 -#endif
363 -};
364 -
365 -struct pid_stat {
366 - int32_t pid;
367 - int32_t ppid;
368 - // int32_t pgrp;
369 - // int32_t session;
370 - // int32_t tty_nr;
371 - // int32_t tpgid;
372 - // uint64_t flags;
373 -
374 - char state;
375 -
376 - char comm[MAX_COMPARE_NAME + 1];
377 - char *cmdline;
378 -
379 - // these are raw values collected
380 - kernel_uint_t minflt_raw;
381 - kernel_uint_t cminflt_raw;
382 - kernel_uint_t majflt_raw;
383 - kernel_uint_t cmajflt_raw;
384 - kernel_uint_t utime_raw;
385 - kernel_uint_t stime_raw;
386 - kernel_uint_t gtime_raw; // guest_time
387 - kernel_uint_t cutime_raw;
388 - kernel_uint_t cstime_raw;
389 - kernel_uint_t cgtime_raw; // cguest_time
390 -
391 - // these are rates
392 - kernel_uint_t minflt;
393 - kernel_uint_t cminflt;
394 - kernel_uint_t majflt;
395 - kernel_uint_t cmajflt;
396 - kernel_uint_t utime;
397 - kernel_uint_t stime;
398 - kernel_uint_t gtime;
399 - kernel_uint_t cutime;
400 - kernel_uint_t cstime;
401 - kernel_uint_t cgtime;
402 -
403 - // int64_t priority;
404 - // int64_t nice;
405 - int32_t num_threads;
406 - // int64_t itrealvalue;
407 - kernel_uint_t collected_starttime;
408 - // kernel_uint_t vsize;
409 - // kernel_uint_t rss;
410 - // kernel_uint_t rsslim;
411 - // kernel_uint_t starcode;
412 - // kernel_uint_t endcode;
413 - // kernel_uint_t startstack;
414 - // kernel_uint_t kstkesp;
415 - // kernel_uint_t kstkeip;
416 - // uint64_t signal;
417 - // uint64_t blocked;
418 - // uint64_t sigignore;
419 - // uint64_t sigcatch;
420 - // uint64_t wchan;
421 - // uint64_t nswap;
422 - // uint64_t cnswap;
423 - // int32_t exit_signal;
424 - // int32_t processor;
425 - // uint32_t rt_priority;
426 - // uint32_t policy;
427 - // kernel_uint_t delayacct_blkio_ticks;
428 -
429 - uid_t uid;
430 - gid_t gid;
431 -
432 - kernel_uint_t status_voluntary_ctxt_switches_raw;
433 - kernel_uint_t status_nonvoluntary_ctxt_switches_raw;
434 -
435 - kernel_uint_t status_vmsize;
436 - kernel_uint_t status_vmrss;
437 - kernel_uint_t status_vmshared;
438 - kernel_uint_t status_rssfile;
439 - kernel_uint_t status_rssshmem;
440 - kernel_uint_t status_vmswap;
441 - kernel_uint_t status_voluntary_ctxt_switches;
442 - kernel_uint_t status_nonvoluntary_ctxt_switches;
443 -#ifndef __FreeBSD__
444 - ARL_BASE *status_arl;
445 -#endif
446 -
447 - kernel_uint_t io_logical_bytes_read_raw;
448 - kernel_uint_t io_logical_bytes_written_raw;
449 - kernel_uint_t io_read_calls_raw;
450 - kernel_uint_t io_write_calls_raw;
451 - kernel_uint_t io_storage_bytes_read_raw;
452 - kernel_uint_t io_storage_bytes_written_raw;
453 - kernel_uint_t io_cancelled_write_bytes_raw;
454 -
455 - kernel_uint_t io_logical_bytes_read;
456 - kernel_uint_t io_logical_bytes_written;
457 - kernel_uint_t io_read_calls;
458 - kernel_uint_t io_write_calls;
459 - kernel_uint_t io_storage_bytes_read;
460 - kernel_uint_t io_storage_bytes_written;
461 - kernel_uint_t io_cancelled_write_bytes;
462 -
463 - kernel_uint_t uptime;
464 -
465 - struct pid_fd *fds; // array of fds it uses
466 - size_t fds_size; // the size of the fds array
467 -
468 - struct openfds openfds;
469 - struct pid_limits limits;
470 -
471 - NETDATA_DOUBLE openfds_limits_percent;
472 -
473 - int sortlist; // higher numbers = top on the process tree
474 - // each process gets a unique number
475 -
476 - int children_count; // number of processes directly referencing this
477 - int keeploops; // increases by 1 every time keep is 1 and updated 0
478 -
479 - PID_LOG log_thrown;
480 -
481 - bool keep; // true when we need to keep this process in memory even after it exited
482 - bool updated; // true when the process is currently running
483 - bool merged; // true when it has been merged to its parent
484 - bool read; // true when we have already read this process for this iteration
485 - bool matched_by_config;
486 -
487 - struct target *target; // app_groups.conf targets
488 - struct target *user_target; // uid based targets
489 - struct target *group_target; // gid based targets
490 -
491 - usec_t stat_collected_usec;
492 - usec_t last_stat_collected_usec;
493 -
494 - usec_t io_collected_usec;
495 - usec_t last_io_collected_usec;
496 - usec_t last_limits_collected_usec;
497 -
498 - char *fds_dirname; // the full directory name in /proc/PID/fd
499 -
500 - char *stat_filename;
501 - char *status_filename;
502 - char *io_filename;
503 - char *cmdline_filename;
504 - char *limits_filename;
505 -
506 - struct pid_stat *parent;
507 - struct pid_stat *prev;
508 - struct pid_stat *next;
509 -};
510 -
511 -size_t pagesize;
512 -
513 -kernel_uint_t global_uptime;
514 -
515 -static struct pid_stat
516 - *root_of_pids = NULL, // global list of all processes running
517 - **all_pids = NULL; // to avoid allocations, we pre-allocate
518 - // a pointer for each pid in the entire pid space.
519 -
520 -static size_t
521 - all_pids_count = 0; // the number of processes running
522 -
523 -#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
524 -// Another pre-allocated list of all possible pids.
525 -// We need it to pids and assign them a unique sortlist id, so that we
526 -// read parents before children. This is needed to prevent a situation where
527 -// a child is found running, but until we read its parent, it has exited and
528 -// its parent has accumulated its resources.
529 -static pid_t
530 - *all_pids_sortlist = NULL;
531 -#endif
532 -
533 -// ----------------------------------------------------------------------------
534 -// file descriptor
535 -//
536 -// this is used to keep a global list of all open files of the system.
537 -// it is needed in order to calculate the unique files processes have open.
538 -
539 -#define FILE_DESCRIPTORS_INCREASE_STEP 100
540 -
541 -// types for struct file_descriptor->type
542 -typedef enum fd_filetype {
543 - FILETYPE_OTHER,
544 - FILETYPE_FILE,
545 - FILETYPE_PIPE,
546 - FILETYPE_SOCKET,
547 - FILETYPE_INOTIFY,
548 - FILETYPE_EVENTFD,
549 - FILETYPE_EVENTPOLL,
550 - FILETYPE_TIMERFD,
551 - FILETYPE_SIGNALFD
552 -} FD_FILETYPE;
553 -
554 -struct file_descriptor {
555 - avl_t avl;
556 -
557 -#ifdef NETDATA_INTERNAL_CHECKS
558 - uint32_t magic;
559 -#endif /* NETDATA_INTERNAL_CHECKS */
560 -
561 - const char *name;
562 - uint32_t hash;
563 -
564 - FD_FILETYPE type;
565 - int count;
566 - int pos;
567 -} *all_files = NULL;
568 -
569 -static int
570 - all_files_len = 0,
571 - all_files_size = 0;
572 -
573 -// ----------------------------------------------------------------------------
574 -// read users and groups from files
575 -
576 -struct user_or_group_id {
577 - avl_t avl;
578 -
579 - union {
580 - uid_t uid;
581 - gid_t gid;
582 - } id;
583 -
584 - char *name;
585 -
586 - int updated;
587 -
588 - struct user_or_group_id * next;
589 -};
590 -
591 -enum user_or_group_id_type {
592 - USER_ID,
593 - GROUP_ID
594 -};
595 -
596 -struct user_or_group_ids{
597 - enum user_or_group_id_type type;
598 -
599 - avl_tree_type index;
600 - struct user_or_group_id *root;
601 -
602 - char filename[FILENAME_MAX + 1];
603 -};
604 -
605 -int user_id_compare(void* a, void* b) {
606 - if(((struct user_or_group_id *)a)->id.uid < ((struct user_or_group_id *)b)->id.uid)
607 - return -1;
608 -
609 - else if(((struct user_or_group_id *)a)->id.uid > ((struct user_or_group_id *)b)->id.uid)
610 - return 1;
611 -
612 - else
613 - return 0;
614 -}
615 -
616 -struct user_or_group_ids all_user_ids = {
617 - .type = USER_ID,
618 -
619 - .index = {
620 - NULL,
621 - user_id_compare
622 - },
623 -
624 - .root = NULL,
625 -
626 - .filename = "",
627 -};
628 -
629 -int group_id_compare(void* a, void* b) {
630 - if(((struct user_or_group_id *)a)->id.gid < ((struct user_or_group_id *)b)->id.gid)
631 - return -1;
632 -
633 - else if(((struct user_or_group_id *)a)->id.gid > ((struct user_or_group_id *)b)->id.gid)
634 - return 1;
635 -
636 - else
637 - return 0;
638 -}
639 -
640 -struct user_or_group_ids all_group_ids = {
641 - .type = GROUP_ID,
642 -
643 - .index = {
644 - NULL,
645 - group_id_compare
646 - },
647 -
648 - .root = NULL,
649 -
650 - .filename = "",
651 -};
652 -
653 -int file_changed(const struct stat *statbuf, struct timespec *last_modification_time) {
654 - if(likely(statbuf->st_mtim.tv_sec == last_modification_time->tv_sec &&
655 - statbuf->st_mtim.tv_nsec == last_modification_time->tv_nsec)) return 0;
656 -
657 - last_modification_time->tv_sec = statbuf->st_mtim.tv_sec;
658 - last_modification_time->tv_nsec = statbuf->st_mtim.tv_nsec;
659 -
660 - return 1;
661 -}
662 -
663 -int read_user_or_group_ids(struct user_or_group_ids *ids, struct timespec *last_modification_time) {
664 - struct stat statbuf;
665 - if(unlikely(stat(ids->filename, &statbuf)))
666 - return 1;
667 - else
668 - if(likely(!file_changed(&statbuf, last_modification_time))) return 0;
669 -
670 - procfile *ff = procfile_open(ids->filename, " :\t", PROCFILE_FLAG_DEFAULT);
671 - if(unlikely(!ff)) return 1;
672 -
673 - ff = procfile_readall(ff);
674 - if(unlikely(!ff)) return 1;
675 -
676 - size_t line, lines = procfile_lines(ff);
677 -
678 - for(line = 0; line < lines ;line++) {
679 - size_t words = procfile_linewords(ff, line);
680 - if(unlikely(words < 3)) continue;
681 -
682 - char *name = procfile_lineword(ff, line, 0);
683 - if(unlikely(!name || !*name)) continue;
684 -
685 - char *id_string = procfile_lineword(ff, line, 2);
686 - if(unlikely(!id_string || !*id_string)) continue;
687 -
688 -
689 - struct user_or_group_id *user_or_group_id = callocz(1, sizeof(struct user_or_group_id));
690 -
691 - if(ids->type == USER_ID)
692 - user_or_group_id->id.uid = (uid_t) str2ull(id_string, NULL);
693 - else
694 - user_or_group_id->id.gid = (uid_t) str2ull(id_string, NULL);
695 -
696 - user_or_group_id->name = strdupz(name);
697 - user_or_group_id->updated = 1;
698 -
699 - struct user_or_group_id *existing_user_id = NULL;
700 -
701 - if(likely(ids->root))
702 - existing_user_id = (struct user_or_group_id *)avl_search(&ids->index, (avl_t *) user_or_group_id);
703 -
704 - if(unlikely(existing_user_id)) {
705 - freez(existing_user_id->name);
706 - existing_user_id->name = user_or_group_id->name;
707 - existing_user_id->updated = 1;
708 - freez(user_or_group_id);
709 - }
710 - else {
711 - if(unlikely(avl_insert(&ids->index, (avl_t *) user_or_group_id) != (void *) user_or_group_id)) {
712 - netdata_log_error("INTERNAL ERROR: duplicate indexing of id during realloc");
713 - }
714 -
715 - user_or_group_id->next = ids->root;
716 - ids->root = user_or_group_id;
717 - }
718 - }
719 -
720 - procfile_close(ff);
721 -
722 - // remove unused ids
723 - struct user_or_group_id *user_or_group_id = ids->root, *prev_user_id = NULL;
724 -
725 - while(user_or_group_id) {
726 - if(unlikely(!user_or_group_id->updated)) {
727 - if(unlikely((struct user_or_group_id *)avl_remove(&ids->index, (avl_t *) user_or_group_id) != user_or_group_id))
728 - netdata_log_error("INTERNAL ERROR: removal of unused id from index, removed a different id");
729 -
730 - if(prev_user_id)
731 - prev_user_id->next = user_or_group_id->next;
732 - else
733 - ids->root = user_or_group_id->next;
734 -
735 - freez(user_or_group_id->name);
736 - freez(user_or_group_id);
737 -
738 - if(prev_user_id)
739 - user_or_group_id = prev_user_id->next;
740 - else
741 - user_or_group_id = ids->root;
742 - }
743 - else {
744 - user_or_group_id->updated = 0;
745 -
746 - prev_user_id = user_or_group_id;
747 - user_or_group_id = user_or_group_id->next;
748 - }
749 - }
750 -
751 - return 0;
752 -}
753 -
754 -// ----------------------------------------------------------------------------
755 -// apps_groups.conf
756 -// aggregate all processes in groups, to have a limited number of dimensions
757 -
758 -static struct target *get_users_target(uid_t uid) {
759 - struct target *w;
760 - for(w = users_root_target ; w ; w = w->next)
761 - if(w->uid == uid) return w;
762 -
763 - w = callocz(sizeof(struct target), 1);
764 - snprintfz(w->compare, MAX_COMPARE_NAME, "%u", uid);
765 - w->comparehash = simple_hash(w->compare);
766 - w->comparelen = strlen(w->compare);
767 -
768 - snprintfz(w->id, MAX_NAME, "%u", uid);
769 - w->idhash = simple_hash(w->id);
770 -
771 - struct user_or_group_id user_id_to_find, *user_or_group_id = NULL;
772 - user_id_to_find.id.uid = uid;
773 -
774 - if(*netdata_configured_host_prefix) {
775 - static struct timespec last_passwd_modification_time;
776 - int ret = read_user_or_group_ids(&all_user_ids, &last_passwd_modification_time);
777 -
778 - if(likely(!ret && all_user_ids.index.root))
779 - user_or_group_id = (struct user_or_group_id *)avl_search(&all_user_ids.index, (avl_t *) &user_id_to_find);
780 - }
781 -
782 - if(user_or_group_id && user_or_group_id->name && *user_or_group_id->name) {
783 - snprintfz(w->name, MAX_NAME, "%s", user_or_group_id->name);
784 - }
785 - else {
786 - struct passwd *pw = getpwuid(uid);
787 - if(!pw || !pw->pw_name || !*pw->pw_name)
788 - snprintfz(w->name, MAX_NAME, "%u", uid);
789 - else
790 - snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
791 - }
792 -
793 - strncpyz(w->clean_name, w->name, MAX_NAME);
794 - netdata_fix_chart_name(w->clean_name);
795 -
796 - w->uid = uid;
797 -
798 - w->next = users_root_target;
799 - users_root_target = w;
800 -
801 - debug_log("added uid %u ('%s') target", w->uid, w->name);
802 -
803 - return w;
804 -}
805 -
806 -struct target *get_groups_target(gid_t gid)
807 -{
808 - struct target *w;
809 - for(w = groups_root_target ; w ; w = w->next)
810 - if(w->gid == gid) return w;
811 -
812 - w = callocz(sizeof(struct target), 1);
813 - snprintfz(w->compare, MAX_COMPARE_NAME, "%u", gid);
814 - w->comparehash = simple_hash(w->compare);
815 - w->comparelen = strlen(w->compare);
816 -
817 - snprintfz(w->id, MAX_NAME, "%u", gid);
818 - w->idhash = simple_hash(w->id);
819 -
820 - struct user_or_group_id group_id_to_find, *group_id = NULL;
821 - group_id_to_find.id.gid = gid;
822 -
823 - if(*netdata_configured_host_prefix) {
824 - static struct timespec last_group_modification_time;
825 - int ret = read_user_or_group_ids(&all_group_ids, &last_group_modification_time);
826 -
827 - if(likely(!ret && all_group_ids.index.root))
828 - group_id = (struct user_or_group_id *)avl_search(&all_group_ids.index, (avl_t *) &group_id_to_find);
829 - }
830 -
831 - if(group_id && group_id->name && *group_id->name) {
832 - snprintfz(w->name, MAX_NAME, "%s", group_id->name);
833 - }
834 - else {
835 - struct group *gr = getgrgid(gid);
836 - if(!gr || !gr->gr_name || !*gr->gr_name)
837 - snprintfz(w->name, MAX_NAME, "%u", gid);
838 - else
839 - snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
840 - }
841 -
842 - strncpyz(w->clean_name, w->name, MAX_NAME);
843 - netdata_fix_chart_name(w->clean_name);
844 -
845 - w->gid = gid;
846 -
847 - w->next = groups_root_target;
848 - groups_root_target = w;
849 -
850 - debug_log("added gid %u ('%s') target", w->gid, w->name);
851 -
852 - return w;
853 -}
854 -
855 -// find or create a new target
856 -// there are targets that are just aggregated to other target (the second argument)
857 -static struct target *get_apps_groups_target(const char *id, struct target *target, const char *name) {
858 - int tdebug = 0, thidden = target?target->hidden:0, ends_with = 0;
859 - const char *nid = id;
860 -
861 - // extract the options
862 - while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
863 - if(nid[0] == '-') thidden = 1;
864 - if(nid[0] == '+') tdebug = 1;
865 - if(nid[0] == '*') ends_with = 1;
866 - nid++;
867 - }
868 - uint32_t hash = simple_hash(id);
869 -
870 - // find if it already exists
871 - struct target *w, *last = apps_groups_root_target;
872 - for(w = apps_groups_root_target ; w ; w = w->next) {
873 - if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
874 - return w;
875 -
876 - last = w;
877 - }
878 -
879 - // find an existing target
880 - if(unlikely(!target)) {
881 - while(*name == '-') {
882 - if(*name == '-') thidden = 1;
883 - name++;
884 - }
885 -
886 - for(target = apps_groups_root_target ; target != NULL ; target = target->next) {
887 - if(!target->target && strcmp(name, target->name) == 0)
888 - break;
889 - }
890 -
891 - if(unlikely(debug_enabled)) {
892 - if(unlikely(target))
893 - debug_log("REUSING TARGET NAME '%s' on ID '%s'", target->name, target->id);
894 - else
895 - debug_log("NEW TARGET NAME '%s' on ID '%s'", name, id);
896 - }
897 - }
898 -
899 - if(target && target->target)
900 - fatal("Internal Error: request to link process '%s' to target '%s' which is linked to target '%s'", id, target->id, target->target->id);
901 -
902 - w = callocz(sizeof(struct target), 1);
903 - strncpyz(w->id, nid, MAX_NAME);
904 - w->idhash = simple_hash(w->id);
905 -
906 - if(unlikely(!target))
907 - // copy the name
908 - strncpyz(w->name, name, MAX_NAME);
909 - else
910 - // copy the id
911 - strncpyz(w->name, nid, MAX_NAME);
912 -
913 - // dots are used to distinguish chart type and id in streaming, so we should replace them
914 - strncpyz(w->clean_name, w->name, MAX_NAME);
915 - netdata_fix_chart_name(w->clean_name);
916 - for (char *d = w->clean_name; *d; d++) {
917 - if (*d == '.')
918 - *d = '_';
919 - }
920 -
921 - strncpyz(w->compare, nid, MAX_COMPARE_NAME);
922 - size_t len = strlen(w->compare);
923 - if(w->compare[len - 1] == '*') {
924 - w->compare[len - 1] = '\0';
925 - w->starts_with = 1;
926 - }
927 - w->ends_with = ends_with;
928 -
929 - if(w->starts_with && w->ends_with)
930 - proc_pid_cmdline_is_needed = 1;
931 -
932 - w->comparehash = simple_hash(w->compare);
933 - w->comparelen = strlen(w->compare);
934 -
935 - w->hidden = thidden;
936 -#ifdef NETDATA_INTERNAL_CHECKS
937 - w->debug_enabled = tdebug;
938 -#else
939 - if(tdebug)
940 - fprintf(stderr, "apps.plugin has been compiled without debugging\n");
941 -#endif
942 - w->target = target;
943 -
944 - // append it, to maintain the order in apps_groups.conf
945 - if(last) last->next = w;
946 - else apps_groups_root_target = w;
947 -
948 - debug_log("ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s"
949 - , w->id
950 - , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
951 - , w->target?w->target->name:w->name
952 - , (w->hidden)?"hidden":"-"
953 - , (w->debug_enabled)?"debug":"-"
954 - );
955 -
956 - return w;
957 -}
958 -
959 -// read the apps_groups.conf file
960 -static int read_apps_groups_conf(const char *path, const char *file)
961 -{
962 - char filename[FILENAME_MAX + 1];
963 -
964 - snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", path, file);
965 -
966 - debug_log("process groups file: '%s'", filename);
967 -
968 - // ----------------------------------------
969 -
970 - procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT);
971 - if(!ff) return 1;
972 -
973 - procfile_set_quotes(ff, "'\"");
974 -
975 - ff = procfile_readall(ff);
976 - if(!ff)
977 - return 1;
978 -
979 - size_t line, lines = procfile_lines(ff);
980 -
981 - for(line = 0; line < lines ;line++) {
982 - size_t word, words = procfile_linewords(ff, line);
983 - if(!words) continue;
984 -
985 - char *name = procfile_lineword(ff, line, 0);
986 - if(!name || !*name) continue;
987 -
988 - // find a possibly existing target
989 - struct target *w = NULL;
990 -
991 - // loop through all words, skipping the first one (the name)
992 - for(word = 0; word < words ;word++) {
993 - char *s = procfile_lineword(ff, line, word);
994 - if(!s || !*s) continue;
995 - if(*s == '#') break;
996 -
997 - // is this the first word? skip it
998 - if(s == name) continue;
999 -
1000 - // add this target
1001 - struct target *n = get_apps_groups_target(s, w, name);
1002 - if(!n) {
1003 - netdata_log_error("Cannot create target '%s' (line %zu, word %zu)", s, line, word);
1004 - continue;
1005 - }
1006 -
1007 - // just some optimization
1008 - // to avoid searching for a target for each process
1009 - if(!w) w = n->target?n->target:n;
1010 - }
1011 - }
1012 -
1013 - procfile_close(ff);
1014 -
1015 - apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL, "other"); // match nothing
1016 - if(!apps_groups_default_target)
1017 - fatal("Cannot create default target");
1018 - apps_groups_default_target->is_other = true;
1019 -
1020 - // allow the user to override group 'other'
1021 - if(apps_groups_default_target->target)
1022 - apps_groups_default_target = apps_groups_default_target->target;
1023 -
1024 - return 0;
1025 -}
1026 -
1027 -
1028 -// ----------------------------------------------------------------------------
1029 -// struct pid_stat management
1030 -static inline void init_pid_fds(struct pid_stat *p, size_t first, size_t size);
1031 -
1032 -static inline struct pid_stat *get_pid_entry(pid_t pid) {
1033 - if(unlikely(all_pids[pid]))
1034 - return all_pids[pid];
1035 -
1036 - struct pid_stat *p = callocz(sizeof(struct pid_stat), 1);
1037 - p->fds = mallocz(sizeof(struct pid_fd) * MAX_SPARE_FDS);
1038 - p->fds_size = MAX_SPARE_FDS;
1039 - init_pid_fds(p, 0, p->fds_size);
1040 - p->pid = pid;
1041 -
1042 - DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(root_of_pids, p, prev, next);
1043 -
1044 - all_pids[pid] = p;
1045 - all_pids_count++;
1046 -
1047 - return p;
1048 -}
1049 -
1050 -static inline void del_pid_entry(pid_t pid) {
1051 - struct pid_stat *p = all_pids[pid];
1052 -
1053 - if(unlikely(!p)) {
1054 - netdata_log_error("attempted to free pid %d that is not allocated.", pid);
1055 - return;
1056 - }
1057 -
1058 - debug_log("process %d %s exited, deleting it.", pid, p->comm);
1059 -
1060 - DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(root_of_pids, p, prev, next);
1061 -
1062 - // free the filename
1063 -#ifndef __FreeBSD__
1064 - {
1065 - size_t i;
1066 - for(i = 0; i < p->fds_size; i++)
1067 - if(p->fds[i].filename)
1068 - freez(p->fds[i].filename);
1069 - }
1070 -#endif
1071 - freez(p->fds);
1072 -
1073 - freez(p->fds_dirname);
1074 - freez(p->stat_filename);
1075 - freez(p->status_filename);
1076 - freez(p->limits_filename);
1077 -#ifndef __FreeBSD__
1078 - arl_free(p->status_arl);
1079 -#endif
1080 - freez(p->io_filename);
1081 - freez(p->cmdline_filename);
1082 - freez(p->cmdline);
1083 - freez(p);
1084 -
1085 - all_pids[pid] = NULL;
1086 - all_pids_count--;
1087 -}
1088 -
1089 -// ----------------------------------------------------------------------------
1090 -
1091 -static inline int managed_log(struct pid_stat *p, PID_LOG log, int status) {
1092 - if(unlikely(!status)) {
1093 - // netdata_log_error("command failed log %u, errno %d", log, errno);
1094 -
1095 - if(unlikely(debug_enabled || errno != ENOENT)) {
1096 - if(unlikely(debug_enabled || !(p->log_thrown & log))) {
1097 - p->log_thrown |= log;
1098 - switch(log) {
1099 - case PID_LOG_IO:
1100 - #ifdef __FreeBSD__
1101 - netdata_log_error("Cannot fetch process %d I/O info (command '%s')", p->pid, p->comm);
1102 - #else
1103 - netdata_log_error("Cannot process %s/proc/%d/io (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
1104 - #endif
1105 - break;
1106 -
1107 - case PID_LOG_STATUS:
1108 - #ifdef __FreeBSD__
1109 - netdata_log_error("Cannot fetch process %d status info (command '%s')", p->pid, p->comm);
1110 - #else
1111 - netdata_log_error("Cannot process %s/proc/%d/status (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
1112 - #endif
1113 - break;
1114 -
1115 - case PID_LOG_CMDLINE:
1116 - #ifdef __FreeBSD__
1117 - netdata_log_error("Cannot fetch process %d command line (command '%s')", p->pid, p->comm);
1118 - #else
1119 - netdata_log_error("Cannot process %s/proc/%d/cmdline (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
1120 - #endif
1121 - break;
1122 -
1123 - case PID_LOG_FDS:
1124 - #ifdef __FreeBSD__
1125 - netdata_log_error("Cannot fetch process %d files (command '%s')", p->pid, p->comm);
1126 - #else
1127 - netdata_log_error("Cannot process entries in %s/proc/%d/fd (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
1128 - #endif
1129 - break;
1130 -
1131 - case PID_LOG_LIMITS:
1132 - #ifdef __FreeBSD__
1133 - ;
1134 - #else
1135 - netdata_log_error("Cannot process %s/proc/%d/limits (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
1136 - #endif
1137 -
1138 - case PID_LOG_STAT:
1139 - break;
1140 -
1141 - default:
1142 - netdata_log_error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
1143 - break;
1144 - }
1145 - }
1146 - }
1147 - errno = 0;
1148 - }
1149 - else if(unlikely(p->log_thrown & log)) {
1150 - // netdata_log_error("unsetting log %u on pid %d", log, p->pid);
1151 - p->log_thrown &= ~log;
1152 - }
1153 -
1154 - return status;
1155 -}
1156 -
1157 -static inline void assign_target_to_pid(struct pid_stat *p) {
1158 - targets_assignment_counter++;
1159 -
1160 - uint32_t hash = simple_hash(p->comm);
1161 - size_t pclen = strlen(p->comm);
1162 -
1163 - struct target *w;
1164 - for(w = apps_groups_root_target; w ; w = w->next) {
1165 - // if(debug_enabled || (p->target && p->target->debug_enabled)) debug_log_int("\t\tcomparing '%s' with '%s'", w->compare, p->comm);
1166 -
1167 - // find it - 4 cases:
1168 - // 1. the target is not a pattern
1169 - // 2. the target has the prefix
1170 - // 3. the target has the suffix
1171 - // 4. the target is something inside cmdline
1172 -
1173 - if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
1174 - || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
1175 - || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
1176 - || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && p->cmdline && strstr(p->cmdline, w->compare))
1177 - ))) {
1178 -
1179 - p->matched_by_config = true;
1180 - if(w->target) p->target = w->target;
1181 - else p->target = w;
1182 -
1183 - if(debug_enabled || (p->target && p->target->debug_enabled))
1184 - debug_log_int("%s linked to target %s", p->comm, p->target->name);
1185 -
1186 - break;
1187 - }
1188 - }
1189 -}
1190 -
1191 -
1192 -// ----------------------------------------------------------------------------
1193 -// update pids from proc
1194 -
1195 -static inline int read_proc_pid_cmdline(struct pid_stat *p) {
1196 - static char cmdline[MAX_CMDLINE + 1];
1197 -
1198 -#ifdef __FreeBSD__
1199 - size_t i, bytes = MAX_CMDLINE;
1200 - int mib[4];
1201 -
1202 - mib[0] = CTL_KERN;
1203 - mib[1] = KERN_PROC;
1204 - mib[2] = KERN_PROC_ARGS;
1205 - mib[3] = p->pid;
1206 - if (unlikely(sysctl(mib, 4, cmdline, &bytes, NULL, 0)))
1207 - goto cleanup;
1208 -#else
1209 - if(unlikely(!p->cmdline_filename)) {
1210 - char filename[FILENAME_MAX + 1];
1211 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", netdata_configured_host_prefix, p->pid);
1212 - p->cmdline_filename = strdupz(filename);
1213 - }
1214 -
1215 - int fd = open(p->cmdline_filename, procfile_open_flags, 0666);
1216 - if(unlikely(fd == -1)) goto cleanup;
1217 -
1218 - ssize_t i, bytes = read(fd, cmdline, MAX_CMDLINE);
1219 - close(fd);
1220 -
1221 - if(unlikely(bytes < 0)) goto cleanup;
1222 -#endif
1223 -
1224 - cmdline[bytes] = '\0';
1225 - for(i = 0; i < bytes ; i++) {
1226 - if(unlikely(!cmdline[i])) cmdline[i] = ' ';
1227 - }
1228 -
1229 - if(p->cmdline) freez(p->cmdline);
1230 - p->cmdline = strdupz(cmdline);
1231 -
1232 - debug_log("Read file '%s' contents: %s", p->cmdline_filename, p->cmdline);
1233 -
1234 - return 1;
1235 -
1236 -cleanup:
1237 - // copy the command to the command line
1238 - if(p->cmdline) freez(p->cmdline);
1239 - p->cmdline = strdupz(p->comm);
1240 - return 0;
1241 -}
1242 -
1243 -// ----------------------------------------------------------------------------
1244 -// macro to calculate the incremental rate of a value
1245 -// each parameter is accessed only ONCE - so it is safe to pass function calls
1246 -// or other macros as parameters
1247 -
1248 -#define incremental_rate(rate_variable, last_kernel_variable, new_kernel_value, collected_usec, last_collected_usec) do { \
1249 - kernel_uint_t _new_tmp = new_kernel_value; \
1250 - (rate_variable) = (_new_tmp - (last_kernel_variable)) * (USEC_PER_SEC * RATES_DETAIL) / ((collected_usec) - (last_collected_usec)); \
1251 - (last_kernel_variable) = _new_tmp; \
1252 - } while(0)
1253 -
1254 -// the same macro for struct pid members
1255 -#define pid_incremental_rate(type, var, value) \
1256 - incremental_rate(var, var##_raw, value, p->type##_collected_usec, p->last_##type##_collected_usec)
1257 -
1258 -
1259 -// ----------------------------------------------------------------------------
1260 -
1261 -#ifndef __FreeBSD__
1262 -struct arl_callback_ptr {
1263 - struct pid_stat *p;
1264 - procfile *ff;
1265 - size_t line;
1266 -};
1267 -
1268 -void arl_callback_status_uid(const char *name, uint32_t hash, const char *value, void *dst) {
1269 - (void)name; (void)hash; (void)value;
1270 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
1271 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 5)) return;
1272 -
1273 - //const char *real_uid = procfile_lineword(aptr->ff, aptr->line, 1);
1274 - const char *effective_uid = procfile_lineword(aptr->ff, aptr->line, 2);
1275 - //const char *saved_uid = procfile_lineword(aptr->ff, aptr->line, 3);
1276 - //const char *filesystem_uid = procfile_lineword(aptr->ff, aptr->line, 4);
1277 -
1278 - if(likely(effective_uid && *effective_uid))
1279 - aptr->p->uid = (uid_t)str2l(effective_uid);
1280 -}
1281 -
1282 -void arl_callback_status_gid(const char *name, uint32_t hash, const char *value, void *dst) {
1283 - (void)name; (void)hash; (void)value;
1284 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
1285 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 5)) return;
1286 -
1287 - //const char *real_gid = procfile_lineword(aptr->ff, aptr->line, 1);
1288 - const char *effective_gid = procfile_lineword(aptr->ff, aptr->line, 2);
1289 - //const char *saved_gid = procfile_lineword(aptr->ff, aptr->line, 3);
1290 - //const char *filesystem_gid = procfile_lineword(aptr->ff, aptr->line, 4);
1291 -
1292 - if(likely(effective_gid && *effective_gid))
1293 - aptr->p->gid = (uid_t)str2l(effective_gid);
1294 -}
1295 -
1296 -void arl_callback_status_vmsize(const char *name, uint32_t hash, const char *value, void *dst) {
1297 - (void)name; (void)hash; (void)value;
1298 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
1299 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
1300 -
1301 - aptr->p->status_vmsize = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
1302 -}
1303 -
1304 -void arl_callback_status_vmswap(const char *name, uint32_t hash, const char *value, void *dst) {
1305 - (void)name; (void)hash; (void)value;
1306 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
1307 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
1308 -
1309 - aptr->p->status_vmswap = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
1310 -}
1311 -
1312 -void arl_callback_status_vmrss(const char *name, uint32_t hash, const char *value, void *dst) {
1313 - (void)name; (void)hash; (void)value;
1314 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
1315 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
1316 -
1317 - aptr->p->status_vmrss = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
1318 -}
1319 -
1320 -void arl_callback_status_rssfile(const char *name, uint32_t hash, const char *value, void *dst) {
1321 - (void)name; (void)hash; (void)value;
1322 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
1323 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
1324 -
1325 - aptr->p->status_rssfile = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
1326 -}
1327 -
1328 -void arl_callback_status_rssshmem(const char *name, uint32_t hash, const char *value, void *dst) {
1329 - (void)name; (void)hash; (void)value;
1330 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
1331 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
1332 -
1333 - aptr->p->status_rssshmem = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
1334 -}
1335 -
1336 -void arl_callback_status_voluntary_ctxt_switches(const char *name, uint32_t hash, const char *value, void *dst) {
1337 - (void)name; (void)hash; (void)value;
1338 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
1339 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 2)) return;
1340 -
1341 - struct pid_stat *p = aptr->p;
1342 - pid_incremental_rate(stat, p->status_voluntary_ctxt_switches, str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)));
1343 -}
1344 -
1345 -void arl_callback_status_nonvoluntary_ctxt_switches(const char *name, uint32_t hash, const char *value, void *dst) {
1346 - (void)name; (void)hash; (void)value;
1347 - struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
1348 - if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 2)) return;
1349 -
1350 - struct pid_stat *p = aptr->p;
1351 - pid_incremental_rate(stat, p->status_nonvoluntary_ctxt_switches, str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)));
1352 -}
1353 -
1354 -static void update_proc_state_count(char proc_stt) {
1355 - switch (proc_stt) {
1356 - case 'S':
1357 - proc_state_count[PROC_STATUS_SLEEPING] += 1;
1358 - break;
1359 - case 'R':
1360 - proc_state_count[PROC_STATUS_RUNNING] += 1;
1361 - break;
1362 - case 'D':
1363 - proc_state_count[PROC_STATUS_SLEEPING_D] += 1;
1364 - break;
1365 - case 'Z':
1366 - proc_state_count[PROC_STATUS_ZOMBIE] += 1;
1367 - break;
1368 - case 'T':
1369 - proc_state_count[PROC_STATUS_STOPPED] += 1;
1370 - break;
1371 - default:
1372 - break;
1373 - }
1374 -}
1375 -#endif // !__FreeBSD__
1376 -
1377 -#define MAX_PROC_PID_LIMITS 8192
1378 -#define PROC_PID_LIMITS_MAX_OPEN_FILES_KEY "\nMax open files "
1379 -
1380 -static inline kernel_uint_t get_proc_pid_limits_limit(char *buf, const char *key, size_t key_len, kernel_uint_t def) {
1381 - char *line = strstr(buf, key);
1382 - if(!line)
1383 - return def;
1384 -
1385 - char *v = &line[key_len];
1386 - while(isspace(*v)) v++;
1387 -
1388 - if(strcmp(v, "unlimited") == 0)
1389 - return 0;
1390 -
1391 - return str2ull(v, NULL);
1392 -}
1393 -
1394 -static inline int read_proc_pid_limits(struct pid_stat *p, void *ptr) {
1395 - (void)ptr;
1396 -
1397 -#ifdef __FreeBSD__
1398 - return 0;
1399 -#else
1400 - static char proc_pid_limits_buffer[MAX_PROC_PID_LIMITS + 1];
1401 - int ret = 0;
1402 - bool read_limits = false;
1403 -
1404 - errno = 0;
1405 - proc_pid_limits_buffer[0] = '\0';
1406 -
1407 - kernel_uint_t all_fds = pid_openfds_sum(p);
1408 - if(all_fds < p->limits.max_open_files / 2 && p->io_collected_usec > p->last_limits_collected_usec && p->io_collected_usec - p->last_limits_collected_usec <= 60 * USEC_PER_SEC) {
1409 - // too frequent, we want to collect limits once per minute
1410 - ret = 1;
1411 - goto cleanup;
1412 - }
1413 -
1414 - if(unlikely(!p->limits_filename)) {
1415 - char filename[FILENAME_MAX + 1];
1416 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/limits", netdata_configured_host_prefix, p->pid);
1417 - p->limits_filename = strdupz(filename);
1418 - }
1419 -
1420 - int fd = open(p->limits_filename, procfile_open_flags, 0666);
1421 - if(unlikely(fd == -1)) goto cleanup;
1422 -
1423 - ssize_t bytes = read(fd, proc_pid_limits_buffer, MAX_PROC_PID_LIMITS);
1424 - close(fd);
1425 -
1426 - if(bytes <= 0)
1427 - goto cleanup;
1428 -
1429 - // make it '\0' terminated
1430 - if(bytes < MAX_PROC_PID_LIMITS)
1431 - proc_pid_limits_buffer[bytes] = '\0';
1432 - else
1433 - proc_pid_limits_buffer[MAX_PROC_PID_LIMITS - 1] = '\0';
1434 -
1435 - p->limits.max_open_files = get_proc_pid_limits_limit(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY, sizeof(PROC_PID_LIMITS_MAX_OPEN_FILES_KEY) - 1, 0);
1436 - if(p->limits.max_open_files == 1) {
1437 - // it seems a bug in the kernel or something similar
1438 - // it sets max open files to 1 but the number of files
1439 - // the process has open are more than 1...
1440 - // https://github.com/netdata/netdata/issues/15443
1441 - p->limits.max_open_files = 0;
1442 - ret = 1;
1443 - goto cleanup;
1444 - }
1445 -
1446 - p->last_limits_collected_usec = p->io_collected_usec;
1447 - read_limits = true;
1448 -
1449 - ret = 1;
1450 -
1451 -cleanup:
1452 - if(p->limits.max_open_files)
1453 - p->openfds_limits_percent = (NETDATA_DOUBLE)all_fds * 100.0 / (NETDATA_DOUBLE)p->limits.max_open_files;
1454 - else
1455 - p->openfds_limits_percent = 0.0;
1456 -
1457 - if(p->openfds_limits_percent > 100.0) {
1458 - if(!(p->log_thrown & PID_LOG_LIMITS_DETAIL)) {
1459 - char *line;
1460 -
1461 - if(!read_limits) {
1462 - proc_pid_limits_buffer[0] = '\0';
1463 - line = "NOT READ";
1464 - }
1465 - else {
1466 - line = strstr(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY);
1467 - if (line) {
1468 - line++; // skip the initial newline
1469 -
1470 - char *end = strchr(line, '\n');
1471 - if (end)
1472 - *end = '\0';
1473 - }
1474 - }
1475 -
1476 - netdata_log_info(
1477 - "FDS_LIMITS: PID %d (%s) is using "
1478 - "%0.2f %% of its fds limits, "
1479 - "open fds = %"PRIu64 "("
1480 - "files = %"PRIu64 ", "
1481 - "pipes = %"PRIu64 ", "
1482 - "sockets = %"PRIu64", "
1483 - "inotifies = %"PRIu64", "
1484 - "eventfds = %"PRIu64", "
1485 - "timerfds = %"PRIu64", "
1486 - "signalfds = %"PRIu64", "
1487 - "eventpolls = %"PRIu64" "
1488 - "other = %"PRIu64" "
1489 - "), open fds limit = %"PRIu64", "
1490 - "%s, "
1491 - "original line [%s]",
1492 - p->pid, p->comm, p->openfds_limits_percent, all_fds,
1493 - p->openfds.files,
1494 - p->openfds.pipes,
1495 - p->openfds.sockets,
1496 - p->openfds.inotifies,
1497 - p->openfds.eventfds,
1498 - p->openfds.timerfds,
1499 - p->openfds.signalfds,
1500 - p->openfds.eventpolls,
1501 - p->openfds.other,
1502 - p->limits.max_open_files,
1503 - read_limits ? "and we have read the limits AFTER counting the fds"
1504 - : "but we have read the limits BEFORE counting the fds",
1505 - line);
1506 -
1507 - p->log_thrown |= PID_LOG_LIMITS_DETAIL;
1508 - }
1509 - }
1510 - else
1511 - p->log_thrown &= ~PID_LOG_LIMITS_DETAIL;
1512 -
1513 - return ret;
1514 -#endif
1515 -}
1516 -
1517 -static inline int read_proc_pid_status(struct pid_stat *p, void *ptr) {
1518 - p->status_vmsize = 0;
1519 - p->status_vmrss = 0;
1520 - p->status_vmshared = 0;
1521 - p->status_rssfile = 0;
1522 - p->status_rssshmem = 0;
1523 - p->status_vmswap = 0;
1524 - p->status_voluntary_ctxt_switches = 0;
1525 - p->status_nonvoluntary_ctxt_switches = 0;
1526 -
1527 -#ifdef __FreeBSD__
1528 - struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
1529 -
1530 - p->uid = proc_info->ki_uid;
1531 - p->gid = proc_info->ki_groups[0];
1532 - p->status_vmsize = proc_info->ki_size / 1024; // in KiB
1533 - p->status_vmrss = proc_info->ki_rssize * pagesize / 1024; // in KiB
1534 - // TODO: what about shared and swap memory on FreeBSD?
1535 - return 1;
1536 -#else
1537 - (void)ptr;
1538 -
1539 - static struct arl_callback_ptr arl_ptr;
1540 - static procfile *ff = NULL;
1541 -
1542 - if(unlikely(!p->status_arl)) {
1543 - p->status_arl = arl_create("/proc/pid/status", NULL, 60);
1544 - arl_expect_custom(p->status_arl, "Uid", arl_callback_status_uid, &arl_ptr);
1545 - arl_expect_custom(p->status_arl, "Gid", arl_callback_status_gid, &arl_ptr);
1546 - arl_expect_custom(p->status_arl, "VmSize", arl_callback_status_vmsize, &arl_ptr);
1547 - arl_expect_custom(p->status_arl, "VmRSS", arl_callback_status_vmrss, &arl_ptr);
1548 - arl_expect_custom(p->status_arl, "RssFile", arl_callback_status_rssfile, &arl_ptr);
1549 - arl_expect_custom(p->status_arl, "RssShmem", arl_callback_status_rssshmem, &arl_ptr);
1550 - arl_expect_custom(p->status_arl, "VmSwap", arl_callback_status_vmswap, &arl_ptr);
1551 - arl_expect_custom(p->status_arl, "voluntary_ctxt_switches", arl_callback_status_voluntary_ctxt_switches, &arl_ptr);
1552 - arl_expect_custom(p->status_arl, "nonvoluntary_ctxt_switches", arl_callback_status_nonvoluntary_ctxt_switches, &arl_ptr);
1553 - }
1554 -
1555 -
1556 - if(unlikely(!p->status_filename)) {
1557 - char filename[FILENAME_MAX + 1];
1558 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/status", netdata_configured_host_prefix, p->pid);
1559 - p->status_filename = strdupz(filename);
1560 - }
1561 -
1562 - ff = procfile_reopen(ff, p->status_filename, (!ff)?" \t:,-()/":NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
1563 - if(unlikely(!ff)) return 0;
1564 -
1565 - ff = procfile_readall(ff);
1566 - if(unlikely(!ff)) return 0;
1567 -
1568 - calls_counter++;
1569 -
1570 - // let ARL use this pid
1571 - arl_ptr.p = p;
1572 - arl_ptr.ff = ff;
1573 -
1574 - size_t lines = procfile_lines(ff), l;
1575 - arl_begin(p->status_arl);
1576 -
1577 - for(l = 0; l < lines ;l++) {
1578 - // debug_log("CHECK: line %zu of %zu, key '%s' = '%s'", l, lines, procfile_lineword(ff, l, 0), procfile_lineword(ff, l, 1));
1579 - arl_ptr.line = l;
1580 - if(unlikely(arl_check(p->status_arl,
1581 - procfile_lineword(ff, l, 0),
1582 - procfile_lineword(ff, l, 1)))) break;
1583 - }
1584 -
1585 - p->status_vmshared = p->status_rssfile + p->status_rssshmem;
1586 -
1587 - // debug_log("%s uid %d, gid %d, VmSize %zu, VmRSS %zu, RssFile %zu, RssShmem %zu, shared %zu", p->comm, (int)p->uid, (int)p->gid, p->status_vmsize, p->status_vmrss, p->status_rssfile, p->status_rssshmem, p->status_vmshared);
1588 -
1589 - return 1;
1590 -#endif
1591 -}
1592 -
1593 -
1594 -// ----------------------------------------------------------------------------
1595 -
1596 -static inline int read_proc_pid_stat(struct pid_stat *p, void *ptr) {
1597 - (void)ptr;
1598 -
1599 -#ifdef __FreeBSD__
1600 - struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
1601 - if (unlikely(proc_info->ki_tdflags & TDF_IDLETD))
1602 - goto cleanup;
1603 -#else
1604 - static procfile *ff = NULL;
1605 -
1606 - if(unlikely(!p->stat_filename)) {
1607 - char filename[FILENAME_MAX + 1];
1608 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", netdata_configured_host_prefix, p->pid);
1609 - p->stat_filename = strdupz(filename);
1610 - }
1611 -
1612 - int set_quotes = (!ff)?1:0;
1613 -
1614 - ff = procfile_reopen(ff, p->stat_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
1615 - if(unlikely(!ff)) goto cleanup;
1616 -
1617 - // if(set_quotes) procfile_set_quotes(ff, "()");
1618 - if(unlikely(set_quotes))
1619 - procfile_set_open_close(ff, "(", ")");
1620 -
1621 - ff = procfile_readall(ff);
1622 - if(unlikely(!ff)) goto cleanup;
1623 -#endif
1624 -
1625 - p->last_stat_collected_usec = p->stat_collected_usec;
1626 - p->stat_collected_usec = now_monotonic_usec();
1627 - calls_counter++;
1628 -
1629 -#ifdef __FreeBSD__
1630 - char *comm = proc_info->ki_comm;
1631 - p->ppid = proc_info->ki_ppid;
1632 -#else
1633 - // p->pid = str2pid_t(procfile_lineword(ff, 0, 0));
1634 - char *comm = procfile_lineword(ff, 0, 1);
1635 - p->state = *(procfile_lineword(ff, 0, 2));
1636 - p->ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3));
1637 - // p->pgrp = (int32_t)str2pid_t(procfile_lineword(ff, 0, 4));
1638 - // p->session = (int32_t)str2pid_t(procfile_lineword(ff, 0, 5));
1639 - // p->tty_nr = (int32_t)str2pid_t(procfile_lineword(ff, 0, 6));
1640 - // p->tpgid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 7));
1641 - // p->flags = str2uint64_t(procfile_lineword(ff, 0, 8));
1642 -#endif
1643 - if(strcmp(p->comm, comm) != 0) {
1644 - if(unlikely(debug_enabled)) {
1645 - if(p->comm[0])
1646 - debug_log("\tpid %d (%s) changed name to '%s'", p->pid, p->comm, comm);
1647 - else
1648 - debug_log("\tJust added %d (%s)", p->pid, comm);
1649 - }
1650 -
1651 - strncpyz(p->comm, comm, MAX_COMPARE_NAME);
1652 -
1653 - // /proc/<pid>/cmdline
1654 - if(likely(proc_pid_cmdline_is_needed))
1655 - managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
1656 -
1657 - assign_target_to_pid(p);
1658 - }
1659 -
1660 -#ifdef __FreeBSD__
1661 - pid_incremental_rate(stat, p->minflt, (kernel_uint_t)proc_info->ki_rusage.ru_minflt);
1662 - pid_incremental_rate(stat, p->cminflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_minflt);
1663 - pid_incremental_rate(stat, p->majflt, (kernel_uint_t)proc_info->ki_rusage.ru_majflt);
1664 - pid_incremental_rate(stat, p->cmajflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_majflt);
1665 - pid_incremental_rate(stat, p->utime, (kernel_uint_t)proc_info->ki_rusage.ru_utime.tv_sec * 100 + proc_info->ki_rusage.ru_utime.tv_usec / 10000);
1666 - pid_incremental_rate(stat, p->stime, (kernel_uint_t)proc_info->ki_rusage.ru_stime.tv_sec * 100 + proc_info->ki_rusage.ru_stime.tv_usec / 10000);
1667 - pid_incremental_rate(stat, p->cutime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_utime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_utime.tv_usec / 10000);
1668 - pid_incremental_rate(stat, p->cstime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_stime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_stime.tv_usec / 10000);
1669 -
1670 - p->num_threads = proc_info->ki_numthreads;
1671 -
1672 - if(enable_guest_charts) {
1673 - enable_guest_charts = 0;
1674 - netdata_log_info("Guest charts aren't supported by FreeBSD");
1675 - }
1676 -#else
1677 - pid_incremental_rate(stat, p->minflt, str2kernel_uint_t(procfile_lineword(ff, 0, 9)));
1678 - pid_incremental_rate(stat, p->cminflt, str2kernel_uint_t(procfile_lineword(ff, 0, 10)));
1679 - pid_incremental_rate(stat, p->majflt, str2kernel_uint_t(procfile_lineword(ff, 0, 11)));
1680 - pid_incremental_rate(stat, p->cmajflt, str2kernel_uint_t(procfile_lineword(ff, 0, 12)));
1681 - pid_incremental_rate(stat, p->utime, str2kernel_uint_t(procfile_lineword(ff, 0, 13)));
1682 - pid_incremental_rate(stat, p->stime, str2kernel_uint_t(procfile_lineword(ff, 0, 14)));
1683 - pid_incremental_rate(stat, p->cutime, str2kernel_uint_t(procfile_lineword(ff, 0, 15)));
1684 - pid_incremental_rate(stat, p->cstime, str2kernel_uint_t(procfile_lineword(ff, 0, 16)));
1685 - // p->priority = str2kernel_uint_t(procfile_lineword(ff, 0, 17));
1686 - // p->nice = str2kernel_uint_t(procfile_lineword(ff, 0, 18));
1687 - p->num_threads = (int32_t) str2uint32_t(procfile_lineword(ff, 0, 19), NULL);
1688 - // p->itrealvalue = str2kernel_uint_t(procfile_lineword(ff, 0, 20));
1689 - p->collected_starttime = str2kernel_uint_t(procfile_lineword(ff, 0, 21)) / system_hz;
1690 - p->uptime = (global_uptime > p->collected_starttime)?(global_uptime - p->collected_starttime):0;
1691 - // p->vsize = str2kernel_uint_t(procfile_lineword(ff, 0, 22));
1692 - // p->rss = str2kernel_uint_t(procfile_lineword(ff, 0, 23));
1693 - // p->rsslim = str2kernel_uint_t(procfile_lineword(ff, 0, 24));
1694 - // p->starcode = str2kernel_uint_t(procfile_lineword(ff, 0, 25));
1695 - // p->endcode = str2kernel_uint_t(procfile_lineword(ff, 0, 26));
1696 - // p->startstack = str2kernel_uint_t(procfile_lineword(ff, 0, 27));
1697 - // p->kstkesp = str2kernel_uint_t(procfile_lineword(ff, 0, 28));
1698 - // p->kstkeip = str2kernel_uint_t(procfile_lineword(ff, 0, 29));
1699 - // p->signal = str2kernel_uint_t(procfile_lineword(ff, 0, 30));
1700 - // p->blocked = str2kernel_uint_t(procfile_lineword(ff, 0, 31));
1701 - // p->sigignore = str2kernel_uint_t(procfile_lineword(ff, 0, 32));
1702 - // p->sigcatch = str2kernel_uint_t(procfile_lineword(ff, 0, 33));
1703 - // p->wchan = str2kernel_uint_t(procfile_lineword(ff, 0, 34));
1704 - // p->nswap = str2kernel_uint_t(procfile_lineword(ff, 0, 35));
1705 - // p->cnswap = str2kernel_uint_t(procfile_lineword(ff, 0, 36));
1706 - // p->exit_signal = str2kernel_uint_t(procfile_lineword(ff, 0, 37));
1707 - // p->processor = str2kernel_uint_t(procfile_lineword(ff, 0, 38));
1708 - // p->rt_priority = str2kernel_uint_t(procfile_lineword(ff, 0, 39));
1709 - // p->policy = str2kernel_uint_t(procfile_lineword(ff, 0, 40));
1710 - // p->delayacct_blkio_ticks = str2kernel_uint_t(procfile_lineword(ff, 0, 41));
1711 -
1712 - if(enable_guest_charts) {
1713 -
1714 - pid_incremental_rate(stat, p->gtime, str2kernel_uint_t(procfile_lineword(ff, 0, 42)));
1715 - pid_incremental_rate(stat, p->cgtime, str2kernel_uint_t(procfile_lineword(ff, 0, 43)));
1716 -
1717 - if (show_guest_time || p->gtime || p->cgtime) {
1718 - p->utime -= (p->utime >= p->gtime) ? p->gtime : p->utime;
1719 - p->cutime -= (p->cutime >= p->cgtime) ? p->cgtime : p->cutime;
1720 - show_guest_time = 1;
1721 - }
1722 - }
1723 -#endif
1724 -
1725 - if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
1726 - debug_log_int("READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d", netdata_configured_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
1727 -
1728 - if(unlikely(global_iterations_counter == 1)) {
1729 - p->minflt = 0;
1730 - p->cminflt = 0;
1731 - p->majflt = 0;
1732 - p->cmajflt = 0;
1733 - p->utime = 0;
1734 - p->stime = 0;
1735 - p->gtime = 0;
1736 - p->cutime = 0;
1737 - p->cstime = 0;
1738 - p->cgtime = 0;
1739 - }
1740 -#ifndef __FreeBSD__
1741 - update_proc_state_count(p->state);
1742 -#endif
1743 - return 1;
1744 -
1745 -cleanup:
1746 - p->minflt = 0;
1747 - p->cminflt = 0;
1748 - p->majflt = 0;
1749 - p->cmajflt = 0;
1750 - p->utime = 0;
1751 - p->stime = 0;
1752 - p->gtime = 0;
1753 - p->cutime = 0;
1754 - p->cstime = 0;
1755 - p->cgtime = 0;
1756 - p->num_threads = 0;
1757 - // p->rss = 0;
1758 - return 0;
1759 -}
1760 -
1761 -// ----------------------------------------------------------------------------
1762 -
1763 -static inline int read_proc_pid_io(struct pid_stat *p, void *ptr) {
1764 - (void)ptr;
1765 -#ifdef __FreeBSD__
1766 - struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
1767 -#else
1768 - static procfile *ff = NULL;
1769 -
1770 - if(unlikely(!p->io_filename)) {
1771 - char filename[FILENAME_MAX + 1];
1772 - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", netdata_configured_host_prefix, p->pid);
1773 - p->io_filename = strdupz(filename);
1774 - }
1775 -
1776 - // open the file
1777 - ff = procfile_reopen(ff, p->io_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
1778 - if(unlikely(!ff)) goto cleanup;
1779 -
1780 - ff = procfile_readall(ff);
1781 - if(unlikely(!ff)) goto cleanup;
1782 -#endif
1783 -
1784 - calls_counter++;
1785 -
1786 - p->last_io_collected_usec = p->io_collected_usec;
1787 - p->io_collected_usec = now_monotonic_usec();
1788 -
1789 -#ifdef __FreeBSD__
1790 - pid_incremental_rate(io, p->io_storage_bytes_read, proc_info->ki_rusage.ru_inblock);
1791 - pid_incremental_rate(io, p->io_storage_bytes_written, proc_info->ki_rusage.ru_oublock);
1792 -#else
1793 - pid_incremental_rate(io, p->io_logical_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 0, 1)));
1794 - pid_incremental_rate(io, p->io_logical_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 1, 1)));
1795 - pid_incremental_rate(io, p->io_read_calls, str2kernel_uint_t(procfile_lineword(ff, 2, 1)));
1796 - pid_incremental_rate(io, p->io_write_calls, str2kernel_uint_t(procfile_lineword(ff, 3, 1)));
1797 - pid_incremental_rate(io, p->io_storage_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 4, 1)));
1798 - pid_incremental_rate(io, p->io_storage_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 5, 1)));
1799 - pid_incremental_rate(io, p->io_cancelled_write_bytes, str2kernel_uint_t(procfile_lineword(ff, 6, 1)));
1800 -#endif
1801 -
1802 - if(unlikely(global_iterations_counter == 1)) {
1803 - p->io_logical_bytes_read = 0;
1804 - p->io_logical_bytes_written = 0;
1805 - p->io_read_calls = 0;
1806 - p->io_write_calls = 0;
1807 - p->io_storage_bytes_read = 0;
1808 - p->io_storage_bytes_written = 0;
1809 - p->io_cancelled_write_bytes = 0;
1810 - }
1811 -
1812 - return 1;
1813 -
1814 -#ifndef __FreeBSD__
1815 -cleanup:
1816 - p->io_logical_bytes_read = 0;
1817 - p->io_logical_bytes_written = 0;
1818 - p->io_read_calls = 0;
1819 - p->io_write_calls = 0;
1820 - p->io_storage_bytes_read = 0;
1821 - p->io_storage_bytes_written = 0;
1822 - p->io_cancelled_write_bytes = 0;
1823 - return 0;
1824 -#endif
1825 -}
1826 -
1827 -#ifndef __FreeBSD__
1828 -static inline int read_global_time() {
1829 - static char filename[FILENAME_MAX + 1] = "";
1830 - static procfile *ff = NULL;
1831 - static kernel_uint_t utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0;
1832 - static usec_t collected_usec = 0, last_collected_usec = 0;
1833 -
1834 - if(unlikely(!ff)) {
1835 - snprintfz(filename, FILENAME_MAX, "%s/proc/stat", netdata_configured_host_prefix);
1836 - ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
1837 - if(unlikely(!ff)) goto cleanup;
1838 - }
1839 -
1840 - ff = procfile_readall(ff);
1841 - if(unlikely(!ff)) goto cleanup;
1842 -
1843 - last_collected_usec = collected_usec;
1844 - collected_usec = now_monotonic_usec();
1845 -
1846 - calls_counter++;
1847 -
1848 - // temporary - it is added global_ntime;
1849 - kernel_uint_t global_ntime = 0;
1850 -
1851 - incremental_rate(global_utime, utime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 1)), collected_usec, last_collected_usec);
1852 - incremental_rate(global_ntime, ntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 2)), collected_usec, last_collected_usec);
1853 - incremental_rate(global_stime, stime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 3)), collected_usec, last_collected_usec);
1854 - incremental_rate(global_gtime, gtime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 10)), collected_usec, last_collected_usec);
1855 -
1856 - global_utime += global_ntime;
1857 -
1858 - if(enable_guest_charts) {
1859 - // temporary - it is added global_ntime;
1860 - kernel_uint_t global_gntime = 0;
1861 -
1862 - // guest nice time, on guest time
1863 - incremental_rate(global_gntime, gntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 11)), collected_usec, last_collected_usec);
1864 -
1865 - global_gtime += global_gntime;
1866 -
1867 - // remove guest time from user time
1868 - global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime;
1869 - }
1870 -
1871 - if(unlikely(global_iterations_counter == 1)) {
1872 - global_utime = 0;
1873 - global_stime = 0;
1874 - global_gtime = 0;
1875 - }
1876 -
1877 - return 1;
1878 -
1879 -cleanup:
1880 - global_utime = 0;
1881 - global_stime = 0;
1882 - global_gtime = 0;
1883 - return 0;
1884 -}
1885 -#else
1886 -static inline int read_global_time() {
1887 - static kernel_uint_t utime_raw = 0, stime_raw = 0, ntime_raw = 0;
1888 - static usec_t collected_usec = 0, last_collected_usec = 0;
1889 - long cp_time[CPUSTATES];
1890 -
1891 - if (unlikely(CPUSTATES != 5)) {
1892 - goto cleanup;
1893 - } else {
1894 - static int mib[2] = {0, 0};
1895 -
1896 - if (unlikely(GETSYSCTL_SIMPLE("kern.cp_time", mib, cp_time))) {
1897 - goto cleanup;
1898 - }
1899 - }
1900 -
1901 - last_collected_usec = collected_usec;
1902 - collected_usec = now_monotonic_usec();
1903 -
1904 - calls_counter++;
1905 -
1906 - // temporary - it is added global_ntime;
1907 - kernel_uint_t global_ntime = 0;
1908 -
1909 - incremental_rate(global_utime, utime_raw, cp_time[0] * 100LLU / system_hz, collected_usec, last_collected_usec);
1910 - incremental_rate(global_ntime, ntime_raw, cp_time[1] * 100LLU / system_hz, collected_usec, last_collected_usec);
1911 - incremental_rate(global_stime, stime_raw, cp_time[2] * 100LLU / system_hz, collected_usec, last_collected_usec);
1912 -
1913 - global_utime += global_ntime;
1914 -
1915 - if(unlikely(global_iterations_counter == 1)) {
1916 - global_utime = 0;
1917 - global_stime = 0;
1918 - global_gtime = 0;
1919 - }
1920 -
1921 - return 1;
1922 -
1923 -cleanup:
1924 - global_utime = 0;
1925 - global_stime = 0;
1926 - global_gtime = 0;
1927 - return 0;
1928 -}
1929 -#endif /* !__FreeBSD__ */
1930 -
1931 -// ----------------------------------------------------------------------------
1932 -
1933 -int file_descriptor_compare(void* a, void* b) {
1934 -#ifdef NETDATA_INTERNAL_CHECKS
1935 - if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
1936 - netdata_log_error("Corrupted index data detected. Please report this.");
1937 -#endif /* NETDATA_INTERNAL_CHECKS */
1938 -
1939 - if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
1940 - return -1;
1941 -
1942 - else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
1943 - return 1;
1944 -
1945 - else
1946 - return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
1947 -}
1948 -
1949 -// int file_descriptor_iterator(avl_t *a) { if(a) {}; return 0; }
1950 -
1951 -avl_tree_type all_files_index = {
1952 - NULL,
1953 - file_descriptor_compare
1954 -};
1955 -
1956 -static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
1957 - struct file_descriptor tmp;
1958 - tmp.hash = (hash)?hash:simple_hash(name);
1959 - tmp.name = name;
1960 - tmp.count = 0;
1961 - tmp.pos = 0;
1962 -#ifdef NETDATA_INTERNAL_CHECKS
1963 - tmp.magic = 0x0BADCAFE;
1964 -#endif /* NETDATA_INTERNAL_CHECKS */
1965 -
1966 - return (struct file_descriptor *)avl_search(&all_files_index, (avl_t *) &tmp);
1967 -}
1968 -
1969 -#define file_descriptor_add(fd) avl_insert(&all_files_index, (avl_t *)(fd))
1970 -#define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl_t *)(fd))
1971 -
1972 -// ----------------------------------------------------------------------------
1973 -
1974 -static inline void file_descriptor_not_used(int id)
1975 -{
1976 - if(id > 0 && id < all_files_size) {
1977 -
1978 -#ifdef NETDATA_INTERNAL_CHECKS
1979 - if(all_files[id].magic != 0x0BADCAFE) {
1980 - netdata_log_error("Ignoring request to remove empty file id %d.", id);
1981 - return;
1982 - }
1983 -#endif /* NETDATA_INTERNAL_CHECKS */
1984 -
1985 - debug_log("decreasing slot %d (count = %d).", id, all_files[id].count);
1986 -
1987 - if(all_files[id].count > 0) {
1988 - all_files[id].count--;
1989 -
1990 - if(!all_files[id].count) {
1991 - debug_log(" >> slot %d is empty.", id);
1992 -
1993 - if(unlikely(file_descriptor_remove(&all_files[id]) != (void *)&all_files[id]))
1994 - netdata_log_error("INTERNAL ERROR: removal of unused fd from index, removed a different fd");
1995 -
1996 -#ifdef NETDATA_INTERNAL_CHECKS
1997 - all_files[id].magic = 0x00000000;
1998 -#endif /* NETDATA_INTERNAL_CHECKS */
1999 - all_files_len--;
2000 - }
2001 - }
2002 - else
2003 - netdata_log_error("Request to decrease counter of fd %d (%s), while the use counter is 0",
2004 - id,
2005 - all_files[id].name);
2006 - }
2007 - else
2008 - netdata_log_error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)",
2009 - id,
2010 - all_files_size);
2011 -}
2012 -
2013 -static inline void all_files_grow() {
2014 - void *old = all_files;
2015 - int i;
2016 -
2017 - // there is no empty slot
2018 - debug_log("extending fd array to %d entries", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
2019 -
2020 - all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
2021 -
2022 - // if the address changed, we have to rebuild the index
2023 - // since all pointers are now invalid
2024 -
2025 - if(unlikely(old && old != (void *)all_files)) {
2026 - debug_log(" >> re-indexing.");
2027 -
2028 - all_files_index.root = NULL;
2029 - for(i = 0; i < all_files_size; i++) {
2030 - if(!all_files[i].count) continue;
2031 - if(unlikely(file_descriptor_add(&all_files[i]) != (void *)&all_files[i]))
2032 - netdata_log_error("INTERNAL ERROR: duplicate indexing of fd during realloc.");
2033 - }
2034 -
2035 - debug_log(" >> re-indexing done.");
2036 - }
2037 -
2038 - // initialize the newly added entries
2039 -
2040 - for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
2041 - all_files[i].count = 0;
2042 - all_files[i].name = NULL;
2043 -#ifdef NETDATA_INTERNAL_CHECKS
2044 - all_files[i].magic = 0x00000000;
2045 -#endif /* NETDATA_INTERNAL_CHECKS */
2046 - all_files[i].pos = i;
2047 - }
2048 -
2049 - if(unlikely(!all_files_size)) all_files_len = 1;
2050 - all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
2051 -}
2052 -
2053 -static inline int file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, FD_FILETYPE type) {
2054 - // check we have enough memory to add it
2055 - if(!all_files || all_files_len == all_files_size)
2056 - all_files_grow();
2057 -
2058 - debug_log(" >> searching for empty slot.");
2059 -
2060 - // search for an empty slot
2061 -
2062 - static int last_pos = 0;
2063 - int i, c;
2064 - for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
2065 - if(c >= all_files_size) c = 0;
2066 - if(c == 0) continue;
2067 -
2068 - if(!all_files[c].count) {
2069 - debug_log(" >> Examining slot %d.", c);
2070 -
2071 -#ifdef NETDATA_INTERNAL_CHECKS
2072 - if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
2073 - netdata_log_error("fd on position %d is not cleared properly. It still has %s in it.", c, all_files[c].name);
2074 -#endif /* NETDATA_INTERNAL_CHECKS */
2075 -
2076 - debug_log(" >> %s fd position %d for %s (last name: %s)", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
2077 -
2078 - freez((void *)all_files[c].name);
2079 - all_files[c].name = NULL;
2080 - last_pos = c;
2081 - break;
2082 - }
2083 - }
2084 -
2085 - all_files_len++;
2086 -
2087 - if(i == all_files_size) {
2088 - fatal("We should find an empty slot, but there isn't any");
2089 - exit(1);
2090 - }
2091 - // else we have an empty slot in 'c'
2092 -
2093 - debug_log(" >> updating slot %d.", c);
2094 -
2095 - all_files[c].name = strdupz(name);
2096 - all_files[c].hash = hash;
2097 - all_files[c].type = type;
2098 - all_files[c].pos = c;
2099 - all_files[c].count = 1;
2100 -#ifdef NETDATA_INTERNAL_CHECKS
2101 - all_files[c].magic = 0x0BADCAFE;
2102 -#endif /* NETDATA_INTERNAL_CHECKS */
2103 - if(unlikely(file_descriptor_add(&all_files[c]) != (void *)&all_files[c]))
2104 - netdata_log_error("INTERNAL ERROR: duplicate indexing of fd.");
2105 -
2106 - debug_log("using fd position %d (name: %s)", c, all_files[c].name);
2107 -
2108 - return c;
2109 -}
2110 -
2111 -static inline int file_descriptor_find_or_add(const char *name, uint32_t hash) {
2112 - if(unlikely(!hash))
2113 - hash = simple_hash(name);
2114 -
2115 - debug_log("adding or finding name '%s' with hash %u", name, hash);
2116 -
2117 - struct file_descriptor *fd = file_descriptor_find(name, hash);
2118 - if(fd) {
2119 - // found
2120 - debug_log(" >> found on slot %d", fd->pos);
2121 -
2122 - fd->count++;
2123 - return fd->pos;
2124 - }
2125 - // not found
2126 -
2127 - FD_FILETYPE type;
2128 - if(likely(name[0] == '/')) type = FILETYPE_FILE;
2129 - else if(likely(strncmp(name, "pipe:", 5) == 0)) type = FILETYPE_PIPE;
2130 - else if(likely(strncmp(name, "socket:", 7) == 0)) type = FILETYPE_SOCKET;
2131 - else if(likely(strncmp(name, "anon_inode:", 11) == 0)) {
2132 - const char *t = &name[11];
2133 -
2134 - if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY;
2135 - else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD;
2136 - else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
2137 - else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD;
2138 - else if(strcmp(t, "[signalfd]") == 0) type = FILETYPE_SIGNALFD;
2139 - else {
2140 - debug_log("UNKNOWN anonymous inode: %s", name);
2141 - type = FILETYPE_OTHER;
2142 - }
2143 - }
2144 - else if(likely(strcmp(name, "inotify") == 0)) type = FILETYPE_INOTIFY;
2145 - else {
2146 - debug_log("UNKNOWN linkname: %s", name);
2147 - type = FILETYPE_OTHER;
2148 - }
2149 -
2150 - return file_descriptor_set_on_empty_slot(name, hash, type);
2151 -}
2152 -
2153 -static inline void clear_pid_fd(struct pid_fd *pfd) {
2154 - pfd->fd = 0;
2155 -
2156 - #ifndef __FreeBSD__
2157 - pfd->link_hash = 0;
2158 - pfd->inode = 0;
2159 - pfd->cache_iterations_counter = 0;
2160 - pfd->cache_iterations_reset = 0;
2161 -#endif
2162 -}
2163 -
2164 -static inline void make_all_pid_fds_negative(struct pid_stat *p) {
2165 - struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
2166 - while(pfd < pfdend) {
2167 - pfd->fd = -(pfd->fd);
2168 - pfd++;
2169 - }
2170 -}
2171 -
2172 -static inline void cleanup_negative_pid_fds(struct pid_stat *p) {
2173 - struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
2174 -
2175 - while(pfd < pfdend) {
2176 - int fd = pfd->fd;
2177 -
2178 - if(unlikely(fd < 0)) {
2179 - file_descriptor_not_used(-(fd));
2180 - clear_pid_fd(pfd);
2181 - }
2182 -
2183 - pfd++;
2184 - }
2185 -}
2186 -
2187 -static inline void init_pid_fds(struct pid_stat *p, size_t first, size_t size) {
2188 - struct pid_fd *pfd = &p->fds[first], *pfdend = &p->fds[first + size];
2189 -
2190 - while(pfd < pfdend) {
2191 -#ifndef __FreeBSD__
2192 - pfd->filename = NULL;
2193 -#endif
2194 - clear_pid_fd(pfd);
2195 - pfd++;
2196 - }
2197 -}
2198 -
2199 -static inline int read_pid_file_descriptors(struct pid_stat *p, void *ptr) {
2200 - (void)ptr;
2201 -#ifdef __FreeBSD__
2202 - int mib[4];
2203 - size_t size;
2204 - struct kinfo_file *fds;
2205 - static char *fdsbuf;
2206 - char *bfdsbuf, *efdsbuf;
2207 - char fdsname[FILENAME_MAX + 1];
2208 -#define SHM_FORMAT_LEN 31 // format: 21 + size: 10
2209 - char shm_name[FILENAME_MAX - SHM_FORMAT_LEN + 1];
2210 -
2211 - // we make all pid fds negative, so that
2212 - // we can detect unused file descriptors
2213 - // at the end, to free them
2214 - make_all_pid_fds_negative(p);
2215 -
2216 - mib[0] = CTL_KERN;
2217 - mib[1] = KERN_PROC;
2218 - mib[2] = KERN_PROC_FILEDESC;
2219 - mib[3] = p->pid;
2220 -
2221 - if (unlikely(sysctl(mib, 4, NULL, &size, NULL, 0))) {
2222 - netdata_log_error("sysctl error: Can't get file descriptors data size for pid %d", p->pid);
2223 - return 0;
2224 - }
2225 - if (likely(size > 0))
2226 - fdsbuf = reallocz(fdsbuf, size);
2227 - if (unlikely(sysctl(mib, 4, fdsbuf, &size, NULL, 0))) {
2228 - netdata_log_error("sysctl error: Can't get file descriptors data for pid %d", p->pid);
2229 - return 0;
2230 - }
2231 -
2232 - bfdsbuf = fdsbuf;
2233 - efdsbuf = fdsbuf + size;
2234 - while (bfdsbuf < efdsbuf) {
2235 - fds = (struct kinfo_file *)(uintptr_t)bfdsbuf;
2236 - if (unlikely(fds->kf_structsize == 0))
2237 - break;
2238 -
2239 - // do not process file descriptors for current working directory, root directory,
2240 - // jail directory, ktrace vnode, text vnode and controlling terminal
2241 - if (unlikely(fds->kf_fd < 0)) {
2242 - bfdsbuf += fds->kf_structsize;
2243 - continue;
2244 - }
2245 -
2246 - // get file descriptors array index
2247 - size_t fdid = fds->kf_fd;
2248 -
2249 - // check if the fds array is small
2250 - if (unlikely(fdid >= p->fds_size)) {
2251 - // it is small, extend it
2252 -
2253 - debug_log("extending fd memory slots for %s from %d to %d", p->comm, p->fds_size, fdid + MAX_SPARE_FDS);
2254 -
2255 - p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(struct pid_fd));
2256 -
2257 - // and initialize it
2258 - init_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size);
2259 - p->fds_size = fdid + MAX_SPARE_FDS;
2260 - }
2261 -
2262 - if (unlikely(p->fds[fdid].fd == 0)) {
2263 - // we don't know this fd, get it
2264 -
2265 - switch (fds->kf_type) {
2266 - case KF_TYPE_FIFO:
2267 - case KF_TYPE_VNODE:
2268 - if (unlikely(!fds->kf_path[0])) {
2269 - sprintf(fdsname, "other: inode: %lu", fds->kf_un.kf_file.kf_file_fileid);
2270 - break;
2271 - }
2272 - sprintf(fdsname, "%s", fds->kf_path);
2273 - break;
2274 - case KF_TYPE_SOCKET:
2275 - switch (fds->kf_sock_domain) {
2276 - case AF_INET:
2277 - case AF_INET6:
2278 - if (fds->kf_sock_protocol == IPPROTO_TCP)
2279 - sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_inpcb);
2280 - else
2281 - sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_pcb);
2282 - break;
2283 - case AF_UNIX:
2284 - /* print address of pcb and connected pcb */
2285 - sprintf(fdsname, "socket: %lx %lx", fds->kf_un.kf_sock.kf_sock_pcb, fds->kf_un.kf_sock.kf_sock_unpconn);
2286 - break;
2287 - default:
2288 - /* print protocol number and socket address */
2289 -#if __FreeBSD_version < 1200031
2290 - sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_sa_local.__ss_pad1, fds->kf_sa_local.__ss_pad2);
2291 -#else
2292 - sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sa_local.__ss_pad1, fds->kf_un.kf_sock.kf_sa_local.__ss_pad2);
2293 -#endif
2294 - }
2295 - break;
2296 - case KF_TYPE_PIPE:
2297 - sprintf(fdsname, "pipe: %lu %lu", fds->kf_un.kf_pipe.kf_pipe_addr, fds->kf_un.kf_pipe.kf_pipe_peer);
2298 - break;
2299 - case KF_TYPE_PTS:
2300 -#if __FreeBSD_version < 1200031
2301 - sprintf(fdsname, "other: pts: %u", fds->kf_un.kf_pts.kf_pts_dev);
2302 -#else
2303 - sprintf(fdsname, "other: pts: %lu", fds->kf_un.kf_pts.kf_pts_dev);
2304 -#endif
2305 - break;
2306 - case KF_TYPE_SHM:
2307 - strncpyz(shm_name, fds->kf_path, FILENAME_MAX - SHM_FORMAT_LEN);
2308 - sprintf(fdsname, "other: shm: %s size: %lu", shm_name, fds->kf_un.kf_file.kf_file_size);
2309 - break;
2310 - case KF_TYPE_SEM:
2311 - sprintf(fdsname, "other: sem: %u", fds->kf_un.kf_sem.kf_sem_value);
2312 - break;
2313 - default:
2314 - sprintf(fdsname, "other: pid: %d fd: %d", fds->kf_un.kf_proc.kf_pid, fds->kf_fd);
2315 - }
2316 -
2317 - // if another process already has this, we will get
2318 - // the same id
2319 - p->fds[fdid].fd = file_descriptor_find_or_add(fdsname, 0);
2320 - }
2321 -
2322 - // else make it positive again, we need it
2323 - // of course, the actual file may have changed
2324 -
2325 - else
2326 - p->fds[fdid].fd = -p->fds[fdid].fd;
2327 -
2328 - bfdsbuf += fds->kf_structsize;
2329 - }
2330 -#else
2331 - if(unlikely(!p->fds_dirname)) {
2332 - char dirname[FILENAME_MAX+1];
2333 - snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", netdata_configured_host_prefix, p->pid);
2334 - p->fds_dirname = strdupz(dirname);
2335 - }
2336 -
2337 - DIR *fds = opendir(p->fds_dirname);
2338 - if(unlikely(!fds)) return 0;
2339 -
2340 - struct dirent *de;
2341 - char linkname[FILENAME_MAX + 1];
2342 -
2343 - // we make all pid fds negative, so that
2344 - // we can detect unused file descriptors
2345 - // at the end, to free them
2346 - make_all_pid_fds_negative(p);
2347 -
2348 - while((de = readdir(fds))) {
2349 - // we need only files with numeric names
2350 -
2351 - if(unlikely(de->d_name[0] < '0' || de->d_name[0] > '9'))
2352 - continue;
2353 -
2354 - // get its number
2355 - int fdid = (int) str2l(de->d_name);
2356 - if(unlikely(fdid < 0)) continue;
2357 -
2358 - // check if the fds array is small
2359 - if(unlikely((size_t)fdid >= p->fds_size)) {
2360 - // it is small, extend it
2361 -
2362 - debug_log("extending fd memory slots for %s from %d to %d"
2363 - , p->comm
2364 - , p->fds_size
2365 - , fdid + MAX_SPARE_FDS
2366 - );
2367 -
2368 - p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(struct pid_fd));
2369 -
2370 - // and initialize it
2371 - init_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size);
2372 - p->fds_size = (size_t)fdid + MAX_SPARE_FDS;
2373 - }
2374 -
2375 - if(unlikely(p->fds[fdid].fd < 0 && de->d_ino != p->fds[fdid].inode)) {
2376 - // inodes do not match, clear the previous entry
2377 - inodes_changed_counter++;
2378 - file_descriptor_not_used(-p->fds[fdid].fd);
2379 - clear_pid_fd(&p->fds[fdid]);
2380 - }
2381 -
2382 - if(p->fds[fdid].fd < 0 && p->fds[fdid].cache_iterations_counter > 0) {
2383 - p->fds[fdid].fd = -p->fds[fdid].fd;
2384 - p->fds[fdid].cache_iterations_counter--;
2385 - continue;
2386 - }
2387 -
2388 - if(unlikely(!p->fds[fdid].filename)) {
2389 - filenames_allocated_counter++;
2390 - char fdname[FILENAME_MAX + 1];
2391 - snprintfz(fdname, FILENAME_MAX, "%s/proc/%d/fd/%s", netdata_configured_host_prefix, p->pid, de->d_name);
2392 - p->fds[fdid].filename = strdupz(fdname);
2393 - }
2394 -
2395 - file_counter++;
2396 - ssize_t l = readlink(p->fds[fdid].filename, linkname, FILENAME_MAX);
2397 - if(unlikely(l == -1)) {
2398 - // cannot read the link
2399 -
2400 - if(debug_enabled || (p->target && p->target->debug_enabled))
2401 - netdata_log_error("Cannot read link %s", p->fds[fdid].filename);
2402 -
2403 - if(unlikely(p->fds[fdid].fd < 0)) {
2404 - file_descriptor_not_used(-p->fds[fdid].fd);
2405 - clear_pid_fd(&p->fds[fdid]);
2406 - }
2407 -
2408 - continue;
2409 - }
2410 - else
2411 - linkname[l] = '\0';
2412 -
2413 - uint32_t link_hash = simple_hash(linkname);
2414 -
2415 - if(unlikely(p->fds[fdid].fd < 0 && p->fds[fdid].link_hash != link_hash)) {
2416 - // the link changed
2417 - links_changed_counter++;
2418 - file_descriptor_not_used(-p->fds[fdid].fd);
2419 - clear_pid_fd(&p->fds[fdid]);
2420 - }
2421 -
2422 - if(unlikely(p->fds[fdid].fd == 0)) {
2423 - // we don't know this fd, get it
2424 -
2425 - // if another process already has this, we will get
2426 - // the same id
2427 - p->fds[fdid].fd = file_descriptor_find_or_add(linkname, link_hash);
2428 - p->fds[fdid].inode = de->d_ino;
2429 - p->fds[fdid].link_hash = link_hash;
2430 - }
2431 - else {
2432 - // else make it positive again, we need it
2433 - p->fds[fdid].fd = -p->fds[fdid].fd;
2434 - }
2435 -
2436 - // caching control
2437 - // without this we read all the files on every iteration
2438 - if(max_fds_cache_seconds > 0) {
2439 - size_t spread = ((size_t)max_fds_cache_seconds > 10) ? 10 : (size_t)max_fds_cache_seconds;
2440 -
2441 - // cache it for a few iterations
2442 - size_t max = ((size_t) max_fds_cache_seconds + (fdid % spread)) / (size_t) update_every;
2443 - p->fds[fdid].cache_iterations_reset++;
2444 -
2445 - if(unlikely(p->fds[fdid].cache_iterations_reset % spread == (size_t) fdid % spread))
2446 - p->fds[fdid].cache_iterations_reset++;
2447 -
2448 - if(unlikely((fdid <= 2 && p->fds[fdid].cache_iterations_reset > 5) ||
2449 - p->fds[fdid].cache_iterations_reset > max)) {
2450 - // for stdin, stdout, stderr (fdid <= 2) we have checked a few times, or if it goes above the max, goto max
2451 - p->fds[fdid].cache_iterations_reset = max;
2452 - }
2453 -
2454 - p->fds[fdid].cache_iterations_counter = p->fds[fdid].cache_iterations_reset;
2455 - }
2456 - }
2457 -
2458 - closedir(fds);
2459 -#endif
2460 - cleanup_negative_pid_fds(p);
2461 -
2462 - return 1;
2463 -}
2464 -
2465 -// ----------------------------------------------------------------------------
2466 -
2467 -static inline int debug_print_process_and_parents(struct pid_stat *p, usec_t time) {
2468 - char *prefix = "\\_ ";
2469 - int indent = 0;
2470 -
2471 - if(p->parent)
2472 - indent = debug_print_process_and_parents(p->parent, p->stat_collected_usec);
2473 - else
2474 - prefix = " > ";
2475 -
2476 - char buffer[indent + 1];
2477 - int i;
2478 -
2479 - for(i = 0; i < indent ;i++) buffer[i] = ' ';
2480 - buffer[i] = '\0';
2481 -
2482 - fprintf(stderr, " %s %s%s (%d %s %"PRIu64""
2483 - , buffer
2484 - , prefix
2485 - , p->comm
2486 - , p->pid
2487 - , p->updated?"running":"exited"
2488 - , p->stat_collected_usec - time
2489 - );
2490 -
2491 - if(p->utime) fprintf(stderr, " utime=" KERNEL_UINT_FORMAT, p->utime);
2492 - if(p->stime) fprintf(stderr, " stime=" KERNEL_UINT_FORMAT, p->stime);
2493 - if(p->gtime) fprintf(stderr, " gtime=" KERNEL_UINT_FORMAT, p->gtime);
2494 - if(p->cutime) fprintf(stderr, " cutime=" KERNEL_UINT_FORMAT, p->cutime);
2495 - if(p->cstime) fprintf(stderr, " cstime=" KERNEL_UINT_FORMAT, p->cstime);
2496 - if(p->cgtime) fprintf(stderr, " cgtime=" KERNEL_UINT_FORMAT, p->cgtime);
2497 - if(p->minflt) fprintf(stderr, " minflt=" KERNEL_UINT_FORMAT, p->minflt);
2498 - if(p->cminflt) fprintf(stderr, " cminflt=" KERNEL_UINT_FORMAT, p->cminflt);
2499 - if(p->majflt) fprintf(stderr, " majflt=" KERNEL_UINT_FORMAT, p->majflt);
2500 - if(p->cmajflt) fprintf(stderr, " cmajflt=" KERNEL_UINT_FORMAT, p->cmajflt);
2501 - fprintf(stderr, ")\n");
2502 -
2503 - return indent + 1;
2504 -}
2505 -
2506 -static inline void debug_print_process_tree(struct pid_stat *p, char *msg __maybe_unused) {
2507 - debug_log("%s: process %s (%d, %s) with parents:", msg, p->comm, p->pid, p->updated?"running":"exited");
2508 - debug_print_process_and_parents(p, p->stat_collected_usec);
2509 -}
2510 -
2511 -static inline void debug_find_lost_child(struct pid_stat *pe, kernel_uint_t lost, int type) {
2512 - int found = 0;
2513 - struct pid_stat *p = NULL;
2514 -
2515 - for(p = root_of_pids; p ; p = p->next) {
2516 - if(p == pe) continue;
2517 -
2518 - switch(type) {
2519 - case 1:
2520 - if(p->cminflt > lost) {
2521 - fprintf(stderr, " > process %d (%s) could use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
2522 - found++;
2523 - }
2524 - break;
2525 -
2526 - case 2:
2527 - if(p->cmajflt > lost) {
2528 - fprintf(stderr, " > process %d (%s) could use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
2529 - found++;
2530 - }
2531 - break;
2532 -
2533 - case 3:
2534 - if(p->cutime > lost) {
2535 - fprintf(stderr, " > process %d (%s) could use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
2536 - found++;
2537 - }
2538 - break;
2539 -
2540 - case 4:
2541 - if(p->cstime > lost) {
2542 - fprintf(stderr, " > process %d (%s) could use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
2543 - found++;
2544 - }
2545 - break;
2546 -
2547 - case 5:
2548 - if(p->cgtime > lost) {
2549 - fprintf(stderr, " > process %d (%s) could use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
2550 - found++;
2551 - }
2552 - break;
2553 - }
2554 - }
2555 -
2556 - if(!found) {
2557 - switch(type) {
2558 - case 1:
2559 - fprintf(stderr, " > cannot find any process to use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
2560 - break;
2561 -
2562 - case 2:
2563 - fprintf(stderr, " > cannot find any process to use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
2564 - break;
2565 -
2566 - case 3:
2567 - fprintf(stderr, " > cannot find any process to use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
2568 - break;
2569 -
2570 - case 4:
2571 - fprintf(stderr, " > cannot find any process to use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
2572 - break;
2573 -
2574 - case 5:
2575 - fprintf(stderr, " > cannot find any process to use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
2576 - break;
2577 - }
2578 - }
2579 -}
2580 -
2581 -static inline kernel_uint_t remove_exited_child_from_parent(kernel_uint_t *field, kernel_uint_t *pfield) {
2582 - kernel_uint_t absorbed = 0;
2583 -
2584 - if(*field > *pfield) {
2585 - absorbed += *pfield;
2586 - *field -= *pfield;
2587 - *pfield = 0;
2588 - }
2589 - else {
2590 - absorbed += *field;
2591 - *pfield -= *field;
2592 - *field = 0;
2593 - }
2594 -
2595 - return absorbed;
2596 -}
2597 -
2598 -static inline void process_exited_processes() {
2599 - struct pid_stat *p;
2600 -
2601 - for(p = root_of_pids; p ; p = p->next) {
2602 - if(p->updated || !p->stat_collected_usec)
2603 - continue;
2604 -
2605 - kernel_uint_t utime = (p->utime_raw + p->cutime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
2606 - kernel_uint_t stime = (p->stime_raw + p->cstime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
2607 - kernel_uint_t gtime = (p->gtime_raw + p->cgtime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
2608 - kernel_uint_t minflt = (p->minflt_raw + p->cminflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
2609 - kernel_uint_t majflt = (p->majflt_raw + p->cmajflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
2610 -
2611 - if(utime + stime + gtime + minflt + majflt == 0)
2612 - continue;
2613 -
2614 - if(unlikely(debug_enabled)) {
2615 - debug_log("Absorb %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")"
2616 - , p->comm
2617 - , p->pid
2618 - , p->updated?"running":"exited"
2619 - , utime
2620 - , stime
2621 - , gtime
2622 - , minflt
2623 - , majflt
2624 - );
2625 - debug_print_process_tree(p, "Searching parents");
2626 - }
2627 -
2628 - struct pid_stat *pp;
2629 - for(pp = p->parent; pp ; pp = pp->parent) {
2630 - if(!pp->updated) continue;
2631 -
2632 - kernel_uint_t absorbed;
2633 - absorbed = remove_exited_child_from_parent(&utime, &pp->cutime);
2634 - if(unlikely(debug_enabled && absorbed))
2635 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " utime (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, utime);
2636 -
2637 - absorbed = remove_exited_child_from_parent(&stime, &pp->cstime);
2638 - if(unlikely(debug_enabled && absorbed))
2639 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " stime (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, stime);
2640 -
2641 - absorbed = remove_exited_child_from_parent(&gtime, &pp->cgtime);
2642 - if(unlikely(debug_enabled && absorbed))
2643 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " gtime (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, gtime);
2644 -
2645 - absorbed = remove_exited_child_from_parent(&minflt, &pp->cminflt);
2646 - if(unlikely(debug_enabled && absorbed))
2647 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " minflt (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, minflt);
2648 -
2649 - absorbed = remove_exited_child_from_parent(&majflt, &pp->cmajflt);
2650 - if(unlikely(debug_enabled && absorbed))
2651 - debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " majflt (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, majflt);
2652 - }
2653 -
2654 - if(unlikely(utime + stime + gtime + minflt + majflt > 0)) {
2655 - if(unlikely(debug_enabled)) {
2656 - if(utime) debug_find_lost_child(p, utime, 3);
2657 - if(stime) debug_find_lost_child(p, stime, 4);
2658 - if(gtime) debug_find_lost_child(p, gtime, 5);
2659 - if(minflt) debug_find_lost_child(p, minflt, 1);
2660 - if(majflt) debug_find_lost_child(p, majflt, 2);
2661 - }
2662 -
2663 - p->keep = true;
2664 -
2665 - debug_log(" > remaining resources - KEEP - for another loop: %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")"
2666 - , p->comm
2667 - , p->pid
2668 - , p->updated?"running":"exited"
2669 - , utime
2670 - , stime
2671 - , gtime
2672 - , minflt
2673 - , majflt
2674 - );
2675 -
2676 - for(pp = p->parent; pp ; pp = pp->parent) {
2677 - if(pp->updated) break;
2678 - pp->keep = true;
2679 -
2680 - debug_log(" > - KEEP - parent for another loop: %s (%d %s)"
2681 - , pp->comm
2682 - , pp->pid
2683 - , pp->updated?"running":"exited"
2684 - );
2685 - }
2686 -
2687 - p->utime_raw = utime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
2688 - p->stime_raw = stime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
2689 - p->gtime_raw = gtime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
2690 - p->minflt_raw = minflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
2691 - p->majflt_raw = majflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
2692 - p->cutime_raw = p->cstime_raw = p->cgtime_raw = p->cminflt_raw = p->cmajflt_raw = 0;
2693 -
2694 - debug_log(" ");
2695 - }
2696 - else
2697 - debug_log(" > totally absorbed - DONE - %s (%d %s)"
2698 - , p->comm
2699 - , p->pid
2700 - , p->updated?"running":"exited"
2701 - );
2702 - }
2703 -}
2704 -
2705 -static inline void link_all_processes_to_their_parents(void) {
2706 - struct pid_stat *p, *pp;
2707 -
2708 - // link all children to their parents
2709 - // and update children count on parents
2710 - for(p = root_of_pids; p ; p = p->next) {
2711 - // for each process found
2712 -
2713 - p->sortlist = 0;
2714 - p->parent = NULL;
2715 -
2716 - if(unlikely(!p->ppid)) {
2717 - //unnecessary code from apps_plugin.c
2718 - //p->parent = NULL;
2719 - continue;
2720 - }
2721 -
2722 - pp = all_pids[p->ppid];
2723 - if(likely(pp)) {
2724 - p->parent = pp;
2725 - pp->children_count++;
12 +#define APPS_PLUGIN_FUNCTIONS() do { \
13 + fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " \"processes\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n", \
14 + PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION, \
15 + (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID|HTTP_ACCESS_SAME_SPACE|HTTP_ACCESS_SENSITIVE_DATA), \
16 + RRDFUNCTIONS_PRIORITY_DEFAULT / 10); \
17 +} while(0)
18
2727 - if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
2728 - debug_log_int("child %d (%s, %s) on target '%s' has parent %d (%s, %s). Parent: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", gtime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", cgtime=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT "", p->pid, p->comm, p->updated?"running":"exited", (p->target)?p->target->name:"UNSET", pp->pid, pp->comm, pp->updated?"running":"exited", pp->utime, pp->stime, pp->gtime, pp->minflt, pp->majflt, pp->cutime, pp->cstime, pp->cgtime, pp->cminflt, pp->cmajflt);
2729 - }
2730 - else {
2731 - p->parent = NULL;
2732 - netdata_log_error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
2733 - }
2734 - }
2735 -}
19 +#define APPS_PLUGIN_GLOBAL_FUNCTIONS() do { \
20 + fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"processes\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n", \
21 + PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION, \
22 + (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID|HTTP_ACCESS_SAME_SPACE|HTTP_ACCESS_SENSITIVE_DATA), \
23 + RRDFUNCTIONS_PRIORITY_DEFAULT / 10); \
24 +} while(0)
25
26 // ----------------------------------------------------------------------------
2738 -
2739 -// 1. read all files in /proc
2740 -// 2. for each numeric directory:
2741 -// i. read /proc/pid/stat
2742 -// ii. read /proc/pid/status
2743 -// iii. read /proc/pid/io (requires root access)
2744 -// iii. read the entries in directory /proc/pid/fd (requires root access)
2745 -// for each entry:
2746 -// a. find or create a struct file_descriptor
2747 -// b. cleanup any old/unused file_descriptors
2748 -
2749 -// after all these, some pids may be linked to targets, while others may not
2750 -
2751 -// in case of errors, only 1 every 1000 errors is printed
2752 -// to avoid filling up all disk space
2753 -// if debug is enabled, all errors are printed
2754 -
2755 -#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
2756 -static int compar_pid(const void *pid1, const void *pid2) {
2757 -
2758 - struct pid_stat *p1 = all_pids[*((pid_t *)pid1)];
2759 - struct pid_stat *p2 = all_pids[*((pid_t *)pid2)];
2760 -
2761 - if(p1->sortlist > p2->sortlist)
2762 - return -1;
2763 - else
2764 - return 1;
2765 -}
27 +// options
28 +
29 +bool debug_enabled = false;
30 +bool enable_guest_charts = false;
31 +bool enable_detailed_uptime_charts = false;
32 +bool enable_users_charts = true;
33 +bool enable_groups_charts = true;
34 +bool include_exited_childs = true;
35 +bool proc_pid_cmdline_is_needed = false; // true when we need to read /proc/cmdline
36 +
37 +#if defined(__FreeBSD__) || defined(__APPLE__)
38 +bool enable_file_charts = false;
39 +#else
40 +bool enable_file_charts = true;
41 #endif
42
2768 -static inline int collect_data_for_pid(pid_t pid, void *ptr) {
2769 - if(unlikely(pid < 0 || pid > pid_max)) {
2770 - netdata_log_error("Invalid pid %d read (expected %d to %d). Ignoring process.", pid, 0, pid_max);
2771 - return 0;
2772 - }
2773 -
2774 - struct pid_stat *p = get_pid_entry(pid);
2775 - if(unlikely(!p || p->read)) return 0;
2776 - p->read = true;
2777 -
2778 - // debug_log("Reading process %d (%s), sortlist %d", p->pid, p->comm, p->sortlist);
2779 -
2780 - // --------------------------------------------------------------------
2781 - // /proc/<pid>/stat
2782 -
2783 - if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p, ptr))))
2784 - // there is no reason to proceed if we cannot get its status
2785 - return 0;
2786 -
2787 - // check its parent pid
2788 - if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
2789 - netdata_log_error("Pid %d (command '%s') states invalid parent pid %d. Using 0.", pid, p->comm, p->ppid);
2790 - p->ppid = 0;
2791 - }
2792 -
2793 - // --------------------------------------------------------------------
2794 - // /proc/<pid>/io
2795 -
2796 - managed_log(p, PID_LOG_IO, read_proc_pid_io(p, ptr));
2797 -
2798 - // --------------------------------------------------------------------
2799 - // /proc/<pid>/status
2800 -
2801 - if(unlikely(!managed_log(p, PID_LOG_STATUS, read_proc_pid_status(p, ptr))))
2802 - // there is no reason to proceed if we cannot get its status
2803 - return 0;
2804 -
2805 - // --------------------------------------------------------------------
2806 - // /proc/<pid>/fd
2807 -
2808 - if(enable_file_charts) {
2809 - managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p, ptr));
2810 - managed_log(p, PID_LOG_LIMITS, read_proc_pid_limits(p, ptr));
2811 - }
2812 -
2813 - // --------------------------------------------------------------------
2814 - // done!
2815 -
2816 - if(unlikely(debug_enabled && include_exited_childs && all_pids_count && p->ppid && all_pids[p->ppid] && !all_pids[p->ppid]->read))
2817 - debug_log("Read process %d (%s) sortlisted %d, but its parent %d (%s) sortlisted %d, is not read", p->pid, p->comm, p->sortlist, all_pids[p->ppid]->pid, all_pids[p->ppid]->comm, all_pids[p->ppid]->sortlist);
2818 -
2819 - // mark it as updated
2820 - p->updated = true;
2821 - p->keep = false;
2822 - p->keeploops = 0;
2823 -
2824 - return 1;
2825 -}
2826 -
2827 -static int collect_data_for_all_processes(void) {
2828 - struct pid_stat *p = NULL;
43 +// ----------------------------------------------------------------------------
44 +// internal counters
45
2830 -#ifndef __FreeBSD__
2831 - // clear process state counter
2832 - memset(proc_state_count, 0, sizeof proc_state_count);
46 +size_t
47 + global_iterations_counter = 1,
48 + calls_counter = 0,
49 + file_counter = 0,
50 + filenames_allocated_counter = 0,
51 + inodes_changed_counter = 0,
52 + links_changed_counter = 0,
53 + targets_assignment_counter = 0,
54 + all_pids_count = 0, // the number of processes running
55 + apps_groups_targets_count = 0; // # of apps_groups.conf targets
56 +
57 +int
58 + all_files_len = 0,
59 + all_files_size = 0,
60 + show_guest_time = 0, // 1 when guest values are collected
61 + show_guest_time_old = 0;
62 +
63 +#if defined(__FreeBSD__) || defined(__APPLE__)
64 +usec_t system_current_time_ut;
65 #else
2834 - int i, procnum;
2835 -
2836 - static size_t procbase_size = 0;
2837 - static struct kinfo_proc *procbase = NULL;
2838 -
2839 - size_t new_procbase_size;
66 +kernel_uint_t system_uptime_secs;
67 +#endif
68
2841 - int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC };
2842 - if (unlikely(sysctl(mib, 3, NULL, &new_procbase_size, NULL, 0))) {
2843 - netdata_log_error("sysctl error: Can't get processes data size");
2844 - return 0;
2845 - }
69 +// ----------------------------------------------------------------------------
70 +// Normalization
71 +//
72 +// With normalization we lower the collected metrics by a factor to make them
73 +// match the total utilization of the system.
74 +// The discrepancy exists because apps.plugin needs some time to collect all
75 +// the metrics. This results in utilization that exceeds the total utilization
76 +// of the system.
77 +//
78 +// During normalization, we align the per-process utilization, to the total of
79 +// the system. We first consume the exited children utilization and it the
80 +// collected values is above the total, we proportionally scale each reported
81 +// metric.
82
2847 - // give it some air for processes that may be started
2848 - // during this little time.
2849 - new_procbase_size += 100 * sizeof(struct kinfo_proc);
83 +// the total system time, as reported by /proc/stat
84 +kernel_uint_t
85 + global_utime = 0,
86 + global_stime = 0,
87 + global_gtime = 0;
88
2851 - // increase the buffer if needed
2852 - if(new_procbase_size > procbase_size) {
2853 - procbase_size = new_procbase_size;
2854 - procbase = reallocz(procbase, procbase_size);
2855 - }
89 +// the normalization ratios, as calculated by normalize_utilization()
90 +NETDATA_DOUBLE
91 + utime_fix_ratio = 1.0,
92 + stime_fix_ratio = 1.0,
93 + gtime_fix_ratio = 1.0,
94 + minflt_fix_ratio = 1.0,
95 + majflt_fix_ratio = 1.0,
96 + cutime_fix_ratio = 1.0,
97 + cstime_fix_ratio = 1.0,
98 + cgtime_fix_ratio = 1.0,
99 + cminflt_fix_ratio = 1.0,
100 + cmajflt_fix_ratio = 1.0;
101
2857 - // sysctl() gets from new_procbase_size the buffer size
2858 - // and also returns to it the amount of data filled in
2859 - new_procbase_size = procbase_size;
102 +// ----------------------------------------------------------------------------
103 +// factor for calculating correct CPU time values depending on units of raw data
104 +unsigned int time_factor = 0;
105
2861 - // get the processes from the system
2862 - if (unlikely(sysctl(mib, 3, procbase, &new_procbase_size, NULL, 0))) {
2863 - netdata_log_error("sysctl error: Can't get processes data");
2864 - return 0;
2865 - }
106 +// ----------------------------------------------------------------------------
107 +// command line options
108
2867 - // based on the amount of data filled in
2868 - // calculate the number of processes we got
2869 - procnum = new_procbase_size / sizeof(struct kinfo_proc);
109 +int update_every = 1;
110
111 +#if defined(__APPLE__)
112 +mach_timebase_info_data_t mach_info;
113 #endif
114
2873 - if(all_pids_count) {
2874 -#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
2875 - size_t slc = 0;
115 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
116 +int max_fds_cache_seconds = 60;
117 +proc_state proc_state_count[PROC_STATUS_END];
118 +const char *proc_states[] = {
119 + [PROC_STATUS_RUNNING] = "running",
120 + [PROC_STATUS_SLEEPING] = "sleeping_interruptible",
121 + [PROC_STATUS_SLEEPING_D] = "sleeping_uninterruptible",
122 + [PROC_STATUS_ZOMBIE] = "zombie",
123 + [PROC_STATUS_STOPPED] = "stopped",
124 +};
125 #endif
2877 - for(p = root_of_pids; p ; p = p->next) {
2878 - p->read = false; // mark it as not read, so that collect_data_for_pid() will read it
2879 - p->updated = false;
2880 - p->merged = false;
2881 - p->children_count = 0;
2882 - p->parent = NULL;
126
2884 -#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
2885 - all_pids_sortlist[slc++] = p->pid;
2886 -#endif
2887 - }
127 +// will be changed to getenv(NETDATA_USER_CONFIG_DIR) if it exists
128 +static char *user_config_dir = CONFIG_DIR;
129 +static char *stock_config_dir = LIBCONFIG_DIR;
130
2889 -#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
2890 - if(unlikely(slc != all_pids_count)) {
2891 - netdata_log_error("Internal error: I was thinking I had %zu processes in my arrays, but it seems there are %zu.", all_pids_count, slc);
2892 - all_pids_count = slc;
2893 - }
131 +struct target
132 + *apps_groups_default_target = NULL, // the default target
133 + *apps_groups_root_target = NULL, // apps_groups.conf defined
134 + *users_root_target = NULL, // users
135 + *groups_root_target = NULL; // user groups
136
2895 - if(include_exited_childs) {
2896 - // Read parents before childs
2897 - // This is needed to prevent a situation where
2898 - // a child is found running, but until we read
2899 - // its parent, it has exited and its parent
2900 - // has accumulated its resources.
137 +size_t pagesize;
138
2902 - qsort((void *)all_pids_sortlist, (size_t)all_pids_count, sizeof(pid_t), compar_pid);
139 +struct pid_stat
140 + *root_of_pids = NULL, // global list of all processes running
141 + **all_pids = NULL; // to avoid allocations, we pre-allocate
142 + // a pointer for each pid in the entire pid space.
143
2904 - // we forward read all running processes
2905 - // collect_data_for_pid() is smart enough,
2906 - // not to read the same pid twice per iteration
2907 - for(slc = 0; slc < all_pids_count; slc++) {
2908 - collect_data_for_pid(all_pids_sortlist[slc], NULL);
2909 - }
2910 - }
144 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
145 +// Another pre-allocated list of all possible pids.
146 +// We need it to pids and assign them a unique sortlist id, so that we
147 +// read parents before children. This is needed to prevent a situation where
148 +// a child is found running, but until we read its parent, it has exited and
149 +// its parent has accumulated its resources.
150 +pid_t *all_pids_sortlist = NULL;
151 #endif
2912 - }
2913 -
2914 -#ifdef __FreeBSD__
2915 - for (i = 0 ; i < procnum ; ++i) {
2916 - pid_t pid = procbase[i].ki_pid;
2917 - collect_data_for_pid(pid, &procbase[i]);
2918 - }
2919 -#else
2920 - static char uptime_filename[FILENAME_MAX + 1] = "";
2921 - if(*uptime_filename == '\0')
2922 - snprintfz(uptime_filename, FILENAME_MAX, "%s/proc/uptime", netdata_configured_host_prefix);
152
2924 - global_uptime = (kernel_uint_t)(uptime_msec(uptime_filename) / MSEC_PER_SEC);
153 +// ----------------------------------------------------------------------------
154
2926 - char dirname[FILENAME_MAX + 1];
155 +int managed_log(struct pid_stat *p, PID_LOG log, int status) {
156 + if(unlikely(!status)) {
157 + // netdata_log_error("command failed log %u, errno %d", log, errno);
158
2928 - snprintfz(dirname, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
2929 - DIR *dir = opendir(dirname);
2930 - if(!dir) return 0;
159 + if(unlikely(debug_enabled || errno != ENOENT)) {
160 + if(unlikely(debug_enabled || !(p->log_thrown & log))) {
161 + p->log_thrown |= log;
162 + switch(log) {
163 + case PID_LOG_IO:
164 + #if defined(__FreeBSD__) || defined(__APPLE__)
165 + netdata_log_error("Cannot fetch process %d I/O info (command '%s')", p->pid, p->comm);
166 + #else
167 + netdata_log_error("Cannot process %s/proc/%d/io (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
168 + #endif
169 + break;
170
2932 - struct dirent *de = NULL;
171 + case PID_LOG_STATUS:
172 + #if defined(__FreeBSD__) || defined(__APPLE__)
173 + netdata_log_error("Cannot fetch process %d status info (command '%s')", p->pid, p->comm);
174 + #else
175 + netdata_log_error("Cannot process %s/proc/%d/status (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
176 + #endif
177 + break;
178
2934 - while((de = readdir(dir))) {
2935 - char *endptr = de->d_name;
179 + case PID_LOG_CMDLINE:
180 + #if defined(__FreeBSD__) || defined(__APPLE__)
181 + netdata_log_error("Cannot fetch process %d command line (command '%s')", p->pid, p->comm);
182 + #else
183 + netdata_log_error("Cannot process %s/proc/%d/cmdline (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
184 + #endif
185 + break;
186
2937 - if(unlikely(de->d_type != DT_DIR || de->d_name[0] < '0' || de->d_name[0] > '9'))
2938 - continue;
187 + case PID_LOG_FDS:
188 + #if defined(__FreeBSD__) || defined(__APPLE__)
189 + netdata_log_error("Cannot fetch process %d files (command '%s')", p->pid, p->comm);
190 + #else
191 + netdata_log_error("Cannot process entries in %s/proc/%d/fd (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
192 + #endif
193 + break;
194
2940 - pid_t pid = (pid_t) strtoul(de->d_name, &endptr, 10);
195 + case PID_LOG_LIMITS:
196 + #if defined(__FreeBSD__) || defined(__APPLE__)
197 + ;
198 + #else
199 + netdata_log_error("Cannot process %s/proc/%d/limits (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
200 + #endif
201
2942 - // make sure we read a valid number
2943 - if(unlikely(endptr == de->d_name || *endptr != '\0'))
2944 - continue;
202 + case PID_LOG_STAT:
203 + break;
204
2946 - collect_data_for_pid(pid, NULL);
205 + default:
206 + netdata_log_error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
207 + break;
208 + }
209 + }
210 + }
211 + errno = 0;
212 + }
213 + else if(unlikely(p->log_thrown & log)) {
214 + // netdata_log_error("unsetting log %u on pid %d", log, p->pid);
215 + p->log_thrown &= ~log;
216 }
2948 - closedir(dir);
2949 -#endif
2950 -
2951 - if(!all_pids_count)
2952 - return 0;
2953 -
2954 - // we need /proc/stat to normalize the cpu consumption of the exited childs
2955 - read_global_time();
2956 -
2957 - // build the process tree
2958 - link_all_processes_to_their_parents();
217
2960 - // normally this is done
2961 - // however we may have processes exited while we collected values
2962 - // so let's find the exited ones
2963 - // we do this by collecting the ownership of process
2964 - // if we manage to get the ownership, the process still runs
2965 - process_exited_processes();
2966 - return 1;
218 + return status;
219 }
220
221 // ----------------------------------------------------------------------------
222 // update statistics on the targets
223
224 // 1. link all childs to their parents
2973 -// 2. go from bottom to top, marking as merged all childs to their parents
225 +// 2. go from bottom to top, marking as merged all children to their parents,
226 // this step links all parents without a target to the child target, if any
2975 -// 3. link all top level processes (the ones not merged) to the default target
2976 -// 4. go from top to bottom, linking all childs without a target, to their parent target
2977 -// after this step, all processes have a target
227 +// 3. link all top level processes (the ones not merged) to default target
228 +// 4. go from top to bottom, linking all children without a target to their parent target
229 +// after this step all processes have a target.
230 // [5. for each killed pid (updated = 0), remove its usage from its target]
231 // 6. zero all apps_groups_targets
232 // 7. concentrate all values on the apps_groups_targets
@@ -2982,33 +234,6 @@ static int collect_data_for_all_processes(void) {
234 // 9. find the unique file count for each target
235 // check: update_apps_groups_statistics()
236
2985 -static void cleanup_exited_pids(void) {
2986 - size_t c;
2987 - struct pid_stat *p = NULL;
2988 -
2989 - for(p = root_of_pids; p ;) {
2990 - if(!p->updated && (!p->keep || p->keeploops > 0)) {
2991 - if(unlikely(debug_enabled && (p->keep || p->keeploops)))
2992 - debug_log(" > CLEANUP cannot keep exited process %d (%s) anymore - removing it.", p->pid, p->comm);
2993 -
2994 - for(c = 0; c < p->fds_size; c++)
2995 - if(p->fds[c].fd > 0) {
2996 - file_descriptor_not_used(p->fds[c].fd);
2997 - clear_pid_fd(&p->fds[c]);
2998 - }
2999 -
3000 - pid_t r = p->pid;
3001 - p = p->next;
3002 - del_pid_entry(r);
3003 - }
3004 - else {
3005 - if(unlikely(p->keep)) p->keeploops++;
3006 - p->keep = false;
3007 - p = p->next;
3008 - }
3009 - }
3010 -}
3011 -
237 static void apply_apps_groups_targets_inheritance(void) {
238 struct pid_stat *p = NULL;
239
@@ -3171,16 +396,15 @@ static size_t zero_all_targets(struct target *root) {
396 w->max_open_files_percent = 0.0;
397 }
398
3174 - w->collected_starttime = 0;
399 w->uptime_min = 0;
400 w->uptime_sum = 0;
401 w->uptime_max = 0;
402
403 if(unlikely(w->root_pid)) {
3180 - struct pid_on_target *pid_on_target_to_free, *pid_on_target = w->root_pid;
404 + struct pid_on_target *pid_on_target = w->root_pid;
405
406 while(pid_on_target) {
3183 - pid_on_target_to_free = pid_on_target;
407 + struct pid_on_target *pid_on_target_to_free = pid_on_target;
408 pid_on_target = pid_on_target->next;
409 freez(pid_on_target_to_free);
410 }
@@ -3192,116 +416,6 @@ static size_t zero_all_targets(struct target *root) {
416 return count;
417 }
418
3195 -static inline void reallocate_target_fds(struct target *w) {
3196 - if(unlikely(!w))
3197 - return;
3198 -
3199 - if(unlikely(!w->target_fds || w->target_fds_size < all_files_size)) {
3200 - w->target_fds = reallocz(w->target_fds, sizeof(int) * all_files_size);
3201 - memset(&w->target_fds[w->target_fds_size], 0, sizeof(int) * (all_files_size - w->target_fds_size));
3202 - w->target_fds_size = all_files_size;
3203 - }
3204 -}
3205 -
3206 -static void aggregage_fd_type_on_openfds(FD_FILETYPE type, struct openfds *openfds) {
3207 - switch(type) {
3208 - case FILETYPE_FILE:
3209 - openfds->files++;
3210 - break;
3211 -
3212 - case FILETYPE_PIPE:
3213 - openfds->pipes++;
3214 - break;
3215 -
3216 - case FILETYPE_SOCKET:
3217 - openfds->sockets++;
3218 - break;
3219 -
3220 - case FILETYPE_INOTIFY:
3221 - openfds->inotifies++;
3222 - break;
3223 -
3224 - case FILETYPE_EVENTFD:
3225 - openfds->eventfds++;
3226 - break;
3227 -
3228 - case FILETYPE_TIMERFD:
3229 - openfds->timerfds++;
3230 - break;
3231 -
3232 - case FILETYPE_SIGNALFD:
3233 - openfds->signalfds++;
3234 - break;
3235 -
3236 - case FILETYPE_EVENTPOLL:
3237 - openfds->eventpolls++;
3238 - break;
3239 -
3240 - case FILETYPE_OTHER:
3241 - openfds->other++;
3242 - break;
3243 - }
3244 -}
3245 -
3246 -static inline void aggregate_fd_on_target(int fd, struct target *w) {
3247 - if(unlikely(!w))
3248 - return;
3249 -
3250 - if(unlikely(w->target_fds[fd])) {
3251 - // it is already aggregated
3252 - // just increase its usage counter
3253 - w->target_fds[fd]++;
3254 - return;
3255 - }
3256 -
3257 - // increase its usage counter
3258 - // so that we will not add it again
3259 - w->target_fds[fd]++;
3260 -
3261 - aggregage_fd_type_on_openfds(all_files[fd].type, &w->openfds);
3262 -}
3263 -
3264 -static inline void aggregate_pid_fds_on_targets(struct pid_stat *p) {
3265 -
3266 - if(unlikely(!p->updated)) {
3267 - // the process is not running
3268 - return;
3269 - }
3270 -
3271 - struct target *w = p->target, *u = p->user_target, *g = p->group_target;
3272 -
3273 - reallocate_target_fds(w);
3274 - reallocate_target_fds(u);
3275 - reallocate_target_fds(g);
3276 -
3277 - p->openfds.files = 0;
3278 - p->openfds.pipes = 0;
3279 - p->openfds.sockets = 0;
3280 - p->openfds.inotifies = 0;
3281 - p->openfds.eventfds = 0;
3282 - p->openfds.timerfds = 0;
3283 - p->openfds.signalfds = 0;
3284 - p->openfds.eventpolls = 0;
3285 - p->openfds.other = 0;
3286 -
3287 - long currentfds = 0;
3288 - size_t c, size = p->fds_size;
3289 - struct pid_fd *fds = p->fds;
3290 - for(c = 0; c < size ;c++) {
3291 - int fd = fds[c].fd;
3292 -
3293 - if(likely(fd <= 0 || fd >= all_files_size))
3294 - continue;
3295 -
3296 - currentfds++;
3297 - aggregage_fd_type_on_openfds(all_files[fd].type, &p->openfds);
3298 -
3299 - aggregate_fd_on_target(fd, w);
3300 - aggregate_fd_on_target(fd, u);
3301 - aggregate_fd_on_target(fd, g);
3302 - }
3303 -}
3304 -
419 static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o) {
420 (void)o;
421
@@ -3352,10 +466,9 @@ static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p,
466 w->processes++;
467 w->num_threads += p->num_threads;
468
3355 - if(!w->collected_starttime || p->collected_starttime < w->collected_starttime) w->collected_starttime = p->collected_starttime;
469 if(!w->uptime_min || p->uptime < w->uptime_min) w->uptime_min = p->uptime;
3357 - w->uptime_sum += p->uptime;
470 if(!w->uptime_max || w->uptime_max < p->uptime) w->uptime_max = p->uptime;
471 + w->uptime_sum += p->uptime;
472
473 if(unlikely(debug_enabled || w->debug_enabled)) {
474 debug_log_int("aggregating '%s' pid %d on target '%s' utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", gtime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", cgtime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT "", p->comm, p->pid, w->name, p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
@@ -3367,21 +480,7 @@ static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p,
480 }
481 }
482
3370 -static inline void post_aggregate_targets(struct target *root) {
3371 - struct target *w;
3372 - for (w = root; w ; w = w->next) {
3373 - if(w->collected_starttime) {
3374 - if (!w->starttime || w->collected_starttime < w->starttime) {
3375 - w->starttime = w->collected_starttime;
3376 - }
3377 - } else {
3378 - w->starttime = 0;
3379 - }
3380 - }
3381 -}
3382 -
483 static void calculate_netdata_statistics(void) {
3384 -
484 apply_apps_groups_targets_inheritance();
485
486 zero_all_targets(users_root_target);
@@ -3440,167 +539,12 @@ static void calculate_netdata_statistics(void) {
539 aggregate_pid_fds_on_targets(p);
540 }
541
3443 - post_aggregate_targets(apps_groups_root_target);
3444 - post_aggregate_targets(users_root_target);
3445 - post_aggregate_targets(groups_root_target);
3446 -
542 cleanup_exited_pids();
543 }
544
545 // ----------------------------------------------------------------------------
546 // update chart dimensions
547
3453 -static inline void send_BEGIN(const char *type, const char *name,const char *metric, usec_t usec) {
3454 - fprintf(stdout, "BEGIN %s.%s_%s %" PRIu64 "\n", type, name, metric, usec);
3455 -}
3456 -
3457 -static inline void send_SET(const char *name, kernel_uint_t value) {
3458 - fprintf(stdout, "SET %s = " KERNEL_UINT_FORMAT "\n", name, value);
3459 -}
3460 -
3461 -static inline void send_END(void) {
3462 - fprintf(stdout, "END\n\n");
3463 -}
3464 -
3465 -void send_resource_usage_to_netdata(usec_t dt) {
3466 - static struct timeval last = { 0, 0 };
3467 - static struct rusage me_last;
3468 -
3469 - struct timeval now;
3470 - struct rusage me;
3471 -
3472 - usec_t cpuuser;
3473 - usec_t cpusyst;
3474 -
3475 - if(!last.tv_sec) {
3476 - now_monotonic_timeval(&last);
3477 - getrusage(RUSAGE_SELF, &me_last);
3478 -
3479 - cpuuser = 0;
3480 - cpusyst = 0;
3481 - }
3482 - else {
3483 - now_monotonic_timeval(&now);
3484 - getrusage(RUSAGE_SELF, &me);
3485 -
3486 - cpuuser = me.ru_utime.tv_sec * USEC_PER_SEC + me.ru_utime.tv_usec;
3487 - cpusyst = me.ru_stime.tv_sec * USEC_PER_SEC + me.ru_stime.tv_usec;
3488 -
3489 - memmove(&last, &now, sizeof(struct timeval));
3490 - memmove(&me_last, &me, sizeof(struct rusage));
3491 - }
3492 -
3493 - static char created_charts = 0;
3494 - if(unlikely(!created_charts)) {
3495 - created_charts = 1;
3496 -
3497 - fprintf(stdout,
3498 - "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n"
3499 - "DIMENSION user '' incremental 1 1000\n"
3500 - "DIMENSION system '' incremental 1 1000\n"
3501 - "CHART netdata.apps_sizes '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_sizes line 140001 %1$d\n"
3502 - "DIMENSION calls '' incremental 1 1\n"
3503 - "DIMENSION files '' incremental 1 1\n"
3504 - "DIMENSION filenames '' incremental 1 1\n"
3505 - "DIMENSION inode_changes '' incremental 1 1\n"
3506 - "DIMENSION link_changes '' incremental 1 1\n"
3507 - "DIMENSION pids '' absolute 1 1\n"
3508 - "DIMENSION fds '' absolute 1 1\n"
3509 - "DIMENSION targets '' absolute 1 1\n"
3510 - "DIMENSION new_pids 'new pids' incremental 1 1\n"
3511 - , update_every
3512 - );
3513 -
3514 - fprintf(stdout,
3515 - "CHART netdata.apps_fix '' 'Apps Plugin Normalization Ratios' 'percentage' apps.plugin netdata.apps_fix line 140002 %1$d\n"
3516 - "DIMENSION utime '' absolute 1 %2$llu\n"
3517 - "DIMENSION stime '' absolute 1 %2$llu\n"
3518 - "DIMENSION gtime '' absolute 1 %2$llu\n"
3519 - "DIMENSION minflt '' absolute 1 %2$llu\n"
3520 - "DIMENSION majflt '' absolute 1 %2$llu\n"
3521 - , update_every
3522 - , RATES_DETAIL
3523 - );
3524 -
3525 - if(include_exited_childs)
3526 - fprintf(stdout,
3527 - "CHART netdata.apps_children_fix '' 'Apps Plugin Exited Children Normalization Ratios' 'percentage' apps.plugin netdata.apps_children_fix line 140003 %1$d\n"
3528 - "DIMENSION cutime '' absolute 1 %2$llu\n"
3529 - "DIMENSION cstime '' absolute 1 %2$llu\n"
3530 - "DIMENSION cgtime '' absolute 1 %2$llu\n"
3531 - "DIMENSION cminflt '' absolute 1 %2$llu\n"
3532 - "DIMENSION cmajflt '' absolute 1 %2$llu\n"
3533 - , update_every
3534 - , RATES_DETAIL
3535 - );
3536 -
3537 - }
3538 -
3539 - fprintf(stdout,
3540 - "BEGIN netdata.apps_cpu %"PRIu64"\n"
3541 - "SET user = %"PRIu64"\n"
3542 - "SET system = %"PRIu64"\n"
3543 - "END\n"
3544 - "BEGIN netdata.apps_sizes %"PRIu64"\n"
3545 - "SET calls = %zu\n"
3546 - "SET files = %zu\n"
3547 - "SET filenames = %zu\n"
3548 - "SET inode_changes = %zu\n"
3549 - "SET link_changes = %zu\n"
3550 - "SET pids = %zu\n"
3551 - "SET fds = %d\n"
3552 - "SET targets = %zu\n"
3553 - "SET new_pids = %zu\n"
3554 - "END\n"
3555 - , dt
3556 - , cpuuser
3557 - , cpusyst
3558 - , dt
3559 - , calls_counter
3560 - , file_counter
3561 - , filenames_allocated_counter
3562 - , inodes_changed_counter
3563 - , links_changed_counter
3564 - , all_pids_count
3565 - , all_files_len
3566 - , apps_groups_targets_count
3567 - , targets_assignment_counter
3568 - );
3569 -
3570 - fprintf(stdout,
3571 - "BEGIN netdata.apps_fix %"PRIu64"\n"
3572 - "SET utime = %u\n"
3573 - "SET stime = %u\n"
3574 - "SET gtime = %u\n"
3575 - "SET minflt = %u\n"
3576 - "SET majflt = %u\n"
3577 - "END\n"
3578 - , dt
3579 - , (unsigned int)(utime_fix_ratio * 100 * RATES_DETAIL)
3580 - , (unsigned int)(stime_fix_ratio * 100 * RATES_DETAIL)
3581 - , (unsigned int)(gtime_fix_ratio * 100 * RATES_DETAIL)
3582 - , (unsigned int)(minflt_fix_ratio * 100 * RATES_DETAIL)
3583 - , (unsigned int)(majflt_fix_ratio * 100 * RATES_DETAIL)
3584 - );
3585 -
3586 - if(include_exited_childs)
3587 - fprintf(stdout,
3588 - "BEGIN netdata.apps_children_fix %"PRIu64"\n"
3589 - "SET cutime = %u\n"
3590 - "SET cstime = %u\n"
3591 - "SET cgtime = %u\n"
3592 - "SET cminflt = %u\n"
3593 - "SET cmajflt = %u\n"
3594 - "END\n"
3595 - , dt
3596 - , (unsigned int)(cutime_fix_ratio * 100 * RATES_DETAIL)
3597 - , (unsigned int)(cstime_fix_ratio * 100 * RATES_DETAIL)
3598 - , (unsigned int)(cgtime_fix_ratio * 100 * RATES_DETAIL)
3599 - , (unsigned int)(cminflt_fix_ratio * 100 * RATES_DETAIL)
3600 - , (unsigned int)(cmajflt_fix_ratio * 100 * RATES_DETAIL)
3601 - );
3602 -}
3603 -
548 static void normalize_utilization(struct target *root) {
549 struct target *w;
550
@@ -3752,294 +696,6 @@ static void normalize_utilization(struct target *root) {
696 );
697 }
698
3755 -static void send_collected_data_to_netdata(struct target *root, const char *type, usec_t dt) {
3756 - struct target *w;
3757 -
3758 - for (w = root; w ; w = w->next) {
3759 - if (unlikely(!w->exposed))
3760 - continue;
3761 -
3762 - send_BEGIN(type, w->clean_name, "processes", dt);
3763 - send_SET("processes", w->processes);
3764 - send_END();
3765 -
3766 - send_BEGIN(type, w->clean_name, "threads", dt);
3767 - send_SET("threads", w->num_threads);
3768 - send_END();
3769 -
3770 - if (unlikely(!w->processes && !w->is_other))
3771 - continue;
3772 -
3773 - send_BEGIN(type, w->clean_name, "cpu_utilization", dt);
3774 - send_SET("user", (kernel_uint_t)(w->utime * utime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cutime * cutime_fix_ratio)) : 0ULL));
3775 - send_SET("system", (kernel_uint_t)(w->stime * stime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cstime * cstime_fix_ratio)) : 0ULL));
3776 - send_END();
3777 -
3778 -#ifndef __FreeBSD__
3779 - if (enable_guest_charts) {
3780 - send_BEGIN(type, w->clean_name, "cpu_guest_utilization", dt);
3781 - send_SET("guest", (kernel_uint_t)(w->gtime * gtime_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cgtime * cgtime_fix_ratio)) : 0ULL));
3782 - send_END();
3783 - }
3784 -
3785 - send_BEGIN(type, w->clean_name, "cpu_context_switches", dt);
3786 - send_SET("voluntary", w->status_voluntary_ctxt_switches);
3787 - send_SET("involuntary", w->status_nonvoluntary_ctxt_switches);
3788 - send_END();
3789 -
3790 - send_BEGIN(type, w->clean_name, "mem_private_usage", dt);
3791 - send_SET("mem", (w->status_vmrss > w->status_vmshared)?(w->status_vmrss - w->status_vmshared) : 0ULL);
3792 - send_END();
3793 -#endif
3794 -
3795 - send_BEGIN(type, w->clean_name, "mem_usage", dt);
3796 - send_SET("rss", w->status_vmrss);
3797 - send_END();
3798 -
3799 - send_BEGIN(type, w->clean_name, "vmem_usage", dt);
3800 - send_SET("vmem", w->status_vmsize);
3801 - send_END();
3802 -
3803 - send_BEGIN(type, w->clean_name, "mem_page_faults", dt);
3804 - send_SET("minor", (kernel_uint_t)(w->minflt * minflt_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cminflt * cminflt_fix_ratio)) : 0ULL));
3805 - send_SET("major", (kernel_uint_t)(w->majflt * majflt_fix_ratio) + (include_exited_childs ? ((kernel_uint_t)(w->cmajflt * cmajflt_fix_ratio)) : 0ULL));
3806 - send_END();
3807 -
3808 -#ifndef __FreeBSD__
3809 - send_BEGIN(type, w->clean_name, "swap_usage", dt);
3810 - send_SET("swap", w->status_vmswap);
3811 - send_END();
3812 -#endif
3813 -
3814 -#ifndef __FreeBSD__
3815 - if (w->processes == 0) {
3816 - send_BEGIN(type, w->clean_name, "uptime", dt);
3817 - send_SET("uptime", 0);
3818 - send_END();
3819 -
3820 - if (enable_detailed_uptime_charts) {
3821 - send_BEGIN(type, w->clean_name, "uptime_summary", dt);
3822 - send_SET("min", 0);
3823 - send_SET("avg", 0);
3824 - send_SET("max", 0);
3825 - send_END();
3826 - }
3827 - } else {
3828 - send_BEGIN(type, w->clean_name, "uptime", dt);
3829 - send_SET("uptime", (global_uptime > w->starttime) ? (global_uptime - w->starttime) : 0);
3830 - send_END();
3831 -
3832 - if (enable_detailed_uptime_charts) {
3833 - send_BEGIN(type, w->clean_name, "uptime_summary", dt);
3834 - send_SET("min", w->uptime_min);
3835 - send_SET("avg", w->processes > 0 ? w->uptime_sum / w->processes : 0);
3836 - send_SET("max", w->uptime_max);
3837 - send_END();
3838 - }
3839 - }
3840 -#endif
3841 -
3842 - send_BEGIN(type, w->clean_name, "disk_physical_io", dt);
3843 - send_SET("reads", w->io_storage_bytes_read);
3844 - send_SET("writes", w->io_storage_bytes_written);
3845 - send_END();
3846 -
3847 -#ifndef __FreeBSD__
3848 - send_BEGIN(type, w->clean_name, "disk_logical_io", dt);
3849 - send_SET("reads", w->io_logical_bytes_read);
3850 - send_SET("writes", w->io_logical_bytes_written);
3851 - send_END();
3852 -#endif
3853 - if (enable_file_charts) {
3854 - send_BEGIN(type, w->clean_name, "fds_open_limit", dt);
3855 - send_SET("limit", w->max_open_files_percent * 100.0);
3856 - send_END();
3857 -
3858 - send_BEGIN(type, w->clean_name, "fds_open", dt);
3859 - send_SET("files", w->openfds.files);
3860 - send_SET("sockets", w->openfds.sockets);
3861 - send_SET("pipes", w->openfds.sockets);
3862 - send_SET("inotifies", w->openfds.inotifies);
3863 - send_SET("event", w->openfds.eventfds);
3864 - send_SET("timer", w->openfds.timerfds);
3865 - send_SET("signal", w->openfds.signalfds);
3866 - send_SET("eventpolls", w->openfds.eventpolls);
3867 - send_SET("other", w->openfds.other);
3868 - send_END();
3869 - }
3870 - }
3871 -}
3872 -
3873 -
3874 -// ----------------------------------------------------------------------------
3875 -// generate the charts
3876 -
3877 -static void send_charts_updates_to_netdata(struct target *root, const char *type, const char *lbl_name, const char *title)
3878 -{
3879 - struct target *w;
3880 -
3881 - if (debug_enabled) {
3882 - for (w = root; w; w = w->next) {
3883 - if (unlikely(!w->target && w->processes)) {
3884 - struct pid_on_target *pid_on_target;
3885 - fprintf(stderr, "apps.plugin: target '%s' has aggregated %u process(es):", w->name, w->processes);
3886 - for (pid_on_target = w->root_pid; pid_on_target; pid_on_target = pid_on_target->next) {
3887 - fprintf(stderr, " %d", pid_on_target->pid);
3888 - }
3889 - fputc('\n', stderr);
3890 - }
3891 - }
3892 - }
3893 -
3894 - for (w = root; w; w = w->next) {
3895 - if (likely(w->exposed || (!w->processes && !w->is_other)))
3896 - continue;
3897 -
3898 - w->exposed = 1;
3899 -
3900 - fprintf(stdout, "CHART %s.%s_cpu_utilization '' '%s CPU utilization (100%% = 1 core)' 'percentage' cpu %s.cpu_utilization stacked 20001 %d\n", type, w->clean_name, title, type, update_every);
3901 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3902 - fprintf(stdout, "CLABEL_COMMIT\n");
3903 - fprintf(stdout, "DIMENSION user '' absolute 1 %llu\n", time_factor * RATES_DETAIL / 100LLU);
3904 - fprintf(stdout, "DIMENSION system '' absolute 1 %llu\n", time_factor * RATES_DETAIL / 100LLU);
3905 -
3906 -#ifndef __FreeBSD__
3907 - if (enable_guest_charts) {
3908 - fprintf(stdout, "CHART %s.%s_cpu_guest_utilization '' '%s CPU guest utlization (100%% = 1 core)' 'percentage' cpu %s.cpu_guest_utilization line 20005 %d\n", type, w->clean_name, title, type, update_every);
3909 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3910 - fprintf(stdout, "CLABEL_COMMIT\n");
3911 - fprintf(stdout, "DIMENSION guest '' absolute 1 %llu\n", time_factor * RATES_DETAIL / 100LLU);
3912 - }
3913 -
3914 - fprintf(stdout, "CHART %s.%s_cpu_context_switches '' '%s CPU context switches' 'switches/s' cpu %s.cpu_context_switches stacked 20010 %d\n", type, w->clean_name, title, type, update_every);
3915 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3916 - fprintf(stdout, "CLABEL_COMMIT\n");
3917 - fprintf(stdout, "DIMENSION voluntary '' absolute 1 %llu\n", RATES_DETAIL);
3918 - fprintf(stdout, "DIMENSION involuntary '' absolute 1 %llu\n", RATES_DETAIL);
3919 -
3920 - fprintf(stdout, "CHART %s.%s_mem_private_usage '' '%s memory usage without shared' 'MiB' mem %s.mem_private_usage area 20050 %d\n", type, w->clean_name, title, type, update_every);
3921 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3922 - fprintf(stdout, "CLABEL_COMMIT\n");
3923 - fprintf(stdout, "DIMENSION mem '' absolute %ld %ld\n", 1L, 1024L);
3924 -#endif
3925 -
3926 - fprintf(stdout, "CHART %s.%s_mem_usage '' '%s memory RSS usage' 'MiB' mem %s.mem_usage area 20055 %d\n", type, w->clean_name, title, type, update_every);
3927 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3928 - fprintf(stdout, "CLABEL_COMMIT\n");
3929 - fprintf(stdout, "DIMENSION rss '' absolute %ld %ld\n", 1L, 1024L);
3930 -
3931 - fprintf(stdout, "CHART %s.%s_mem_page_faults '' '%s memory page faults' 'pgfaults/s' mem %s.mem_page_faults stacked 20060 %d\n", type, w->clean_name, title, type, update_every);
3932 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3933 - fprintf(stdout, "CLABEL_COMMIT\n");
3934 - fprintf(stdout, "DIMENSION major '' absolute 1 %llu\n", RATES_DETAIL);
3935 - fprintf(stdout, "DIMENSION minor '' absolute 1 %llu\n", RATES_DETAIL);
3936 -
3937 - fprintf(stdout, "CHART %s.%s_vmem_usage '' '%s virtual memory size' 'MiB' mem %s.vmem_usage line 20065 %d\n", type, w->clean_name, title, type, update_every);
3938 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3939 - fprintf(stdout, "CLABEL_COMMIT\n");
3940 - fprintf(stdout, "DIMENSION vmem '' absolute %ld %ld\n", 1L, 1024L);
3941 -
3942 -#ifndef __FreeBSD__
3943 - fprintf(stdout, "CHART %s.%s_swap_usage '' '%s swap usage' 'MiB' mem %s.swap_usage area 20065 %d\n", type, w->clean_name, title, type, update_every);
3944 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3945 - fprintf(stdout, "CLABEL_COMMIT\n");
3946 - fprintf(stdout, "DIMENSION swap '' absolute %ld %ld\n", 1L, 1024L);
3947 -#endif
3948 -
3949 -#ifndef __FreeBSD__
3950 - fprintf(stdout, "CHART %s.%s_disk_physical_io '' '%s disk physical IO' 'KiB/s' disk %s.disk_physical_io area 20100 %d\n", type, w->clean_name, title, type, update_every);
3951 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3952 - fprintf(stdout, "CLABEL_COMMIT\n");
3953 - fprintf(stdout, "DIMENSION reads '' absolute 1 %llu\n", 1024LLU * RATES_DETAIL);
3954 - fprintf(stdout, "DIMENSION writes '' absolute -1 %llu\n", 1024LLU * RATES_DETAIL);
3955 -
3956 - fprintf(stdout, "CHART %s.%s_disk_logical_io '' '%s disk logical IO' 'KiB/s' disk %s.disk_logical_io area 20105 %d\n", type, w->clean_name, title, type, update_every);
3957 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3958 - fprintf(stdout, "CLABEL_COMMIT\n");
3959 - fprintf(stdout, "DIMENSION reads '' absolute 1 %llu\n", 1024LLU * RATES_DETAIL);
3960 - fprintf(stdout, "DIMENSION writes '' absolute -1 %llu\n", 1024LLU * RATES_DETAIL);
3961 -#else
3962 - fprintf(stdout, "CHART %s.%s_disk_physical_io '' '%s disk physical IO' 'blocks/s' disk %s.disk_physical_block_io area 20100 %d\n", type, w->clean_name, title, type, update_every);
3963 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3964 - fprintf(stdout, "CLABEL_COMMIT\n");
3965 - fprintf(stdout, "DIMENSION reads '' absolute 1 %llu\n", RATES_DETAIL);
3966 - fprintf(stdout, "DIMENSION writes '' absolute -1 %llu\n", RATES_DETAIL);
3967 -#endif
3968 -
3969 - fprintf(stdout, "CHART %s.%s_processes '' '%s processes' 'processes' processes %s.processes line 20150 %d\n", type, w->clean_name, title, type, update_every);
3970 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3971 - fprintf(stdout, "CLABEL_COMMIT\n");
3972 - fprintf(stdout, "DIMENSION processes '' absolute 1 1\n");
3973 -
3974 - fprintf(stdout, "CHART %s.%s_threads '' '%s threads' 'threads' processes %s.threads line 20155 %d\n", type, w->clean_name, title, type, update_every);
3975 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3976 - fprintf(stdout, "CLABEL_COMMIT\n");
3977 - fprintf(stdout, "DIMENSION threads '' absolute 1 1\n");
3978 -
3979 - if (enable_file_charts) {
3980 - fprintf(stdout, "CHART %s.%s_fds_open_limit '' '%s open file descriptors limit' '%%' fds %s.fds_open_limit line 20200 %d\n", type, w->clean_name, title, type, update_every);
3981 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3982 - fprintf(stdout, "CLABEL_COMMIT\n");
3983 - fprintf(stdout, "DIMENSION limit '' absolute 1 100\n");
3984 -
3985 - fprintf(stdout, "CHART %s.%s_fds_open '' '%s open files descriptors' 'fds' fds %s.fds_open stacked 20210 %d\n", type, w->clean_name, title, type, update_every);
3986 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
3987 - fprintf(stdout, "CLABEL_COMMIT\n");
3988 - fprintf(stdout, "DIMENSION files '' absolute 1 1\n");
3989 - fprintf(stdout, "DIMENSION sockets '' absolute 1 1\n");
3990 - fprintf(stdout, "DIMENSION pipes '' absolute 1 1\n");
3991 - fprintf(stdout, "DIMENSION inotifies '' absolute 1 1\n");
3992 - fprintf(stdout, "DIMENSION event '' absolute 1 1\n");
3993 - fprintf(stdout, "DIMENSION timer '' absolute 1 1\n");
3994 - fprintf(stdout, "DIMENSION signal '' absolute 1 1\n");
3995 - fprintf(stdout, "DIMENSION eventpolls '' absolute 1 1\n");
3996 - fprintf(stdout, "DIMENSION other '' absolute 1 1\n");
3997 - }
3998 -
3999 -#ifndef __FreeBSD__
4000 - fprintf(stdout, "CHART %s.%s_uptime '' '%s uptime' 'seconds' uptime %s.uptime line 20250 %d\n", type, w->clean_name, title, type, update_every);
4001 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
4002 - fprintf(stdout, "CLABEL_COMMIT\n");
4003 - fprintf(stdout, "DIMENSION uptime '' absolute 1 1\n");
4004 -
4005 - if (enable_detailed_uptime_charts) {
4006 - fprintf(stdout, "CHART %s.%s_uptime_summary '' '%s uptime summary' 'seconds' uptime %s.uptime_summary area 20255 %d\n", type, w->clean_name, title, type, update_every);
4007 - fprintf(stdout, "CLABEL '%s' '%s' 1\n", lbl_name, w->name);
4008 - fprintf(stdout, "CLABEL_COMMIT\n");
4009 - fprintf(stdout, "DIMENSION min '' absolute 1 1\n");
4010 - fprintf(stdout, "DIMENSION avg '' absolute 1 1\n");
4011 - fprintf(stdout, "DIMENSION max '' absolute 1 1\n");
4012 - }
4013 -#endif
4014 - }
4015 -}
4016 -
4017 -#ifndef __FreeBSD__
4018 -static void send_proc_states_count(usec_t dt)
4019 -{
4020 - static bool chart_added = false;
4021 - // create chart for count of processes in different states
4022 - if (!chart_added) {
4023 - fprintf(
4024 - stdout,
4025 - "CHART system.processes_state '' 'System Processes State' 'processes' processes system.processes_state line %d %d\n",
4026 - NETDATA_CHART_PRIO_SYSTEM_PROCESS_STATES,
4027 - update_every);
4028 - for (proc_state i = PROC_STATUS_RUNNING; i < PROC_STATUS_END; i++) {
4029 - fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", proc_states[i]);
4030 - }
4031 - chart_added = true;
4032 - }
4033 -
4034 - // send process state count
4035 - fprintf(stdout, "BEGIN system.processes_state %" PRIu64 "\n", dt);
4036 - for (proc_state i = PROC_STATUS_RUNNING; i < PROC_STATUS_END; i++) {
4037 - send_SET(proc_states[i], proc_state_count[i]);
4038 - }
4039 - send_END();
4040 -}
4041 -#endif
4042 -
699 // ----------------------------------------------------------------------------
700 // parse command line arguments
701
@@ -4087,14 +743,14 @@ static void parse_args(int argc, char **argv)
743 }
744
745 if(strcmp("debug", argv[i]) == 0) {
4090 - debug_enabled = 1;
746 + debug_enabled = true;
747 #ifndef NETDATA_INTERNAL_CHECKS
748 fprintf(stderr, "apps.plugin has been compiled without debugging\n");
749 #endif
750 continue;
751 }
752
4097 -#ifndef __FreeBSD__
753 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
754 if(strcmp("fds-cache-secs", argv[i]) == 0) {
755 if(argc <= i + 1) {
756 fprintf(stderr, "Parameter 'fds-cache-secs' requires a number as argument.\n");
@@ -4118,12 +774,12 @@ static void parse_args(int argc, char **argv)
774 }
775
776 if(strcmp("with-guest", argv[i]) == 0) {
4121 - enable_guest_charts = 1;
777 + enable_guest_charts = true;
778 continue;
779 }
780
781 if(strcmp("no-guest", argv[i]) == 0 || strcmp("without-guest", argv[i]) == 0) {
4126 - enable_guest_charts = 0;
782 + enable_guest_charts = false;
783 continue;
784 }
785
@@ -4152,7 +808,7 @@ static void parse_args(int argc, char **argv)
808 continue;
809 }
810 if(strcmp("with-function-cmdline", argv[i]) == 0) {
4155 - enable_function_cmdline = 1;
811 + enable_function_cmdline = true;
812 continue;
813 }
814
@@ -4196,7 +852,7 @@ static void parse_args(int argc, char **argv)
852 "\n"
853 " with-detailed-uptime enable reporting min/avg/max uptime charts\n"
854 "\n"
4199 -#ifndef __FreeBSD__
855 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
856 " fds-cache-secs N cache the files of processed for N seconds\n"
857 " caching is adaptive per file (when a file\n"
858 " is found, it starts at 0 and while the file\n"
@@ -4208,7 +864,7 @@ static void parse_args(int argc, char **argv)
864 " version or -v or -V print program version and exit\n"
865 "\n"
866 , VERSION
4211 -#ifndef __FreeBSD__
867 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
868 , max_fds_cache_seconds
869 #endif
870 );
@@ -4299,15 +955,7 @@ static int check_capabilities() {
955
956 static netdata_mutex_t apps_and_stdout_mutex = NETDATA_MUTEX_INITIALIZER;
957
4302 -#define PROCESS_FILTER_CATEGORY "category:"
4303 -#define PROCESS_FILTER_USER "user:"
4304 -#define PROCESS_FILTER_GROUP "group:"
4305 -#define PROCESS_FILTER_PROCESS "process:"
4306 -#define PROCESS_FILTER_PID "pid:"
4307 -#define PROCESS_FILTER_UID "uid:"
4308 -#define PROCESS_FILTER_GID "gid:"
4309 -
4310 -static struct target *find_target_by_name(struct target *base, const char *name) {
958 +struct target *find_target_by_name(struct target *base, const char *name) {
959 struct target *t;
960 for(t = base; t ; t = t->next) {
961 if (strcmp(t->name, name) == 0)
@@ -4317,950 +965,6 @@ static struct target *find_target_by_name(struct target *base, const char *name)
965 return NULL;
966 }
967
4320 -static kernel_uint_t MemTotal = 0;
4321 -
4322 -static void get_MemTotal(void) {
4323 -#ifdef __FreeBSD__
4324 - // TODO - fix this for FreeBSD
4325 - return;
4326 -#else
4327 - char filename[FILENAME_MAX + 1];
4328 - snprintfz(filename, FILENAME_MAX, "%s/proc/meminfo", netdata_configured_host_prefix);
4329 -
4330 - procfile *ff = procfile_open(filename, ": \t", PROCFILE_FLAG_DEFAULT);
4331 - if(!ff)
4332 - return;
4333 -
4334 - ff = procfile_readall(ff);
4335 - if(!ff)
4336 - return;
4337 -
4338 - size_t line, lines = procfile_lines(ff);
4339 -
4340 - for(line = 0; line < lines ;line++) {
4341 - size_t words = procfile_linewords(ff, line);
4342 - if(words == 3 && strcmp(procfile_lineword(ff, line, 0), "MemTotal") == 0 && strcmp(procfile_lineword(ff, line, 2), "kB") == 0) {
4343 - kernel_uint_t n = str2ull(procfile_lineword(ff, line, 1), NULL);
4344 - if(n) MemTotal = n;
4345 - break;
4346 - }
4347 - }
4348 -
4349 - procfile_close(ff);
4350 -#endif
4351 -}
4352 -
4353 -static void apps_plugin_function_processes_help(const char *transaction) {
4354 - BUFFER *wb = buffer_create(0, NULL);
4355 - buffer_sprintf(wb, "%s",
4356 - "apps.plugin / processes\n"
4357 - "\n"
4358 - "Function `processes` presents all the currently running processes of the system.\n"
4359 - "\n"
4360 - "The following filters are supported:\n"
4361 - "\n"
4362 - " category:NAME\n"
4363 - " Shows only processes that are assigned the category `NAME` in apps_groups.conf\n"
4364 - "\n"
4365 - " user:NAME\n"
4366 - " Shows only processes that are running as user name `NAME`.\n"
4367 - "\n"
4368 - " group:NAME\n"
4369 - " Shows only processes that are running as group name `NAME`.\n"
4370 - "\n"
4371 - " process:NAME\n"
4372 - " Shows only processes that their Command is `NAME` or their parent's Command is `NAME`.\n"
4373 - "\n"
4374 - " pid:NUMBER\n"
4375 - " Shows only processes that their PID is `NUMBER` or their parent's PID is `NUMBER`\n"
4376 - "\n"
4377 - " uid:NUMBER\n"
4378 - " Shows only processes that their UID is `NUMBER`\n"
4379 - "\n"
4380 - " gid:NUMBER\n"
4381 - " Shows only processes that their GID is `NUMBER`\n"
4382 - "\n"
4383 - "Filters can be combined. Each filter can be given only one time.\n"
4384 - );
4385 -
4386 - pluginsd_function_result_to_stdout(transaction, HTTP_RESP_OK, "text/plain", now_realtime_sec() + 3600, wb);
4387 - buffer_free(wb);
4388 -}
4389 -
4390 -#define add_value_field_llu_with_max(wb, key, value) do { \
4391 - unsigned long long _tmp = (value); \
4392 - key ## _max = (rows == 0) ? (_tmp) : MAX(key ## _max, _tmp); \
4393 - buffer_json_add_array_item_uint64(wb, _tmp); \
4394 -} while(0)
4395 -
4396 -#define add_value_field_ndd_with_max(wb, key, value) do { \
4397 - NETDATA_DOUBLE _tmp = (value); \
4398 - key ## _max = (rows == 0) ? (_tmp) : MAX(key ## _max, _tmp); \
4399 - buffer_json_add_array_item_double(wb, _tmp); \
4400 -} while(0)
4401 -
4402 -static void function_processes(const char *transaction, char *function,
4403 - usec_t *stop_monotonic_ut __maybe_unused, bool *cancelled __maybe_unused,
4404 - BUFFER *payload __maybe_unused, HTTP_ACCESS access,
4405 - const char *source __maybe_unused, void *data __maybe_unused) {
4406 - time_t now_s = now_realtime_sec();
4407 - struct pid_stat *p;
4408 -
4409 - bool show_cmdline = http_access_user_has_enough_access_level_for_endpoint(
4410 - access, HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE |
4411 - HTTP_ACCESS_SENSITIVE_DATA | HTTP_ACCESS_VIEW_AGENT_CONFIG) ||
4412 - enable_function_cmdline;
4413 -
4414 - char *words[PLUGINSD_MAX_WORDS] = { NULL };
4415 - size_t num_words = quoted_strings_splitter_pluginsd(function, words, PLUGINSD_MAX_WORDS);
4416 -
4417 - struct target *category = NULL, *user = NULL, *group = NULL;
4418 - const char *process_name = NULL;
4419 - pid_t pid = 0;
4420 - uid_t uid = 0;
4421 - gid_t gid = 0;
4422 - bool info = false;
4423 -
4424 - bool filter_pid = false, filter_uid = false, filter_gid = false;
4425 -
4426 - for(int i = 1; i < PLUGINSD_MAX_WORDS ;i++) {
4427 - const char *keyword = get_word(words, num_words, i);
4428 - if(!keyword) break;
4429 -
4430 - if(!category && strncmp(keyword, PROCESS_FILTER_CATEGORY, strlen(PROCESS_FILTER_CATEGORY)) == 0) {
4431 - category = find_target_by_name(apps_groups_root_target, &keyword[strlen(PROCESS_FILTER_CATEGORY)]);
4432 - if(!category) {
4433 - pluginsd_function_json_error_to_stdout(transaction, HTTP_RESP_BAD_REQUEST,
4434 - "No category with that name found.");
4435 - return;
4436 - }
4437 - }
4438 - else if(!user && strncmp(keyword, PROCESS_FILTER_USER, strlen(PROCESS_FILTER_USER)) == 0) {
4439 - user = find_target_by_name(users_root_target, &keyword[strlen(PROCESS_FILTER_USER)]);
4440 - if(!user) {
4441 - pluginsd_function_json_error_to_stdout(transaction, HTTP_RESP_BAD_REQUEST,
4442 - "No user with that name found.");
4443 - return;
4444 - }
4445 - }
4446 - else if(strncmp(keyword, PROCESS_FILTER_GROUP, strlen(PROCESS_FILTER_GROUP)) == 0) {
4447 - group = find_target_by_name(groups_root_target, &keyword[strlen(PROCESS_FILTER_GROUP)]);
4448 - if(!group) {
4449 - pluginsd_function_json_error_to_stdout(transaction, HTTP_RESP_BAD_REQUEST,
4450 - "No group with that name found.");
4451 - return;
4452 - }
4453 - }
4454 - else if(!process_name && strncmp(keyword, PROCESS_FILTER_PROCESS, strlen(PROCESS_FILTER_PROCESS)) == 0) {
4455 - process_name = &keyword[strlen(PROCESS_FILTER_PROCESS)];
4456 - }
4457 - else if(!pid && strncmp(keyword, PROCESS_FILTER_PID, strlen(PROCESS_FILTER_PID)) == 0) {
4458 - pid = str2i(&keyword[strlen(PROCESS_FILTER_PID)]);
4459 - filter_pid = true;
4460 - }
4461 - else if(!uid && strncmp(keyword, PROCESS_FILTER_UID, strlen(PROCESS_FILTER_UID)) == 0) {
4462 - uid = str2i(&keyword[strlen(PROCESS_FILTER_UID)]);
4463 - filter_uid = true;
4464 - }
4465 - else if(!gid && strncmp(keyword, PROCESS_FILTER_GID, strlen(PROCESS_FILTER_GID)) == 0) {
4466 - gid = str2i(&keyword[strlen(PROCESS_FILTER_GID)]);
4467 - filter_gid = true;
4468 - }
4469 - else if(strcmp(keyword, "help") == 0) {
4470 - apps_plugin_function_processes_help(transaction);
4471 - return;
4472 - }
4473 - else if(strcmp(keyword, "info") == 0) {
4474 - info = true;
4475 - }
4476 - }
4477 -
4478 - unsigned int cpu_divisor = time_factor * RATES_DETAIL / 100;
4479 - unsigned int memory_divisor = 1024;
4480 - unsigned int io_divisor = 1024 * RATES_DETAIL;
4481 -
4482 - BUFFER *wb = buffer_create(4096, NULL);
4483 - buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
4484 - buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
4485 - buffer_json_member_add_string(wb, "type", "table");
4486 - buffer_json_member_add_time_t(wb, "update_every", update_every);
4487 - buffer_json_member_add_boolean(wb, "has_history", false);
4488 - buffer_json_member_add_string(wb, "help", APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION);
4489 - buffer_json_member_add_array(wb, "data");
4490 -
4491 - if(info)
4492 - goto close_and_send;
4493 -
4494 - NETDATA_DOUBLE
4495 - UserCPU_max = 0.0
4496 - , SysCPU_max = 0.0
4497 - , GuestCPU_max = 0.0
4498 - , CUserCPU_max = 0.0
4499 - , CSysCPU_max = 0.0
4500 - , CGuestCPU_max = 0.0
4501 - , CPU_max = 0.0
4502 - , VMSize_max = 0.0
4503 - , RSS_max = 0.0
4504 - , Shared_max = 0.0
4505 - , Swap_max = 0.0
4506 - , Memory_max = 0.0
4507 - , FDsLimitPercent_max = 0.0
4508 - ;
4509 -
4510 - unsigned long long
4511 - Processes_max = 0
4512 - , Threads_max = 0
4513 - , VoluntaryCtxtSwitches_max = 0
4514 - , NonVoluntaryCtxtSwitches_max = 0
4515 - , Uptime_max = 0
4516 - , MinFlt_max = 0
4517 - , CMinFlt_max = 0
4518 - , TMinFlt_max = 0
4519 - , MajFlt_max = 0
4520 - , CMajFlt_max = 0
4521 - , TMajFlt_max = 0
4522 - , PReads_max = 0
4523 - , PWrites_max = 0
4524 - , RCalls_max = 0
4525 - , WCalls_max = 0
4526 - , Files_max = 0
4527 - , Pipes_max = 0
4528 - , Sockets_max = 0
4529 - , iNotiFDs_max = 0
4530 - , EventFDs_max = 0
4531 - , TimerFDs_max = 0
4532 - , SigFDs_max = 0
4533 - , EvPollFDs_max = 0
4534 - , OtherFDs_max = 0
4535 - , FDs_max = 0
4536 - ;
4537 -
4538 -#ifndef __FreeBSD__
4539 - unsigned long long
4540 - LReads_max = 0
4541 - , LWrites_max = 0
4542 - ;
4543 -#endif
4544 -
4545 - int rows= 0;
4546 - for(p = root_of_pids; p ; p = p->next) {
4547 - if(!p->updated)
4548 - continue;
4549 -
4550 - if(category && p->target != category)
4551 - continue;
4552 -
4553 - if(user && p->user_target != user)
4554 - continue;
4555 -
4556 - if(group && p->group_target != group)
4557 - continue;
4558 -
4559 - if(process_name && ((strcmp(p->comm, process_name) != 0 && !p->parent) || (p->parent && strcmp(p->comm, process_name) != 0 && strcmp(p->parent->comm, process_name) != 0)))
4560 - continue;
4561 -
4562 - if(filter_pid && p->pid != pid && p->ppid != pid)
4563 - continue;
4564 -
4565 - if(filter_uid && p->uid != uid)
4566 - continue;
4567 -
4568 - if(filter_gid && p->gid != gid)
4569 - continue;
4570 -
4571 - rows++;
4572 -
4573 - buffer_json_add_array_item_array(wb); // for each pid
4574 -
4575 - // IMPORTANT!
4576 - // THE ORDER SHOULD BE THE SAME WITH THE FIELDS!
4577 -
4578 - // pid
4579 - buffer_json_add_array_item_uint64(wb, p->pid);
4580 -
4581 - // cmd
4582 - buffer_json_add_array_item_string(wb, p->comm);
4583 -
4584 - // cmdline
4585 - if (show_cmdline) {
4586 - buffer_json_add_array_item_string(wb, (p->cmdline && *p->cmdline) ? p->cmdline : p->comm);
4587 - }
4588 -
4589 - // ppid
4590 - buffer_json_add_array_item_uint64(wb, p->ppid);
4591 -
4592 - // category
4593 - buffer_json_add_array_item_string(wb, p->target ? p->target->name : "-");
4594 -
4595 - // user
4596 - buffer_json_add_array_item_string(wb, p->user_target ? p->user_target->name : "-");
4597 -
4598 - // uid
4599 - buffer_json_add_array_item_uint64(wb, p->uid);
4600 -
4601 - // group
4602 - buffer_json_add_array_item_string(wb, p->group_target ? p->group_target->name : "-");
4603 -
4604 - // gid
4605 - buffer_json_add_array_item_uint64(wb, p->gid);
4606 -
4607 - // CPU utilization %
4608 - add_value_field_ndd_with_max(wb, CPU, (NETDATA_DOUBLE)(p->utime + p->stime + p->gtime + p->cutime + p->cstime + p->cgtime) / cpu_divisor);
4609 - add_value_field_ndd_with_max(wb, UserCPU, (NETDATA_DOUBLE)(p->utime) / cpu_divisor);
4610 - add_value_field_ndd_with_max(wb, SysCPU, (NETDATA_DOUBLE)(p->stime) / cpu_divisor);
4611 - add_value_field_ndd_with_max(wb, GuestCPU, (NETDATA_DOUBLE)(p->gtime) / cpu_divisor);
4612 - add_value_field_ndd_with_max(wb, CUserCPU, (NETDATA_DOUBLE)(p->cutime) / cpu_divisor);
4613 - add_value_field_ndd_with_max(wb, CSysCPU, (NETDATA_DOUBLE)(p->cstime) / cpu_divisor);
4614 - add_value_field_ndd_with_max(wb, CGuestCPU, (NETDATA_DOUBLE)(p->cgtime) / cpu_divisor);
4615 -
4616 - add_value_field_llu_with_max(wb, VoluntaryCtxtSwitches, p->status_voluntary_ctxt_switches / RATES_DETAIL);
4617 - add_value_field_llu_with_max(wb, NonVoluntaryCtxtSwitches, p->status_nonvoluntary_ctxt_switches / RATES_DETAIL);
4618 -
4619 - // memory MiB
4620 - if(MemTotal)
4621 - add_value_field_ndd_with_max(wb, Memory, (NETDATA_DOUBLE)p->status_vmrss * 100.0 / (NETDATA_DOUBLE)MemTotal);
4622 -
4623 - add_value_field_ndd_with_max(wb, RSS, (NETDATA_DOUBLE)p->status_vmrss / memory_divisor);
4624 - add_value_field_ndd_with_max(wb, Shared, (NETDATA_DOUBLE)p->status_vmshared / memory_divisor);
4625 - add_value_field_ndd_with_max(wb, VMSize, (NETDATA_DOUBLE)p->status_vmsize / memory_divisor);
4626 - add_value_field_ndd_with_max(wb, Swap, (NETDATA_DOUBLE)p->status_vmswap / memory_divisor);
4627 -
4628 - // Physical I/O
4629 - add_value_field_llu_with_max(wb, PReads, p->io_storage_bytes_read / io_divisor);
4630 - add_value_field_llu_with_max(wb, PWrites, p->io_storage_bytes_written / io_divisor);
4631 -
4632 - // Logical I/O
4633 -#ifndef __FreeBSD__
4634 - add_value_field_llu_with_max(wb, LReads, p->io_logical_bytes_read / io_divisor);
4635 - add_value_field_llu_with_max(wb, LWrites, p->io_logical_bytes_written / io_divisor);
4636 -#endif
4637 -
4638 - // I/O calls
4639 - add_value_field_llu_with_max(wb, RCalls, p->io_read_calls / RATES_DETAIL);
4640 - add_value_field_llu_with_max(wb, WCalls, p->io_write_calls / RATES_DETAIL);
4641 -
4642 - // minor page faults
4643 - add_value_field_llu_with_max(wb, MinFlt, p->minflt / RATES_DETAIL);
4644 - add_value_field_llu_with_max(wb, CMinFlt, p->cminflt / RATES_DETAIL);
4645 - add_value_field_llu_with_max(wb, TMinFlt, (p->minflt + p->cminflt) / RATES_DETAIL);
4646 -
4647 - // major page faults
4648 - add_value_field_llu_with_max(wb, MajFlt, p->majflt / RATES_DETAIL);
4649 - add_value_field_llu_with_max(wb, CMajFlt, p->cmajflt / RATES_DETAIL);
4650 - add_value_field_llu_with_max(wb, TMajFlt, (p->majflt + p->cmajflt) / RATES_DETAIL);
4651 -
4652 - // open file descriptors
4653 - add_value_field_ndd_with_max(wb, FDsLimitPercent, p->openfds_limits_percent);
4654 - add_value_field_llu_with_max(wb, FDs, pid_openfds_sum(p));
4655 - add_value_field_llu_with_max(wb, Files, p->openfds.files);
4656 - add_value_field_llu_with_max(wb, Pipes, p->openfds.pipes);
4657 - add_value_field_llu_with_max(wb, Sockets, p->openfds.sockets);
4658 - add_value_field_llu_with_max(wb, iNotiFDs, p->openfds.inotifies);
4659 - add_value_field_llu_with_max(wb, EventFDs, p->openfds.eventfds);
4660 - add_value_field_llu_with_max(wb, TimerFDs, p->openfds.timerfds);
4661 - add_value_field_llu_with_max(wb, SigFDs, p->openfds.signalfds);
4662 - add_value_field_llu_with_max(wb, EvPollFDs, p->openfds.eventpolls);
4663 - add_value_field_llu_with_max(wb, OtherFDs, p->openfds.other);
4664 -
4665 -
4666 - // processes, threads, uptime
4667 - add_value_field_llu_with_max(wb, Processes, p->children_count);
4668 - add_value_field_llu_with_max(wb, Threads, p->num_threads);
4669 - add_value_field_llu_with_max(wb, Uptime, p->uptime);
4670 -
4671 - buffer_json_array_close(wb); // for each pid
4672 - }
4673 -
4674 - buffer_json_array_close(wb); // data
4675 - buffer_json_member_add_object(wb, "columns");
4676 -
4677 - {
4678 - int field_id = 0;
4679 -
4680 - // IMPORTANT!
4681 - // THE ORDER SHOULD BE THE SAME WITH THE VALUES!
4682 - // wb, key, name, visible, type, visualization, transform, decimal_points, units, max, sort, sortable, sticky, unique_key, pointer_to, summary, range
4683 - buffer_rrdf_table_add_field(wb, field_id++, "PID", "Process ID", RRDF_FIELD_TYPE_INTEGER,
4684 - RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
4685 - RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4686 - RRDF_FIELD_FILTER_MULTISELECT,
4687 - RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY |
4688 - RRDF_FIELD_OPTS_UNIQUE_KEY, NULL);
4689 -
4690 - buffer_rrdf_table_add_field(wb, field_id++, "Cmd", "Process Name", RRDF_FIELD_TYPE_STRING,
4691 - RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
4692 - RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4693 - RRDF_FIELD_FILTER_MULTISELECT,
4694 - RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
4695 -
4696 - if (show_cmdline) {
4697 - buffer_rrdf_table_add_field(wb, field_id++, "CmdLine", "Command Line", RRDF_FIELD_TYPE_STRING,
4698 - RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0,
4699 - NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4700 - RRDF_FIELD_FILTER_MULTISELECT,
4701 - RRDF_FIELD_OPTS_NONE, NULL);
4702 - }
4703 -
4704 - buffer_rrdf_table_add_field(wb, field_id++, "PPID", "Parent Process ID", RRDF_FIELD_TYPE_INTEGER,
4705 - RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL,
4706 - NAN, RRDF_FIELD_SORT_ASCENDING, "PID", RRDF_FIELD_SUMMARY_COUNT,
4707 - RRDF_FIELD_FILTER_MULTISELECT,
4708 - RRDF_FIELD_OPTS_NONE, NULL);
4709 - buffer_rrdf_table_add_field(wb, field_id++, "Category", "Category (apps_groups.conf)", RRDF_FIELD_TYPE_STRING,
4710 - RRDF_FIELD_VISUAL_VALUE,
4711 - RRDF_FIELD_TRANSFORM_NONE,
4712 - 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4713 - RRDF_FIELD_FILTER_MULTISELECT,
4714 - RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
4715 - buffer_rrdf_table_add_field(wb, field_id++, "User", "User Owner", RRDF_FIELD_TYPE_STRING,
4716 - RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
4717 - RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4718 - RRDF_FIELD_FILTER_MULTISELECT,
4719 - RRDF_FIELD_OPTS_VISIBLE, NULL);
4720 - buffer_rrdf_table_add_field(wb, field_id++, "Uid", "User ID", RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE,
4721 - RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
4722 - RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4723 - RRDF_FIELD_FILTER_MULTISELECT,
4724 - RRDF_FIELD_OPTS_NONE, NULL);
4725 - buffer_rrdf_table_add_field(wb, field_id++, "Group", "Group Owner", RRDF_FIELD_TYPE_STRING,
4726 - RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
4727 - RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4728 - RRDF_FIELD_FILTER_MULTISELECT,
4729 - RRDF_FIELD_OPTS_NONE, NULL);
4730 - buffer_rrdf_table_add_field(wb, field_id++, "Gid", "Group ID", RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE,
4731 - RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL, NAN,
4732 - RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
4733 - RRDF_FIELD_FILTER_MULTISELECT,
4734 - RRDF_FIELD_OPTS_NONE, NULL);
4735 -
4736 - // CPU utilization
4737 - buffer_rrdf_table_add_field(wb, field_id++, "CPU", "Total CPU Time (100% = 1 core)",
4738 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4739 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4740 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4741 - RRDF_FIELD_OPTS_VISIBLE, NULL);
4742 - buffer_rrdf_table_add_field(wb, field_id++, "UserCPU", "User CPU time (100% = 1 core)",
4743 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4744 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", UserCPU_max,
4745 - RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4746 - RRDF_FIELD_OPTS_NONE, NULL);
4747 - buffer_rrdf_table_add_field(wb, field_id++, "SysCPU", "System CPU Time (100% = 1 core)",
4748 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4749 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", SysCPU_max,
4750 - RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4751 - RRDF_FIELD_OPTS_NONE, NULL);
4752 - buffer_rrdf_table_add_field(wb, field_id++, "GuestCPU", "Guest CPU Time (100% = 1 core)",
4753 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4754 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", GuestCPU_max,
4755 - RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4756 - RRDF_FIELD_OPTS_NONE, NULL);
4757 - buffer_rrdf_table_add_field(wb, field_id++, "CUserCPU", "Children User CPU Time (100% = 1 core)",
4758 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4759 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CUserCPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4760 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4761 - RRDF_FIELD_OPTS_NONE, NULL);
4762 - buffer_rrdf_table_add_field(wb, field_id++, "CSysCPU", "Children System CPU Time (100% = 1 core)",
4763 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4764 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CSysCPU_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4765 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4766 - RRDF_FIELD_OPTS_NONE, NULL);
4767 - buffer_rrdf_table_add_field(wb, field_id++, "CGuestCPU", "Children Guest CPU Time (100% = 1 core)",
4768 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4769 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", CGuestCPU_max, RRDF_FIELD_SORT_DESCENDING,
4770 - NULL,
4771 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
4772 -
4773 - // CPU context switches
4774 - buffer_rrdf_table_add_field(wb, field_id++, "vCtxSwitch", "Voluntary Context Switches",
4775 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4776 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "switches/s",
4777 - VoluntaryCtxtSwitches_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4778 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
4779 - buffer_rrdf_table_add_field(wb, field_id++, "iCtxSwitch", "Involuntary Context Switches",
4780 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4781 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2, "switches/s",
4782 - NonVoluntaryCtxtSwitches_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4783 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE, RRDF_FIELD_OPTS_NONE, NULL);
4784 -
4785 - // memory
4786 - if (MemTotal)
4787 - buffer_rrdf_table_add_field(wb, field_id++, "Memory", "Memory Percentage", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4788 - RRDF_FIELD_VISUAL_BAR,
4789 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", 100.0, RRDF_FIELD_SORT_DESCENDING, NULL,
4790 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4791 - RRDF_FIELD_OPTS_VISIBLE, NULL);
4792 -
4793 - buffer_rrdf_table_add_field(wb, field_id++, "Resident", "Resident Set Size", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4794 - RRDF_FIELD_VISUAL_BAR,
4795 - RRDF_FIELD_TRANSFORM_NUMBER,
4796 - 2, "MiB", RSS_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4797 - RRDF_FIELD_FILTER_RANGE,
4798 - RRDF_FIELD_OPTS_VISIBLE, NULL);
4799 - buffer_rrdf_table_add_field(wb, field_id++, "Shared", "Shared Pages", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4800 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
4801 - "MiB", Shared_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4802 - RRDF_FIELD_FILTER_RANGE,
4803 - RRDF_FIELD_OPTS_VISIBLE, NULL);
4804 - buffer_rrdf_table_add_field(wb, field_id++, "Virtual", "Virtual Memory Size", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4805 - RRDF_FIELD_VISUAL_BAR,
4806 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "MiB", VMSize_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4807 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4808 - RRDF_FIELD_OPTS_VISIBLE, NULL);
4809 - buffer_rrdf_table_add_field(wb, field_id++, "Swap", "Swap Memory", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4810 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
4811 - "MiB",
4812 - Swap_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4813 - RRDF_FIELD_FILTER_RANGE,
4814 - RRDF_FIELD_OPTS_NONE, NULL);
4815 -
4816 - // Physical I/O
4817 - buffer_rrdf_table_add_field(wb, field_id++, "PReads", "Physical I/O Reads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4818 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
4819 - 2, "KiB/s", PReads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4820 - RRDF_FIELD_FILTER_RANGE,
4821 - RRDF_FIELD_OPTS_VISIBLE, NULL);
4822 - buffer_rrdf_table_add_field(wb, field_id++, "PWrites", "Physical I/O Writes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4823 - RRDF_FIELD_VISUAL_BAR,
4824 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "KiB/s", PWrites_max, RRDF_FIELD_SORT_DESCENDING,
4825 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4826 - RRDF_FIELD_OPTS_VISIBLE, NULL);
4827 -
4828 - // Logical I/O
4829 -#ifndef __FreeBSD__
4830 - buffer_rrdf_table_add_field(wb, field_id++, "LReads", "Logical I/O Reads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4831 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER,
4832 - 2, "KiB/s", LReads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4833 - RRDF_FIELD_FILTER_RANGE,
4834 - RRDF_FIELD_OPTS_NONE, NULL);
4835 - buffer_rrdf_table_add_field(wb, field_id++, "LWrites", "Logical I/O Writes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4836 - RRDF_FIELD_VISUAL_BAR,
4837 - RRDF_FIELD_TRANSFORM_NUMBER,
4838 - 2, "KiB/s", LWrites_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4839 - RRDF_FIELD_FILTER_RANGE,
4840 - RRDF_FIELD_OPTS_NONE, NULL);
4841 -#endif
4842 -
4843 - // I/O calls
4844 - buffer_rrdf_table_add_field(wb, field_id++, "RCalls", "I/O Read Calls", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4845 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
4846 - "calls/s", RCalls_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4847 - RRDF_FIELD_FILTER_RANGE,
4848 - RRDF_FIELD_OPTS_NONE, NULL);
4849 - buffer_rrdf_table_add_field(wb, field_id++, "WCalls", "I/O Write Calls", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4850 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 2,
4851 - "calls/s", WCalls_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4852 - RRDF_FIELD_FILTER_RANGE,
4853 - RRDF_FIELD_OPTS_NONE, NULL);
4854 -
4855 - // minor page faults
4856 - buffer_rrdf_table_add_field(wb, field_id++, "MinFlt", "Minor Page Faults/s", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4857 - RRDF_FIELD_VISUAL_BAR,
4858 - RRDF_FIELD_TRANSFORM_NUMBER,
4859 - 2, "pgflts/s", MinFlt_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4860 - RRDF_FIELD_FILTER_RANGE,
4861 - RRDF_FIELD_OPTS_NONE, NULL);
4862 - buffer_rrdf_table_add_field(wb, field_id++, "CMinFlt", "Children Minor Page Faults/s",
4863 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4864 - RRDF_FIELD_VISUAL_BAR,
4865 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", CMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
4866 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4867 - RRDF_FIELD_OPTS_NONE, NULL);
4868 - buffer_rrdf_table_add_field(wb, field_id++, "TMinFlt", "Total Minor Page Faults/s",
4869 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4870 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", TMinFlt_max, RRDF_FIELD_SORT_DESCENDING,
4871 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4872 - RRDF_FIELD_OPTS_NONE, NULL);
4873 -
4874 - // major page faults
4875 - buffer_rrdf_table_add_field(wb, field_id++, "MajFlt", "Major Page Faults/s", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4876 - RRDF_FIELD_VISUAL_BAR,
4877 - RRDF_FIELD_TRANSFORM_NUMBER,
4878 - 2, "pgflts/s", MajFlt_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4879 - RRDF_FIELD_FILTER_RANGE,
4880 - RRDF_FIELD_OPTS_NONE, NULL);
4881 - buffer_rrdf_table_add_field(wb, field_id++, "CMajFlt", "Children Major Page Faults/s",
4882 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4883 - RRDF_FIELD_VISUAL_BAR,
4884 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", CMajFlt_max, RRDF_FIELD_SORT_DESCENDING,
4885 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4886 - RRDF_FIELD_OPTS_NONE, NULL);
4887 - buffer_rrdf_table_add_field(wb, field_id++, "TMajFlt", "Total Major Page Faults/s",
4888 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4889 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "pgflts/s", TMajFlt_max, RRDF_FIELD_SORT_DESCENDING,
4890 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4891 - RRDF_FIELD_OPTS_NONE, NULL);
4892 -
4893 - // open file descriptors
4894 - buffer_rrdf_table_add_field(wb, field_id++, "FDsLimitPercent", "Percentage of Open Descriptors vs Limits",
4895 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4896 - RRDF_FIELD_TRANSFORM_NUMBER, 2, "%", FDsLimitPercent_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4897 - RRDF_FIELD_SUMMARY_MAX, RRDF_FIELD_FILTER_RANGE,
4898 - RRDF_FIELD_OPTS_NONE, NULL);
4899 - buffer_rrdf_table_add_field(wb, field_id++, "FDs", "All Open File Descriptors",
4900 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4901 - RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", FDs_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4902 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4903 - RRDF_FIELD_OPTS_NONE, NULL);
4904 - buffer_rrdf_table_add_field(wb, field_id++, "Files", "Open Files", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4905 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4906 - "fds",
4907 - Files_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4908 - RRDF_FIELD_FILTER_RANGE,
4909 - RRDF_FIELD_OPTS_NONE, NULL);
4910 - buffer_rrdf_table_add_field(wb, field_id++, "Pipes", "Open Pipes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4911 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4912 - "fds",
4913 - Pipes_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4914 - RRDF_FIELD_FILTER_RANGE,
4915 - RRDF_FIELD_OPTS_NONE, NULL);
4916 - buffer_rrdf_table_add_field(wb, field_id++, "Sockets", "Open Sockets", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4917 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4918 - "fds", Sockets_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4919 - RRDF_FIELD_FILTER_RANGE,
4920 - RRDF_FIELD_OPTS_NONE, NULL);
4921 - buffer_rrdf_table_add_field(wb, field_id++, "iNotiFDs", "Open iNotify Descriptors",
4922 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4923 - RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", iNotiFDs_max, RRDF_FIELD_SORT_DESCENDING,
4924 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4925 - RRDF_FIELD_OPTS_NONE, NULL);
4926 - buffer_rrdf_table_add_field(wb, field_id++, "EventFDs", "Open Event Descriptors",
4927 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4928 - RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", EventFDs_max, RRDF_FIELD_SORT_DESCENDING,
4929 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4930 - RRDF_FIELD_OPTS_NONE, NULL);
4931 - buffer_rrdf_table_add_field(wb, field_id++, "TimerFDs", "Open Timer Descriptors",
4932 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4933 - RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", TimerFDs_max, RRDF_FIELD_SORT_DESCENDING,
4934 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4935 - RRDF_FIELD_OPTS_NONE, NULL);
4936 - buffer_rrdf_table_add_field(wb, field_id++, "SigFDs", "Open Signal Descriptors",
4937 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4938 - RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", SigFDs_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4939 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4940 - RRDF_FIELD_OPTS_NONE, NULL);
4941 - buffer_rrdf_table_add_field(wb, field_id++, "EvPollFDs", "Open Event Poll Descriptors",
4942 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4943 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", EvPollFDs_max,
4944 - RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4945 - RRDF_FIELD_OPTS_NONE, NULL);
4946 - buffer_rrdf_table_add_field(wb, field_id++, "OtherFDs", "Other Open Descriptors",
4947 - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, RRDF_FIELD_VISUAL_BAR,
4948 - RRDF_FIELD_TRANSFORM_NUMBER, 0, "fds", OtherFDs_max, RRDF_FIELD_SORT_DESCENDING,
4949 - NULL, RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4950 - RRDF_FIELD_OPTS_NONE, NULL);
4951 -
4952 - // processes, threads, uptime
4953 - buffer_rrdf_table_add_field(wb, field_id++, "Processes", "Processes", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4954 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4955 - "processes", Processes_max, RRDF_FIELD_SORT_DESCENDING, NULL,
4956 - RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_RANGE,
4957 - RRDF_FIELD_OPTS_NONE, NULL);
4958 - buffer_rrdf_table_add_field(wb, field_id++, "Threads", "Threads", RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
4959 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_NUMBER, 0,
4960 - "threads", Threads_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_SUM,
4961 - RRDF_FIELD_FILTER_RANGE,
4962 - RRDF_FIELD_OPTS_NONE, NULL);
4963 - buffer_rrdf_table_add_field(wb, field_id++, "Uptime", "Uptime in seconds", RRDF_FIELD_TYPE_DURATION,
4964 - RRDF_FIELD_VISUAL_BAR, RRDF_FIELD_TRANSFORM_DURATION_S, 2,
4965 - "seconds", Uptime_max, RRDF_FIELD_SORT_DESCENDING, NULL, RRDF_FIELD_SUMMARY_MAX,
4966 - RRDF_FIELD_FILTER_RANGE,
4967 - RRDF_FIELD_OPTS_VISIBLE, NULL);
4968 - }
4969 - buffer_json_object_close(wb); // columns
4970 -
4971 - buffer_json_member_add_string(wb, "default_sort_column", "CPU");
4972 -
4973 - buffer_json_member_add_object(wb, "charts");
4974 - {
4975 - // CPU chart
4976 - buffer_json_member_add_object(wb, "CPU");
4977 - {
4978 - buffer_json_member_add_string(wb, "name", "CPU Utilization");
4979 - buffer_json_member_add_string(wb, "type", "stacked-bar");
4980 - buffer_json_member_add_array(wb, "columns");
4981 - {
4982 - buffer_json_add_array_item_string(wb, "UserCPU");
4983 - buffer_json_add_array_item_string(wb, "SysCPU");
4984 - buffer_json_add_array_item_string(wb, "GuestCPU");
4985 - buffer_json_add_array_item_string(wb, "CUserCPU");
4986 - buffer_json_add_array_item_string(wb, "CSysCPU");
4987 - buffer_json_add_array_item_string(wb, "CGuestCPU");
4988 - }
4989 - buffer_json_array_close(wb);
4990 - }
4991 - buffer_json_object_close(wb);
4992 -
4993 - buffer_json_member_add_object(wb, "CPUCtxSwitches");
4994 - {
4995 - buffer_json_member_add_string(wb, "name", "CPU Context Switches");
4996 - buffer_json_member_add_string(wb, "type", "stacked-bar");
4997 - buffer_json_member_add_array(wb, "columns");
4998 - {
4999 - buffer_json_add_array_item_string(wb, "vCtxSwitch");
5000 - buffer_json_add_array_item_string(wb, "iCtxSwitch");
5001 - }
5002 - buffer_json_array_close(wb);
5003 - }
5004 - buffer_json_object_close(wb);
5005 -
5006 - // Memory chart
5007 - buffer_json_member_add_object(wb, "Memory");
5008 - {
5009 - buffer_json_member_add_string(wb, "name", "Memory");
5010 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5011 - buffer_json_member_add_array(wb, "columns");
5012 - {
5013 - buffer_json_add_array_item_string(wb, "Virtual");
5014 - buffer_json_add_array_item_string(wb, "Resident");
5015 - buffer_json_add_array_item_string(wb, "Shared");
5016 - buffer_json_add_array_item_string(wb, "Swap");
5017 - }
5018 - buffer_json_array_close(wb);
5019 - }
5020 - buffer_json_object_close(wb);
5021 -
5022 - if(MemTotal) {
5023 - // Memory chart
5024 - buffer_json_member_add_object(wb, "MemoryPercent");
5025 - {
5026 - buffer_json_member_add_string(wb, "name", "Memory Percentage");
5027 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5028 - buffer_json_member_add_array(wb, "columns");
5029 - {
5030 - buffer_json_add_array_item_string(wb, "Memory");
5031 - }
5032 - buffer_json_array_close(wb);
5033 - }
5034 - buffer_json_object_close(wb);
5035 - }
5036 -
5037 -#ifndef __FreeBSD__
5038 - // I/O Reads chart
5039 - buffer_json_member_add_object(wb, "Reads");
5040 - {
5041 - buffer_json_member_add_string(wb, "name", "I/O Reads");
5042 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5043 - buffer_json_member_add_array(wb, "columns");
5044 - {
5045 - buffer_json_add_array_item_string(wb, "LReads");
5046 - buffer_json_add_array_item_string(wb, "PReads");
5047 - }
5048 - buffer_json_array_close(wb);
5049 - }
5050 - buffer_json_object_close(wb);
5051 -
5052 - // I/O Writes chart
5053 - buffer_json_member_add_object(wb, "Writes");
5054 - {
5055 - buffer_json_member_add_string(wb, "name", "I/O Writes");
5056 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5057 - buffer_json_member_add_array(wb, "columns");
5058 - {
5059 - buffer_json_add_array_item_string(wb, "LWrites");
5060 - buffer_json_add_array_item_string(wb, "PWrites");
5061 - }
5062 - buffer_json_array_close(wb);
5063 - }
5064 - buffer_json_object_close(wb);
5065 -
5066 - // Logical I/O chart
5067 - buffer_json_member_add_object(wb, "LogicalIO");
5068 - {
5069 - buffer_json_member_add_string(wb, "name", "Logical I/O");
5070 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5071 - buffer_json_member_add_array(wb, "columns");
5072 - {
5073 - buffer_json_add_array_item_string(wb, "LReads");
5074 - buffer_json_add_array_item_string(wb, "LWrites");
5075 - }
5076 - buffer_json_array_close(wb);
5077 - }
5078 - buffer_json_object_close(wb);
5079 -#endif
5080 -
5081 - // Physical I/O chart
5082 - buffer_json_member_add_object(wb, "PhysicalIO");
5083 - {
5084 - buffer_json_member_add_string(wb, "name", "Physical I/O");
5085 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5086 - buffer_json_member_add_array(wb, "columns");
5087 - {
5088 - buffer_json_add_array_item_string(wb, "PReads");
5089 - buffer_json_add_array_item_string(wb, "PWrites");
5090 - }
5091 - buffer_json_array_close(wb);
5092 - }
5093 - buffer_json_object_close(wb);
5094 -
5095 - // I/O Calls chart
5096 - buffer_json_member_add_object(wb, "IOCalls");
5097 - {
5098 - buffer_json_member_add_string(wb, "name", "I/O Calls");
5099 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5100 - buffer_json_member_add_array(wb, "columns");
5101 - {
5102 - buffer_json_add_array_item_string(wb, "RCalls");
5103 - buffer_json_add_array_item_string(wb, "WCalls");
5104 - }
5105 - buffer_json_array_close(wb);
5106 - }
5107 - buffer_json_object_close(wb);
5108 -
5109 - // Minor Page Faults chart
5110 - buffer_json_member_add_object(wb, "MinFlt");
5111 - {
5112 - buffer_json_member_add_string(wb, "name", "Minor Page Faults");
5113 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5114 - buffer_json_member_add_array(wb, "columns");
5115 - {
5116 - buffer_json_add_array_item_string(wb, "MinFlt");
5117 - buffer_json_add_array_item_string(wb, "CMinFlt");
5118 - }
5119 - buffer_json_array_close(wb);
5120 - }
5121 - buffer_json_object_close(wb);
5122 -
5123 - // Major Page Faults chart
5124 - buffer_json_member_add_object(wb, "MajFlt");
5125 - {
5126 - buffer_json_member_add_string(wb, "name", "Major Page Faults");
5127 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5128 - buffer_json_member_add_array(wb, "columns");
5129 - {
5130 - buffer_json_add_array_item_string(wb, "MajFlt");
5131 - buffer_json_add_array_item_string(wb, "CMajFlt");
5132 - }
5133 - buffer_json_array_close(wb);
5134 - }
5135 - buffer_json_object_close(wb);
5136 -
5137 - // Threads chart
5138 - buffer_json_member_add_object(wb, "Threads");
5139 - {
5140 - buffer_json_member_add_string(wb, "name", "Threads");
5141 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5142 - buffer_json_member_add_array(wb, "columns");
5143 - {
5144 - buffer_json_add_array_item_string(wb, "Threads");
5145 - }
5146 - buffer_json_array_close(wb);
5147 - }
5148 - buffer_json_object_close(wb);
5149 -
5150 - // Processes chart
5151 - buffer_json_member_add_object(wb, "Processes");
5152 - {
5153 - buffer_json_member_add_string(wb, "name", "Processes");
5154 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5155 - buffer_json_member_add_array(wb, "columns");
5156 - {
5157 - buffer_json_add_array_item_string(wb, "Processes");
5158 - }
5159 - buffer_json_array_close(wb);
5160 - }
5161 - buffer_json_object_close(wb);
5162 -
5163 - // FDs chart
5164 - buffer_json_member_add_object(wb, "FDs");
5165 - {
5166 - buffer_json_member_add_string(wb, "name", "File Descriptors");
5167 - buffer_json_member_add_string(wb, "type", "stacked-bar");
5168 - buffer_json_member_add_array(wb, "columns");
5169 - {
5170 - buffer_json_add_array_item_string(wb, "Files");
5171 - buffer_json_add_array_item_string(wb, "Pipes");
5172 - buffer_json_add_array_item_string(wb, "Sockets");
5173 - buffer_json_add_array_item_string(wb, "iNotiFDs");
5174 - buffer_json_add_array_item_string(wb, "EventFDs");
5175 - buffer_json_add_array_item_string(wb, "TimerFDs");
5176 - buffer_json_add_array_item_string(wb, "SigFDs");
5177 - buffer_json_add_array_item_string(wb, "EvPollFDs");
5178 - buffer_json_add_array_item_string(wb, "OtherFDs");
5179 - }
5180 - buffer_json_array_close(wb);
5181 - }
5182 - buffer_json_object_close(wb);
5183 - }
5184 - buffer_json_object_close(wb); // charts
5185 -
5186 - buffer_json_member_add_array(wb, "default_charts");
5187 - {
5188 - buffer_json_add_array_item_array(wb);
5189 - buffer_json_add_array_item_string(wb, "CPU");
5190 - buffer_json_add_array_item_string(wb, "Category");
5191 - buffer_json_array_close(wb);
5192 -
5193 - buffer_json_add_array_item_array(wb);
5194 - buffer_json_add_array_item_string(wb, "Memory");
5195 - buffer_json_add_array_item_string(wb, "Category");
5196 - buffer_json_array_close(wb);
5197 - }
5198 - buffer_json_array_close(wb);
5199 -
5200 - buffer_json_member_add_object(wb, "group_by");
5201 - {
5202 - // group by PID
5203 - buffer_json_member_add_object(wb, "PID");
5204 - {
5205 - buffer_json_member_add_string(wb, "name", "Process Tree by PID");
5206 - buffer_json_member_add_array(wb, "columns");
5207 - {
5208 - buffer_json_add_array_item_string(wb, "PPID");
5209 - }
5210 - buffer_json_array_close(wb);
5211 - }
5212 - buffer_json_object_close(wb);
5213 -
5214 - // group by Category
5215 - buffer_json_member_add_object(wb, "Category");
5216 - {
5217 - buffer_json_member_add_string(wb, "name", "Process Tree by Category");
5218 - buffer_json_member_add_array(wb, "columns");
5219 - {
5220 - buffer_json_add_array_item_string(wb, "Category");
5221 - buffer_json_add_array_item_string(wb, "PPID");
5222 - }
5223 - buffer_json_array_close(wb);
5224 - }
5225 - buffer_json_object_close(wb);
5226 -
5227 - // group by User
5228 - buffer_json_member_add_object(wb, "User");
5229 - {
5230 - buffer_json_member_add_string(wb, "name", "Process Tree by User");
5231 - buffer_json_member_add_array(wb, "columns");
5232 - {
5233 - buffer_json_add_array_item_string(wb, "User");
5234 - buffer_json_add_array_item_string(wb, "PPID");
5235 - }
5236 - buffer_json_array_close(wb);
5237 - }
5238 - buffer_json_object_close(wb);
5239 -
5240 - // group by Group
5241 - buffer_json_member_add_object(wb, "Group");
5242 - {
5243 - buffer_json_member_add_string(wb, "name", "Process Tree by Group");
5244 - buffer_json_member_add_array(wb, "columns");
5245 - {
5246 - buffer_json_add_array_item_string(wb, "Group");
5247 - buffer_json_add_array_item_string(wb, "PPID");
5248 - }
5249 - buffer_json_array_close(wb);
5250 - }
5251 - buffer_json_object_close(wb);
5252 - }
5253 - buffer_json_object_close(wb); // group_by
5254 -
5255 -close_and_send:
5256 - buffer_json_member_add_time_t(wb, "expires", now_s + update_every);
5257 - buffer_json_finalize(wb);
5258 -
5259 - pluginsd_function_result_to_stdout(transaction, HTTP_RESP_OK, "application/json", now_s + update_every, wb);
5260 -
5261 - buffer_free(wb);
5262 -}
5263 -
968 static bool apps_plugin_exit = false;
969
970 int main(int argc, char **argv) {
@@ -5310,9 +1014,14 @@ int main(int argc, char **argv) {
1014 procfile_adaptive_initial_allocation = 1;
1015
1016 get_system_HZ();
5313 -#ifdef __FreeBSD__
1017 +#if defined(__FreeBSD__)
1018 time_factor = 1000000ULL / RATES_DETAIL; // FreeBSD uses usecs
5315 -#else
1019 +#endif
1020 +#if defined(__APPLE__)
1021 + mach_timebase_info(&mach_info);
1022 + time_factor = 1000000ULL / RATES_DETAIL;
1023 +#endif
1024 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
1025 time_factor = system_hz; // Linux uses clock ticks
1026 #endif
1027
@@ -5342,11 +1051,7 @@ int main(int argc, char **argv) {
1051
1052 netdata_log_info("started on pid %d", getpid());
1053
5345 - snprintfz(all_user_ids.filename, FILENAME_MAX, "%s/etc/passwd", netdata_configured_host_prefix);
5346 - debug_log("passwd file: '%s'", all_user_ids.filename);
5347 -
5348 - snprintfz(all_group_ids.filename, FILENAME_MAX, "%s/etc/group", netdata_configured_host_prefix);
5349 - debug_log("group file: '%s'", all_group_ids.filename);
1054 + users_and_groups_init();
1055
1056 #if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
1057 all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max + 1);
@@ -5398,7 +1103,7 @@ int main(int argc, char **argv) {
1103 if(global_iterations_counter % 10 == 0)
1104 get_MemTotal();
1105
5401 - if(!collect_data_for_all_processes()) {
1106 + if(!collect_data_for_all_pids()) {
1107 netdata_log_error("Cannot collect /proc data for running processes. Disabling apps.plugin...");
1108 printf("DISABLE\n");
1109 netdata_mutex_unlock(&apps_and_stdout_mutex);
@@ -5411,10 +1116,7 @@ int main(int argc, char **argv) {
1116 if(send_resource_usage)
1117 send_resource_usage_to_netdata(dt);
1118
5414 -#ifndef __FreeBSD__
1119 send_proc_states_count(dt);
5416 -#endif
5417 -
1120 send_charts_updates_to_netdata(apps_groups_root_target, "app", "app_group", "Apps");
1121 send_collected_data_to_netdata(apps_groups_root_target, "app", dt);
1122
src/collectors/apps.plugin/apps_plugin.h new
+562
@@ -0,0 +1,562 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_APPS_PLUGIN_H
4 +#define NETDATA_APPS_PLUGIN_H
5 +
6 +#include "collectors/all.h"
7 +#include "libnetdata/libnetdata.h"
8 +
9 +#ifdef __FreeBSD__
10 +#include <sys/user.h>
11 +#endif
12 +
13 +#ifdef __APPLE__
14 +#include <mach/mach.h>
15 +#include <mach/mach_host.h>
16 +#include <libproc.h>
17 +#include <sys/proc_info.h>
18 +#include <sys/sysctl.h>
19 +#include <mach/mach_time.h> // For mach_timebase_info_data_t and mach_timebase_info
20 +#endif
21 +
22 +#if defined(__APPLE__)
23 +extern mach_timebase_info_data_t mach_info;
24 +#endif
25 +
26 +// ----------------------------------------------------------------------------
27 +// per O/S configuration
28 +
29 +// the minimum PID of the system
30 +// this is also the pid of the init process
31 +#define INIT_PID 1
32 +
33 +// if the way apps.plugin will work, will read the entire process list,
34 +// including the resource utilization of each process, instantly
35 +// set this to 1
36 +// when set to 0, apps.plugin builds a sort list of processes, in order
37 +// to process children processes, before parent processes
38 +#if defined(__FreeBSD__) || defined(__APPLE__)
39 +#define ALL_PIDS_ARE_READ_INSTANTLY 1
40 +#else
41 +#define ALL_PIDS_ARE_READ_INSTANTLY 0
42 +#endif
43 +
44 +#if defined(__APPLE__)
45 +struct pid_info {
46 + struct kinfo_proc proc;
47 + struct proc_taskinfo taskinfo;
48 + struct proc_bsdinfo bsdinfo;
49 + struct rusage_info_v4 rusageinfo;
50 +
51 +};
52 +#endif
53 +
54 +// ----------------------------------------------------------------------------
55 +
56 +extern bool debug_enabled;
57 +extern bool enable_guest_charts;
58 +extern bool enable_detailed_uptime_charts;
59 +extern bool enable_users_charts;
60 +extern bool enable_groups_charts;
61 +extern bool include_exited_childs;
62 +extern bool enable_function_cmdline;
63 +extern bool proc_pid_cmdline_is_needed;
64 +extern bool enable_file_charts;
65 +
66 +extern size_t
67 + global_iterations_counter,
68 + calls_counter,
69 + file_counter,
70 + filenames_allocated_counter,
71 + inodes_changed_counter,
72 + links_changed_counter,
73 + targets_assignment_counter,
74 + all_pids_count,
75 + apps_groups_targets_count;
76 +
77 +extern int
78 + all_files_len,
79 + all_files_size,
80 + show_guest_time,
81 + show_guest_time_old;
82 +
83 +extern kernel_uint_t
84 + global_utime,
85 + global_stime,
86 + global_gtime;
87 +
88 +// the normalization ratios, as calculated by normalize_utilization()
89 +extern NETDATA_DOUBLE
90 + utime_fix_ratio,
91 + stime_fix_ratio,
92 + gtime_fix_ratio,
93 + minflt_fix_ratio,
94 + majflt_fix_ratio,
95 + cutime_fix_ratio,
96 + cstime_fix_ratio,
97 + cgtime_fix_ratio,
98 + cminflt_fix_ratio,
99 + cmajflt_fix_ratio;
100 +
101 +#if defined(__FreeBSD__) || defined(__APPLE__)
102 +extern usec_t system_current_time_ut;
103 +#else
104 +extern kernel_uint_t system_uptime_secs;
105 +#endif
106 +
107 +extern size_t pagesize;
108 +
109 +// ----------------------------------------------------------------------------
110 +// string lengths
111 +
112 +#define MAX_COMPARE_NAME 100
113 +#define MAX_NAME 100
114 +#define MAX_CMDLINE 65536
115 +
116 +// ----------------------------------------------------------------------------
117 +// to avoid reallocating too frequently, we can increase the number of spare
118 +// file descriptors used by processes.
119 +// IMPORTANT:
120 +// having a lot of spares, increases the CPU utilization of the plugin.
121 +#define MAX_SPARE_FDS 1
122 +
123 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
124 +extern int max_fds_cache_seconds;
125 +#endif
126 +
127 +// ----------------------------------------------------------------------------
128 +// some variables for keeping track of processes count by states
129 +
130 +typedef enum {
131 + PROC_STATUS_RUNNING = 0,
132 + PROC_STATUS_SLEEPING_D, // uninterruptible sleep
133 + PROC_STATUS_SLEEPING, // interruptible sleep
134 + PROC_STATUS_ZOMBIE,
135 + PROC_STATUS_STOPPED,
136 + PROC_STATUS_END, //place holder for ending enum fields
137 +} proc_state;
138 +
139 +extern proc_state proc_state_count[PROC_STATUS_END];
140 +extern const char *proc_states[];
141 +
142 +// ----------------------------------------------------------------------------
143 +// the rates we are going to send to netdata will have this detail a value of:
144 +// - 1 will send just integer parts to netdata
145 +// - 100 will send 2 decimal points
146 +// - 1000 will send 3 decimal points
147 +// etc.
148 +#define RATES_DETAIL 10000ULL
149 +
150 +struct openfds {
151 + kernel_uint_t files;
152 + kernel_uint_t pipes;
153 + kernel_uint_t sockets;
154 + kernel_uint_t inotifies;
155 + kernel_uint_t eventfds;
156 + kernel_uint_t timerfds;
157 + kernel_uint_t signalfds;
158 + kernel_uint_t eventpolls;
159 + kernel_uint_t other;
160 +};
161 +
162 +#define pid_openfds_sum(p) ((p)->openfds.files + (p)->openfds.pipes + (p)->openfds.sockets + (p)->openfds.inotifies + (p)->openfds.eventfds + (p)->openfds.timerfds + (p)->openfds.signalfds + (p)->openfds.eventpolls + (p)->openfds.other)
163 +
164 +// ----------------------------------------------------------------------------
165 +// target
166 +//
167 +// target is the structure that processes are aggregated to be reported
168 +// to netdata.
169 +//
170 +// - Each entry in /etc/apps_groups.conf creates a target.
171 +// - Each user and group used by a process in the system, creates a target.
172 +
173 +struct pid_on_target {
174 + int32_t pid;
175 + struct pid_on_target *next;
176 +};
177 +
178 +struct target {
179 + char compare[MAX_COMPARE_NAME + 1];
180 + uint32_t comparehash;
181 + size_t comparelen;
182 +
183 + char id[MAX_NAME + 1];
184 + uint32_t idhash;
185 +
186 + char name[MAX_NAME + 1];
187 + char clean_name[MAX_NAME + 1]; // sanitized name used in chart id (need to replace at least dots)
188 + uid_t uid;
189 + gid_t gid;
190 +
191 + bool is_other;
192 +
193 + kernel_uint_t minflt;
194 + kernel_uint_t cminflt;
195 + kernel_uint_t majflt;
196 + kernel_uint_t cmajflt;
197 + kernel_uint_t utime;
198 + kernel_uint_t stime;
199 + kernel_uint_t gtime;
200 + kernel_uint_t cutime;
201 + kernel_uint_t cstime;
202 + kernel_uint_t cgtime;
203 + kernel_uint_t num_threads;
204 + // kernel_uint_t rss;
205 +
206 + kernel_uint_t status_vmsize;
207 + kernel_uint_t status_vmrss;
208 + kernel_uint_t status_vmshared;
209 + kernel_uint_t status_rssfile;
210 + kernel_uint_t status_rssshmem;
211 + kernel_uint_t status_vmswap;
212 + kernel_uint_t status_voluntary_ctxt_switches;
213 + kernel_uint_t status_nonvoluntary_ctxt_switches;
214 +
215 + kernel_uint_t io_logical_bytes_read;
216 + kernel_uint_t io_logical_bytes_written;
217 + kernel_uint_t io_read_calls;
218 + kernel_uint_t io_write_calls;
219 + kernel_uint_t io_storage_bytes_read;
220 + kernel_uint_t io_storage_bytes_written;
221 + kernel_uint_t io_cancelled_write_bytes;
222 +
223 + int *target_fds;
224 + int target_fds_size;
225 +
226 + struct openfds openfds;
227 +
228 + NETDATA_DOUBLE max_open_files_percent;
229 +
230 + kernel_uint_t uptime_min;
231 + kernel_uint_t uptime_sum;
232 + kernel_uint_t uptime_max;
233 +
234 + unsigned int processes; // how many processes have been merged to this
235 + int exposed; // if set, we have sent this to netdata
236 + int hidden; // if set, we set the hidden flag on the dimension
237 + int debug_enabled;
238 + int ends_with;
239 + int starts_with; // if set, the compare string matches only the
240 + // beginning of the command
241 +
242 + struct pid_on_target *root_pid; // list of aggregated pids for target debugging
243 +
244 + struct target *target; // the one that will be reported to netdata
245 + struct target *next;
246 +};
247 +
248 +// ----------------------------------------------------------------------------
249 +// internal flags
250 +// handled in code (automatically set)
251 +
252 +// log each problem once per process
253 +// log flood protection flags (log_thrown)
254 +typedef enum __attribute__((packed)) {
255 + PID_LOG_IO = (1 << 0),
256 + PID_LOG_STATUS = (1 << 1),
257 + PID_LOG_CMDLINE = (1 << 2),
258 + PID_LOG_FDS = (1 << 3),
259 + PID_LOG_STAT = (1 << 4),
260 + PID_LOG_LIMITS = (1 << 5),
261 + PID_LOG_LIMITS_DETAIL = (1 << 6),
262 +} PID_LOG;
263 +
264 +// ----------------------------------------------------------------------------
265 +// pid_stat
266 +//
267 +// structure to store data for each process running
268 +// see: man proc for the description of the fields
269 +
270 +struct pid_limits {
271 + // kernel_uint_t max_cpu_time;
272 + // kernel_uint_t max_file_size;
273 + // kernel_uint_t max_data_size;
274 + // kernel_uint_t max_stack_size;
275 + // kernel_uint_t max_core_file_size;
276 + // kernel_uint_t max_resident_set;
277 + // kernel_uint_t max_processes;
278 + kernel_uint_t max_open_files;
279 + // kernel_uint_t max_locked_memory;
280 + // kernel_uint_t max_address_space;
281 + // kernel_uint_t max_file_locks;
282 + // kernel_uint_t max_pending_signals;
283 + // kernel_uint_t max_msgqueue_size;
284 + // kernel_uint_t max_nice_priority;
285 + // kernel_uint_t max_realtime_priority;
286 + // kernel_uint_t max_realtime_timeout;
287 +};
288 +
289 +struct pid_fd {
290 + int fd;
291 +
292 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
293 + ino_t inode;
294 + char *filename;
295 + uint32_t link_hash;
296 + size_t cache_iterations_counter;
297 + size_t cache_iterations_reset;
298 +#endif
299 +};
300 +
301 +struct pid_stat {
302 + int32_t pid;
303 + int32_t ppid;
304 + // int32_t pgrp;
305 + // int32_t session;
306 + // int32_t tty_nr;
307 + // int32_t tpgid;
308 + // uint64_t flags;
309 +
310 + char state;
311 +
312 + char comm[MAX_COMPARE_NAME + 1];
313 + char *cmdline;
314 +
315 + // these are raw values collected
316 + kernel_uint_t minflt_raw;
317 + kernel_uint_t cminflt_raw;
318 + kernel_uint_t majflt_raw;
319 + kernel_uint_t cmajflt_raw;
320 + kernel_uint_t utime_raw;
321 + kernel_uint_t stime_raw;
322 + kernel_uint_t gtime_raw; // guest_time
323 + kernel_uint_t cutime_raw;
324 + kernel_uint_t cstime_raw;
325 + kernel_uint_t cgtime_raw; // cguest_time
326 +
327 + // these are rates
328 + kernel_uint_t minflt;
329 + kernel_uint_t cminflt;
330 + kernel_uint_t majflt;
331 + kernel_uint_t cmajflt;
332 + kernel_uint_t utime;
333 + kernel_uint_t stime;
334 + kernel_uint_t gtime;
335 + kernel_uint_t cutime;
336 + kernel_uint_t cstime;
337 + kernel_uint_t cgtime;
338 +
339 + // int64_t priority;
340 + // int64_t nice;
341 + int32_t num_threads;
342 + // int64_t itrealvalue;
343 + // kernel_uint_t collected_starttime;
344 + // kernel_uint_t vsize;
345 + // kernel_uint_t rss;
346 + // kernel_uint_t rsslim;
347 + // kernel_uint_t starcode;
348 + // kernel_uint_t endcode;
349 + // kernel_uint_t startstack;
350 + // kernel_uint_t kstkesp;
351 + // kernel_uint_t kstkeip;
352 + // uint64_t signal;
353 + // uint64_t blocked;
354 + // uint64_t sigignore;
355 + // uint64_t sigcatch;
356 + // uint64_t wchan;
357 + // uint64_t nswap;
358 + // uint64_t cnswap;
359 + // int32_t exit_signal;
360 + // int32_t processor;
361 + // uint32_t rt_priority;
362 + // uint32_t policy;
363 + // kernel_uint_t delayacct_blkio_ticks;
364 +
365 + uid_t uid;
366 + gid_t gid;
367 +
368 + kernel_uint_t status_voluntary_ctxt_switches_raw;
369 + kernel_uint_t status_nonvoluntary_ctxt_switches_raw;
370 +
371 + kernel_uint_t status_vmsize;
372 + kernel_uint_t status_vmrss;
373 + kernel_uint_t status_vmshared;
374 + kernel_uint_t status_rssfile;
375 + kernel_uint_t status_rssshmem;
376 + kernel_uint_t status_vmswap;
377 + kernel_uint_t status_voluntary_ctxt_switches;
378 + kernel_uint_t status_nonvoluntary_ctxt_switches;
379 +#ifndef __FreeBSD__
380 + ARL_BASE *status_arl;
381 +#endif
382 +
383 + kernel_uint_t io_logical_bytes_read_raw;
384 + kernel_uint_t io_logical_bytes_written_raw;
385 + kernel_uint_t io_read_calls_raw;
386 + kernel_uint_t io_write_calls_raw;
387 + kernel_uint_t io_storage_bytes_read_raw;
388 + kernel_uint_t io_storage_bytes_written_raw;
389 + kernel_uint_t io_cancelled_write_bytes_raw;
390 +
391 + kernel_uint_t io_logical_bytes_read;
392 + kernel_uint_t io_logical_bytes_written;
393 + kernel_uint_t io_read_calls;
394 + kernel_uint_t io_write_calls;
395 + kernel_uint_t io_storage_bytes_read;
396 + kernel_uint_t io_storage_bytes_written;
397 + kernel_uint_t io_cancelled_write_bytes;
398 +
399 + kernel_uint_t uptime;
400 +
401 + struct pid_fd *fds; // array of fds it uses
402 + size_t fds_size; // the size of the fds array
403 +
404 + struct openfds openfds;
405 + struct pid_limits limits;
406 +
407 + NETDATA_DOUBLE openfds_limits_percent;
408 +
409 + int sortlist; // higher numbers = top on the process tree
410 + // each process gets a unique number
411 +
412 + int children_count; // number of processes directly referencing this
413 + int keeploops; // increases by 1 every time keep is 1 and updated 0
414 +
415 + PID_LOG log_thrown;
416 +
417 + bool keep; // true when we need to keep this process in memory even after it exited
418 + bool updated; // true when the process is currently running
419 + bool merged; // true when it has been merged to its parent
420 + bool read; // true when we have already read this process for this iteration
421 + bool matched_by_config;
422 +
423 + struct target *target; // app_groups.conf targets
424 + struct target *user_target; // uid based targets
425 + struct target *group_target; // gid based targets
426 +
427 + usec_t stat_collected_usec;
428 + usec_t last_stat_collected_usec;
429 +
430 + usec_t io_collected_usec;
431 + usec_t last_io_collected_usec;
432 + usec_t last_limits_collected_usec;
433 +
434 + char *fds_dirname; // the full directory name in /proc/PID/fd
435 +
436 + char *stat_filename;
437 + char *status_filename;
438 + char *io_filename;
439 + char *cmdline_filename;
440 + char *limits_filename;
441 +
442 + struct pid_stat *parent;
443 + struct pid_stat *prev;
444 + struct pid_stat *next;
445 +};
446 +
447 +// ----------------------------------------------------------------------------
448 +
449 +struct user_or_group_id {
450 + avl_t avl;
451 +
452 + union {
453 + uid_t uid;
454 + gid_t gid;
455 + } id;
456 +
457 + char *name;
458 +
459 + int updated;
460 +
461 + struct user_or_group_id * next;
462 +};
463 +
464 +extern struct target
465 + *apps_groups_default_target,
466 + *apps_groups_root_target,
467 + *users_root_target,
468 + *groups_root_target;
469 +
470 +extern struct pid_stat
471 + *root_of_pids,
472 + **all_pids;
473 +
474 +extern int update_every;
475 +extern unsigned int time_factor;
476 +extern kernel_uint_t MemTotal;
477 +
478 +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
479 +extern pid_t *all_pids_sortlist;
480 +#endif
481 +
482 +#define APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION "Detailed information on the currently running processes."
483 +
484 +void function_processes(const char *transaction, char *function,
485 + usec_t *stop_monotonic_ut __maybe_unused, bool *cancelled __maybe_unused,
486 + BUFFER *payload __maybe_unused, HTTP_ACCESS access,
487 + const char *source __maybe_unused, void *data __maybe_unused);
488 +
489 +struct target *find_target_by_name(struct target *base, const char *name);
490 +
491 +struct target *get_users_target(uid_t uid);
492 +struct target *get_groups_target(gid_t gid);
493 +int read_apps_groups_conf(const char *path, const char *file);
494 +
495 +void users_and_groups_init(void);
496 +struct user_or_group_id *user_id_find(struct user_or_group_id *user_id_to_find);
497 +struct user_or_group_id *group_id_find(struct user_or_group_id *group_id_to_find);
498 +
499 +// ----------------------------------------------------------------------------
500 +// debugging
501 +
502 +static inline void debug_log_int(const char *fmt, ... ) {
503 + va_list args;
504 +
505 + fprintf( stderr, "apps.plugin: ");
506 + va_start( args, fmt );
507 + vfprintf( stderr, fmt, args );
508 + va_end( args );
509 +
510 + fputc('\n', stderr);
511 +}
512 +
513 +#ifdef NETDATA_INTERNAL_CHECKS
514 +
515 +#define debug_log(fmt, args...) do { if(unlikely(debug_enabled)) debug_log_int(fmt, ##args); } while(0)
516 +
517 +#else
518 +
519 +static inline void debug_log_dummy(void) {}
520 +#define debug_log(fmt, args...) debug_log_dummy()
521 +
522 +#endif
523 +int managed_log(struct pid_stat *p, PID_LOG log, int status);
524 +
525 +// ----------------------------------------------------------------------------
526 +// macro to calculate the incremental rate of a value
527 +// each parameter is accessed only ONCE - so it is safe to pass function calls
528 +// or other macros as parameters
529 +
530 +#define incremental_rate(rate_variable, last_kernel_variable, new_kernel_value, collected_usec, last_collected_usec) do { \
531 + kernel_uint_t _new_tmp = new_kernel_value; \
532 + (rate_variable) = (_new_tmp - (last_kernel_variable)) * (USEC_PER_SEC * RATES_DETAIL) / ((collected_usec) - (last_collected_usec)); \
533 + (last_kernel_variable) = _new_tmp; \
534 + } while(0)
535 +
536 +// the same macro for struct pid members
537 +#define pid_incremental_rate(type, var, value) \
538 + incremental_rate(var, var##_raw, value, p->type##_collected_usec, p->last_##type##_collected_usec)
539 +
540 +int read_proc_pid_stat(struct pid_stat *p, void *ptr);
541 +int read_proc_pid_limits(struct pid_stat *p, void *ptr);
542 +int read_proc_pid_status(struct pid_stat *p, void *ptr);
543 +int read_proc_pid_cmdline(struct pid_stat *p);
544 +int read_proc_pid_io(struct pid_stat *p, void *ptr);
545 +int read_pid_file_descriptors(struct pid_stat *p, void *ptr);
546 +int read_global_time(void);
547 +void get_MemTotal(void);
548 +
549 +bool collect_data_for_all_pids(void);
550 +void cleanup_exited_pids(void);
551 +
552 +void clear_pid_fd(struct pid_fd *pfd);
553 +void file_descriptor_not_used(int id);
554 +void init_pid_fds(struct pid_stat *p, size_t first, size_t size);
555 +void aggregate_pid_fds_on_targets(struct pid_stat *p);
556 +
557 +void send_proc_states_count(usec_t dt);
558 +void send_charts_updates_to_netdata(struct target *root, const char *type, const char *lbl_name, const char *title);
559 +void send_collected_data_to_netdata(struct target *root, const char *type, usec_t dt);
560 +void send_resource_usage_to_netdata(usec_t dt);
561 +
562 +#endif //NETDATA_APPS_PLUGIN_H
src/collectors/apps.plugin/apps_proc_meminfo.c new
+68
@@ -0,0 +1,68 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +kernel_uint_t MemTotal = 0;
6 +
7 +#ifdef __FreeBSD__
8 +static inline bool get_MemTotal_per_os(void) {
9 + int mib[2] = {CTL_HW, HW_PHYSMEM};
10 + size_t size = sizeof(MemTotal);
11 + if (sysctl(mib, 2, &MemTotal, &size, NULL, 0) == -1) {
12 + netdata_log_error("Failed to get total memory using sysctl");
13 + return false;
14 + }
15 + // FreeBSD returns bytes; convert to kB
16 + MemTotal /= 1024;
17 + return true;
18 +}
19 +#endif // __FreeBSD__
20 +
21 +#ifdef __APPLE__
22 +static inline bool get_MemTotal_per_os(void) {
23 + int mib[2] = {CTL_HW, HW_MEMSIZE};
24 + size_t size = sizeof(MemTotal);
25 + if (sysctl(mib, 2, &MemTotal, &size, NULL, 0) == -1) {
26 + netdata_log_error("Failed to get total memory using sysctl");
27 + return false;
28 + }
29 + // MacOS returns bytes; convert to kB
30 + MemTotal /= 1024;
31 + return true;
32 +}
33 +#endif // __APPLE__
34 +
35 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
36 +static inline bool get_MemTotal_per_os(void) {
37 + char filename[FILENAME_MAX + 1];
38 + snprintfz(filename, FILENAME_MAX, "%s/proc/meminfo", netdata_configured_host_prefix);
39 +
40 + procfile *ff = procfile_open(filename, ": \t", PROCFILE_FLAG_DEFAULT);
41 + if(!ff)
42 + return false;
43 +
44 + ff = procfile_readall(ff);
45 + if(!ff)
46 + return false;
47 +
48 + size_t line, lines = procfile_lines(ff);
49 +
50 + for(line = 0; line < lines ;line++) {
51 + size_t words = procfile_linewords(ff, line);
52 + if(words == 3 && strcmp(procfile_lineword(ff, line, 0), "MemTotal") == 0 && strcmp(procfile_lineword(ff, line, 2), "kB") == 0) {
53 + kernel_uint_t n = str2ull(procfile_lineword(ff, line, 1), NULL);
54 + if(n) MemTotal = n;
55 + break;
56 + }
57 + }
58 +
59 + procfile_close(ff);
60 +
61 + return true;
62 +}
63 +#endif
64 +
65 +void get_MemTotal(void) {
66 + if(!get_MemTotal_per_os())
67 + MemTotal = 0;
68 +}
src/collectors/apps.plugin/apps_proc_pid_cmdline.c new
+130
@@ -0,0 +1,130 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +#ifdef __APPLE__
6 +bool get_cmdline_per_os(struct pid_stat *p, char *cmdline, size_t maxBytes) {
7 + int mib[3] = {CTL_KERN, KERN_PROCARGS2, p->pid};
8 + static char *args = NULL;
9 + static size_t size = 0;
10 +
11 + size_t new_size;
12 + if (sysctl(mib, 3, NULL, &new_size, NULL, 0) == -1) {
13 + return false;
14 + }
15 +
16 + if (new_size > size) {
17 + if (args)
18 + freez(args);
19 +
20 + args = (char *)mallocz(new_size);
21 + size = new_size;
22 + }
23 +
24 + memset(cmdline, 0, new_size < maxBytes ? new_size : maxBytes);
25 +
26 + size_t used_size = size;
27 + if (sysctl(mib, 3, args, &used_size, NULL, 0) == -1)
28 + return false;
29 +
30 + int argc;
31 + memcpy(&argc, args, sizeof(argc));
32 + char *ptr = args + sizeof(argc);
33 + used_size -= sizeof(argc);
34 +
35 + // Skip the executable path
36 + while (*ptr && used_size > 0) {
37 + ptr++;
38 + used_size--;
39 + }
40 +
41 + // Copy only the arguments to the cmdline buffer, skipping the environment variables
42 + size_t i = 0, copied_args = 0;
43 + bool inArg = false;
44 + for (; used_size > 0 && i < maxBytes - 1 && copied_args < argc; --used_size, ++ptr) {
45 + if (*ptr == '\0') {
46 + if (inArg) {
47 + cmdline[i++] = ' '; // Replace nulls between arguments with spaces
48 + inArg = false;
49 + copied_args++;
50 + }
51 + } else {
52 + cmdline[i++] = *ptr;
53 + inArg = true;
54 + }
55 + }
56 +
57 + if (i > 0 && cmdline[i - 1] == ' ')
58 + i--; // Remove the trailing space if present
59 +
60 + cmdline[i] = '\0'; // Null-terminate the string
61 +
62 + return true;
63 +}
64 +#endif // __APPLE__
65 +
66 +#if defined(__FreeBSD__)
67 +static inline bool get_cmdline_per_os(struct pid_stat *p, char *cmdline, size_t bytes) {
68 + size_t i, b = bytes - 1;
69 + int mib[4];
70 +
71 + mib[0] = CTL_KERN;
72 + mib[1] = KERN_PROC;
73 + mib[2] = KERN_PROC_ARGS;
74 + mib[3] = p->pid;
75 + if (unlikely(sysctl(mib, 4, cmdline, &b, NULL, 0)))
76 + return false;
77 +
78 + cmdline[b] = '\0';
79 + for(i = 0; i < b ; i++)
80 + if(unlikely(!cmdline[i])) cmdline[i] = ' ';
81 +
82 + return true;
83 +}
84 +#endif // __FreeBSD__
85 +
86 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
87 +static inline bool get_cmdline_per_os(struct pid_stat *p, char *cmdline, size_t bytes) {
88 + if(unlikely(!p->cmdline_filename)) {
89 + char filename[FILENAME_MAX];
90 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", netdata_configured_host_prefix, p->pid);
91 + p->cmdline_filename = strdupz(filename);
92 + }
93 +
94 + int fd = open(p->cmdline_filename, procfile_open_flags, 0666);
95 + if(unlikely(fd == -1))
96 + return false;
97 +
98 + ssize_t i, b = read(fd, cmdline, bytes - 1);
99 + close(fd);
100 +
101 + if(unlikely(b < 0))
102 + return false;
103 +
104 + cmdline[b] = '\0';
105 + for(i = 0; i < b ; i++)
106 + if(unlikely(!cmdline[i])) cmdline[i] = ' ';
107 +
108 + return true;
109 +}
110 +#endif // !__FreeBSD__ !__APPLE__
111 +
112 +int read_proc_pid_cmdline(struct pid_stat *p) {
113 + static char cmdline[MAX_CMDLINE];
114 +
115 + if(unlikely(!get_cmdline_per_os(p, cmdline, sizeof(cmdline))))
116 + goto cleanup;
117 +
118 + if(p->cmdline) freez(p->cmdline);
119 + p->cmdline = strdupz(cmdline);
120 +
121 + debug_log("Read file '%s' contents: %s", p->cmdline_filename, p->cmdline);
122 +
123 + return 1;
124 +
125 +cleanup:
126 + // copy the command to the command line
127 + if(p->cmdline) freez(p->cmdline);
128 + p->cmdline = strdupz(p->comm);
129 + return 0;
130 +}
src/collectors/apps.plugin/apps_proc_pid_fd.c new
+751
@@ -0,0 +1,751 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// file descriptor
7 +//
8 +// this is used to keep a global list of all open files of the system.
9 +// it is needed in order to calculate the unique files processes have open.
10 +
11 +#define FILE_DESCRIPTORS_INCREASE_STEP 100
12 +
13 +// types for struct file_descriptor->type
14 +typedef enum fd_filetype {
15 + FILETYPE_OTHER,
16 + FILETYPE_FILE,
17 + FILETYPE_PIPE,
18 + FILETYPE_SOCKET,
19 + FILETYPE_INOTIFY,
20 + FILETYPE_EVENTFD,
21 + FILETYPE_EVENTPOLL,
22 + FILETYPE_TIMERFD,
23 + FILETYPE_SIGNALFD
24 +} FD_FILETYPE;
25 +
26 +struct file_descriptor {
27 + avl_t avl;
28 +
29 +#ifdef NETDATA_INTERNAL_CHECKS
30 + uint32_t magic;
31 +#endif /* NETDATA_INTERNAL_CHECKS */
32 +
33 + const char *name;
34 + uint32_t hash;
35 +
36 + FD_FILETYPE type;
37 + int count;
38 + int pos;
39 +} *all_files = NULL;
40 +
41 +// ----------------------------------------------------------------------------
42 +
43 +static inline void reallocate_target_fds(struct target *w) {
44 + if(unlikely(!w))
45 + return;
46 +
47 + if(unlikely(!w->target_fds || w->target_fds_size < all_files_size)) {
48 + w->target_fds = reallocz(w->target_fds, sizeof(int) * all_files_size);
49 + memset(&w->target_fds[w->target_fds_size], 0, sizeof(int) * (all_files_size - w->target_fds_size));
50 + w->target_fds_size = all_files_size;
51 + }
52 +}
53 +
54 +static void aggregage_fd_type_on_openfds(FD_FILETYPE type, struct openfds *openfds) {
55 + switch(type) {
56 + case FILETYPE_FILE:
57 + openfds->files++;
58 + break;
59 +
60 + case FILETYPE_PIPE:
61 + openfds->pipes++;
62 + break;
63 +
64 + case FILETYPE_SOCKET:
65 + openfds->sockets++;
66 + break;
67 +
68 + case FILETYPE_INOTIFY:
69 + openfds->inotifies++;
70 + break;
71 +
72 + case FILETYPE_EVENTFD:
73 + openfds->eventfds++;
74 + break;
75 +
76 + case FILETYPE_TIMERFD:
77 + openfds->timerfds++;
78 + break;
79 +
80 + case FILETYPE_SIGNALFD:
81 + openfds->signalfds++;
82 + break;
83 +
84 + case FILETYPE_EVENTPOLL:
85 + openfds->eventpolls++;
86 + break;
87 +
88 + case FILETYPE_OTHER:
89 + openfds->other++;
90 + break;
91 + }
92 +}
93 +
94 +static inline void aggregate_fd_on_target(int fd, struct target *w) {
95 + if(unlikely(!w))
96 + return;
97 +
98 + if(unlikely(w->target_fds[fd])) {
99 + // it is already aggregated
100 + // just increase its usage counter
101 + w->target_fds[fd]++;
102 + return;
103 + }
104 +
105 + // increase its usage counter
106 + // so that we will not add it again
107 + w->target_fds[fd]++;
108 +
109 + aggregage_fd_type_on_openfds(all_files[fd].type, &w->openfds);
110 +}
111 +
112 +void aggregate_pid_fds_on_targets(struct pid_stat *p) {
113 +
114 + if(unlikely(!p->updated)) {
115 + // the process is not running
116 + return;
117 + }
118 +
119 + struct target *w = p->target, *u = p->user_target, *g = p->group_target;
120 +
121 + reallocate_target_fds(w);
122 + reallocate_target_fds(u);
123 + reallocate_target_fds(g);
124 +
125 + p->openfds.files = 0;
126 + p->openfds.pipes = 0;
127 + p->openfds.sockets = 0;
128 + p->openfds.inotifies = 0;
129 + p->openfds.eventfds = 0;
130 + p->openfds.timerfds = 0;
131 + p->openfds.signalfds = 0;
132 + p->openfds.eventpolls = 0;
133 + p->openfds.other = 0;
134 +
135 + long currentfds = 0;
136 + size_t c, size = p->fds_size;
137 + struct pid_fd *fds = p->fds;
138 + for(c = 0; c < size ;c++) {
139 + int fd = fds[c].fd;
140 +
141 + if(likely(fd <= 0 || fd >= all_files_size))
142 + continue;
143 +
144 + currentfds++;
145 + aggregage_fd_type_on_openfds(all_files[fd].type, &p->openfds);
146 +
147 + aggregate_fd_on_target(fd, w);
148 + aggregate_fd_on_target(fd, u);
149 + aggregate_fd_on_target(fd, g);
150 + }
151 +}
152 +
153 +// ----------------------------------------------------------------------------
154 +
155 +int file_descriptor_compare(void* a, void* b) {
156 +#ifdef NETDATA_INTERNAL_CHECKS
157 + if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
158 + netdata_log_error("Corrupted index data detected. Please report this.");
159 +#endif /* NETDATA_INTERNAL_CHECKS */
160 +
161 + if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
162 + return -1;
163 +
164 + else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
165 + return 1;
166 +
167 + else
168 + return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
169 +}
170 +
171 +// int file_descriptor_iterator(avl_t *a) { if(a) {}; return 0; }
172 +
173 +avl_tree_type all_files_index = {
174 + NULL,
175 + file_descriptor_compare
176 +};
177 +
178 +static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
179 + struct file_descriptor tmp;
180 + tmp.hash = (hash)?hash:simple_hash(name);
181 + tmp.name = name;
182 + tmp.count = 0;
183 + tmp.pos = 0;
184 +#ifdef NETDATA_INTERNAL_CHECKS
185 + tmp.magic = 0x0BADCAFE;
186 +#endif /* NETDATA_INTERNAL_CHECKS */
187 +
188 + return (struct file_descriptor *)avl_search(&all_files_index, (avl_t *) &tmp);
189 +}
190 +
191 +#define file_descriptor_add(fd) avl_insert(&all_files_index, (avl_t *)(fd))
192 +#define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl_t *)(fd))
193 +
194 +// ----------------------------------------------------------------------------
195 +
196 +void file_descriptor_not_used(int id) {
197 + if(id > 0 && id < all_files_size) {
198 +
199 +#ifdef NETDATA_INTERNAL_CHECKS
200 + if(all_files[id].magic != 0x0BADCAFE) {
201 + netdata_log_error("Ignoring request to remove empty file id %d.", id);
202 + return;
203 + }
204 +#endif /* NETDATA_INTERNAL_CHECKS */
205 +
206 + debug_log("decreasing slot %d (count = %d).", id, all_files[id].count);
207 +
208 + if(all_files[id].count > 0) {
209 + all_files[id].count--;
210 +
211 + if(!all_files[id].count) {
212 + debug_log(" >> slot %d is empty.", id);
213 +
214 + if(unlikely(file_descriptor_remove(&all_files[id]) != (void *)&all_files[id]))
215 + netdata_log_error("INTERNAL ERROR: removal of unused fd from index, removed a different fd");
216 +
217 +#ifdef NETDATA_INTERNAL_CHECKS
218 + all_files[id].magic = 0x00000000;
219 +#endif /* NETDATA_INTERNAL_CHECKS */
220 + all_files_len--;
221 + }
222 + }
223 + else
224 + netdata_log_error("Request to decrease counter of fd %d (%s), while the use counter is 0",
225 + id,
226 + all_files[id].name);
227 + }
228 + else
229 + netdata_log_error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)",
230 + id,
231 + all_files_size);
232 +}
233 +
234 +static inline void all_files_grow() {
235 + void *old = all_files;
236 + int i;
237 +
238 + // there is no empty slot
239 + debug_log("extending fd array to %d entries", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
240 +
241 + all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
242 +
243 + // if the address changed, we have to rebuild the index
244 + // since all pointers are now invalid
245 +
246 + if(unlikely(old && old != (void *)all_files)) {
247 + debug_log(" >> re-indexing.");
248 +
249 + all_files_index.root = NULL;
250 + for(i = 0; i < all_files_size; i++) {
251 + if(!all_files[i].count) continue;
252 + if(unlikely(file_descriptor_add(&all_files[i]) != (void *)&all_files[i]))
253 + netdata_log_error("INTERNAL ERROR: duplicate indexing of fd during realloc.");
254 + }
255 +
256 + debug_log(" >> re-indexing done.");
257 + }
258 +
259 + // initialize the newly added entries
260 +
261 + for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
262 + all_files[i].count = 0;
263 + all_files[i].name = NULL;
264 +#ifdef NETDATA_INTERNAL_CHECKS
265 + all_files[i].magic = 0x00000000;
266 +#endif /* NETDATA_INTERNAL_CHECKS */
267 + all_files[i].pos = i;
268 + }
269 +
270 + if(unlikely(!all_files_size)) all_files_len = 1;
271 + all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
272 +}
273 +
274 +static inline int file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, FD_FILETYPE type) {
275 + // check we have enough memory to add it
276 + if(!all_files || all_files_len == all_files_size)
277 + all_files_grow();
278 +
279 + debug_log(" >> searching for empty slot.");
280 +
281 + // search for an empty slot
282 +
283 + static int last_pos = 0;
284 + int i, c;
285 + for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
286 + if(c >= all_files_size) c = 0;
287 + if(c == 0) continue;
288 +
289 + if(!all_files[c].count) {
290 + debug_log(" >> Examining slot %d.", c);
291 +
292 +#ifdef NETDATA_INTERNAL_CHECKS
293 + if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
294 + netdata_log_error("fd on position %d is not cleared properly. It still has %s in it.", c, all_files[c].name);
295 +#endif /* NETDATA_INTERNAL_CHECKS */
296 +
297 + debug_log(" >> %s fd position %d for %s (last name: %s)", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
298 +
299 + freez((void *)all_files[c].name);
300 + all_files[c].name = NULL;
301 + last_pos = c;
302 + break;
303 + }
304 + }
305 +
306 + all_files_len++;
307 +
308 + if(i == all_files_size) {
309 + fatal("We should find an empty slot, but there isn't any");
310 + exit(1);
311 + }
312 + // else we have an empty slot in 'c'
313 +
314 + debug_log(" >> updating slot %d.", c);
315 +
316 + all_files[c].name = strdupz(name);
317 + all_files[c].hash = hash;
318 + all_files[c].type = type;
319 + all_files[c].pos = c;
320 + all_files[c].count = 1;
321 +#ifdef NETDATA_INTERNAL_CHECKS
322 + all_files[c].magic = 0x0BADCAFE;
323 +#endif /* NETDATA_INTERNAL_CHECKS */
324 + if(unlikely(file_descriptor_add(&all_files[c]) != (void *)&all_files[c]))
325 + netdata_log_error("INTERNAL ERROR: duplicate indexing of fd.");
326 +
327 + debug_log("using fd position %d (name: %s)", c, all_files[c].name);
328 +
329 + return c;
330 +}
331 +
332 +static inline int file_descriptor_find_or_add(const char *name, uint32_t hash) {
333 + if(unlikely(!hash))
334 + hash = simple_hash(name);
335 +
336 + debug_log("adding or finding name '%s' with hash %u", name, hash);
337 +
338 + struct file_descriptor *fd = file_descriptor_find(name, hash);
339 + if(fd) {
340 + // found
341 + debug_log(" >> found on slot %d", fd->pos);
342 +
343 + fd->count++;
344 + return fd->pos;
345 + }
346 + // not found
347 +
348 + FD_FILETYPE type;
349 + if(likely(name[0] == '/')) type = FILETYPE_FILE;
350 + else if(likely(strncmp(name, "pipe:", 5) == 0)) type = FILETYPE_PIPE;
351 + else if(likely(strncmp(name, "socket:", 7) == 0)) type = FILETYPE_SOCKET;
352 + else if(likely(strncmp(name, "anon_inode:", 11) == 0)) {
353 + const char *t = &name[11];
354 +
355 + if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY;
356 + else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD;
357 + else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
358 + else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD;
359 + else if(strcmp(t, "[signalfd]") == 0) type = FILETYPE_SIGNALFD;
360 + else {
361 + debug_log("UNKNOWN anonymous inode: %s", name);
362 + type = FILETYPE_OTHER;
363 + }
364 + }
365 + else if(likely(strcmp(name, "inotify") == 0)) type = FILETYPE_INOTIFY;
366 + else {
367 + debug_log("UNKNOWN linkname: %s", name);
368 + type = FILETYPE_OTHER;
369 + }
370 +
371 + return file_descriptor_set_on_empty_slot(name, hash, type);
372 +}
373 +
374 +void clear_pid_fd(struct pid_fd *pfd) {
375 + pfd->fd = 0;
376 +
377 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
378 + pfd->link_hash = 0;
379 + pfd->inode = 0;
380 + pfd->cache_iterations_counter = 0;
381 + pfd->cache_iterations_reset = 0;
382 +#endif
383 +}
384 +
385 +static inline void make_all_pid_fds_negative(struct pid_stat *p) {
386 + struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
387 + while(pfd < pfdend) {
388 + pfd->fd = -(pfd->fd);
389 + pfd++;
390 + }
391 +}
392 +
393 +static inline void cleanup_negative_pid_fds(struct pid_stat *p) {
394 + struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
395 +
396 + while(pfd < pfdend) {
397 + int fd = pfd->fd;
398 +
399 + if(unlikely(fd < 0)) {
400 + file_descriptor_not_used(-(fd));
401 + clear_pid_fd(pfd);
402 + }
403 +
404 + pfd++;
405 + }
406 +}
407 +
408 +void init_pid_fds(struct pid_stat *p, size_t first, size_t size) {
409 + struct pid_fd *pfd = &p->fds[first], *pfdend = &p->fds[first + size];
410 +
411 + while(pfd < pfdend) {
412 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
413 + pfd->filename = NULL;
414 +#endif
415 + clear_pid_fd(pfd);
416 + pfd++;
417 + }
418 +}
419 +
420 +#ifdef __APPLE__
421 +static bool read_pid_file_descriptors_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
422 + static struct proc_fdinfo *fds = NULL;
423 + static int fdsCapacity = 0;
424 +
425 + int bufferSize = proc_pidinfo(p->pid, PROC_PIDLISTFDS, 0, NULL, 0);
426 + if (bufferSize <= 0) {
427 + netdata_log_error("Failed to get the size of file descriptors for PID %d", p->pid);
428 + return false;
429 + }
430 +
431 + // Resize buffer if necessary
432 + if (bufferSize > fdsCapacity) {
433 + if(fds)
434 + freez(fds);
435 +
436 + fds = mallocz(bufferSize);
437 + fdsCapacity = bufferSize;
438 + }
439 +
440 + int num_fds = proc_pidinfo(p->pid, PROC_PIDLISTFDS, 0, fds, bufferSize) / PROC_PIDLISTFD_SIZE;
441 + if (num_fds <= 0) {
442 + netdata_log_error("Failed to get the file descriptors for PID %d", p->pid);
443 + return false;
444 + }
445 +
446 + for (int i = 0; i < num_fds; i++) {
447 + switch (fds[i].proc_fdtype) {
448 + case PROX_FDTYPE_VNODE: {
449 + struct vnode_fdinfowithpath vi;
450 + if (proc_pidfdinfo(p->pid, fds[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vi, sizeof(vi)) > 0)
451 + p->openfds.files++;
452 + else
453 + p->openfds.other++;
454 +
455 + break;
456 + }
457 + case PROX_FDTYPE_SOCKET: {
458 + p->openfds.sockets++;
459 + break;
460 + }
461 + case PROX_FDTYPE_PIPE: {
462 + p->openfds.pipes++;
463 + break;
464 + }
465 +
466 + default:
467 + p->openfds.other++;
468 + break;
469 + }
470 + }
471 +
472 + return true;
473 +}
474 +#endif // __APPLE__
475 +
476 +#if defined(__FreeBSD__)
477 +static bool read_pid_file_descriptors_per_os(struct pid_stat *p, void *ptr) {
478 + int mib[4];
479 + size_t size;
480 + struct kinfo_file *fds;
481 + static char *fdsbuf;
482 + char *bfdsbuf, *efdsbuf;
483 + char fdsname[FILENAME_MAX + 1];
484 +#define SHM_FORMAT_LEN 31 // format: 21 + size: 10
485 + char shm_name[FILENAME_MAX - SHM_FORMAT_LEN + 1];
486 +
487 + // we make all pid fds negative, so that
488 + // we can detect unused file descriptors
489 + // at the end, to free them
490 + make_all_pid_fds_negative(p);
491 +
492 + mib[0] = CTL_KERN;
493 + mib[1] = KERN_PROC;
494 + mib[2] = KERN_PROC_FILEDESC;
495 + mib[3] = p->pid;
496 +
497 + if (unlikely(sysctl(mib, 4, NULL, &size, NULL, 0))) {
498 + netdata_log_error("sysctl error: Can't get file descriptors data size for pid %d", p->pid);
499 + return false;
500 + }
501 + if (likely(size > 0))
502 + fdsbuf = reallocz(fdsbuf, size);
503 + if (unlikely(sysctl(mib, 4, fdsbuf, &size, NULL, 0))) {
504 + netdata_log_error("sysctl error: Can't get file descriptors data for pid %d", p->pid);
505 + return false;
506 + }
507 +
508 + bfdsbuf = fdsbuf;
509 + efdsbuf = fdsbuf + size;
510 + while (bfdsbuf < efdsbuf) {
511 + fds = (struct kinfo_file *)(uintptr_t)bfdsbuf;
512 + if (unlikely(fds->kf_structsize == 0))
513 + break;
514 +
515 + // do not process file descriptors for current working directory, root directory,
516 + // jail directory, ktrace vnode, text vnode and controlling terminal
517 + if (unlikely(fds->kf_fd < 0)) {
518 + bfdsbuf += fds->kf_structsize;
519 + continue;
520 + }
521 +
522 + // get file descriptors array index
523 + size_t fdid = fds->kf_fd;
524 +
525 + // check if the fds array is small
526 + if (unlikely(fdid >= p->fds_size)) {
527 + // it is small, extend it
528 +
529 + debug_log("extending fd memory slots for %s from %d to %d", p->comm, p->fds_size, fdid + MAX_SPARE_FDS);
530 +
531 + p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(struct pid_fd));
532 +
533 + // and initialize it
534 + init_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size);
535 + p->fds_size = fdid + MAX_SPARE_FDS;
536 + }
537 +
538 + if (unlikely(p->fds[fdid].fd == 0)) {
539 + // we don't know this fd, get it
540 +
541 + switch (fds->kf_type) {
542 + case KF_TYPE_FIFO:
543 + case KF_TYPE_VNODE:
544 + if (unlikely(!fds->kf_path[0])) {
545 + sprintf(fdsname, "other: inode: %lu", fds->kf_un.kf_file.kf_file_fileid);
546 + break;
547 + }
548 + sprintf(fdsname, "%s", fds->kf_path);
549 + break;
550 + case KF_TYPE_SOCKET:
551 + switch (fds->kf_sock_domain) {
552 + case AF_INET:
553 + case AF_INET6:
554 + if (fds->kf_sock_protocol == IPPROTO_TCP)
555 + sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_inpcb);
556 + else
557 + sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_pcb);
558 + break;
559 + case AF_UNIX:
560 + /* print address of pcb and connected pcb */
561 + sprintf(fdsname, "socket: %lx %lx", fds->kf_un.kf_sock.kf_sock_pcb, fds->kf_un.kf_sock.kf_sock_unpconn);
562 + break;
563 + default:
564 + /* print protocol number and socket address */
565 +#if __FreeBSD_version < 1200031
566 + sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_sa_local.__ss_pad1, fds->kf_sa_local.__ss_pad2);
567 +#else
568 + sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sa_local.__ss_pad1, fds->kf_un.kf_sock.kf_sa_local.__ss_pad2);
569 +#endif
570 + }
571 + break;
572 + case KF_TYPE_PIPE:
573 + sprintf(fdsname, "pipe: %lu %lu", fds->kf_un.kf_pipe.kf_pipe_addr, fds->kf_un.kf_pipe.kf_pipe_peer);
574 + break;
575 + case KF_TYPE_PTS:
576 +#if __FreeBSD_version < 1200031
577 + sprintf(fdsname, "other: pts: %u", fds->kf_un.kf_pts.kf_pts_dev);
578 +#else
579 + sprintf(fdsname, "other: pts: %lu", fds->kf_un.kf_pts.kf_pts_dev);
580 +#endif
581 + break;
582 + case KF_TYPE_SHM:
583 + strncpyz(shm_name, fds->kf_path, FILENAME_MAX - SHM_FORMAT_LEN);
584 + sprintf(fdsname, "other: shm: %s size: %lu", shm_name, fds->kf_un.kf_file.kf_file_size);
585 + break;
586 + case KF_TYPE_SEM:
587 + sprintf(fdsname, "other: sem: %u", fds->kf_un.kf_sem.kf_sem_value);
588 + break;
589 + default:
590 + sprintf(fdsname, "other: pid: %d fd: %d", fds->kf_un.kf_proc.kf_pid, fds->kf_fd);
591 + }
592 +
593 + // if another process already has this, we will get
594 + // the same id
595 + p->fds[fdid].fd = file_descriptor_find_or_add(fdsname, 0);
596 + }
597 +
598 + // else make it positive again, we need it
599 + // of course, the actual file may have changed
600 +
601 + else
602 + p->fds[fdid].fd = -p->fds[fdid].fd;
603 +
604 + bfdsbuf += fds->kf_structsize;
605 + }
606 +
607 + return true;
608 +}
609 +#endif // __FreeBSD__
610 +
611 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
612 +static bool read_pid_file_descriptors_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
613 + if(unlikely(!p->fds_dirname)) {
614 + char dirname[FILENAME_MAX+1];
615 + snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", netdata_configured_host_prefix, p->pid);
616 + p->fds_dirname = strdupz(dirname);
617 + }
618 +
619 + DIR *fds = opendir(p->fds_dirname);
620 + if(unlikely(!fds)) return false;
621 +
622 + struct dirent *de;
623 + char linkname[FILENAME_MAX + 1];
624 +
625 + // we make all pid fds negative, so that
626 + // we can detect unused file descriptors
627 + // at the end, to free them
628 + make_all_pid_fds_negative(p);
629 +
630 + while((de = readdir(fds))) {
631 + // we need only files with numeric names
632 +
633 + if(unlikely(de->d_name[0] < '0' || de->d_name[0] > '9'))
634 + continue;
635 +
636 + // get its number
637 + int fdid = (int) str2l(de->d_name);
638 + if(unlikely(fdid < 0)) continue;
639 +
640 + // check if the fds array is small
641 + if(unlikely((size_t)fdid >= p->fds_size)) {
642 + // it is small, extend it
643 +
644 + debug_log("extending fd memory slots for %s from %d to %d"
645 + , p->comm
646 + , p->fds_size
647 + , fdid + MAX_SPARE_FDS
648 + );
649 +
650 + p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(struct pid_fd));
651 +
652 + // and initialize it
653 + init_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size);
654 + p->fds_size = (size_t)fdid + MAX_SPARE_FDS;
655 + }
656 +
657 + if(unlikely(p->fds[fdid].fd < 0 && de->d_ino != p->fds[fdid].inode)) {
658 + // inodes do not match, clear the previous entry
659 + inodes_changed_counter++;
660 + file_descriptor_not_used(-p->fds[fdid].fd);
661 + clear_pid_fd(&p->fds[fdid]);
662 + }
663 +
664 + if(p->fds[fdid].fd < 0 && p->fds[fdid].cache_iterations_counter > 0) {
665 + p->fds[fdid].fd = -p->fds[fdid].fd;
666 + p->fds[fdid].cache_iterations_counter--;
667 + continue;
668 + }
669 +
670 + if(unlikely(!p->fds[fdid].filename)) {
671 + filenames_allocated_counter++;
672 + char fdname[FILENAME_MAX + 1];
673 + snprintfz(fdname, FILENAME_MAX, "%s/proc/%d/fd/%s", netdata_configured_host_prefix, p->pid, de->d_name);
674 + p->fds[fdid].filename = strdupz(fdname);
675 + }
676 +
677 + file_counter++;
678 + ssize_t l = readlink(p->fds[fdid].filename, linkname, FILENAME_MAX);
679 + if(unlikely(l == -1)) {
680 + // cannot read the link
681 +
682 + if(debug_enabled || (p->target && p->target->debug_enabled))
683 + netdata_log_error("Cannot read link %s", p->fds[fdid].filename);
684 +
685 + if(unlikely(p->fds[fdid].fd < 0)) {
686 + file_descriptor_not_used(-p->fds[fdid].fd);
687 + clear_pid_fd(&p->fds[fdid]);
688 + }
689 +
690 + continue;
691 + }
692 + else
693 + linkname[l] = '\0';
694 +
695 + uint32_t link_hash = simple_hash(linkname);
696 +
697 + if(unlikely(p->fds[fdid].fd < 0 && p->fds[fdid].link_hash != link_hash)) {
698 + // the link changed
699 + links_changed_counter++;
700 + file_descriptor_not_used(-p->fds[fdid].fd);
701 + clear_pid_fd(&p->fds[fdid]);
702 + }
703 +
704 + if(unlikely(p->fds[fdid].fd == 0)) {
705 + // we don't know this fd, get it
706 +
707 + // if another process already has this, we will get
708 + // the same id
709 + p->fds[fdid].fd = file_descriptor_find_or_add(linkname, link_hash);
710 + p->fds[fdid].inode = de->d_ino;
711 + p->fds[fdid].link_hash = link_hash;
712 + }
713 + else {
714 + // else make it positive again, we need it
715 + p->fds[fdid].fd = -p->fds[fdid].fd;
716 + }
717 +
718 + // caching control
719 + // without this we read all the files on every iteration
720 + if(max_fds_cache_seconds > 0) {
721 + size_t spread = ((size_t)max_fds_cache_seconds > 10) ? 10 : (size_t)max_fds_cache_seconds;
722 +
723 + // cache it for a few iterations
724 + size_t max = ((size_t) max_fds_cache_seconds + (fdid % spread)) / (size_t) update_every;
725 + p->fds[fdid].cache_iterations_reset++;
726 +
727 + if(unlikely(p->fds[fdid].cache_iterations_reset % spread == (size_t) fdid % spread))
728 + p->fds[fdid].cache_iterations_reset++;
729 +
730 + if(unlikely((fdid <= 2 && p->fds[fdid].cache_iterations_reset > 5) ||
731 + p->fds[fdid].cache_iterations_reset > max)) {
732 + // for stdin, stdout, stderr (fdid <= 2) we have checked a few times, or if it goes above the max, goto max
733 + p->fds[fdid].cache_iterations_reset = max;
734 + }
735 +
736 + p->fds[fdid].cache_iterations_counter = p->fds[fdid].cache_iterations_reset;
737 + }
738 + }
739 +
740 + closedir(fds);
741 +
742 + return true;
743 +}
744 +#endif // !__FreeBSD__ !__APPLE
745 +
746 +int read_pid_file_descriptors(struct pid_stat *p, void *ptr) {
747 + bool ret = read_pid_file_descriptors_per_os(p, ptr);
748 + cleanup_negative_pid_fds(p);
749 +
750 + return ret ? 1 : 0;
751 +}
src/collectors/apps.plugin/apps_proc_pid_io.c new
+95
@@ -0,0 +1,95 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +static inline void clear_pid_io(struct pid_stat *p) {
6 + p->io_logical_bytes_read = 0;
7 + p->io_logical_bytes_written = 0;
8 + p->io_read_calls = 0;
9 + p->io_write_calls = 0;
10 + p->io_storage_bytes_read = 0;
11 + p->io_storage_bytes_written = 0;
12 + p->io_cancelled_write_bytes = 0;
13 +}
14 +
15 +#if defined(__FreeBSD__)
16 +static inline bool read_proc_pid_io_per_os(struct pid_stat *p, void *ptr) {
17 + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
18 +
19 + pid_incremental_rate(io, p->io_storage_bytes_read, proc_info->ki_rusage.ru_inblock);
20 + pid_incremental_rate(io, p->io_storage_bytes_written, proc_info->ki_rusage.ru_oublock);
21 +
22 + p->io_logical_bytes_read = 0;
23 + p->io_logical_bytes_written = 0;
24 + p->io_read_calls = 0;
25 + p->io_write_calls = 0;
26 + p->io_cancelled_write_bytes = 0;
27 +
28 + return true;
29 +}
30 +#endif
31 +
32 +#ifdef __APPLE__
33 +static inline bool read_proc_pid_io_per_os(struct pid_stat *p, void *ptr) {
34 + struct pid_info *pi = ptr;
35 +
36 + // On MacOS, the proc_pid_rusage provides disk_io_statistics which includes io bytes read and written
37 + // but does not provide the same level of detail as Linux, like separating logical and physical I/O bytes.
38 + pid_incremental_rate(io, p->io_storage_bytes_read, pi->rusageinfo.ri_diskio_bytesread);
39 + pid_incremental_rate(io, p->io_storage_bytes_written, pi->rusageinfo.ri_diskio_byteswritten);
40 +
41 + p->io_logical_bytes_read = 0;
42 + p->io_logical_bytes_written = 0;
43 + p->io_read_calls = 0;
44 + p->io_write_calls = 0;
45 + p->io_cancelled_write_bytes = 0;
46 +
47 + return true;
48 +}
49 +#endif // __APPLE__
50 +
51 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
52 +static inline int read_proc_pid_io_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
53 + static procfile *ff = NULL;
54 +
55 + if(unlikely(!p->io_filename)) {
56 + char filename[FILENAME_MAX + 1];
57 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", netdata_configured_host_prefix, p->pid);
58 + p->io_filename = strdupz(filename);
59 + }
60 +
61 + // open the file
62 + ff = procfile_reopen(ff, p->io_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
63 + if(unlikely(!ff)) goto cleanup;
64 +
65 + ff = procfile_readall(ff);
66 + if(unlikely(!ff)) goto cleanup;
67 +
68 + pid_incremental_rate(io, p->io_logical_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 0, 1)));
69 + pid_incremental_rate(io, p->io_logical_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 1, 1)));
70 + pid_incremental_rate(io, p->io_read_calls, str2kernel_uint_t(procfile_lineword(ff, 2, 1)));
71 + pid_incremental_rate(io, p->io_write_calls, str2kernel_uint_t(procfile_lineword(ff, 3, 1)));
72 + pid_incremental_rate(io, p->io_storage_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 4, 1)));
73 + pid_incremental_rate(io, p->io_storage_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 5, 1)));
74 + pid_incremental_rate(io, p->io_cancelled_write_bytes, str2kernel_uint_t(procfile_lineword(ff, 6, 1)));
75 +
76 + return true;
77 +
78 +cleanup:
79 + clear_pid_io(p);
80 + return false;
81 +}
82 +#endif // !__FreeBSD__ !__APPLE__
83 +
84 +int read_proc_pid_io(struct pid_stat *p, void *ptr) {
85 + p->last_io_collected_usec = p->io_collected_usec;
86 + p->io_collected_usec = now_monotonic_usec();
87 + calls_counter++;
88 +
89 + bool ret = read_proc_pid_io_per_os(p, ptr);
90 +
91 + if(unlikely(global_iterations_counter == 1))
92 + clear_pid_io(p);
93 +
94 + return ret ? 1 : 0;
95 +}
src/collectors/apps.plugin/apps_proc_pid_limits.c new
+151
@@ -0,0 +1,151 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +
7 +#define MAX_PROC_PID_LIMITS 8192
8 +#define PROC_PID_LIMITS_MAX_OPEN_FILES_KEY "\nMax open files "
9 +
10 +static inline kernel_uint_t get_proc_pid_limits_limit(char *buf, const char *key, size_t key_len, kernel_uint_t def) {
11 + char *line = strstr(buf, key);
12 + if(!line)
13 + return def;
14 +
15 + char *v = &line[key_len];
16 + while(isspace(*v)) v++;
17 +
18 + if(strcmp(v, "unlimited") == 0)
19 + return 0;
20 +
21 + return str2ull(v, NULL);
22 +}
23 +
24 +#if defined(__FreeBSD__) || defined(__APPLE__)
25 +int read_proc_pid_limits_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
26 + return false;
27 +}
28 +#endif
29 +
30 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
31 +static inline bool read_proc_pid_limits_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
32 + static char proc_pid_limits_buffer[MAX_PROC_PID_LIMITS + 1];
33 + bool ret = false;
34 + bool read_limits = false;
35 +
36 + errno = 0;
37 + proc_pid_limits_buffer[0] = '\0';
38 +
39 + kernel_uint_t all_fds = pid_openfds_sum(p);
40 + if(all_fds < p->limits.max_open_files / 2 && p->io_collected_usec > p->last_limits_collected_usec && p->io_collected_usec - p->last_limits_collected_usec <= 60 * USEC_PER_SEC) {
41 + // too frequent, we want to collect limits once per minute
42 + ret = true;
43 + goto cleanup;
44 + }
45 +
46 + if(unlikely(!p->limits_filename)) {
47 + char filename[FILENAME_MAX + 1];
48 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/limits", netdata_configured_host_prefix, p->pid);
49 + p->limits_filename = strdupz(filename);
50 + }
51 +
52 + int fd = open(p->limits_filename, procfile_open_flags, 0666);
53 + if(unlikely(fd == -1)) goto cleanup;
54 +
55 + ssize_t bytes = read(fd, proc_pid_limits_buffer, MAX_PROC_PID_LIMITS);
56 + close(fd);
57 +
58 + if(bytes <= 0)
59 + goto cleanup;
60 +
61 + // make it '\0' terminated
62 + if(bytes < MAX_PROC_PID_LIMITS)
63 + proc_pid_limits_buffer[bytes] = '\0';
64 + else
65 + proc_pid_limits_buffer[MAX_PROC_PID_LIMITS - 1] = '\0';
66 +
67 + p->limits.max_open_files = get_proc_pid_limits_limit(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY, sizeof(PROC_PID_LIMITS_MAX_OPEN_FILES_KEY) - 1, 0);
68 + if(p->limits.max_open_files == 1) {
69 + // it seems a bug in the kernel or something similar
70 + // it sets max open files to 1 but the number of files
71 + // the process has open are more than 1...
72 + // https://github.com/netdata/netdata/issues/15443
73 + p->limits.max_open_files = 0;
74 + ret = true;
75 + goto cleanup;
76 + }
77 +
78 + p->last_limits_collected_usec = p->io_collected_usec;
79 + read_limits = true;
80 +
81 + ret = true;
82 +
83 +cleanup:
84 + if(p->limits.max_open_files)
85 + p->openfds_limits_percent = (NETDATA_DOUBLE)all_fds * 100.0 / (NETDATA_DOUBLE)p->limits.max_open_files;
86 + else
87 + p->openfds_limits_percent = 0.0;
88 +
89 + if(p->openfds_limits_percent > 100.0) {
90 + if(!(p->log_thrown & PID_LOG_LIMITS_DETAIL)) {
91 + char *line;
92 +
93 + if(!read_limits) {
94 + proc_pid_limits_buffer[0] = '\0';
95 + line = "NOT READ";
96 + }
97 + else {
98 + line = strstr(proc_pid_limits_buffer, PROC_PID_LIMITS_MAX_OPEN_FILES_KEY);
99 + if (line) {
100 + line++; // skip the initial newline
101 +
102 + char *end = strchr(line, '\n');
103 + if (end)
104 + *end = '\0';
105 + }
106 + }
107 +
108 + netdata_log_info(
109 + "FDS_LIMITS: PID %d (%s) is using "
110 + "%0.2f %% of its fds limits, "
111 + "open fds = %"PRIu64 "("
112 + "files = %"PRIu64 ", "
113 + "pipes = %"PRIu64 ", "
114 + "sockets = %"PRIu64", "
115 + "inotifies = %"PRIu64", "
116 + "eventfds = %"PRIu64", "
117 + "timerfds = %"PRIu64", "
118 + "signalfds = %"PRIu64", "
119 + "eventpolls = %"PRIu64" "
120 + "other = %"PRIu64" "
121 + "), open fds limit = %"PRIu64", "
122 + "%s, "
123 + "original line [%s]",
124 + p->pid, p->comm, p->openfds_limits_percent, all_fds,
125 + p->openfds.files,
126 + p->openfds.pipes,
127 + p->openfds.sockets,
128 + p->openfds.inotifies,
129 + p->openfds.eventfds,
130 + p->openfds.timerfds,
131 + p->openfds.signalfds,
132 + p->openfds.eventpolls,
133 + p->openfds.other,
134 + p->limits.max_open_files,
135 + read_limits ? "and we have read the limits AFTER counting the fds"
136 + : "but we have read the limits BEFORE counting the fds",
137 + line);
138 +
139 + p->log_thrown |= PID_LOG_LIMITS_DETAIL;
140 + }
141 + }
142 + else
143 + p->log_thrown &= ~PID_LOG_LIMITS_DETAIL;
144 +
145 + return ret;
146 +}
147 +#endif // !__FreeBSD__ !__APPLE__
148 +
149 +int read_proc_pid_limits(struct pid_stat *p, void *ptr) {
150 + return read_proc_pid_limits_per_os(p, ptr) ? 1 : 0;
151 +}
src/collectors/apps.plugin/apps_proc_pid_stat.c new
+293
@@ -0,0 +1,293 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +
7 +static inline void assign_target_to_pid(struct pid_stat *p) {
8 + targets_assignment_counter++;
9 +
10 + uint32_t hash = simple_hash(p->comm);
11 + size_t pclen = strlen(p->comm);
12 +
13 + struct target *w;
14 + for(w = apps_groups_root_target; w ; w = w->next) {
15 + // if(debug_enabled || (p->target && p->target->debug_enabled)) debug_log_int("\t\tcomparing '%s' with '%s'", w->compare, p->comm);
16 +
17 + // find it - 4 cases:
18 + // 1. the target is not a pattern
19 + // 2. the target has the prefix
20 + // 3. the target has the suffix
21 + // 4. the target is something inside cmdline
22 +
23 + if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
24 + || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
25 + || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
26 + || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && p->cmdline && strstr(p->cmdline, w->compare))
27 + ))) {
28 +
29 + p->matched_by_config = true;
30 + if(w->target) p->target = w->target;
31 + else p->target = w;
32 +
33 + if(debug_enabled || (p->target && p->target->debug_enabled))
34 + debug_log_int("%s linked to target %s", p->comm, p->target->name);
35 +
36 + break;
37 + }
38 + }
39 +}
40 +
41 +static inline void update_pid_comm(struct pid_stat *p, const char *comm) {
42 + if(strcmp(p->comm, comm) != 0) {
43 + if(unlikely(debug_enabled)) {
44 + if(p->comm[0])
45 + debug_log("\tpid %d (%s) changed name to '%s'", p->pid, p->comm, comm);
46 + else
47 + debug_log("\tJust added %d (%s)", p->pid, comm);
48 + }
49 +
50 + strncpyz(p->comm, comm, MAX_COMPARE_NAME);
51 +
52 + // /proc/<pid>/cmdline
53 + if(likely(proc_pid_cmdline_is_needed))
54 + managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
55 +
56 + assign_target_to_pid(p);
57 + }
58 +}
59 +
60 +static inline void clear_pid_stat(struct pid_stat *p, bool threads) {
61 + p->minflt = 0;
62 + p->cminflt = 0;
63 + p->majflt = 0;
64 + p->cmajflt = 0;
65 + p->utime = 0;
66 + p->stime = 0;
67 + p->gtime = 0;
68 + p->cutime = 0;
69 + p->cstime = 0;
70 + p->cgtime = 0;
71 +
72 + if(threads)
73 + p->num_threads = 0;
74 +
75 + // p->rss = 0;
76 +}
77 +
78 +#if defined(__FreeBSD__)
79 +static inline bool read_proc_pid_stat_per_os(struct pid_stat *p, void *ptr) {
80 + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
81 + if (unlikely(proc_info->ki_tdflags & TDF_IDLETD))
82 + goto cleanup;
83 +
84 + char *comm = proc_info->ki_comm;
85 + p->ppid = proc_info->ki_ppid;
86 +
87 + update_pid_comm(p, comm);
88 +
89 + pid_incremental_rate(stat, p->minflt, (kernel_uint_t)proc_info->ki_rusage.ru_minflt);
90 + pid_incremental_rate(stat, p->cminflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_minflt);
91 + pid_incremental_rate(stat, p->majflt, (kernel_uint_t)proc_info->ki_rusage.ru_majflt);
92 + pid_incremental_rate(stat, p->cmajflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_majflt);
93 + pid_incremental_rate(stat, p->utime, (kernel_uint_t)proc_info->ki_rusage.ru_utime.tv_sec * 100 + proc_info->ki_rusage.ru_utime.tv_usec / 10000);
94 + pid_incremental_rate(stat, p->stime, (kernel_uint_t)proc_info->ki_rusage.ru_stime.tv_sec * 100 + proc_info->ki_rusage.ru_stime.tv_usec / 10000);
95 + pid_incremental_rate(stat, p->cutime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_utime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_utime.tv_usec / 10000);
96 + pid_incremental_rate(stat, p->cstime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_stime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_stime.tv_usec / 10000);
97 +
98 + p->num_threads = proc_info->ki_numthreads;
99 +
100 + usec_t started_ut = timeval_usec(&proc_info->ki_start);
101 + p->uptime = (system_current_time_ut > started_ut) ? (system_current_time_ut - started_ut) / USEC_PER_SEC : 0;
102 +
103 + if(enable_guest_charts) {
104 + enable_guest_charts = false;
105 + netdata_log_info("Guest charts aren't supported by FreeBSD");
106 + }
107 +
108 + if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
109 + debug_log_int("READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d", netdata_configured_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
110 +
111 + if(unlikely(global_iterations_counter == 1))
112 + clear_pid_stat(p, false);
113 +
114 + return true;
115 +
116 +cleanup:
117 + clear_pid_stat(p, true);
118 + return false;
119 +}
120 +#endif // __FreeBSD__
121 +
122 +#ifdef __APPLE__
123 +static inline bool read_proc_pid_stat_per_os(struct pid_stat *p, void *ptr) {
124 + struct pid_info *pi = ptr;
125 +
126 + p->ppid = pi->proc.kp_eproc.e_ppid;
127 +
128 + // Update command name and target if changed
129 + char comm[PROC_PIDPATHINFO_MAXSIZE];
130 + int ret = proc_name(p->pid, comm, sizeof(comm));
131 + if (ret <= 0)
132 + strncpyz(comm, "unknown", sizeof(comm) - 1);
133 +
134 + update_pid_comm(p, comm);
135 +
136 + kernel_uint_t userCPU = (pi->taskinfo.pti_total_user * mach_info.numer) / mach_info.denom / NSEC_PER_USEC / 10000;
137 + kernel_uint_t systemCPU = (pi->taskinfo.pti_total_system * mach_info.numer) / mach_info.denom / NSEC_PER_USEC / 10000;
138 +
139 + // Map the values from taskinfo to the pid_stat structure
140 + pid_incremental_rate(stat, p->minflt, pi->taskinfo.pti_faults);
141 + pid_incremental_rate(stat, p->majflt, pi->taskinfo.pti_pageins);
142 + pid_incremental_rate(stat, p->utime, userCPU);
143 + pid_incremental_rate(stat, p->stime, systemCPU);
144 + p->num_threads = pi->taskinfo.pti_threadnum;
145 +
146 + usec_t started_ut = timeval_usec(&pi->proc.kp_proc.p_starttime);
147 + p->uptime = (system_current_time_ut > started_ut) ? (system_current_time_ut - started_ut) / USEC_PER_SEC : 0;
148 +
149 + // Note: Some values such as guest time, cutime, cstime, etc., are not directly available in MacOS.
150 + // You might need to approximate or leave them unset depending on your needs.
151 +
152 + if(unlikely(debug_enabled || (p->target && p->target->debug_enabled))) {
153 + debug_log_int("READ PROC/PID/STAT for MacOS: process: '%s' on target '%s' VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", threads=%d",
154 + p->comm, (p->target) ? p->target->name : "UNSET", p->utime, p->stime, p->minflt, p->majflt, p->num_threads);
155 + }
156 +
157 + if(unlikely(global_iterations_counter == 1))
158 + clear_pid_stat(p, false);
159 +
160 + // MacOS doesn't have a direct concept of process state like Linux,
161 + // so updating process state count might need a different approach.
162 +
163 + return true;
164 +}
165 +#endif // __APPLE__
166 +
167 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
168 +static inline void update_proc_state_count(char proc_stt) {
169 + switch (proc_stt) {
170 + case 'S':
171 + proc_state_count[PROC_STATUS_SLEEPING] += 1;
172 + break;
173 + case 'R':
174 + proc_state_count[PROC_STATUS_RUNNING] += 1;
175 + break;
176 + case 'D':
177 + proc_state_count[PROC_STATUS_SLEEPING_D] += 1;
178 + break;
179 + case 'Z':
180 + proc_state_count[PROC_STATUS_ZOMBIE] += 1;
181 + break;
182 + case 'T':
183 + proc_state_count[PROC_STATUS_STOPPED] += 1;
184 + break;
185 + default:
186 + break;
187 + }
188 +}
189 +
190 +static inline bool read_proc_pid_stat_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
191 + static procfile *ff = NULL;
192 +
193 + if(unlikely(!p->stat_filename)) {
194 + char filename[FILENAME_MAX + 1];
195 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", netdata_configured_host_prefix, p->pid);
196 + p->stat_filename = strdupz(filename);
197 + }
198 +
199 + int set_quotes = (!ff)?1:0;
200 +
201 + ff = procfile_reopen(ff, p->stat_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
202 + if(unlikely(!ff)) goto cleanup;
203 +
204 + // if(set_quotes) procfile_set_quotes(ff, "()");
205 + if(unlikely(set_quotes))
206 + procfile_set_open_close(ff, "(", ")");
207 +
208 + ff = procfile_readall(ff);
209 + if(unlikely(!ff)) goto cleanup;
210 +
211 + // p->pid = str2pid_t(procfile_lineword(ff, 0, 0));
212 + char *comm = procfile_lineword(ff, 0, 1);
213 + p->state = *(procfile_lineword(ff, 0, 2));
214 + p->ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3));
215 + // p->pgrp = (int32_t)str2pid_t(procfile_lineword(ff, 0, 4));
216 + // p->session = (int32_t)str2pid_t(procfile_lineword(ff, 0, 5));
217 + // p->tty_nr = (int32_t)str2pid_t(procfile_lineword(ff, 0, 6));
218 + // p->tpgid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 7));
219 + // p->flags = str2uint64_t(procfile_lineword(ff, 0, 8));
220 +
221 + update_pid_comm(p, comm);
222 +
223 + pid_incremental_rate(stat, p->minflt, str2kernel_uint_t(procfile_lineword(ff, 0, 9)));
224 + pid_incremental_rate(stat, p->cminflt, str2kernel_uint_t(procfile_lineword(ff, 0, 10)));
225 + pid_incremental_rate(stat, p->majflt, str2kernel_uint_t(procfile_lineword(ff, 0, 11)));
226 + pid_incremental_rate(stat, p->cmajflt, str2kernel_uint_t(procfile_lineword(ff, 0, 12)));
227 + pid_incremental_rate(stat, p->utime, str2kernel_uint_t(procfile_lineword(ff, 0, 13)));
228 + pid_incremental_rate(stat, p->stime, str2kernel_uint_t(procfile_lineword(ff, 0, 14)));
229 + pid_incremental_rate(stat, p->cutime, str2kernel_uint_t(procfile_lineword(ff, 0, 15)));
230 + pid_incremental_rate(stat, p->cstime, str2kernel_uint_t(procfile_lineword(ff, 0, 16)));
231 + // p->priority = str2kernel_uint_t(procfile_lineword(ff, 0, 17));
232 + // p->nice = str2kernel_uint_t(procfile_lineword(ff, 0, 18));
233 + p->num_threads = (int32_t) str2uint32_t(procfile_lineword(ff, 0, 19), NULL);
234 + // p->itrealvalue = str2kernel_uint_t(procfile_lineword(ff, 0, 20));
235 + kernel_uint_t collected_starttime = str2kernel_uint_t(procfile_lineword(ff, 0, 21)) / system_hz;
236 + p->uptime = (system_uptime_secs > collected_starttime)?(system_uptime_secs - collected_starttime):0;
237 + // p->vsize = str2kernel_uint_t(procfile_lineword(ff, 0, 22));
238 + // p->rss = str2kernel_uint_t(procfile_lineword(ff, 0, 23));
239 + // p->rsslim = str2kernel_uint_t(procfile_lineword(ff, 0, 24));
240 + // p->starcode = str2kernel_uint_t(procfile_lineword(ff, 0, 25));
241 + // p->endcode = str2kernel_uint_t(procfile_lineword(ff, 0, 26));
242 + // p->startstack = str2kernel_uint_t(procfile_lineword(ff, 0, 27));
243 + // p->kstkesp = str2kernel_uint_t(procfile_lineword(ff, 0, 28));
244 + // p->kstkeip = str2kernel_uint_t(procfile_lineword(ff, 0, 29));
245 + // p->signal = str2kernel_uint_t(procfile_lineword(ff, 0, 30));
246 + // p->blocked = str2kernel_uint_t(procfile_lineword(ff, 0, 31));
247 + // p->sigignore = str2kernel_uint_t(procfile_lineword(ff, 0, 32));
248 + // p->sigcatch = str2kernel_uint_t(procfile_lineword(ff, 0, 33));
249 + // p->wchan = str2kernel_uint_t(procfile_lineword(ff, 0, 34));
250 + // p->nswap = str2kernel_uint_t(procfile_lineword(ff, 0, 35));
251 + // p->cnswap = str2kernel_uint_t(procfile_lineword(ff, 0, 36));
252 + // p->exit_signal = str2kernel_uint_t(procfile_lineword(ff, 0, 37));
253 + // p->processor = str2kernel_uint_t(procfile_lineword(ff, 0, 38));
254 + // p->rt_priority = str2kernel_uint_t(procfile_lineword(ff, 0, 39));
255 + // p->policy = str2kernel_uint_t(procfile_lineword(ff, 0, 40));
256 + // p->delayacct_blkio_ticks = str2kernel_uint_t(procfile_lineword(ff, 0, 41));
257 +
258 + if(enable_guest_charts) {
259 + pid_incremental_rate(stat, p->gtime, str2kernel_uint_t(procfile_lineword(ff, 0, 42)));
260 + pid_incremental_rate(stat, p->cgtime, str2kernel_uint_t(procfile_lineword(ff, 0, 43)));
261 +
262 + if (show_guest_time || p->gtime || p->cgtime) {
263 + p->utime -= (p->utime >= p->gtime) ? p->gtime : p->utime;
264 + p->cutime -= (p->cutime >= p->cgtime) ? p->cgtime : p->cutime;
265 + show_guest_time = 1;
266 + }
267 + }
268 +
269 + if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
270 + debug_log_int("READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d", netdata_configured_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
271 +
272 + if(unlikely(global_iterations_counter == 1))
273 + clear_pid_stat(p, false);
274 +
275 + update_proc_state_count(p->state);
276 + return true;
277 +
278 +cleanup:
279 + clear_pid_stat(p, true);
280 + return false;
281 +}
282 +#endif // !__FreeBSD__ !__APPLE__
283 +
284 +int read_proc_pid_stat(struct pid_stat *p, void *ptr) {
285 + p->last_stat_collected_usec = p->stat_collected_usec;
286 + p->stat_collected_usec = now_monotonic_usec();
287 + calls_counter++;
288 +
289 + if(!read_proc_pid_stat_per_os(p, ptr))
290 + return 0;
291 +
292 + return 1;
293 +}
src/collectors/apps.plugin/apps_proc_pid_status.c new
+192
@@ -0,0 +1,192 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +#if defined(__FreeBSD__)
6 +static inline bool read_proc_pid_status_per_os(struct pid_stat *p, void *ptr) {
7 + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr;
8 +
9 + p->uid = proc_info->ki_uid;
10 + p->gid = proc_info->ki_groups[0];
11 + p->status_vmsize = proc_info->ki_size / 1024; // in KiB
12 + p->status_vmrss = proc_info->ki_rssize * pagesize / 1024; // in KiB
13 + // TODO: what about shared and swap memory on FreeBSD?
14 + return true;
15 +}
16 +#endif
17 +
18 +#ifdef __APPLE__
19 +static inline bool read_proc_pid_status_per_os(struct pid_stat *p, void *ptr) {
20 + struct pid_info *pi = ptr;
21 +
22 + p->uid = pi->bsdinfo.pbi_uid;
23 + p->gid = pi->bsdinfo.pbi_gid;
24 + p->status_vmsize = pi->taskinfo.pti_virtual_size / 1024; // Convert bytes to KiB
25 + p->status_vmrss = pi->taskinfo.pti_resident_size / 1024; // Convert bytes to KiB
26 + // p->status_vmswap = rusageinfo.ri_swapins + rusageinfo.ri_swapouts; // This is not directly available, consider an alternative representation
27 + p->status_voluntary_ctxt_switches = pi->taskinfo.pti_csw;
28 + // p->status_nonvoluntary_ctxt_switches = taskinfo.pti_nivcsw;
29 +
30 + return true;
31 +}
32 +#endif // __APPLE__
33 +
34 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
35 +struct arl_callback_ptr {
36 + struct pid_stat *p;
37 + procfile *ff;
38 + size_t line;
39 +};
40 +
41 +void arl_callback_status_uid(const char *name, uint32_t hash, const char *value, void *dst) {
42 + (void)name; (void)hash; (void)value;
43 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
44 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 5)) return;
45 +
46 + //const char *real_uid = procfile_lineword(aptr->ff, aptr->line, 1);
47 + const char *effective_uid = procfile_lineword(aptr->ff, aptr->line, 2);
48 + //const char *saved_uid = procfile_lineword(aptr->ff, aptr->line, 3);
49 + //const char *filesystem_uid = procfile_lineword(aptr->ff, aptr->line, 4);
50 +
51 + if(likely(effective_uid && *effective_uid))
52 + aptr->p->uid = (uid_t)str2l(effective_uid);
53 +}
54 +
55 +void arl_callback_status_gid(const char *name, uint32_t hash, const char *value, void *dst) {
56 + (void)name; (void)hash; (void)value;
57 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
58 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 5)) return;
59 +
60 + //const char *real_gid = procfile_lineword(aptr->ff, aptr->line, 1);
61 + const char *effective_gid = procfile_lineword(aptr->ff, aptr->line, 2);
62 + //const char *saved_gid = procfile_lineword(aptr->ff, aptr->line, 3);
63 + //const char *filesystem_gid = procfile_lineword(aptr->ff, aptr->line, 4);
64 +
65 + if(likely(effective_gid && *effective_gid))
66 + aptr->p->gid = (uid_t)str2l(effective_gid);
67 +}
68 +
69 +void arl_callback_status_vmsize(const char *name, uint32_t hash, const char *value, void *dst) {
70 + (void)name; (void)hash; (void)value;
71 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
72 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
73 +
74 + aptr->p->status_vmsize = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
75 +}
76 +
77 +void arl_callback_status_vmswap(const char *name, uint32_t hash, const char *value, void *dst) {
78 + (void)name; (void)hash; (void)value;
79 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
80 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
81 +
82 + aptr->p->status_vmswap = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
83 +}
84 +
85 +void arl_callback_status_vmrss(const char *name, uint32_t hash, const char *value, void *dst) {
86 + (void)name; (void)hash; (void)value;
87 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
88 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
89 +
90 + aptr->p->status_vmrss = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
91 +}
92 +
93 +void arl_callback_status_rssfile(const char *name, uint32_t hash, const char *value, void *dst) {
94 + (void)name; (void)hash; (void)value;
95 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
96 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
97 +
98 + aptr->p->status_rssfile = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
99 +}
100 +
101 +void arl_callback_status_rssshmem(const char *name, uint32_t hash, const char *value, void *dst) {
102 + (void)name; (void)hash; (void)value;
103 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
104 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 3)) return;
105 +
106 + aptr->p->status_rssshmem = str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1));
107 +}
108 +
109 +void arl_callback_status_voluntary_ctxt_switches(const char *name, uint32_t hash, const char *value, void *dst) {
110 + (void)name; (void)hash; (void)value;
111 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
112 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 2)) return;
113 +
114 + struct pid_stat *p = aptr->p;
115 + pid_incremental_rate(stat, p->status_voluntary_ctxt_switches, str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)));
116 +}
117 +
118 +void arl_callback_status_nonvoluntary_ctxt_switches(const char *name, uint32_t hash, const char *value, void *dst) {
119 + (void)name; (void)hash; (void)value;
120 + struct arl_callback_ptr *aptr = (struct arl_callback_ptr *)dst;
121 + if(unlikely(procfile_linewords(aptr->ff, aptr->line) < 2)) return;
122 +
123 + struct pid_stat *p = aptr->p;
124 + pid_incremental_rate(stat, p->status_nonvoluntary_ctxt_switches, str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)));
125 +}
126 +
127 +static inline bool read_proc_pid_status_per_os(struct pid_stat *p, void *ptr __maybe_unused) {
128 + static struct arl_callback_ptr arl_ptr;
129 + static procfile *ff = NULL;
130 +
131 + if(unlikely(!p->status_arl)) {
132 + p->status_arl = arl_create("/proc/pid/status", NULL, 60);
133 + arl_expect_custom(p->status_arl, "Uid", arl_callback_status_uid, &arl_ptr);
134 + arl_expect_custom(p->status_arl, "Gid", arl_callback_status_gid, &arl_ptr);
135 + arl_expect_custom(p->status_arl, "VmSize", arl_callback_status_vmsize, &arl_ptr);
136 + arl_expect_custom(p->status_arl, "VmRSS", arl_callback_status_vmrss, &arl_ptr);
137 + arl_expect_custom(p->status_arl, "RssFile", arl_callback_status_rssfile, &arl_ptr);
138 + arl_expect_custom(p->status_arl, "RssShmem", arl_callback_status_rssshmem, &arl_ptr);
139 + arl_expect_custom(p->status_arl, "VmSwap", arl_callback_status_vmswap, &arl_ptr);
140 + arl_expect_custom(p->status_arl, "voluntary_ctxt_switches", arl_callback_status_voluntary_ctxt_switches, &arl_ptr);
141 + arl_expect_custom(p->status_arl, "nonvoluntary_ctxt_switches", arl_callback_status_nonvoluntary_ctxt_switches, &arl_ptr);
142 + }
143 +
144 + if(unlikely(!p->status_filename)) {
145 + char filename[FILENAME_MAX + 1];
146 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/status", netdata_configured_host_prefix, p->pid);
147 + p->status_filename = strdupz(filename);
148 + }
149 +
150 + ff = procfile_reopen(ff, p->status_filename, (!ff)?" \t:,-()/":NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
151 + if(unlikely(!ff)) return false;
152 +
153 + ff = procfile_readall(ff);
154 + if(unlikely(!ff)) return false;
155 +
156 + calls_counter++;
157 +
158 + // let ARL use this pid
159 + arl_ptr.p = p;
160 + arl_ptr.ff = ff;
161 +
162 + size_t lines = procfile_lines(ff), l;
163 + arl_begin(p->status_arl);
164 +
165 + for(l = 0; l < lines ;l++) {
166 + // debug_log("CHECK: line %zu of %zu, key '%s' = '%s'", l, lines, procfile_lineword(ff, l, 0), procfile_lineword(ff, l, 1));
167 + arl_ptr.line = l;
168 + if(unlikely(arl_check(p->status_arl,
169 + procfile_lineword(ff, l, 0),
170 + procfile_lineword(ff, l, 1)))) break;
171 + }
172 +
173 + p->status_vmshared = p->status_rssfile + p->status_rssshmem;
174 +
175 + // debug_log("%s uid %d, gid %d, VmSize %zu, VmRSS %zu, RssFile %zu, RssShmem %zu, shared %zu", p->comm, (int)p->uid, (int)p->gid, p->status_vmsize, p->status_vmrss, p->status_rssfile, p->status_rssshmem, p->status_vmshared);
176 +
177 + return true;
178 +}
179 +#endif // !__FreeBSD__ !__APPLE__
180 +
181 +int read_proc_pid_status(struct pid_stat *p, void *ptr) {
182 + p->status_vmsize = 0;
183 + p->status_vmrss = 0;
184 + p->status_vmshared = 0;
185 + p->status_rssfile = 0;
186 + p->status_rssshmem = 0;
187 + p->status_vmswap = 0;
188 + p->status_voluntary_ctxt_switches = 0;
189 + p->status_nonvoluntary_ctxt_switches = 0;
190 +
191 + return read_proc_pid_status_per_os(p, ptr) ? 1 : 0;
192 +}
src/collectors/apps.plugin/apps_proc_pids.c new
+694
@@ -0,0 +1,694 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +static inline struct pid_stat *get_pid_entry(pid_t pid) {
6 + if(likely(all_pids[pid]))
7 + return all_pids[pid];
8 +
9 + struct pid_stat *p = callocz(sizeof(struct pid_stat), 1);
10 + p->fds = mallocz(sizeof(struct pid_fd) * MAX_SPARE_FDS);
11 + p->fds_size = MAX_SPARE_FDS;
12 + init_pid_fds(p, 0, p->fds_size);
13 + p->pid = pid;
14 +
15 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(root_of_pids, p, prev, next);
16 +
17 + all_pids[pid] = p;
18 + all_pids_count++;
19 +
20 + return p;
21 +}
22 +
23 +static inline void del_pid_entry(pid_t pid) {
24 + struct pid_stat *p = all_pids[pid];
25 +
26 + if(unlikely(!p)) {
27 + netdata_log_error("attempted to free pid %d that is not allocated.", pid);
28 + return;
29 + }
30 +
31 + debug_log("process %d %s exited, deleting it.", pid, p->comm);
32 +
33 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(root_of_pids, p, prev, next);
34 +
35 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
36 + {
37 + size_t i;
38 + for(i = 0; i < p->fds_size; i++)
39 + if(p->fds[i].filename)
40 + freez(p->fds[i].filename);
41 + }
42 + arl_free(p->status_arl);
43 +#endif
44 +
45 + freez(p->fds);
46 + freez(p->fds_dirname);
47 + freez(p->stat_filename);
48 + freez(p->status_filename);
49 + freez(p->limits_filename);
50 + freez(p->io_filename);
51 + freez(p->cmdline_filename);
52 + freez(p->cmdline);
53 + freez(p);
54 +
55 + all_pids[pid] = NULL;
56 + all_pids_count--;
57 +}
58 +
59 +static inline int collect_data_for_pid(pid_t pid, void *ptr) {
60 + if(unlikely(pid < 0 || pid > pid_max)) {
61 + netdata_log_error("Invalid pid %d read (expected %d to %d). Ignoring process.", pid, 0, pid_max);
62 + return 0;
63 + }
64 +
65 + struct pid_stat *p = get_pid_entry(pid);
66 + if(unlikely(!p || p->read)) return 0;
67 + p->read = true;
68 +
69 + // debug_log("Reading process %d (%s), sortlist %d", p->pid, p->comm, p->sortlist);
70 +
71 + // --------------------------------------------------------------------
72 + // /proc/<pid>/stat
73 +
74 + if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p, ptr))))
75 + // there is no reason to proceed if we cannot get its status
76 + return 0;
77 +
78 + // check its parent pid
79 + if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
80 + netdata_log_error("Pid %d (command '%s') states invalid parent pid %d. Using 0.", pid, p->comm, p->ppid);
81 + p->ppid = 0;
82 + }
83 +
84 + // --------------------------------------------------------------------
85 + // /proc/<pid>/io
86 +
87 + managed_log(p, PID_LOG_IO, read_proc_pid_io(p, ptr));
88 +
89 + // --------------------------------------------------------------------
90 + // /proc/<pid>/status
91 +
92 + if(unlikely(!managed_log(p, PID_LOG_STATUS, read_proc_pid_status(p, ptr))))
93 + // there is no reason to proceed if we cannot get its status
94 + return 0;
95 +
96 + // --------------------------------------------------------------------
97 + // /proc/<pid>/fd
98 +
99 + if(enable_file_charts) {
100 + managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p, ptr));
101 + managed_log(p, PID_LOG_LIMITS, read_proc_pid_limits(p, ptr));
102 + }
103 +
104 + // --------------------------------------------------------------------
105 + // done!
106 +
107 + if(unlikely(debug_enabled && include_exited_childs && all_pids_count && p->ppid && all_pids[p->ppid] && !all_pids[p->ppid]->read))
108 + debug_log("Read process %d (%s) sortlisted %d, but its parent %d (%s) sortlisted %d, is not read", p->pid, p->comm, p->sortlist, all_pids[p->ppid]->pid, all_pids[p->ppid]->comm, all_pids[p->ppid]->sortlist);
109 +
110 + // mark it as updated
111 + p->updated = true;
112 + p->keep = false;
113 + p->keeploops = 0;
114 +
115 + return 1;
116 +}
117 +
118 +void cleanup_exited_pids(void) {
119 + size_t c;
120 + struct pid_stat *p = NULL;
121 +
122 + for(p = root_of_pids; p ;) {
123 + if(!p->updated && (!p->keep || p->keeploops > 0)) {
124 + if(unlikely(debug_enabled && (p->keep || p->keeploops)))
125 + debug_log(" > CLEANUP cannot keep exited process %d (%s) anymore - removing it.", p->pid, p->comm);
126 +
127 + for(c = 0; c < p->fds_size; c++)
128 + if(p->fds[c].fd > 0) {
129 + file_descriptor_not_used(p->fds[c].fd);
130 + clear_pid_fd(&p->fds[c]);
131 + }
132 +
133 + pid_t r = p->pid;
134 + p = p->next;
135 + del_pid_entry(r);
136 + }
137 + else {
138 + if(unlikely(p->keep)) p->keeploops++;
139 + p->keep = false;
140 + p = p->next;
141 + }
142 + }
143 +}
144 +
145 +// ----------------------------------------------------------------------------
146 +
147 +static inline void link_all_processes_to_their_parents(void) {
148 + struct pid_stat *p, *pp;
149 +
150 + // link all children to their parents
151 + // and update children count on parents
152 + for(p = root_of_pids; p ; p = p->next) {
153 + // for each process found
154 +
155 + p->sortlist = 0;
156 + p->parent = NULL;
157 +
158 + if(unlikely(!p->ppid)) {
159 + //unnecessary code from apps_plugin.c
160 + //p->parent = NULL;
161 + continue;
162 + }
163 +
164 + pp = all_pids[p->ppid];
165 + if(likely(pp)) {
166 + p->parent = pp;
167 + pp->children_count++;
168 +
169 + if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
170 + debug_log_int("child %d (%s, %s) on target '%s' has parent %d (%s, %s). Parent: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", gtime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", cgtime=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT "", p->pid, p->comm, p->updated?"running":"exited", (p->target)?p->target->name:"UNSET", pp->pid, pp->comm, pp->updated?"running":"exited", pp->utime, pp->stime, pp->gtime, pp->minflt, pp->majflt, pp->cutime, pp->cstime, pp->cgtime, pp->cminflt, pp->cmajflt);
171 + }
172 + else {
173 + p->parent = NULL;
174 + netdata_log_error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
175 + }
176 + }
177 +}
178 +
179 +// ----------------------------------------------------------------------------
180 +
181 +static inline int debug_print_process_and_parents(struct pid_stat *p, usec_t time) {
182 + char *prefix = "\\_ ";
183 + int indent = 0;
184 +
185 + if(p->parent)
186 + indent = debug_print_process_and_parents(p->parent, p->stat_collected_usec);
187 + else
188 + prefix = " > ";
189 +
190 + char buffer[indent + 1];
191 + int i;
192 +
193 + for(i = 0; i < indent ;i++) buffer[i] = ' ';
194 + buffer[i] = '\0';
195 +
196 + fprintf(stderr, " %s %s%s (%d %s %"PRIu64""
197 + , buffer
198 + , prefix
199 + , p->comm
200 + , p->pid
201 + , p->updated?"running":"exited"
202 + , p->stat_collected_usec - time
203 + );
204 +
205 + if(p->utime) fprintf(stderr, " utime=" KERNEL_UINT_FORMAT, p->utime);
206 + if(p->stime) fprintf(stderr, " stime=" KERNEL_UINT_FORMAT, p->stime);
207 + if(p->gtime) fprintf(stderr, " gtime=" KERNEL_UINT_FORMAT, p->gtime);
208 + if(p->cutime) fprintf(stderr, " cutime=" KERNEL_UINT_FORMAT, p->cutime);
209 + if(p->cstime) fprintf(stderr, " cstime=" KERNEL_UINT_FORMAT, p->cstime);
210 + if(p->cgtime) fprintf(stderr, " cgtime=" KERNEL_UINT_FORMAT, p->cgtime);
211 + if(p->minflt) fprintf(stderr, " minflt=" KERNEL_UINT_FORMAT, p->minflt);
212 + if(p->cminflt) fprintf(stderr, " cminflt=" KERNEL_UINT_FORMAT, p->cminflt);
213 + if(p->majflt) fprintf(stderr, " majflt=" KERNEL_UINT_FORMAT, p->majflt);
214 + if(p->cmajflt) fprintf(stderr, " cmajflt=" KERNEL_UINT_FORMAT, p->cmajflt);
215 + fprintf(stderr, ")\n");
216 +
217 + return indent + 1;
218 +}
219 +
220 +static inline void debug_print_process_tree(struct pid_stat *p, char *msg __maybe_unused) {
221 + debug_log("%s: process %s (%d, %s) with parents:", msg, p->comm, p->pid, p->updated?"running":"exited");
222 + debug_print_process_and_parents(p, p->stat_collected_usec);
223 +}
224 +
225 +static inline void debug_find_lost_child(struct pid_stat *pe, kernel_uint_t lost, int type) {
226 + int found = 0;
227 + struct pid_stat *p = NULL;
228 +
229 + for(p = root_of_pids; p ; p = p->next) {
230 + if(p == pe) continue;
231 +
232 + switch(type) {
233 + case 1:
234 + if(p->cminflt > lost) {
235 + fprintf(stderr, " > process %d (%s) could use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
236 + found++;
237 + }
238 + break;
239 +
240 + case 2:
241 + if(p->cmajflt > lost) {
242 + fprintf(stderr, " > process %d (%s) could use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
243 + found++;
244 + }
245 + break;
246 +
247 + case 3:
248 + if(p->cutime > lost) {
249 + fprintf(stderr, " > process %d (%s) could use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
250 + found++;
251 + }
252 + break;
253 +
254 + case 4:
255 + if(p->cstime > lost) {
256 + fprintf(stderr, " > process %d (%s) could use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
257 + found++;
258 + }
259 + break;
260 +
261 + case 5:
262 + if(p->cgtime > lost) {
263 + fprintf(stderr, " > process %d (%s) could use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
264 + found++;
265 + }
266 + break;
267 + }
268 + }
269 +
270 + if(!found) {
271 + switch(type) {
272 + case 1:
273 + fprintf(stderr, " > cannot find any process to use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
274 + break;
275 +
276 + case 2:
277 + fprintf(stderr, " > cannot find any process to use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
278 + break;
279 +
280 + case 3:
281 + fprintf(stderr, " > cannot find any process to use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
282 + break;
283 +
284 + case 4:
285 + fprintf(stderr, " > cannot find any process to use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
286 + break;
287 +
288 + case 5:
289 + fprintf(stderr, " > cannot find any process to use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n", lost, pe->pid, pe->comm);
290 + break;
291 + }
292 + }
293 +}
294 +
295 +static inline kernel_uint_t remove_exited_child_from_parent(kernel_uint_t *field, kernel_uint_t *pfield) {
296 + kernel_uint_t absorbed = 0;
297 +
298 + if(*field > *pfield) {
299 + absorbed += *pfield;
300 + *field -= *pfield;
301 + *pfield = 0;
302 + }
303 + else {
304 + absorbed += *field;
305 + *pfield -= *field;
306 + *field = 0;
307 + }
308 +
309 + return absorbed;
310 +}
311 +
312 +static inline void process_exited_pids() {
313 + struct pid_stat *p;
314 +
315 + for(p = root_of_pids; p ; p = p->next) {
316 + if(p->updated || !p->stat_collected_usec)
317 + continue;
318 +
319 + kernel_uint_t utime = (p->utime_raw + p->cutime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
320 + kernel_uint_t stime = (p->stime_raw + p->cstime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
321 + kernel_uint_t gtime = (p->gtime_raw + p->cgtime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
322 + kernel_uint_t minflt = (p->minflt_raw + p->cminflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
323 + kernel_uint_t majflt = (p->majflt_raw + p->cmajflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
324 +
325 + if(utime + stime + gtime + minflt + majflt == 0)
326 + continue;
327 +
328 + if(unlikely(debug_enabled)) {
329 + debug_log("Absorb %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")"
330 + , p->comm
331 + , p->pid
332 + , p->updated?"running":"exited"
333 + , utime
334 + , stime
335 + , gtime
336 + , minflt
337 + , majflt
338 + );
339 + debug_print_process_tree(p, "Searching parents");
340 + }
341 +
342 + struct pid_stat *pp;
343 + for(pp = p->parent; pp ; pp = pp->parent) {
344 + if(!pp->updated) continue;
345 +
346 + kernel_uint_t absorbed;
347 + absorbed = remove_exited_child_from_parent(&utime, &pp->cutime);
348 + if(unlikely(debug_enabled && absorbed))
349 + debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " utime (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, utime);
350 +
351 + absorbed = remove_exited_child_from_parent(&stime, &pp->cstime);
352 + if(unlikely(debug_enabled && absorbed))
353 + debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " stime (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, stime);
354 +
355 + absorbed = remove_exited_child_from_parent(&gtime, &pp->cgtime);
356 + if(unlikely(debug_enabled && absorbed))
357 + debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " gtime (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, gtime);
358 +
359 + absorbed = remove_exited_child_from_parent(&minflt, &pp->cminflt);
360 + if(unlikely(debug_enabled && absorbed))
361 + debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " minflt (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, minflt);
362 +
363 + absorbed = remove_exited_child_from_parent(&majflt, &pp->cmajflt);
364 + if(unlikely(debug_enabled && absorbed))
365 + debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " majflt (remaining: " KERNEL_UINT_FORMAT ")", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, majflt);
366 + }
367 +
368 + if(unlikely(utime + stime + gtime + minflt + majflt > 0)) {
369 + if(unlikely(debug_enabled)) {
370 + if(utime) debug_find_lost_child(p, utime, 3);
371 + if(stime) debug_find_lost_child(p, stime, 4);
372 + if(gtime) debug_find_lost_child(p, gtime, 5);
373 + if(minflt) debug_find_lost_child(p, minflt, 1);
374 + if(majflt) debug_find_lost_child(p, majflt, 2);
375 + }
376 +
377 + p->keep = true;
378 +
379 + debug_log(" > remaining resources - KEEP - for another loop: %s (%d %s total resources: utime=" KERNEL_UINT_FORMAT " stime=" KERNEL_UINT_FORMAT " gtime=" KERNEL_UINT_FORMAT " minflt=" KERNEL_UINT_FORMAT " majflt=" KERNEL_UINT_FORMAT ")"
380 + , p->comm
381 + , p->pid
382 + , p->updated?"running":"exited"
383 + , utime
384 + , stime
385 + , gtime
386 + , minflt
387 + , majflt
388 + );
389 +
390 + for(pp = p->parent; pp ; pp = pp->parent) {
391 + if(pp->updated) break;
392 + pp->keep = true;
393 +
394 + debug_log(" > - KEEP - parent for another loop: %s (%d %s)"
395 + , pp->comm
396 + , pp->pid
397 + , pp->updated?"running":"exited"
398 + );
399 + }
400 +
401 + p->utime_raw = utime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
402 + p->stime_raw = stime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
403 + p->gtime_raw = gtime * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
404 + p->minflt_raw = minflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
405 + p->majflt_raw = majflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (USEC_PER_SEC * RATES_DETAIL);
406 + p->cutime_raw = p->cstime_raw = p->cgtime_raw = p->cminflt_raw = p->cmajflt_raw = 0;
407 +
408 + debug_log(" ");
409 + }
410 + else
411 + debug_log(" > totally absorbed - DONE - %s (%d %s)"
412 + , p->comm
413 + , p->pid
414 + , p->updated?"running":"exited"
415 + );
416 + }
417 +}
418 +
419 +// ----------------------------------------------------------------------------
420 +
421 +// 1. read all files in /proc
422 +// 2. for each numeric directory:
423 +// i. read /proc/pid/stat
424 +// ii. read /proc/pid/status
425 +// iii. read /proc/pid/io (requires root access)
426 +// iii. read the entries in directory /proc/pid/fd (requires root access)
427 +// for each entry:
428 +// a. find or create a struct file_descriptor
429 +// b. cleanup any old/unused file_descriptors
430 +
431 +// after all these, some pids may be linked to targets, while others may not
432 +
433 +// in case of errors, only 1 every 1000 errors is printed
434 +// to avoid filling up all disk space
435 +// if debug is enabled, all errors are printed
436 +
437 +static inline void mark_pid_as_unread(struct pid_stat *p) {
438 + p->read = false; // mark it as not read, so that collect_data_for_pid() will read it
439 + p->updated = false;
440 + p->merged = false;
441 + p->children_count = 0;
442 + p->parent = NULL;
443 +}
444 +
445 +#if defined(__FreeBSD__) || defined(__APPLE__)
446 +static inline void get_current_time(void) {
447 + struct timeval current_time;
448 + gettimeofday(&current_time, NULL);
449 + system_current_time_ut = timeval_usec(&current_time);
450 +}
451 +#endif
452 +
453 +#if defined(__FreeBSD__)
454 +static inline bool collect_data_for_all_pids_per_os(void) {
455 + // Mark all processes as unread before collecting new data
456 + struct pid_stat *p = NULL;
457 + if(all_pids_count) {
458 + for(p = root_of_pids; p ; p = p->next)
459 + mark_pid_as_unread(p);
460 + }
461 +
462 + int i, procnum;
463 +
464 + static size_t procbase_size = 0;
465 + static struct kinfo_proc *procbase = NULL;
466 +
467 + size_t new_procbase_size;
468 +
469 + int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC };
470 + if (unlikely(sysctl(mib, 3, NULL, &new_procbase_size, NULL, 0))) {
471 + netdata_log_error("sysctl error: Can't get processes data size");
472 + return false;
473 + }
474 +
475 + // give it some air for processes that may be started
476 + // during this little time.
477 + new_procbase_size += 100 * sizeof(struct kinfo_proc);
478 +
479 + // increase the buffer if needed
480 + if(new_procbase_size > procbase_size) {
481 + procbase_size = new_procbase_size;
482 + procbase = reallocz(procbase, procbase_size);
483 + }
484 +
485 + // sysctl() gets from new_procbase_size the buffer size
486 + // and also returns to it the amount of data filled in
487 + new_procbase_size = procbase_size;
488 +
489 + // get the processes from the system
490 + if (unlikely(sysctl(mib, 3, procbase, &new_procbase_size, NULL, 0))) {
491 + netdata_log_error("sysctl error: Can't get processes data");
492 + return false;
493 + }
494 +
495 + // based on the amount of data filled in
496 + // calculate the number of processes we got
497 + procnum = new_procbase_size / sizeof(struct kinfo_proc);
498 +
499 + get_current_time();
500 +
501 + for (i = 0 ; i < procnum ; ++i) {
502 + pid_t pid = procbase[i].ki_pid;
503 + if (pid <= 0) continue;
504 + collect_data_for_pid(pid, &procbase[i]);
505 + }
506 +
507 + return true;
508 +}
509 +#endif // __FreeBSD__
510 +
511 +#if defined(__APPLE__)
512 +static inline bool collect_data_for_all_pids_per_os(void) {
513 + // Mark all processes as unread before collecting new data
514 + struct pid_stat *p;
515 + if(all_pids_count) {
516 + for(p = root_of_pids; p; p = p->next)
517 + mark_pid_as_unread(p);
518 + }
519 +
520 + static pid_t *pids = NULL;
521 + static int allocatedProcessCount = 0;
522 +
523 + // Get the number of processes
524 + int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
525 + if (numberOfProcesses <= 0) {
526 + netdata_log_error("Failed to retrieve the process count");
527 + return false;
528 + }
529 +
530 + // Allocate or reallocate space to hold all the process IDs if necessary
531 + if (numberOfProcesses > allocatedProcessCount) {
532 + // Allocate additional space to avoid frequent reallocations
533 + allocatedProcessCount = numberOfProcesses + 100;
534 + pids = reallocz(pids, allocatedProcessCount * sizeof(pid_t));
535 + }
536 +
537 + // this is required, otherwise the PIDs become totally random
538 + memset(pids, 0, allocatedProcessCount * sizeof(pid_t));
539 +
540 + // get the list of PIDs
541 + numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, pids, allocatedProcessCount * sizeof(pid_t));
542 + if (numberOfProcesses <= 0) {
543 + netdata_log_error("Failed to retrieve the process IDs");
544 + return false;
545 + }
546 +
547 + get_current_time();
548 +
549 + // Collect data for each process
550 + for (int i = 0; i < numberOfProcesses; ++i) {
551 + pid_t pid = pids[i];
552 + if (pid <= 0) continue;
553 +
554 + struct pid_info pi = { 0 };
555 +
556 + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
557 +
558 + size_t procSize = sizeof(pi.proc);
559 + if(sysctl(mib, 4, &pi.proc, &procSize, NULL, 0) == -1) {
560 + netdata_log_error("Failed to get proc for PID %d", pid);
561 + continue;
562 + }
563 + if(procSize == 0) // no such process
564 + continue;
565 +
566 + int st = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pi.taskinfo, sizeof(pi.taskinfo));
567 + if (st <= 0) {
568 + netdata_log_error("Failed to get task info for PID %d", pid);
569 + continue;
570 + }
571 +
572 + st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &pi.bsdinfo, sizeof(pi.bsdinfo));
573 + if (st <= 0) {
574 + netdata_log_error("Failed to get BSD info for PID %d", pid);
575 + continue;
576 + }
577 +
578 + st = proc_pid_rusage(pid, RUSAGE_INFO_V4, (rusage_info_t *)&pi.rusageinfo);
579 + if (st < 0) {
580 + netdata_log_error("Failed to get resource usage info for PID %d", pid);
581 + continue;
582 + }
583 +
584 + collect_data_for_pid(pid, &pi);
585 + }
586 +
587 + return true;
588 +}
589 +#endif // __APPLE__
590 +
591 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
592 +static int compar_pid(const void *pid1, const void *pid2) {
593 +
594 + struct pid_stat *p1 = all_pids[*((pid_t *)pid1)];
595 + struct pid_stat *p2 = all_pids[*((pid_t *)pid2)];
596 +
597 + if(p1->sortlist > p2->sortlist)
598 + return -1;
599 + else
600 + return 1;
601 +}
602 +
603 +static inline bool collect_data_for_all_pids_per_os(void) {
604 + struct pid_stat *p = NULL;
605 +
606 + // clear process state counter
607 + memset(proc_state_count, 0, sizeof proc_state_count);
608 +
609 + if(all_pids_count) {
610 + size_t slc = 0;
611 + for(p = root_of_pids; p ; p = p->next) {
612 + mark_pid_as_unread(p);
613 + all_pids_sortlist[slc++] = p->pid;
614 + }
615 +
616 + if(unlikely(slc != all_pids_count)) {
617 + netdata_log_error("Internal error: I was thinking I had %zu processes in my arrays, but it seems there are %zu.", all_pids_count, slc);
618 + all_pids_count = slc;
619 + }
620 +
621 + if(include_exited_childs) {
622 + // Read parents before childs
623 + // This is needed to prevent a situation where
624 + // a child is found running, but until we read
625 + // its parent, it has exited and its parent
626 + // has accumulated its resources.
627 +
628 + qsort((void *)all_pids_sortlist, (size_t)all_pids_count, sizeof(pid_t), compar_pid);
629 +
630 + // we forward read all running processes
631 + // collect_data_for_pid() is smart enough,
632 + // not to read the same pid twice per iteration
633 + for(slc = 0; slc < all_pids_count; slc++) {
634 + collect_data_for_pid(all_pids_sortlist[slc], NULL);
635 + }
636 + }
637 + }
638 +
639 + static char uptime_filename[FILENAME_MAX + 1] = "";
640 + if(*uptime_filename == '\0')
641 + snprintfz(uptime_filename, FILENAME_MAX, "%s/proc/uptime", netdata_configured_host_prefix);
642 +
643 + system_uptime_secs = (kernel_uint_t)(uptime_msec(uptime_filename) / MSEC_PER_SEC);
644 +
645 + char dirname[FILENAME_MAX + 1];
646 +
647 + snprintfz(dirname, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
648 + DIR *dir = opendir(dirname);
649 + if(!dir) return false;
650 +
651 + struct dirent *de = NULL;
652 +
653 + while((de = readdir(dir))) {
654 + char *endptr = de->d_name;
655 +
656 + if(unlikely(de->d_type != DT_DIR || de->d_name[0] < '0' || de->d_name[0] > '9'))
657 + continue;
658 +
659 + pid_t pid = (pid_t) strtoul(de->d_name, &endptr, 10);
660 +
661 + // make sure we read a valid number
662 + if(unlikely(endptr == de->d_name || *endptr != '\0'))
663 + continue;
664 +
665 + collect_data_for_pid(pid, NULL);
666 + }
667 + closedir(dir);
668 +
669 + return true;
670 +}
671 +#endif // !__FreeBSD__ && !__APPLE__
672 +
673 +bool collect_data_for_all_pids(void) {
674 + if(!collect_data_for_all_pids_per_os())
675 + return false;
676 +
677 + if(!all_pids_count)
678 + return false;
679 +
680 + // we need /proc/stat to normalize the cpu consumption of the exited childs
681 + read_global_time();
682 +
683 + // build the process tree
684 + link_all_processes_to_their_parents();
685 +
686 + // normally this is done
687 + // however we may have processes exited while we collected values
688 + // so let's find the exited ones
689 + // we do this by collecting the ownership of process
690 + // if we manage to get the ownership, the process still runs
691 + process_exited_pids();
692 +
693 + return true;
694 +}
src/collectors/apps.plugin/apps_proc_stat.c new
+154
@@ -0,0 +1,154 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +#if defined(__APPLE__)
6 +int read_global_time(void) {
7 + static kernel_uint_t utime_raw = 0, stime_raw = 0, ntime_raw = 0;
8 + static usec_t collected_usec = 0, last_collected_usec = 0;
9 +
10 + host_cpu_load_info_data_t cpuinfo;
11 + mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
12 +
13 + if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count) != KERN_SUCCESS) {
14 + // Handle error
15 + goto cleanup;
16 + }
17 +
18 + last_collected_usec = collected_usec;
19 + collected_usec = now_monotonic_usec();
20 +
21 + calls_counter++;
22 +
23 + // Convert ticks to time
24 + // Note: MacOS does not separate nice time from user time in the CPU stats, so you might need to adjust this logic
25 + kernel_uint_t global_ntime = 0; // Assuming you want to keep track of nice time separately
26 +
27 + incremental_rate(global_utime, utime_raw, cpuinfo.cpu_ticks[CPU_STATE_USER] + cpuinfo.cpu_ticks[CPU_STATE_NICE], collected_usec, last_collected_usec);
28 + incremental_rate(global_ntime, ntime_raw, cpuinfo.cpu_ticks[CPU_STATE_NICE], collected_usec, last_collected_usec);
29 + incremental_rate(global_stime, stime_raw, cpuinfo.cpu_ticks[CPU_STATE_SYSTEM], collected_usec, last_collected_usec);
30 +
31 + global_utime += global_ntime;
32 +
33 + if(unlikely(global_iterations_counter == 1)) {
34 + global_utime = 0;
35 + global_stime = 0;
36 + global_gtime = 0;
37 + }
38 +
39 + return 1;
40 +
41 +cleanup:
42 + global_utime = 0;
43 + global_stime = 0;
44 + global_gtime = 0;
45 + return 0;
46 +}
47 +#endif // __APPLE__
48 +
49 +
50 +#if defined(__FreeBSD__)
51 +int read_global_time(void) {
52 + static kernel_uint_t utime_raw = 0, stime_raw = 0, ntime_raw = 0;
53 + static usec_t collected_usec = 0, last_collected_usec = 0;
54 + long cp_time[CPUSTATES];
55 +
56 + if (unlikely(CPUSTATES != 5)) {
57 + goto cleanup;
58 + } else {
59 + static int mib[2] = {0, 0};
60 +
61 + if (unlikely(GETSYSCTL_SIMPLE("kern.cp_time", mib, cp_time))) {
62 + goto cleanup;
63 + }
64 + }
65 +
66 + last_collected_usec = collected_usec;
67 + collected_usec = now_monotonic_usec();
68 +
69 + calls_counter++;
70 +
71 + // temporary - it is added global_ntime;
72 + kernel_uint_t global_ntime = 0;
73 +
74 + incremental_rate(global_utime, utime_raw, cp_time[0] * 100LLU / system_hz, collected_usec, last_collected_usec);
75 + incremental_rate(global_ntime, ntime_raw, cp_time[1] * 100LLU / system_hz, collected_usec, last_collected_usec);
76 + incremental_rate(global_stime, stime_raw, cp_time[2] * 100LLU / system_hz, collected_usec, last_collected_usec);
77 +
78 + global_utime += global_ntime;
79 +
80 + if(unlikely(global_iterations_counter == 1)) {
81 + global_utime = 0;
82 + global_stime = 0;
83 + global_gtime = 0;
84 + }
85 +
86 + return 1;
87 +
88 +cleanup:
89 + global_utime = 0;
90 + global_stime = 0;
91 + global_gtime = 0;
92 + return 0;
93 +}
94 +#endif // __APPLE__
95 +
96 +#if !defined(__FreeBSD__) && !defined(__APPLE__)
97 +int read_global_time(void) {
98 + static char filename[FILENAME_MAX + 1] = "";
99 + static procfile *ff = NULL;
100 + static kernel_uint_t utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0;
101 + static usec_t collected_usec = 0, last_collected_usec = 0;
102 +
103 + if(unlikely(!ff)) {
104 + snprintfz(filename, FILENAME_MAX, "%s/proc/stat", netdata_configured_host_prefix);
105 + ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
106 + if(unlikely(!ff)) goto cleanup;
107 + }
108 +
109 + ff = procfile_readall(ff);
110 + if(unlikely(!ff)) goto cleanup;
111 +
112 + last_collected_usec = collected_usec;
113 + collected_usec = now_monotonic_usec();
114 +
115 + calls_counter++;
116 +
117 + // temporary - it is added global_ntime;
118 + kernel_uint_t global_ntime = 0;
119 +
120 + incremental_rate(global_utime, utime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 1)), collected_usec, last_collected_usec);
121 + incremental_rate(global_ntime, ntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 2)), collected_usec, last_collected_usec);
122 + incremental_rate(global_stime, stime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 3)), collected_usec, last_collected_usec);
123 + incremental_rate(global_gtime, gtime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 10)), collected_usec, last_collected_usec);
124 +
125 + global_utime += global_ntime;
126 +
127 + if(enable_guest_charts) {
128 + // temporary - it is added global_ntime;
129 + kernel_uint_t global_gntime = 0;
130 +
131 + // guest nice time, on guest time
132 + incremental_rate(global_gntime, gntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 11)), collected_usec, last_collected_usec);
133 +
134 + global_gtime += global_gntime;
135 +
136 + // remove guest time from user time
137 + global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime;
138 + }
139 +
140 + if(unlikely(global_iterations_counter == 1)) {
141 + global_utime = 0;
142 + global_stime = 0;
143 + global_gtime = 0;
144 + }
145 +
146 + return 1;
147 +
148 +cleanup:
149 + global_utime = 0;
150 + global_stime = 0;
151 + global_gtime = 0;
152 + return 0;
153 +}
154 +#endif // !__FreeBSD__ !__APPLE__
src/collectors/apps.plugin/apps_targets.c new
+266
@@ -0,0 +1,266 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// apps_groups.conf
7 +// aggregate all processes in groups, to have a limited number of dimensions
8 +
9 +struct target *get_users_target(uid_t uid) {
10 + struct target *w;
11 + for(w = users_root_target ; w ; w = w->next)
12 + if(w->uid == uid) return w;
13 +
14 + w = callocz(sizeof(struct target), 1);
15 + snprintfz(w->compare, MAX_COMPARE_NAME, "%u", uid);
16 + w->comparehash = simple_hash(w->compare);
17 + w->comparelen = strlen(w->compare);
18 +
19 + snprintfz(w->id, MAX_NAME, "%u", uid);
20 + w->idhash = simple_hash(w->id);
21 +
22 + struct user_or_group_id user_id_to_find = {
23 + .id = {
24 + .uid = uid,
25 + }
26 + };
27 + struct user_or_group_id *user_or_group_id = user_id_find(&user_id_to_find);
28 +
29 + if(user_or_group_id && user_or_group_id->name && *user_or_group_id->name)
30 + snprintfz(w->name, MAX_NAME, "%s", user_or_group_id->name);
31 +
32 + else {
33 + struct passwd *pw = getpwuid(uid);
34 + if(!pw || !pw->pw_name || !*pw->pw_name)
35 + snprintfz(w->name, MAX_NAME, "%u", uid);
36 + else
37 + snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
38 + }
39 +
40 + strncpyz(w->clean_name, w->name, MAX_NAME);
41 + netdata_fix_chart_name(w->clean_name);
42 +
43 + w->uid = uid;
44 +
45 + w->next = users_root_target;
46 + users_root_target = w;
47 +
48 + debug_log("added uid %u ('%s') target", w->uid, w->name);
49 +
50 + return w;
51 +}
52 +
53 +struct target *get_groups_target(gid_t gid) {
54 + struct target *w;
55 + for(w = groups_root_target ; w ; w = w->next)
56 + if(w->gid == gid) return w;
57 +
58 + w = callocz(sizeof(struct target), 1);
59 + snprintfz(w->compare, MAX_COMPARE_NAME, "%u", gid);
60 + w->comparehash = simple_hash(w->compare);
61 + w->comparelen = strlen(w->compare);
62 +
63 + snprintfz(w->id, MAX_NAME, "%u", gid);
64 + w->idhash = simple_hash(w->id);
65 +
66 + struct user_or_group_id group_id_to_find = {
67 + .id = {
68 + .gid = gid,
69 + }
70 + };
71 + struct user_or_group_id *group_id = group_id_find(&group_id_to_find);
72 +
73 + if(group_id && group_id->name && *group_id->name) {
74 + snprintfz(w->name, MAX_NAME, "%s", group_id->name);
75 + }
76 + else {
77 + struct group *gr = getgrgid(gid);
78 + if(!gr || !gr->gr_name || !*gr->gr_name)
79 + snprintfz(w->name, MAX_NAME, "%u", gid);
80 + else
81 + snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
82 + }
83 +
84 + strncpyz(w->clean_name, w->name, MAX_NAME);
85 + netdata_fix_chart_name(w->clean_name);
86 +
87 + w->gid = gid;
88 +
89 + w->next = groups_root_target;
90 + groups_root_target = w;
91 +
92 + debug_log("added gid %u ('%s') target", w->gid, w->name);
93 +
94 + return w;
95 +}
96 +
97 +// find or create a new target
98 +// there are targets that are just aggregated to other target (the second argument)
99 +static struct target *get_apps_groups_target(const char *id, struct target *target, const char *name) {
100 + int tdebug = 0, thidden = target?target->hidden:0, ends_with = 0;
101 + const char *nid = id;
102 +
103 + // extract the options
104 + while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
105 + if(nid[0] == '-') thidden = 1;
106 + if(nid[0] == '+') tdebug = 1;
107 + if(nid[0] == '*') ends_with = 1;
108 + nid++;
109 + }
110 + uint32_t hash = simple_hash(id);
111 +
112 + // find if it already exists
113 + struct target *w, *last = apps_groups_root_target;
114 + for(w = apps_groups_root_target ; w ; w = w->next) {
115 + if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
116 + return w;
117 +
118 + last = w;
119 + }
120 +
121 + // find an existing target
122 + if(unlikely(!target)) {
123 + while(*name == '-') {
124 + if(*name == '-') thidden = 1;
125 + name++;
126 + }
127 +
128 + for(target = apps_groups_root_target ; target != NULL ; target = target->next) {
129 + if(!target->target && strcmp(name, target->name) == 0)
130 + break;
131 + }
132 +
133 + if(unlikely(debug_enabled)) {
134 + if(unlikely(target))
135 + debug_log("REUSING TARGET NAME '%s' on ID '%s'", target->name, target->id);
136 + else
137 + debug_log("NEW TARGET NAME '%s' on ID '%s'", name, id);
138 + }
139 + }
140 +
141 + if(target && target->target)
142 + fatal("Internal Error: request to link process '%s' to target '%s' which is linked to target '%s'", id, target->id, target->target->id);
143 +
144 + w = callocz(sizeof(struct target), 1);
145 + strncpyz(w->id, nid, MAX_NAME);
146 + w->idhash = simple_hash(w->id);
147 +
148 + if(unlikely(!target))
149 + // copy the name
150 + strncpyz(w->name, name, MAX_NAME);
151 + else
152 + // copy the id
153 + strncpyz(w->name, nid, MAX_NAME);
154 +
155 + // dots are used to distinguish chart type and id in streaming, so we should replace them
156 + strncpyz(w->clean_name, w->name, MAX_NAME);
157 + netdata_fix_chart_name(w->clean_name);
158 + for (char *d = w->clean_name; *d; d++) {
159 + if (*d == '.')
160 + *d = '_';
161 + }
162 +
163 + strncpyz(w->compare, nid, MAX_COMPARE_NAME);
164 + size_t len = strlen(w->compare);
165 + if(w->compare[len - 1] == '*') {
166 + w->compare[len - 1] = '\0';
167 + w->starts_with = 1;
168 + }
169 + w->ends_with = ends_with;
170 +
171 + if(w->starts_with && w->ends_with)
172 + proc_pid_cmdline_is_needed = true;
173 +
174 + w->comparehash = simple_hash(w->compare);
175 + w->comparelen = strlen(w->compare);
176 +
177 + w->hidden = thidden;
178 +#ifdef NETDATA_INTERNAL_CHECKS
179 + w->debug_enabled = tdebug;
180 +#else
181 + if(tdebug)
182 + fprintf(stderr, "apps.plugin has been compiled without debugging\n");
183 +#endif
184 + w->target = target;
185 +
186 + // append it, to maintain the order in apps_groups.conf
187 + if(last) last->next = w;
188 + else apps_groups_root_target = w;
189 +
190 + debug_log("ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s"
191 + , w->id
192 + , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
193 + , w->target?w->target->name:w->name
194 + , (w->hidden)?"hidden":"-"
195 + , (w->debug_enabled)?"debug":"-"
196 + );
197 +
198 + return w;
199 +}
200 +
201 +// read the apps_groups.conf file
202 +int read_apps_groups_conf(const char *path, const char *file) {
203 + char filename[FILENAME_MAX + 1];
204 +
205 + snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", path, file);
206 +
207 + debug_log("process groups file: '%s'", filename);
208 +
209 + // ----------------------------------------
210 +
211 + procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT);
212 + if(!ff) return 1;
213 +
214 + procfile_set_quotes(ff, "'\"");
215 +
216 + ff = procfile_readall(ff);
217 + if(!ff)
218 + return 1;
219 +
220 + size_t line, lines = procfile_lines(ff);
221 +
222 + for(line = 0; line < lines ;line++) {
223 + size_t word, words = procfile_linewords(ff, line);
224 + if(!words) continue;
225 +
226 + char *name = procfile_lineword(ff, line, 0);
227 + if(!name || !*name) continue;
228 +
229 + // find a possibly existing target
230 + struct target *w = NULL;
231 +
232 + // loop through all words, skipping the first one (the name)
233 + for(word = 0; word < words ;word++) {
234 + char *s = procfile_lineword(ff, line, word);
235 + if(!s || !*s) continue;
236 + if(*s == '#') break;
237 +
238 + // is this the first word? skip it
239 + if(s == name) continue;
240 +
241 + // add this target
242 + struct target *n = get_apps_groups_target(s, w, name);
243 + if(!n) {
244 + netdata_log_error("Cannot create target '%s' (line %zu, word %zu)", s, line, word);
245 + continue;
246 + }
247 +
248 + // just some optimization
249 + // to avoid searching for a target for each process
250 + if(!w) w = n->target?n->target:n;
251 + }
252 + }
253 +
254 + procfile_close(ff);
255 +
256 + apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL, "other"); // match nothing
257 + if(!apps_groups_default_target)
258 + fatal("Cannot create default target");
259 + apps_groups_default_target->is_other = true;
260 +
261 + // allow the user to override group 'other'
262 + if(apps_groups_default_target->target)
263 + apps_groups_default_target = apps_groups_default_target->target;
264 +
265 + return 0;
266 +}
src/collectors/apps.plugin/apps_users_and_groups.c new
+206
@@ -0,0 +1,206 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "apps_plugin.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// read users and groups from files
7 +
8 +enum user_or_group_id_type {
9 + USER_ID,
10 + GROUP_ID
11 +};
12 +
13 +struct user_or_group_ids {
14 + enum user_or_group_id_type type;
15 +
16 + avl_tree_type index;
17 + struct user_or_group_id *root;
18 +
19 + char filename[FILENAME_MAX + 1];
20 +};
21 +
22 +int user_id_compare(void* a, void* b) {
23 + if(((struct user_or_group_id *)a)->id.uid < ((struct user_or_group_id *)b)->id.uid)
24 + return -1;
25 +
26 + else if(((struct user_or_group_id *)a)->id.uid > ((struct user_or_group_id *)b)->id.uid)
27 + return 1;
28 +
29 + else
30 + return 0;
31 +}
32 +
33 +struct user_or_group_ids all_user_ids = {
34 + .type = USER_ID,
35 +
36 + .index = {
37 + NULL,
38 + user_id_compare
39 + },
40 +
41 + .root = NULL,
42 +
43 + .filename = "",
44 +};
45 +
46 +int group_id_compare(void* a, void* b) {
47 + if(((struct user_or_group_id *)a)->id.gid < ((struct user_or_group_id *)b)->id.gid)
48 + return -1;
49 +
50 + else if(((struct user_or_group_id *)a)->id.gid > ((struct user_or_group_id *)b)->id.gid)
51 + return 1;
52 +
53 + else
54 + return 0;
55 +}
56 +
57 +struct user_or_group_ids all_group_ids = {
58 + .type = GROUP_ID,
59 +
60 + .index = {
61 + NULL,
62 + group_id_compare
63 + },
64 +
65 + .root = NULL,
66 +
67 + .filename = "",
68 +};
69 +
70 +int file_changed(const struct stat *statbuf __maybe_unused, struct timespec *last_modification_time __maybe_unused) {
71 +#if defined(__APPLE__)
72 + return 0;
73 +#else
74 + if(likely(statbuf->st_mtim.tv_sec == last_modification_time->tv_sec &&
75 + statbuf->st_mtim.tv_nsec == last_modification_time->tv_nsec)) return 0;
76 +
77 + last_modification_time->tv_sec = statbuf->st_mtim.tv_sec;
78 + last_modification_time->tv_nsec = statbuf->st_mtim.tv_nsec;
79 +
80 + return 1;
81 +#endif
82 +}
83 +
84 +int read_user_or_group_ids(struct user_or_group_ids *ids, struct timespec *last_modification_time) {
85 + struct stat statbuf;
86 + if(unlikely(stat(ids->filename, &statbuf)))
87 + return 1;
88 + else
89 + if(likely(!file_changed(&statbuf, last_modification_time))) return 0;
90 +
91 + procfile *ff = procfile_open(ids->filename, " :\t", PROCFILE_FLAG_DEFAULT);
92 + if(unlikely(!ff)) return 1;
93 +
94 + ff = procfile_readall(ff);
95 + if(unlikely(!ff)) return 1;
96 +
97 + size_t line, lines = procfile_lines(ff);
98 +
99 + for(line = 0; line < lines ;line++) {
100 + size_t words = procfile_linewords(ff, line);
101 + if(unlikely(words < 3)) continue;
102 +
103 + char *name = procfile_lineword(ff, line, 0);
104 + if(unlikely(!name || !*name)) continue;
105 +
106 + char *id_string = procfile_lineword(ff, line, 2);
107 + if(unlikely(!id_string || !*id_string)) continue;
108 +
109 +
110 + struct user_or_group_id *user_or_group_id = callocz(1, sizeof(struct user_or_group_id));
111 +
112 + if(ids->type == USER_ID)
113 + user_or_group_id->id.uid = (uid_t) str2ull(id_string, NULL);
114 + else
115 + user_or_group_id->id.gid = (uid_t) str2ull(id_string, NULL);
116 +
117 + user_or_group_id->name = strdupz(name);
118 + user_or_group_id->updated = 1;
119 +
120 + struct user_or_group_id *existing_user_id = NULL;
121 +
122 + if(likely(ids->root))
123 + existing_user_id = (struct user_or_group_id *)avl_search(&ids->index, (avl_t *) user_or_group_id);
124 +
125 + if(unlikely(existing_user_id)) {
126 + freez(existing_user_id->name);
127 + existing_user_id->name = user_or_group_id->name;
128 + existing_user_id->updated = 1;
129 + freez(user_or_group_id);
130 + }
131 + else {
132 + if(unlikely(avl_insert(&ids->index, (avl_t *) user_or_group_id) != (void *) user_or_group_id)) {
133 + netdata_log_error("INTERNAL ERROR: duplicate indexing of id during realloc");
134 + }
135 +
136 + user_or_group_id->next = ids->root;
137 + ids->root = user_or_group_id;
138 + }
139 + }
140 +
141 + procfile_close(ff);
142 +
143 + // remove unused ids
144 + struct user_or_group_id *user_or_group_id = ids->root, *prev_user_id = NULL;
145 +
146 + while(user_or_group_id) {
147 + if(unlikely(!user_or_group_id->updated)) {
148 + if(unlikely((struct user_or_group_id *)avl_remove(&ids->index, (avl_t *) user_or_group_id) != user_or_group_id))
149 + netdata_log_error("INTERNAL ERROR: removal of unused id from index, removed a different id");
150 +
151 + if(prev_user_id)
152 + prev_user_id->next = user_or_group_id->next;
153 + else
154 + ids->root = user_or_group_id->next;
155 +
156 + freez(user_or_group_id->name);
157 + freez(user_or_group_id);
158 +
159 + if(prev_user_id)
160 + user_or_group_id = prev_user_id->next;
161 + else
162 + user_or_group_id = ids->root;
163 + }
164 + else {
165 + user_or_group_id->updated = 0;
166 +
167 + prev_user_id = user_or_group_id;
168 + user_or_group_id = user_or_group_id->next;
169 + }
170 + }
171 +
172 + return 0;
173 +}
174 +
175 +struct user_or_group_id *user_id_find(struct user_or_group_id *user_id_to_find) {
176 + if(*netdata_configured_host_prefix) {
177 + static struct timespec last_passwd_modification_time;
178 + int ret = read_user_or_group_ids(&all_user_ids, &last_passwd_modification_time);
179 +
180 + if(likely(!ret && all_user_ids.index.root))
181 + return (struct user_or_group_id *)avl_search(&all_user_ids.index, (avl_t *)user_id_to_find);
182 + }
183 +
184 + return NULL;
185 +}
186 +
187 +struct user_or_group_id *group_id_find(struct user_or_group_id *group_id_to_find) {
188 + if(*netdata_configured_host_prefix) {
189 + static struct timespec last_group_modification_time;
190 + int ret = read_user_or_group_ids(&all_group_ids, &last_group_modification_time);
191 +
192 + if(likely(!ret && all_group_ids.index.root))
193 + return (struct user_or_group_id *)avl_search(&all_group_ids.index, (avl_t *) &group_id_to_find);
194 + }
195 +
196 + return NULL;
197 +}
198 +
199 +void users_and_groups_init(void) {
200 + snprintfz(all_user_ids.filename, FILENAME_MAX, "%s/etc/passwd", netdata_configured_host_prefix);
201 + debug_log("passwd file: '%s'", all_user_ids.filename);
202 +
203 + snprintfz(all_group_ids.filename, FILENAME_MAX, "%s/etc/group", netdata_configured_host_prefix);
204 + debug_log("group file: '%s'", all_group_ids.filename);
205 +}
206 +