master
c 947 lines 25.8 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "ebpf.h"
4 #include "ebpf_socket.h"
5 #include "ebpf_apps.h"
6
7 // ----------------------------------------------------------------------------
8 // ARAL vectors used to speed up processing
9 ARAL *ebpf_aral_apps_pid_stat = NULL;
10
11 /**
12 * eBPF ARAL Init
13 *
14 * Initiallize array allocator that will be used when integration with apps and ebpf is created.
15 */
16 void ebpf_aral_init(void)
17 {
18 size_t max_elements = NETDATA_EBPF_ALLOC_MAX_PID;
19 if (max_elements < NETDATA_EBPF_ALLOC_MIN_ELEMENTS) {
20 netdata_log_error(
21 "Number of elements given is too small, adjusting it for %d", NETDATA_EBPF_ALLOC_MIN_ELEMENTS);
22 max_elements = NETDATA_EBPF_ALLOC_MIN_ELEMENTS;
23 }
24
25 #ifdef NETDATA_DEV_MODE
26 netdata_log_info("Plugin is using ARAL with values %d", NETDATA_EBPF_ALLOC_MAX_PID);
27 #endif
28 }
29
30 // ----------------------------------------------------------------------------
31 // internal flags
32 // handled in code (automatically set)
33
34 static int proc_pid_cmdline_is_needed = 0; // 1 when we need to read /proc/cmdline
35
36 /*****************************************************************
37 *
38 * FUNCTIONS USED TO READ HASH TABLES
39 *
40 *****************************************************************/
41
42 /**
43 * Read statistic hash table.
44 *
45 * @param ep the output structure.
46 * @param fd the file descriptor mapped from kernel ring.
47 * @param pid the index used to select the data.
48 * @param bpf_map_lookup_elem a pointer for the function used to read data.
49 *
50 * @return It returns 0 when the data was copied and -1 otherwise
51 */
52 int ebpf_read_hash_table(void *ep, int fd, uint32_t pid)
53 {
54 if (!ep)
55 return -1;
56
57 if (!bpf_map_lookup_elem(fd, &pid, ep))
58 return 0;
59
60 return -1;
61 }
62
63 /*****************************************************************
64 *
65 * FUNCTIONS CALLED FROM COLLECTORS
66 *
67 *****************************************************************/
68
69 /**
70 * Reset the target values
71 *
72 * @param root the pointer to the chain that will be reset.
73 *
74 * @return it returns the number of structures that was reset.
75 */
76 size_t zero_all_targets(struct ebpf_target *root)
77 {
78 struct ebpf_target *w;
79 size_t count = 0;
80
81 for (w = root; w; w = w->next) {
82 if (ebpf_plugin_stop())
83 break;
84
85 count++;
86
87 if (unlikely(w->root_pid)) {
88 struct ebpf_pid_on_target *pid_on_target = w->root_pid;
89
90 while (pid_on_target) {
91 struct ebpf_pid_on_target *pid_on_target_to_free = pid_on_target;
92 pid_on_target = pid_on_target->next;
93 freez(pid_on_target_to_free);
94 }
95
96 w->root_pid = NULL;
97 }
98 }
99
100 return count;
101 }
102
103 /**
104 * Clean the allocated structures
105 *
106 * @param agrt the pointer to be cleaned.
107 */
108 void clean_apps_groups_target(struct ebpf_target *agrt)
109 {
110 struct ebpf_target *current_target;
111 while (agrt) {
112 if (ebpf_plugin_stop())
113 break;
114
115 current_target = agrt;
116 agrt = current_target->target;
117
118 freez(current_target);
119 }
120 }
121
122 /**
123 * Find or create a new target
124 * there are targets that are just aggregated to other target (the second argument)
125 *
126 * @param id
127 * @param target
128 * @param name
129 *
130 * @return It returns the target on success and NULL otherwise
131 */
132 struct ebpf_target *
133 get_apps_groups_target(struct ebpf_target **agrt, const char *id, struct ebpf_target *target, const char *name)
134 {
135 int tdebug = 0, thidden = target ? target->hidden : 0, ends_with = 0;
136 const char *nid = id;
137
138 // extract the options
139 while (nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
140 if (nid[0] == '-')
141 thidden = 1;
142 if (nid[0] == '+')
143 tdebug = 1;
144 if (nid[0] == '*')
145 ends_with = 1;
146 nid++;
147 }
148 uint32_t hash = simple_hash(id);
149
150 // find if it already exists
151 struct ebpf_target *w, *last = *agrt;
152 for (w = *agrt; w; w = w->next) {
153 if (ebpf_plugin_stop())
154 break;
155
156 if (w->idhash == hash && strncmp(nid, w->id, EBPF_MAX_NAME) == 0)
157 return w;
158
159 last = w;
160 }
161
162 // find an existing target
163 if (unlikely(!target)) {
164 while (*name == '-') {
165 if (*name == '-')
166 thidden = 1;
167 name++;
168 }
169
170 for (target = *agrt; target != NULL; target = target->next) {
171 if (!target->target && strcmp(name, target->name) == 0)
172 break;
173 }
174 }
175
176 if (target && target->target)
177 fatal(
178 "Internal Error: request to link process '%s' to target '%s' which is linked to target '%s'",
179 id,
180 target->id,
181 target->target->id);
182
183 w = callocz(1, sizeof(struct ebpf_target));
184 strncpyz(w->id, nid, EBPF_MAX_NAME);
185 w->idhash = simple_hash(w->id);
186
187 if (unlikely(!target))
188 // copy the name
189 strncpyz(w->name, name, EBPF_MAX_NAME);
190 else
191 // copy the id
192 strncpyz(w->name, nid, EBPF_MAX_NAME);
193
194 strncpyz(w->clean_name, w->name, EBPF_MAX_NAME);
195 netdata_fix_chart_name(w->clean_name);
196 for (char *d = w->clean_name; *d; d++) {
197 if (*d == '.')
198 *d = '_';
199 }
200
201 strncpyz(w->compare, nid, EBPF_MAX_COMPARE_NAME);
202 size_t len = strlen(w->compare);
203 if (w->compare[len - 1] == '*') {
204 w->compare[len - 1] = '\0';
205 w->starts_with = 1;
206 }
207 w->ends_with = ends_with;
208
209 if (w->starts_with && w->ends_with)
210 proc_pid_cmdline_is_needed = 1;
211
212 w->comparehash = simple_hash(w->compare);
213 w->comparelen = strlen(w->compare);
214
215 w->hidden = thidden;
216 #ifdef NETDATA_INTERNAL_CHECKS
217 w->debug_enabled = tdebug;
218 #else
219 if (tdebug)
220 fprintf(stderr, "apps.plugin has been compiled without debugging\n");
221 #endif
222 w->target = target;
223
224 // append it, to maintain the order in apps_groups.conf
225 if (last)
226 last->next = w;
227 else
228 *agrt = w;
229
230 return w;
231 }
232
233 /**
234 * Read the apps_groups.conf file
235 *
236 * @param agrt a pointer to apps_group_root_target
237 * @param path the directory to search apps_%s.conf
238 * @param file the word to complement the file name.
239 *
240 * @return It returns 0 on success and -1 otherwise
241 */
242 int ebpf_read_apps_groups_conf(struct ebpf_target **agdt, struct ebpf_target **agrt, const char *path, const char *file)
243 {
244 char filename[FILENAME_MAX + 1];
245
246 snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", path, file);
247
248 // ----------------------------------------
249
250 procfile *ff = procfile_open_no_log(filename, " :\t", PROCFILE_FLAG_DEFAULT);
251 if (!ff)
252 return -1;
253
254 procfile_set_quotes(ff, "'\"");
255
256 ff = procfile_readall(ff);
257 if (!ff)
258 return -1;
259
260 size_t line, lines = procfile_lines(ff);
261
262 for (line = 0; line < lines; line++) {
263 size_t word, words = procfile_linewords(ff, line);
264 if (!words)
265 continue;
266
267 char *name = procfile_lineword(ff, line, 0);
268 if (!name || !*name)
269 continue;
270
271 // find a possibly existing target
272 struct ebpf_target *w = NULL;
273
274 // loop through all words, skipping the first one (the name)
275 for (word = 0; word < words; word++) {
276 char *s = procfile_lineword(ff, line, word);
277 if (!s || !*s)
278 continue;
279 if (*s == '#')
280 break;
281
282 // is this the first word? skip it
283 if (s == name)
284 continue;
285
286 // add this target
287 struct ebpf_target *n = get_apps_groups_target(agrt, s, w, name);
288 if (!n) {
289 netdata_log_error("Cannot create target '%s' (line %zu, word %zu)", s, line, word);
290 continue;
291 }
292
293 // just some optimization
294 // to avoid searching for a target for each process
295 if (!w)
296 w = n->target ? n->target : n;
297 }
298 }
299
300 procfile_close(ff);
301
302 *agdt = get_apps_groups_target(agrt, "p+!o@w#e$i^r&7*5(-i)l-o_", NULL, "other"); // match nothing
303 if (!*agdt)
304 fatal("Cannot create default target");
305
306 struct ebpf_target *ptr = *agdt;
307 if (ptr->target)
308 *agdt = ptr->target;
309
310 return 0;
311 }
312
313 // the minimum PID of the system
314 // this is also the pid of the init process
315 #define INIT_PID 1
316
317 // ----------------------------------------------------------------------------
318 // string lengths
319
320 #define MAX_CMDLINE 16384
321
322 Pvoid_t ebpf_pid_judyL = NULL;
323 SPINLOCK ebpf_pid_spinlock = SPINLOCK_INITIALIZER;
324
325 void ebpf_pid_del(pid_t pid)
326 {
327 spinlock_lock(&ebpf_pid_spinlock);
328 (void)JudyLDel(&ebpf_pid_judyL, (Word_t)pid, PJE0);
329 spinlock_unlock(&ebpf_pid_spinlock);
330 }
331
332 static ebpf_pid_data_t *ebpf_find_pid_data_unsafe(pid_t pid)
333 {
334 ebpf_pid_data_t *pid_data = NULL;
335 Pvoid_t *Pvalue = JudyLGet(ebpf_pid_judyL, (Word_t)pid, PJE0);
336 if (Pvalue)
337 pid_data = *Pvalue;
338 return pid_data;
339 }
340
341 ebpf_pid_data_t *ebpf_find_pid_data(pid_t pid)
342 {
343 spinlock_lock(&ebpf_pid_spinlock);
344 ebpf_pid_data_t *pid_data = ebpf_find_pid_data_unsafe(pid);
345 spinlock_unlock(&ebpf_pid_spinlock);
346 return pid_data;
347 }
348
349 ebpf_pid_data_t *ebpf_find_or_create_pid_data(pid_t pid)
350 {
351 spinlock_lock(&ebpf_pid_spinlock);
352 ebpf_pid_data_t *pid_data = ebpf_find_pid_data_unsafe(pid);
353 if (!pid_data) {
354 Pvoid_t *Pvalue = JudyLIns(&ebpf_pid_judyL, (Word_t)pid, PJE0);
355 internal_fatal(!Pvalue || Pvalue == PJERR, "EBPF: pid judy array");
356 if (likely(!*Pvalue))
357 *Pvalue = pid_data = callocz(1, sizeof(*pid_data));
358 else
359 pid_data = *Pvalue;
360 }
361 spinlock_unlock(&ebpf_pid_spinlock);
362
363 return pid_data;
364 }
365
366 ebpf_pid_data_t *ebpf_pids_link_list = NULL; // global list of all processes running
367
368 size_t ebpf_all_pids_count = 0; // the number of processes running read from /proc
369 size_t ebpf_hash_table_pids_count = 0; // the number of tasks in our hash tables
370
371 struct ebpf_target *apps_groups_default_target = NULL, // the default target
372 *apps_groups_root_target = NULL, // apps_groups.conf defined
373 *users_root_target = NULL, // users
374 *groups_root_target = NULL; // user groups
375
376 size_t apps_groups_targets_count = 0; // # of apps_groups.conf targets
377
378 static int ebpf_pid_map_fds[NETDATA_EBPF_PIDS_END_IDX];
379
380 void ebpf_reset_pid_map_fds(void)
381 {
382 memset(ebpf_pid_map_fds, -1, sizeof(ebpf_pid_map_fds));
383 }
384
385 void ebpf_set_pid_map_fd(int idx, int fd)
386 {
387 if (unlikely(idx < 0 || idx >= NETDATA_EBPF_PIDS_END_IDX))
388 return;
389
390 ebpf_pid_map_fds[idx] = fd;
391 }
392
393 int ebpf_get_pid_map_fd(int idx)
394 {
395 if (unlikely(idx < 0 || idx >= NETDATA_EBPF_PIDS_END_IDX))
396 return -1;
397
398 return ebpf_pid_map_fds[idx];
399 }
400
401 // ----------------------------------------------------------------------------
402 // internal counters
403
404 static size_t targets_assignment_counter = 0;
405
406 // ----------------------------------------------------------------------------
407 // debugging
408
409 // log each problem once per process
410 // log flood protection flags (log_thrown)
411 #define PID_LOG_IO 0x00000001
412 #define PID_LOG_STATUS 0x00000002
413 #define PID_LOG_CMDLINE 0x00000004
414 #define PID_LOG_FDS 0x00000008
415 #define PID_LOG_STAT 0x00000010
416
417 int debug_enabled = 0;
418
419 #ifdef NETDATA_INTERNAL_CHECKS
420
421 #define debug_log(fmt, args...) \
422 do { \
423 if (unlikely(debug_enabled)) \
424 debug_log_int(fmt, ##args); \
425 } while (0)
426
427 #else
428
429 static inline void debug_log_dummy(void)
430 {
431 }
432 #define debug_log(fmt, args...) debug_log_dummy()
433
434 #endif
435
436 /**
437 * Assign the PID to a target.
438 *
439 * @param p the pid_stat structure to assign for a target.
440 */
441 static inline void assign_target_to_pid(ebpf_pid_data_t *p)
442 {
443 targets_assignment_counter++;
444
445 uint32_t hash = simple_hash(p->comm);
446 size_t pclen = strlen(p->comm);
447
448 struct ebpf_target *w;
449 bool assigned = false;
450 for (w = apps_groups_root_target; w; w = w->next) {
451 if (ebpf_plugin_stop())
452 break;
453
454 // if(debug_enabled || (p->target && p->target->debug_enabled)) debug_log_int("\t\tcomparing '%s' with '%s'", w->compare, p->comm);
455
456 // find it - 4 cases:
457 // 1. the target is not a pattern
458 // 2. the target has the prefix
459 // 3. the target has the suffix
460 // 4. the target is something inside cmdline
461
462 if (unlikely(
463 ((!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm)) ||
464 (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen)) ||
465 (!w->starts_with && w->ends_with && pclen >= w->comparelen &&
466 !strcmp(w->compare, &p->comm[pclen - w->comparelen])) ||
467 (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && p->cmdline &&
468 strstr(p->cmdline, w->compare))))) {
469 if (w->target)
470 p->target = w->target;
471 else
472 p->target = w;
473
474 if (debug_enabled || (p->target && p->target->debug_enabled))
475 debug_log_int("%s linked to target %s", p->comm, p->target->name);
476
477 w->processes++;
478 assigned = true;
479
480 break;
481 }
482 }
483
484 if (!assigned) {
485 apps_groups_default_target->processes++;
486 p->target = apps_groups_default_target;
487 }
488 }
489
490 // ----------------------------------------------------------------------------
491 // update pids from proc
492
493 /**
494 * Read cmd line from /proc/PID/cmdline
495 *
496 * @param p the ebpf_pid_data structure.
497 *
498 * @return It returns 1 on success and 0 otherwise.
499 */
500 static inline int read_proc_pid_cmdline(ebpf_pid_data_t *p, char *cmdline)
501 {
502 char filename[FILENAME_MAX + 1];
503 snprintfz(filename, FILENAME_MAX, "%s/proc/%u/cmdline", netdata_configured_host_prefix, p->pid);
504
505 int ret = 0;
506
507 int fd = open(filename, procfile_open_flags, 0666);
508 if (unlikely(fd == -1))
509 goto cleanup;
510
511 ssize_t i, bytes = read(fd, cmdline, MAX_CMDLINE);
512 close(fd);
513
514 if (unlikely(bytes < 0))
515 goto cleanup;
516
517 cmdline[bytes] = '\0';
518 for (i = 0; i < bytes; i++) {
519 if (unlikely(!cmdline[i]))
520 cmdline[i] = ' ';
521 }
522
523 ret = 1;
524
525 cleanup:
526 if (p->cmdline)
527 p->cmdline[0] = '\0';
528
529 return ret;
530 }
531
532 /**
533 * Read information from /proc/PID/stat and /proc/PID/cmdline
534 * Assign target to pid
535 *
536 * @param p the pid stat structure to store the data.
537 */
538 static inline int read_proc_pid_stat(ebpf_pid_data_t *p)
539 {
540 procfile *ff;
541
542 char filename[FILENAME_MAX + 1];
543 int ret = 0;
544 snprintfz(filename, FILENAME_MAX, "%s/proc/%u/stat", netdata_configured_host_prefix, p->pid);
545
546 struct stat statbuf;
547 if (stat(filename, &statbuf)) {
548 // PID ended before we stat the file
549 p->has_proc_file = 0;
550 return 0;
551 }
552
553 ff = procfile_open(filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
554 if (unlikely(!ff))
555 goto cleanup_pid_stat;
556
557 procfile_set_open_close(ff, "(", ")");
558
559 ff = procfile_readall(ff);
560 if (unlikely(!ff))
561 goto cleanup_pid_stat;
562
563 char *comm = procfile_lineword(ff, 0, 1);
564 int32_t ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3));
565
566 if (p->ppid == (uint32_t)ppid && p->target)
567 goto without_cmdline_target;
568
569 p->ppid = ppid;
570
571 char cmdline[MAX_CMDLINE + 1];
572 if (read_proc_pid_cmdline(p, cmdline))
573 p->cmdline = cmdline; /* point at filled buffer so assign_target_to_pid can match *pattern* rules */
574 if (strcmp(p->comm, comm) != 0) {
575 if (unlikely(debug_enabled)) {
576 if (p->comm[0])
577 debug_log("\tpid %d (%s) changed name to '%s'", p->pid, p->comm, comm);
578 else
579 debug_log("\tJust added %d (%s)", p->pid, comm);
580 }
581
582 strncpyz(p->comm, comm, EBPF_MAX_COMPARE_NAME);
583 }
584
585 if (!p->target)
586 assign_target_to_pid(p);
587
588 p->cmdline = NULL; /* cmdline is stack-local; do not let it escape this frame */
589
590 if (unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
591 debug_log_int(
592 "READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s'",
593 netdata_configured_host_prefix,
594 p->pid,
595 p->comm,
596 (p->target) ? p->target->name : "UNSET");
597
598 without_cmdline_target:
599 p->has_proc_file = 1;
600 p->not_updated = 0;
601 ret = 1;
602 cleanup_pid_stat:
603 procfile_close(ff);
604
605 return ret;
606 }
607
608 /**
609 * Collect data for PID
610 *
611 * @param pid the current pid that we are working
612 *
613 * @return It returns 1 on success and 0 otherwise
614 */
615 static inline int ebpf_collect_data_for_pid(pid_t pid)
616 {
617 if (unlikely(pid < 0 || pid > pid_max)) {
618 netdata_log_error("Invalid pid %d read (expected %d to %d). Ignoring process.", pid, 0, pid_max);
619 return 0;
620 }
621
622 ebpf_pid_data_t *p = ebpf_get_pid_data((uint32_t)pid, 0, NULL, NETDATA_EBPF_PIDS_PROC_FILE);
623 read_proc_pid_stat(p);
624
625 // check its parent pid
626 if (unlikely(p->ppid > (uint32_t)pid_max)) {
627 netdata_log_error("Pid %d (command '%s') states invalid parent pid %u. Using 0.", pid, p->comm, p->ppid);
628 p->ppid = 0;
629 }
630
631 return 1;
632 }
633
634 /**
635 * Fill link list of parents with children PIDs
636 */
637 static inline void link_all_processes_to_their_parents(void)
638 {
639 ebpf_pid_data_t *p, *pp;
640
641 // link all children to their parents
642 // and update children count on parents
643 for (p = ebpf_pids_link_list; p; p = p->next) {
644 // for each process found
645
646 // Reset before the stop check: breaking early must not leave stale parent
647 // pointers that apply_apps_groups_targets_inheritance() would dereference.
648 p->parent = NULL;
649
650 if (ebpf_plugin_stop())
651 break;
652
653 if (unlikely(!p->ppid)) {
654 continue;
655 }
656
657 pp = ebpf_find_pid_data(p->ppid);
658 if (likely(pp && pp->pid)) {
659 p->parent = pp;
660 pp->children_count++;
661
662 if (unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
663 debug_log_int(
664 "child %d (%s) on target '%s' has parent %d (%s).",
665 p->pid,
666 p->comm,
667 (p->target) ? p->target->name : "UNSET",
668 pp->pid,
669 pp->comm);
670 } else {
671 p->parent = NULL;
672 debug_log("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
673 }
674 }
675 }
676
677 /**
678 * Aggregate PIDs to targets.
679 *
680 * This function performs target inheritance iteratively to ensure
681 * proper propagation even when children appear before parents in the list.
682 * Algorithm:
683 * 1. Propagate targets from parent to children without targets (iterative)
684 * 2. Merge leaf processes upward to their parents (iterative)
685 * 3. Assign default target to unmerged top-level processes
686 * 4. Propagate targets to merged children via their parents (iterative)
687 */
688 static void apply_apps_groups_targets_inheritance(void)
689 {
690 int sortlist = 1;
691 struct ebpf_pid_data *p = NULL;
692
693 ebpf_pid_data_t *pid_entry = ebpf_find_or_create_pid_data(INIT_PID);
694 pid_entry->target = apps_groups_default_target;
695
696 pid_entry = ebpf_find_or_create_pid_data(0);
697 pid_entry->target = apps_groups_default_target;
698
699 int found = 1;
700 while (found) {
701 found = 0;
702 for (p = ebpf_pids_link_list; p; p = p->next) {
703 if (unlikely(!p->target && p->parent && p->parent->target)) {
704 p->target = p->parent->target;
705 found++;
706 }
707 }
708 }
709
710 for (p = ebpf_pids_link_list; p; p = p->next) {
711 if (unlikely(!p->sortlist && !p->children_count))
712 p->sortlist = sortlist++;
713 }
714
715 found = 1;
716 while (found) {
717 found = 0;
718 for (p = ebpf_pids_link_list; p; p = p->next) {
719 if (ebpf_plugin_stop())
720 break;
721
722 if (unlikely(
723 !p->children_count && !p->merged && p->parent && p->parent->children_count &&
724 (p->target == p->parent->target || !p->parent->target) && p->ppid != INIT_PID)) {
725 p->parent->children_count--;
726 p->merged = 1;
727
728 if (unlikely(p->target && !p->parent->target))
729 p->parent->target = p->target;
730
731 found++;
732 }
733 }
734 }
735
736 for (p = ebpf_pids_link_list; p; p = p->next) {
737 if (unlikely(!p->merged && !p->target))
738 p->target = apps_groups_default_target;
739
740 if (unlikely(!p->sortlist))
741 p->sortlist = sortlist++;
742 }
743
744 pid_entry = ebpf_find_or_create_pid_data(1);
745 pid_entry->sortlist = sortlist++;
746
747 found = 1;
748 while (found) {
749 found = 0;
750 for (p = ebpf_pids_link_list; p; p = p->next) {
751 if (unlikely(!p->target && p->merged && p->parent && p->parent->target)) {
752 p->target = p->parent->target;
753 found++;
754 }
755 }
756 }
757 }
758
759 /**
760 * Update target timestamp.
761 *
762 * @param root the targets that will be updated.
763 */
764 static inline void post_aggregate_targets(struct ebpf_target *root)
765 {
766 struct ebpf_target *w;
767 for (w = root; w; w = w->next) {
768 if (ebpf_plugin_stop())
769 break;
770
771 if (w->collected_starttime) {
772 if (!w->starttime || w->collected_starttime < w->starttime) {
773 w->starttime = w->collected_starttime;
774 }
775 } else {
776 w->starttime = 0;
777 }
778 }
779 }
780
781 /**
782 * Remove PID from the link list.
783 *
784 * @param pid the PID that will be removed.
785 */
786 void ebpf_del_pid_entry(pid_t pid)
787 {
788 ebpf_pid_data_t *p = ebpf_find_pid_data(pid);
789
790 debug_log("process %d %s exited, deleting it.", pid, p->comm);
791
792 if (ebpf_pids_link_list == p)
793 ebpf_pids_link_list = p->next;
794
795 if (p->next)
796 p->next->prev = p->prev;
797 if (p->prev)
798 p->prev->next = p->next;
799
800 if ((p->thread_collecting & NETDATA_EBPF_PIDS_PROC_FILE) || p->has_proc_file)
801 ebpf_all_pids_count--;
802
803 rw_spinlock_write_lock(&ebpf_judy_pid.index.rw_spinlock);
804 netdata_ebpf_judy_pid_stats_t *pid_ptr = ebpf_get_pid_from_judy_unsafe(&ebpf_judy_pid.index.JudyLArray, p->pid);
805 if (pid_ptr) {
806 if (pid_ptr->socket_stats.JudyLArray) {
807 Word_t local_socket = 0;
808 Pvoid_t *socket_value;
809 bool first_socket = true;
810 while (
811 (socket_value = JudyLFirstThenNext(pid_ptr->socket_stats.JudyLArray, &local_socket, &first_socket))) {
812 netdata_socket_plus_t *socket_clean = *socket_value;
813 aral_freez(aral_socket_table, socket_clean);
814 }
815 JudyLFreeArray(&pid_ptr->socket_stats.JudyLArray, PJE0);
816 }
817 aral_freez(ebpf_judy_pid.pid_table, pid_ptr);
818 JudyLDel(&ebpf_judy_pid.index.JudyLArray, p->pid, PJE0);
819 }
820 rw_spinlock_write_unlock(&ebpf_judy_pid.index.rw_spinlock);
821
822 freez(p);
823 ebpf_pid_del(pid);
824 }
825
826 /**
827 * Remove PIDs when they are not running more.
828 */
829 static void ebpf_cleanup_exited_pids()
830 {
831 ebpf_pid_data_t *p = NULL;
832 for (p = ebpf_pids_link_list; p;) {
833 ebpf_pid_data_t *next = p->next;
834 if (!p->has_proc_file) {
835 ebpf_reset_specific_pid_data(p);
836 }
837 p = next;
838 }
839 }
840
841 /**
842 * Read proc filesystem for the first time.
843 *
844 * @return It returns 0 on success and -1 otherwise.
845 */
846 static int ebpf_read_proc_filesystem()
847 {
848 char dirname[FILENAME_MAX + 1];
849
850 snprintfz(dirname, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
851 DIR *dir = opendir(dirname);
852 if (!dir)
853 return -1;
854
855 struct dirent *de = NULL;
856
857 while ((de = readdir(dir))) {
858 if (ebpf_plugin_stop())
859 break;
860
861 char *endptr = de->d_name;
862
863 if (unlikely(de->d_type != DT_DIR || de->d_name[0] < '0' || de->d_name[0] > '9'))
864 continue;
865
866 pid_t pid = (pid_t)strtoul(de->d_name, &endptr, 10);
867
868 // make sure we read a valid number
869 if (unlikely(endptr == de->d_name || *endptr != '\0'))
870 continue;
871
872 ebpf_collect_data_for_pid(pid);
873 }
874 closedir(dir);
875
876 return 0;
877 }
878
879 /**
880 * Aggregated PID on target
881 *
882 * @param w the target output
883 * @param p the pid with information to update
884 * @param o never used
885 */
886 static inline void aggregate_pid_on_target(struct ebpf_target *w, ebpf_pid_data_t *p, struct ebpf_target *o)
887 {
888 UNUSED(o);
889
890 if (unlikely(!p->has_proc_file)) {
891 // the process is not running
892 return;
893 }
894
895 if (unlikely(!w)) {
896 netdata_log_error("pid %u %s was left without a target!", p->pid, p->comm);
897 return;
898 }
899
900 w->processes++;
901 struct ebpf_pid_on_target *pid_on_target = mallocz(sizeof(struct ebpf_pid_on_target));
902 pid_on_target->pid = p->pid;
903 pid_on_target->next = w->root_pid;
904 w->root_pid = pid_on_target;
905 }
906
907 /**
908 *
909 */
910 void ebpf_parse_proc_files()
911 {
912 ebpf_pid_data_t *pids;
913 for (pids = ebpf_pids_link_list; pids;) {
914 if (ebpf_plugin_stop())
915 break;
916
917 if (kill(pids->pid, 0) == -1 && errno == ESRCH) {
918 ebpf_pid_data_t *next = pids->next;
919 ebpf_reset_specific_pid_data(pids);
920 pids = next;
921 continue;
922 }
923
924 pids->not_updated = EBPF_CLEANUP_FACTOR;
925 pids->merged = 0;
926 pids->children_count = 0;
927 pids = pids->next;
928 }
929
930 if (ebpf_read_proc_filesystem())
931 return;
932
933 link_all_processes_to_their_parents();
934
935 apply_apps_groups_targets_inheritance();
936
937 apps_groups_targets_count = zero_all_targets(apps_groups_root_target);
938
939 for (pids = ebpf_pids_link_list; pids; pids = pids->next) {
940 if (ebpf_plugin_stop())
941 break;
942
943 aggregate_pid_on_target(pids->target, pids, NULL);
944 }
945
946 ebpf_cleanup_exited_pids();
947 }