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(>ime, &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(>ime, &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(¤t_time, NULL);
449
+ system_current_time_ut = timeval_usec(¤t_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
+