master
c 952 lines 31.6 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "apps_plugin.h"
4
5 static inline void link_pid_to_its_parent(struct pid_stat *p);
6
7 // --------------------------------------------------------------------------------------------------------------------
8 // The index of all pids
9
10 #define SIMPLE_HASHTABLE_NAME _PID
11 #define SIMPLE_HASHTABLE_VALUE_TYPE struct pid_stat *
12 #define SIMPLE_HASHTABLE_KEY_TYPE int32_t
13 #define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION pid_stat_to_pid_ptr
14 #define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION pid_ptr_eq
15 #define SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION 0
16 #include "libnetdata/simple_hashtable/simple_hashtable.h"
17
18 static inline int32_t *pid_stat_to_pid_ptr(struct pid_stat *p) {
19 return &p->pid;
20 }
21
22 static inline bool pid_ptr_eq(int32_t *a, int32_t *b) {
23 return *a == *b;
24 }
25
26 struct {
27 #if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
28 // Another pre-allocated list of all possible pids.
29 // We need it to assign them a unique sortlist id, so that we
30 // read parents before children. This is needed to prevent a situation where
31 // a child is found running, but until we read its parent, it has exited and
32 // its parent has accumulated its resources.
33 struct {
34 size_t size;
35 struct pid_stat **array;
36 } sorted;
37 #endif
38
39 struct {
40 size_t count; // the number of processes running
41 struct pid_stat *root;
42 SIMPLE_HASHTABLE_PID ht;
43 ARAL *aral;
44 } all_pids;
45 } pids = { 0 };
46
47 struct pid_stat *root_of_pids(void) {
48 return pids.all_pids.root;
49 }
50
51 size_t all_pids_count(void) {
52 return pids.all_pids.count;
53 }
54
55 void apps_pids_init(void) {
56 pids.all_pids.aral = aral_create("pid_stat", sizeof(struct pid_stat),
57 1, 0, NULL, NULL, NULL,
58 false, true, false);
59 simple_hashtable_init_PID(&pids.all_pids.ht, 1024);
60 }
61
62 static inline uint64_t pid_hash(pid_t pid) {
63 return XXH3_64bits(&pid, sizeof(pid));
64 }
65
66 inline struct pid_stat *find_pid_entry(pid_t pid) {
67 if(pid < INIT_PID) return NULL;
68
69 uint64_t hash = pid_hash(pid);
70 int32_t key = pid;
71 SIMPLE_HASHTABLE_SLOT_PID *sl = simple_hashtable_get_slot_PID(&pids.all_pids.ht, hash, &key, true);
72 return(SIMPLE_HASHTABLE_SLOT_DATA(sl));
73 }
74
75 struct pid_stat *get_or_allocate_pid_entry(pid_t pid) {
76 uint64_t hash = pid_hash(pid);
77 int32_t key = pid;
78 SIMPLE_HASHTABLE_SLOT_PID *sl = simple_hashtable_get_slot_PID(&pids.all_pids.ht, hash, &key, true);
79 struct pid_stat *p = SIMPLE_HASHTABLE_SLOT_DATA(sl);
80 if(likely(p))
81 return p;
82
83 p = aral_callocz(pids.all_pids.aral);
84
85 #if (PROCESSES_HAVE_FDS == 1)
86 p->fds = mallocz(sizeof(struct pid_fd) * 3); // stdin, stdout, stderr
87 p->fds_size = 3;
88 init_pid_fds(p, 0, p->fds_size);
89 #endif
90
91 p->pid = pid;
92 p->values[PDF_PROCESSES] = 1;
93 #if (PROCESSES_HAVE_SMAPS_ROLLUP == 1)
94 p->pss_total_ratio = 1.0;
95 p->last_pss_iteration = 0;
96 p->pss_bytes = 0;
97 #endif
98
99 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(pids.all_pids.root, p, prev, next);
100 simple_hashtable_set_slot_PID(&pids.all_pids.ht, sl, hash, p);
101 pids.all_pids.count++;
102
103 return p;
104 }
105
106 void del_pid_entry(pid_t pid) {
107 uint64_t hash = pid_hash(pid);
108 int32_t key = pid;
109 SIMPLE_HASHTABLE_SLOT_PID *sl = simple_hashtable_get_slot_PID(&pids.all_pids.ht, hash, &key, true);
110 struct pid_stat *p = SIMPLE_HASHTABLE_SLOT_DATA(sl);
111
112 if(unlikely(!p)) {
113 netdata_log_error("attempted to free pid %d that is not allocated.", pid);
114 return;
115 }
116
117 debug_log("process %d %s exited, deleting it.", pid, pid_stat_comm(p));
118
119 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(pids.all_pids.root, p, prev, next);
120 simple_hashtable_del_slot_PID(&pids.all_pids.ht, sl);
121
122 #if defined(OS_LINUX)
123 {
124 size_t i;
125 for(i = 0; i < p->fds_size; i++)
126 if(p->fds[i].filename)
127 freez(p->fds[i].filename);
128 }
129
130 arl_free(p->status_arl);
131 #if (PROCESSES_HAVE_SMAPS_ROLLUP == 1)
132 arl_free(p->smaps_rollup_arl);
133 #endif
134
135 freez(p->fds_dirname);
136 freez(p->stat_filename);
137 freez(p->status_filename);
138 freez(p->limits_filename);
139 freez(p->io_filename);
140 freez(p->cmdline_filename);
141 #if (PROCESSES_HAVE_SMAPS_ROLLUP == 1)
142 freez(p->smaps_rollup_filename);
143 #endif
144 #endif
145
146 #if (PROCESSES_HAVE_FDS == 1)
147 freez(p->fds);
148 #endif
149
150 #if (PROCESSES_HAVE_SID == 1)
151 string_freez(p->sid_name);
152 #endif
153
154 #if (PROCESSES_HAVE_SERVICE == 1)
155 string_freez(p->service_name);
156 #endif
157
158 string_freez(p->comm_orig);
159 string_freez(p->comm);
160 string_freez(p->cmdline);
161 aral_freez(pids.all_pids.aral, p);
162
163 pids.all_pids.count--;
164 }
165
166 // --------------------------------------------------------------------------------------------------------------------
167
168 static __thread pid_t current_pid;
169 static __thread kernel_uint_t current_pid_values[PDF_MAX];
170
171 void pid_collection_started(struct pid_stat *p) {
172 fatal_assert(sizeof(current_pid_values) == sizeof(p->values));
173 current_pid = p->pid;
174 memcpy(current_pid_values, p->values, sizeof(current_pid_values));
175 memset(p->values, 0, sizeof(p->values));
176 p->values[PDF_PROCESSES] = 1;
177 p->read = true;
178 }
179
180 void pid_collection_failed(struct pid_stat *p) {
181 fatal_assert(current_pid == p->pid);
182 fatal_assert(sizeof(current_pid_values) == sizeof(p->values));
183 memcpy(p->values, current_pid_values, sizeof(p->values));
184 }
185
186 void pid_collection_completed(struct pid_stat *p) {
187 p->updated = true;
188 p->keep = false;
189 p->keeploops = 0;
190 }
191
192 // --------------------------------------------------------------------------------------------------------------------
193 // preloading of parents before their children
194
195 #if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
196 static inline size_t compute_new_sorted_size(size_t old_size, size_t required_size) {
197 size_t size = (required_size % 1024 == 0) ? required_size : required_size + 1024;
198 size = (size / 1024) * 1024;
199
200 if(size < old_size * 2)
201 size = old_size * 2;
202
203 return size;
204 }
205
206 static int compar_pid_sortlist(const void *a, const void *b) {
207 const struct pid_stat *p1 = *(struct pid_stat **)a;
208 const struct pid_stat *p2 = *(struct pid_stat **)b;
209
210 if(p1->sortlist > p2->sortlist)
211 return -1;
212 else
213 return 1;
214 }
215
216 bool collect_parents_before_children(void) {
217 if (!pids.all_pids.count) return false;
218
219 if (pids.all_pids.count > pids.sorted.size) {
220 size_t new_size = compute_new_sorted_size(pids.sorted.size, pids.all_pids.count);
221 freez(pids.sorted.array);
222 pids.sorted.array = mallocz(new_size * sizeof(struct pid_stat *));
223 pids.sorted.size = new_size;
224 }
225
226 size_t slc = 0;
227 struct pid_stat *p = NULL;
228 uint32_t sortlist = 1;
229 for (p = root_of_pids(); p && slc < pids.sorted.size; p = p->next) {
230 pids.sorted.array[slc++] = p;
231
232 // assign a sortlist id to all it and its parents
233 for (struct pid_stat *pp = p; pp ; pp = pp->parent)
234 pp->sortlist = sortlist++;
235 }
236 size_t sorted = slc;
237
238 static bool logged = false;
239 if (unlikely(p && !logged)) {
240 nd_log(
241 NDLS_COLLECTORS,
242 NDLP_ERR,
243 "Internal error: I was thinking I had %zu processes in my arrays, but it seems there are more.",
244 pids.all_pids.count);
245 logged = true;
246 }
247
248 if (include_exited_childs && sorted) {
249 // Read parents before childs
250 // This is needed to prevent a situation where
251 // a child is found running, but until we read
252 // its parent, it has exited and its parent
253 // has accumulated its resources.
254
255 qsort((void *)pids.sorted.array, sorted, sizeof(struct pid_stat *), compar_pid_sortlist);
256
257 // we forward read all running processes
258 // incrementally_collect_data_for_pid() is smart enough,
259 // not to read the same pid twice per iteration
260 for (slc = 0; slc < sorted; slc++) {
261 p = pids.sorted.array[slc];
262 incrementally_collect_data_for_pid_stat(p, NULL);
263 }
264 }
265
266 return true;
267 }
268 #endif
269
270 // --------------------------------------------------------------------------------------------------------------------
271
272 static void log_parent_loop(struct pid_stat *p) {
273 CLEAN_BUFFER *wb = buffer_create(0, NULL);
274 buffer_sprintf(wb, "original pid %d (%s)", p->pid, string2str(p->comm));
275
276 size_t loops = 0;
277 for(struct pid_stat *t = p->parent; t && loops < 2 ;t = t->parent) {
278 buffer_sprintf(wb, " => %d (%s)", t->pid, string2str(t->comm));
279 if(t == p->parent) loops++;
280 }
281
282 buffer_sprintf(wb, " : broke loop at %d (%s)", p->pid, string2str(p->comm));
283
284 errno_clear();
285 nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Parents loop detected: %s", buffer_tostring(wb));
286 }
287
288 static inline bool is_already_a_parent(struct pid_stat *p, struct pid_stat *pp) {
289 for(struct pid_stat *t = pp; t ;t = t->parent)
290 if(t == p) return true;
291
292 return false;
293 }
294
295 static inline void link_pid_to_its_parent(struct pid_stat *p) {
296 p->parent = NULL;
297 if(unlikely(!p->ppid))
298 return;
299
300 if(unlikely(p->ppid == p->pid)) {
301 nd_log(NDLS_COLLECTORS, NDLP_WARNING,
302 "Process %d (%s) states parent %d, which is the same PID. Ignoring it.",
303 p->pid, string2str(p->comm), p->ppid);
304 p->ppid = 0;
305 return;
306 }
307
308 struct pid_stat *pp = find_pid_entry(p->ppid);
309 if(likely(pp)) {
310 fatal_assert(pp->pid == p->ppid);
311
312 if(!is_already_a_parent(p, pp)) {
313 p->parent = pp;
314 pp->children_count++;
315 }
316 else {
317 p->parent = pp;
318 log_parent_loop(p);
319 p->parent = NULL;
320 p->ppid = 0;
321 }
322 }
323 #if (PPID_SHOULD_BE_RUNNING == 1)
324 else {
325 nd_log(NDLS_COLLECTORS, NDLP_WARNING,
326 "pid %d %s states parent %d, but the later does not exist.",
327 p->pid, pid_stat_comm(p), p->ppid);
328 }
329 #endif
330 }
331
332 static inline void link_all_processes_to_their_parents(void) {
333 // link all children to their parents
334 // and update children count on parents
335 for(struct pid_stat *p = root_of_pids(); p ; p = p->next)
336 link_pid_to_its_parent(p);
337 }
338
339 // --------------------------------------------------------------------------------------------------------------------
340
341 static bool is_filename(const char *s) {
342 if(!s || !*s) return false;
343
344 #if defined(OS_WINDOWS)
345 if( (isalpha((uint8_t)*s) || (s[1] == ':' && s[2] == '\\')) || // windows native "x:\"
346 (isalpha((uint8_t)*s) || (s[1] == ':' && s[2] == '/')) || // windows native "x:/"
347 (*s == '\\' && s[1] == '\\' && isalpha((uint8_t)s[2]) && s[3] == '\\') || // windows native "\\x\"
348 (*s == '/' && s[1] == '/' && isalpha((uint8_t)s[2]) && s[3] == '/')) { // windows native "//x/"
349
350 WCHAR ws[FILENAME_MAX];
351 if(utf8_to_utf16(ws, _countof(ws), s, -1) > 0) {
352 DWORD attributes = GetFileAttributesW(ws);
353 if (attributes != INVALID_FILE_ATTRIBUTES)
354 return true;
355 }
356 }
357 #endif
358
359 // for: sh -c "exec /path/to/command parameters"
360 if(strncmp(s, "exec ", 5) == 0 && s[5]) {
361 s += 5;
362 char look_for = ' ';
363 if(*s == '\'') { look_for = '\''; s++; }
364 if(*s == '"') { look_for = '"'; s++; }
365 char *end = strchr(s, look_for);
366 if(end) *end = '\0';
367 }
368
369 // linux, freebsd, macos, msys, cygwin
370 if(*s == '/') {
371 struct statvfs stat;
372 return statvfs(s, &stat) == 0;
373 }
374
375 return false;
376 }
377
378 static const char *extensions_to_strip[] = {
379 ".sh", // shell scripts
380 ".py", // python scripts
381 ".pl", // perl scripts
382 ".js", // node.js
383 #if defined(OS_WINDOWS)
384 ".exe",
385 #endif
386 NULL,
387 };
388
389 // strip extensions we don't want to show
390 static void remove_extension(char *name) {
391 size_t name_len = strlen(name);
392 for(size_t i = 0; extensions_to_strip[i] != NULL; i++) {
393 const char *ext = extensions_to_strip[i];
394 size_t ext_len = strlen(ext);
395 if(name_len > ext_len) {
396 char *check = &name[name_len - ext_len];
397 if(strcmp(check, ext) == 0) {
398 *check = '\0';
399 break;
400 }
401 }
402 }
403 }
404
405 static inline STRING *comm_from_cmdline_param_sanitized(STRING *cmdline) {
406 if(!cmdline) return NULL;
407
408 char *buf = strdupz(string2str(cmdline));
409
410 char *words[100];
411 size_t num_words = quoted_strings_splitter_whitespace(buf, words, 100);
412 for(size_t word = 1; word < num_words ;word++) {
413 char *s = words[word];
414 if(is_filename(s)) {
415 char *name = strrchr(s, '/');
416
417 #if defined(OS_WINDOWS)
418 if(!name)
419 name = strrchr(s, '\\');
420 #endif
421
422 if(name && *name) {
423 name++;
424 remove_extension(name);
425 sanitize_apps_plugin_chart_meta(name);
426 STRING *sanitized = string_strdupz(name);
427 freez(buf);
428 return sanitized;
429 }
430 }
431 }
432
433 freez(buf);
434 return NULL;
435 }
436
437 static inline STRING *comm_from_cmdline_sanitized(STRING *comm, STRING *cmdline) {
438 if(!cmdline) return NULL;
439
440 char *buf = strdupz(string2str(cmdline));
441
442 size_t comm_len = string_strlen(comm);
443 char *start = strstr(buf, string2str(comm));
444 if (start) {
445 char *end = start + comm_len;
446 while (*end &&
447 !isspace((uint8_t) *end) &&
448 *end != '/' && // path separator - linux
449 *end != '\\' && // path separator - windows
450 *end != '"' && // closing double quotes
451 *end != '\'' && // closing single quotes
452 *end != ')' && // sometimes process add ) at their end
453 *end != ':') // sometimes process add : at their end
454 end++;
455
456 *end = '\0';
457
458 remove_extension(start);
459 sanitize_apps_plugin_chart_meta(start);
460 STRING *sanitized = string_strdupz(start);
461 freez(buf);
462 return sanitized;
463 }
464
465 freez(buf);
466 return NULL;
467 }
468
469 static void update_pid_comm_from_cmdline(struct pid_stat *p) {
470 bool updated = false;
471
472 STRING *new_comm = comm_from_cmdline_sanitized(p->comm, p->cmdline);
473 if(new_comm) {
474 string_freez(p->comm);
475 p->comm = new_comm;
476 updated = true;
477 }
478
479 if(is_process_an_interpreter(p)) {
480 new_comm = comm_from_cmdline_param_sanitized(p->cmdline);
481 if(new_comm) {
482 string_freez(p->comm);
483 p->comm = new_comm;
484 updated = true;
485 }
486 }
487
488 if(updated) {
489 p->is_manager = is_process_a_manager(p);
490 p->is_aggregator = is_process_an_aggregator(p);
491 }
492 }
493
494 void update_pid_cmdline(struct pid_stat *p, const char *cmdline) {
495 string_freez(p->cmdline);
496 p->cmdline = cmdline ? string_strdupz(cmdline) : NULL;
497
498 if(p->cmdline)
499 update_pid_comm_from_cmdline(p);
500 }
501
502 void update_pid_comm(struct pid_stat *p, const char *comm) {
503 if(p->comm_orig && string_strcmp(p->comm_orig, comm) == 0)
504 // no change
505 return;
506
507 string_freez(p->comm_orig);
508 p->comm_orig = string_strdupz(comm);
509
510 // some process names have ( and ), remove the parenthesis
511 size_t len = strlen(comm);
512 char *buf;
513 if(comm[0] == '(' && comm[len - 1] == ')') {
514 buf = mallocz(len - 1);
515 memcpy(buf, &comm[1], len - 2);
516 buf[len - 2] = '\0';
517 }
518 else
519 buf = strdupz(comm);
520
521 sanitize_apps_plugin_chart_meta(buf);
522 string_freez(p->comm);
523 p->comm = string_strdupz(buf);
524 freez(buf);
525 p->is_manager = is_process_a_manager(p);
526 p->is_aggregator = is_process_an_aggregator(p);
527
528 #if (PROCESSES_HAVE_CMDLINE == 1)
529 if(likely(proc_pid_cmdline_is_needed && !p->cmdline))
530 managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
531 #else
532 update_pid_comm_from_cmdline(p);
533 #endif
534
535 // the process changed comm, we may have to reassign it to
536 // an apps_groups.conf target.
537 p->target = NULL;
538 }
539
540 // --------------------------------------------------------------------------------------------------------------------
541
542 #if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) || (PROCESSES_HAVE_CHILDREN_FLTS == 1)
543 //static inline int debug_print_process_and_parents(struct pid_stat *p, usec_t time) {
544 // char *prefix = "\\_ ";
545 // int indent = 0;
546 //
547 // if(p->parent)
548 // indent = debug_print_process_and_parents(p->parent, p->stat_collected_usec);
549 // else
550 // prefix = " > ";
551 //
552 // char buffer[indent + 1];
553 // int i;
554 //
555 // for(i = 0; i < indent ;i++) buffer[i] = ' ';
556 // buffer[i] = '\0';
557 //
558 // fprintf(stderr, " %s %s%s (%d %s %"PRIu64""
559 // , buffer
560 // , prefix
561 // , pid_stat_comm(p)
562 // , p->pid
563 // , p->updated?"running":"exited"
564 // , p->stat_collected_usec - time
565 // );
566 //
567 // if(p->values[PDF_UTIME]) fprintf(stderr, " utime=" KERNEL_UINT_FORMAT, p->values[PDF_UTIME]);
568 // if(p->values[PDF_STIME]) fprintf(stderr, " stime=" KERNEL_UINT_FORMAT, p->values[PDF_STIME]);
569 //#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
570 // if(p->values[PDF_GTIME]) fprintf(stderr, " gtime=" KERNEL_UINT_FORMAT, p->values[PDF_GTIME]);
571 //#endif
572 //#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
573 // if(p->values[PDF_CUTIME]) fprintf(stderr, " cutime=" KERNEL_UINT_FORMAT, p->values[PDF_CUTIME]);
574 // if(p->values[PDF_CSTIME]) fprintf(stderr, " cstime=" KERNEL_UINT_FORMAT, p->values[PDF_CSTIME]);
575 //#if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
576 // if(p->values[PDF_CGTIME]) fprintf(stderr, " cgtime=" KERNEL_UINT_FORMAT, p->values[PDF_CGTIME]);
577 //#endif
578 //#endif
579 // if(p->values[PDF_MINFLT]) fprintf(stderr, " minflt=" KERNEL_UINT_FORMAT, p->values[PDF_MINFLT]);
580 //#if (PROCESSES_HAVE_MAJFLT == 1)
581 // if(p->values[PDF_MAJFLT]) fprintf(stderr, " majflt=" KERNEL_UINT_FORMAT, p->values[PDF_MAJFLT]);
582 //#endif
583 //#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
584 // if(p->values[PDF_CMINFLT]) fprintf(stderr, " cminflt=" KERNEL_UINT_FORMAT, p->values[PDF_CMINFLT]);
585 // if(p->values[PDF_CMAJFLT]) fprintf(stderr, " cmajflt=" KERNEL_UINT_FORMAT, p->values[PDF_CMAJFLT]);
586 //#endif
587 // fprintf(stderr, ")\n");
588 //
589 // return indent + 1;
590 //}
591 //
592 //static inline void debug_print_process_tree(struct pid_stat *p, char *msg __maybe_unused) {
593 // debug_log("%s: process %s (%d, %s) with parents:", msg, pid_stat_comm(p), p->pid, p->updated?"running":"exited");
594 // debug_print_process_and_parents(p, p->stat_collected_usec);
595 //}
596 //
597 //static inline void debug_find_lost_child(struct pid_stat *pe, kernel_uint_t lost, int type) {
598 // int found = 0;
599 // struct pid_stat *p = NULL;
600 //
601 // for(p = root_of_pids(); p ; p = p->next) {
602 // if(p == pe) continue;
603 //
604 // switch(type) {
605 // case 1:
606 //#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
607 // if(p->values[PDF_CMINFLT] > lost) {
608 // fprintf(stderr, " > process %d (%s) could use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n",
609 // p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
610 // found++;
611 // }
612 //#endif
613 // break;
614 //
615 // case 2:
616 //#if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
617 // if(p->values[PDF_CMAJFLT] > lost) {
618 // fprintf(stderr, " > process %d (%s) could use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n",
619 // p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
620 // found++;
621 // }
622 //#endif
623 // break;
624 //
625 // case 3:
626 //#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
627 // if(p->values[PDF_CUTIME] > lost) {
628 // fprintf(stderr, " > process %d (%s) could use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
629 // p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
630 // found++;
631 // }
632 //#endif
633 // break;
634 //
635 // case 4:
636 //#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
637 // if(p->values[PDF_CSTIME] > lost) {
638 // fprintf(stderr, " > process %d (%s) could use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
639 // p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
640 // found++;
641 // }
642 //#endif
643 // break;
644 //
645 // case 5:
646 //#if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) && (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
647 // if(p->values[PDF_CGTIME] > lost) {
648 // fprintf(stderr, " > process %d (%s) could use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
649 // p->pid, pid_stat_comm(p), lost, pe->pid, pid_stat_comm(pe));
650 // found++;
651 // }
652 //#endif
653 // break;
654 // }
655 // }
656 //
657 // if(!found) {
658 // switch(type) {
659 // case 1:
660 // fprintf(stderr, " > cannot find any process to use the lost exited child minflt " KERNEL_UINT_FORMAT " of process %d (%s)\n",
661 // lost, pe->pid, pid_stat_comm(pe));
662 // break;
663 //
664 // case 2:
665 // fprintf(stderr, " > cannot find any process to use the lost exited child majflt " KERNEL_UINT_FORMAT " of process %d (%s)\n",
666 // lost, pe->pid, pid_stat_comm(pe));
667 // break;
668 //
669 // case 3:
670 // fprintf(stderr, " > cannot find any process to use the lost exited child utime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
671 // lost, pe->pid, pid_stat_comm(pe));
672 // break;
673 //
674 // case 4:
675 // fprintf(stderr, " > cannot find any process to use the lost exited child stime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
676 // lost, pe->pid, pid_stat_comm(pe));
677 // break;
678 //
679 // case 5:
680 // fprintf(stderr, " > cannot find any process to use the lost exited child gtime " KERNEL_UINT_FORMAT " of process %d (%s)\n",
681 // lost, pe->pid, pid_stat_comm(pe));
682 // break;
683 // }
684 // }
685 //}
686
687 static inline kernel_uint_t remove_exited_child_from_parent(kernel_uint_t *field, kernel_uint_t *pfield) {
688 kernel_uint_t absorbed = 0;
689
690 if(*field > *pfield) {
691 absorbed += *pfield;
692 *field -= *pfield;
693 *pfield = 0;
694 }
695 else {
696 absorbed += *field;
697 *pfield -= *field;
698 *field = 0;
699 }
700
701 return absorbed;
702 }
703
704 static inline void process_exited_pids(void) {
705 /*
706 * WHY WE NEED THIS?
707 *
708 * When a child process exits in Linux, its accumulated user time (utime) and its children's accumulated
709 * user time (cutime) are added to the parent's cutime. This means the parent process's cutime reflects
710 * the total user time spent by its exited children and their descendants
711 *
712 * This results in spikes in the charts.
713 * In this function we remove the exited children resources from the parent's cutime, but only for the
714 * children we have been monitoring and to the degree we have data for them. Since previously running
715 * children have already been reported by us, removing them is the right thing to do.
716 *
717 */
718
719 for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
720 if(p->updated || !p->stat_collected_usec)
721 continue;
722
723 bool have_work = false;
724
725 #if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
726 kernel_uint_t utime = (p->raw[PDF_UTIME] + p->raw[PDF_CUTIME]) * CPU_TO_NANOSECONDCORES;
727 kernel_uint_t stime = (p->raw[PDF_STIME] + p->raw[PDF_CSTIME]) * CPU_TO_NANOSECONDCORES;
728 if(utime + stime) have_work = true;
729 #if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
730 kernel_uint_t gtime = (p->raw[PDF_GTIME] + p->raw[PDF_CGTIME]) * CPU_TO_NANOSECONDCORES;
731 if(gtime) have_work = true;
732 #endif
733 #endif
734
735 #if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
736 kernel_uint_t minflt = (p->raw[PDF_MINFLT] + p->raw[PDF_CMINFLT]) * RATES_DETAIL;
737 if(minflt) have_work = true;
738 #if (PROCESSES_HAVE_MAJFLT == 1)
739 kernel_uint_t majflt = (p->raw[PDF_MAJFLT] + p->raw[PDF_CMAJFLT]) * RATES_DETAIL;
740 if(majflt) have_work = true;
741 #endif
742 #endif
743
744 if(!have_work)
745 continue;
746
747 // if(unlikely(debug_enabled)) {
748 // 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 ")"
749 // , pid_stat_comm(p)
750 // , p->pid
751 // , p->updated?"running":"exited"
752 // , utime
753 // , stime
754 // , gtime
755 // , minflt
756 // , majflt
757 // );
758 // debug_print_process_tree(p, "Searching parents");
759 // }
760
761 for(struct pid_stat *pp = p->parent; pp ; pp = pp->parent) {
762 if(!pp->updated) continue;
763
764 kernel_uint_t absorbed;
765 #if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
766 absorbed = remove_exited_child_from_parent(&utime, &pp->values[PDF_CUTIME]);
767 // if(unlikely(debug_enabled && absorbed))
768 // debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " utime (remaining: " KERNEL_UINT_FORMAT ")",
769 // pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, utime);
770
771 absorbed = remove_exited_child_from_parent(&stime, &pp->values[PDF_CSTIME]);
772 // if(unlikely(debug_enabled && absorbed))
773 // debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " stime (remaining: " KERNEL_UINT_FORMAT ")",
774 // pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, stime);
775
776 #if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
777 absorbed = remove_exited_child_from_parent(&gtime, &pp->values[PDF_CGTIME]);
778 // if(unlikely(debug_enabled && absorbed))
779 // debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " gtime (remaining: " KERNEL_UINT_FORMAT ")",
780 // pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, gtime);
781 #endif
782 #endif
783
784 #if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
785 absorbed = remove_exited_child_from_parent(&minflt, &pp->values[PDF_CMINFLT]);
786 // if(unlikely(debug_enabled && absorbed))
787 // debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " minflt (remaining: " KERNEL_UINT_FORMAT ")",
788 // pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, minflt);
789
790 #if (PROCESSES_HAVE_MAJFLT == 1)
791 absorbed = remove_exited_child_from_parent(&majflt, &pp->values[PDF_CMAJFLT]);
792 // if(unlikely(debug_enabled && absorbed))
793 // debug_log(" > process %s (%d %s) absorbed " KERNEL_UINT_FORMAT " majflt (remaining: " KERNEL_UINT_FORMAT ")",
794 // pid_stat_comm(pp), pp->pid, pp->updated?"running":"exited", absorbed, majflt);
795 #endif
796 #endif
797
798 (void)absorbed;
799 break;
800 }
801
802 // if(unlikely(debug_enabled)) {
803 // if(utime) debug_find_lost_child(p, utime, 3);
804 // if(stime) debug_find_lost_child(p, stime, 4);
805 // if(gtime) debug_find_lost_child(p, gtime, 5);
806 // if(minflt) debug_find_lost_child(p, minflt, 1);
807 // if(majflt) debug_find_lost_child(p, majflt, 2);
808 // }
809
810 // 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 ")"
811 // , pid_stat_comm(p)
812 // , p->pid
813 // , p->updated?"running":"exited"
814 // , utime
815 // , stime
816 // , gtime
817 // , minflt
818 // , majflt
819 // );
820
821 bool done = true;
822
823 #if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
824 p->values[PDF_UTIME] = utime / CPU_TO_NANOSECONDCORES;
825 p->values[PDF_STIME] = stime / CPU_TO_NANOSECONDCORES;
826 p->values[PDF_CUTIME] = 0;
827 p->values[PDF_CSTIME] = 0;
828 if(utime + stime) done = false;
829 #if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
830 p->values[PDF_GTIME] = gtime / CPU_TO_NANOSECONDCORES;
831 p->values[PDF_CGTIME] = 0;
832 if(gtime) done = false;
833 #endif
834 #endif
835
836 #if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
837 p->values[PDF_MINFLT] = minflt / RATES_DETAIL;
838 p->values[PDF_CMINFLT] = 0;
839 if(minflt) done = false;
840 #if (PROCESSES_HAVE_MAJFLT == 1)
841 p->values[PDF_MAJFLT] = majflt / RATES_DETAIL;
842 p->values[PDF_CMAJFLT] = 0;
843 if(majflt) done = false;
844 #endif
845 #endif
846
847 p->keep = !done;
848
849 if(p->keep) {
850 // we need to keep its exited parents too, to ensure we will have
851 // the information to reach the running parent at the next iteration
852 for (struct pid_stat *pp = p->parent; pp; pp = pp->parent) {
853 if (pp->updated) break;
854 pp->keep = true;
855 }
856 }
857 }
858 }
859 #endif
860
861 // --------------------------------------------------------------------------------------------------------------------
862 // the main loop for collecting process data
863
864 static inline void clear_pid_rates(struct pid_stat *p) {
865 p->values[PDF_UTIME] = 0;
866 p->values[PDF_STIME] = 0;
867
868 #if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
869 p->values[PDF_GTIME] = 0;
870 #endif
871
872 #if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1)
873 p->values[PDF_CUTIME] = 0;
874 p->values[PDF_CSTIME] = 0;
875 #if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
876 p->values[PDF_CGTIME] = 0;
877 #endif
878 #endif
879
880 p->values[PDF_MINFLT] = 0;
881 #if (PROCESSES_HAVE_MAJFLT == 1)
882 p->values[PDF_MAJFLT] = 0;
883 #endif
884
885 #if (PROCESSES_HAVE_CHILDREN_FLTS == 1)
886 p->values[PDF_CMINFLT] = 0;
887 p->values[PDF_CMAJFLT] = 0;
888 #endif
889
890 #if (PROCESSES_HAVE_LOGICAL_IO == 1)
891 p->values[PDF_LREAD] = 0;
892 p->values[PDF_LWRITE] = 0;
893 #endif
894
895 #if (PROCESSES_HAVE_PHYSICAL_IO == 1)
896 p->values[PDF_PREAD] = 0;
897 p->values[PDF_PWRITE] = 0;
898 #endif
899
900 #if (PROCESSES_HAVE_IO_CALLS == 1)
901 p->values[PDF_OREAD] = 0;
902 p->values[PDF_OWRITE] = 0;
903 #endif
904
905 #if (PROCESSES_HAVE_VOLCTX == 1)
906 p->values[PDF_VOLCTX] = 0;
907 #endif
908
909 #if (PROCESSES_HAVE_NVOLCTX == 1)
910 p->values[PDF_NVOLCTX] = 0;
911 #endif
912 }
913
914 bool collect_data_for_all_pids(void) {
915 // mark all pids as unread
916 #if (INCREMENTAL_DATA_COLLECTION == 0)
917 usec_t now_mon_ut = now_monotonic_usec();
918 #endif
919
920 for(struct pid_stat *p = root_of_pids(); p ; p = p->next) {
921 p->read = p->updated = p->merged = false;
922 p->children_count = 0;
923 p->parent = NULL; // clear stale parent pointers from previous iteration to prevent use-after-free
924
925 #if (INCREMENTAL_DATA_COLLECTION == 0)
926 p->last_stat_collected_usec = p->stat_collected_usec;
927 p->last_io_collected_usec = p->io_collected_usec;
928 p->stat_collected_usec = p->io_collected_usec = now_mon_ut;
929 #endif
930 }
931
932 // collect data for all pids
933 if(!OS_FUNCTION(apps_os_collect_all_pids)())
934 return false;
935
936 // build the process tree
937 link_all_processes_to_their_parents();
938
939 #if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) || (PROCESSES_HAVE_CHILDREN_FLTS == 1)
940 // merge exited pids to their parents
941 process_exited_pids();
942 #endif
943
944 // the first iteration needs to be eliminated
945 // since we are looking for rates
946 if(unlikely(global_iterations_counter == 1)) {
947 for(struct pid_stat *p = root_of_pids(); p ; p = p->next)
948 if(p->read) clear_pid_rates(p);
949 }
950
951 return true;
952 }