@cryptotaxi247 / netdata-1 / commits / 9c52a1e33

Integration between eBPF and Apps (#9178)

* ebpf_apps: Create files to do integration * ebpf_apps: move functions from apps to ebpf part 1 * ebpf_apps: move functions from apps to ebpf part 2 * ebpf_apps: Functions to read apps_groups.conf * ebpf_apps: reset structures * ebpf_apps: Verify user running plugin * ebpf_apps: Adjust structures used for eBPF.plugin * rebase * ebpf_apps: Move ebpf submenu near apps submenu * ebpf_apps: Brings structures from kernel-collector repository4 * ebpf_apps: Set definitions for the new chart names * ebpf_apps: bring new functions to read data from kernel ring * ebpf_apps: New variable to store PID information on process thread * ebpf_apps: It brings PID variable to socket * ebpf_apps: Chart creation * ebpf_apps: New probe * ebpf_apps: Fix order * ebpf_apps: Clean targets * ebpf_apps: Necessary changes to avoid parse proc * rebase * ebpf_apps: Fix key inside collect_data_for_all_processes to remove infinite loop * ebpf_apps: Bring variables to read data from hash table * ebpf_apps: Fix coverity warnings * ebpf_apps: Fix the missing allocation for all_pids and another small adjusts * ebpf_apps: Postpone thread creation to fill targets * ebpf_apps: Add comments while functions were tested * ebpf_apps: Fix LGTM warnings * ebpf_apps: Clean allocated structure with raw data * ebpf_apps: Bring missing functions to calculate apps charts for process * ebpf_apps: Rename a function and update apps_groups.conf * ebpf_apps: Copy logic for chart creation to sockets * ebpf_apps: Remove unecessary variable * Update chart creating * Remove Users and User Groups targets * ebpf_apps: missing argument * ebpf_apps: Fix LGTM https://github.com/netdata/netdata/pull/9178#issuecomment-638253538 error * Synchronize getting and sending data * eb^C_apps: Remove error messages adding read of proc file * ebpf_apps: Missing function * ebpf_apps: Move socket chart creation and change necessary functions to read cmdline * ebpf_apps: Bring missing loop to reset variable * ebpf_apps: Remove unecessary sleep * ebpf_apps: Fix return and allocation to allow the apps integration * ebpf_apps: create structure to set apps dimension * ebpf_apps: create structure to set apps dimension for socket * ebpf_apps: Fix wrong family for process * ebpf_apps: Enable apps by default * Functions to enable charts for apps * Fix small bugs * ebpf_apps: Fix missing root_pid * Fix negative output on charts * ebpf_apps: Remove debug messages from process * ebpf_apps: Fix socket lock scheme * ebpf_apps: Small fixes * ebpf_apps; Remove unecessary function call and header * ebpf_apps: Enable apps by default * remove wrong files * ebpf_apps: update cheksum * Update dashboard.info bringing missing parameters for charts that do integration with ebpf * Fix a bug * ebpf_apps: Fix open of proc files that are not more present * ebpf_apps:Fix synchronization problems * ebpf_apps: Remove dupplication that was killing socket charts * ebpf_apps: Fix codacy warnings * ebpf_apps: Fix process read from memory * ebpf_apps: Remove unecessary variable from code * ebpf_apps: Fix wrong variable usage * ebpf_apps: complete separation between global and apps charts * Fix missing check and a wrong check * ebpf_apps: Comment unnecessary code copied from apps_plugin.c * ebpf_apps: Restart ebpf when number of monitored PIDs is smaller than 0.7 * ebpf_apps: Fix coverity scan error * ebpf_release: Update release version * ebpf_apps: Remove unecessary test * ebpf_apps: Remove unecessary error message * ebpf_apps: Clean probes during the startup * ebpf_apps: Bring additional variables to monitor chart plot * ebpf_apps: Missing chart when entry mode is enabled * ebpf_apps: Do synchronization between apps.plugin and ebpf.plugin dimension * ebpf_apps: socket synchronization * ebpf_apps: fix problems with task_close * ebpf_apps: Brings title to charts * ebpf_apps: restore to fix errors * ebpf_apps: Remove hard code update every * ebpf_apps: rename variables * ebpf_apps: Bring title for the charts * ebpf_apps: Remove unecessary line * ebpf_apps: bring new release to PR * ebpf_apps: Fix constant value when a function is not used for a process * ebpf_apps: This fixes a possible memory leak * ebpf_apps: move delete function to bring back synchronization between charts * Change chart type * Fix for exited pids Co-authored-by: Vladimir Kobal <vlad@prokk.net>

thiagoftsm committed Jun 12, 2020 at 12:35 UTC 9c52a1e33b83632572a4758d9d13383402a275fb
18 files changed +3038 -299
CMakeLists.txt
+2
@@ -433,6 +433,8 @@ set(EBPF_PROCESS_PLUGIN_FILES
433 collectors/ebpf.plugin/ebpf_process.h
434 collectors/ebpf.plugin/ebpf_socket.c
435 collectors/ebpf.plugin/ebpf_socket.h
436 + collectors/ebpf.plugin/ebpf_apps.c
437 + collectors/ebpf.plugin/ebpf_apps.h
438 )
439
440 set(PROC_PLUGIN_FILES
Makefile.am
+2
@@ -273,6 +273,8 @@ EBPF_PLUGIN_FILES = \
273 collectors/ebpf.plugin/ebpf_socket.c \
274 collectors/ebpf.plugin/ebpf_socket.h \
275 collectors/ebpf.plugin/ebpf.h \
276 + collectors/ebpf.plugin/ebpf_apps.c \
277 + collectors/ebpf.plugin/ebpf_apps.h \
278 $(LIBNETDATA_FILES) \
279 $(NULL)
280
collectors/apps.plugin/apps_groups.conf
+1
@@ -86,6 +86,7 @@ fping: fping
86 ioping: ioping
87 go.d.plugin: *go.d.plugin*
88 slabinfo.plugin: slabinfo.plugin
89 +ebpf.plugin: *ebpf.plugin*
90
91 # agent-service-discovery
92 agent_sd: agent_sd
collectors/apps.plugin/apps_plugin.c
+2 -1
@@ -2451,7 +2451,8 @@ static inline void link_all_processes_to_their_parents(void) {
2451 p->parent = NULL;
2452
2453 if(unlikely(!p->ppid)) {
2454 - p->parent = NULL;
2454 + //unnecessary code from apps_plugin.c
2455 + //p->parent = NULL;
2456 continue;
2457 }
2458
collectors/ebpf.plugin/ebpf.c
+217 -64
@@ -5,6 +5,13 @@
5
6 #include "ebpf.h"
7
8 +/*****************************************************************
9 + *
10 + * FUNCTIONS USED BY NETDATA
11 + *
12 + *****************************************************************/
13 +
14 +
15 // callback required by eval()
16 int health_variable_lookup(const char *variable, uint32_t hash, struct rrdcalc *rc, calculated_number *result) {
17 (void)variable;
@@ -35,12 +42,18 @@ void netdata_cleanup_and_exit(int ret) {
42 }
43
44 // ----------------------------------------------------------------------
45 +/*****************************************************************
46 + *
47 + * GLOBAL VARIABLES
48 + *
49 + *****************************************************************/
50 +
51 char *ebpf_plugin_dir = PLUGINS_DIR;
39 -static char *ebpf_user_config_dir = CONFIG_DIR;
40 -static char *ebpf_stock_config_dir = LIBCONFIG_DIR;
52 +char *ebpf_user_config_dir = CONFIG_DIR;
53 +char *ebpf_stock_config_dir = LIBCONFIG_DIR;
54 static char *ebpf_configured_log_dir = LOG_DIR;
55
43 -static int update_every = 1;
56 +int update_every = 1;
57 static int thread_finished = 0;
58 int close_ebpf_plugin = 0;
59 struct config collector_config = { .first_section = NULL, .last_section = NULL, .mutex = NETDATA_MUTEX_INITIALIZER,
@@ -51,9 +64,10 @@ int running_on_kernel = 0;
64 char kernel_string[64];
65 int ebpf_nprocs;
66 static int isrh;
54 -netdata_idx_t *hash_values;
67
68 pthread_mutex_t lock;
69 +pthread_mutex_t collect_data_mutex;
70 +pthread_cond_t collect_data_cond_var;
71
72 netdata_ebpf_events_t process_probes[] = {
73 { .type = 'r', .name = "vfs_write" },
@@ -66,6 +80,7 @@ netdata_ebpf_events_t process_probes[] = {
80 { .type = 'p', .name = "release_task" },
81 { .type = 'r', .name = "_do_fork" },
82 { .type = 'r', .name = "__close_fd" },
83 + { .type = 'p', .name = "try_to_wake_up" },
84 { .type = 'r', .name = "__x64_sys_clone" },
85 { .type = 0, .name = NULL }
86 };
@@ -90,6 +105,28 @@ ebpf_module_t ebpf_modules[] = {
105 .global_charts = 0, .apps_charts = 1, .mode = MODE_ENTRY, .probes = NULL },
106 };
107
108 +//Link with apps.plugin
109 +pid_t *pid_index;
110 +ebpf_process_stat_t *global_process_stat = NULL;
111 +
112 +/*****************************************************************
113 + *
114 + * FUNCTIONS USED TO CLEAN MEMORY AND OPERATE SYSTEM FILES
115 + *
116 + *****************************************************************/
117 +
118 +/**
119 + * Clean Loaded Events
120 + *
121 + * This function cleans the events previous loaded on Linux.
122 + */
123 +void clean_loaded_events()
124 +{
125 + int event_pid;
126 + for (event_pid = 0; ebpf_modules[event_pid].probes; event_pid++)
127 + clean_kprobe_events(NULL, (int)ebpf_modules[event_pid].thread_id, ebpf_modules[event_pid].probes);
128 +}
129 +
130 /**
131 * Close the collector gracefully
132 *
@@ -97,7 +134,6 @@ ebpf_module_t ebpf_modules[] = {
134 */
135 static void ebpf_exit(int sig)
136 {
100 - int event_pid;
137 close_ebpf_plugin = 1;
138
139 //When both threads were not finished case I try to go in front this address, the collector will crash
@@ -105,7 +141,11 @@ static void ebpf_exit(int sig)
141 return;
142 }
143
108 - event_pid = getpid();
144 + clean_apps_groups_target(apps_groups_root_target);
145 +
146 + freez(pid_index);
147 + freez(global_process_stat);
148 +
149 int ret = fork();
150 if (ret < 0) //error
151 error("Cannot fork(), so I won't be able to clean %skprobe_events", NETDATA_DEBUGFS);
@@ -127,10 +167,8 @@ static void ebpf_exit(int sig)
167 int sid = setsid();
168 if(sid >= 0) {
169 sleep(1);
130 - debug(D_EXIT, "Wait for father %d die", event_pid);
131 -
132 - for (event_pid = 0; ebpf_modules[event_pid].probes; event_pid++)
133 - clean_kprobe_events(NULL, (int)ebpf_modules[event_pid].thread_id, ebpf_modules[event_pid].probes);
170 + debug(D_EXIT, "Wait for father %d die", getpid());
171 + clean_loaded_events();
172 } else {
173 error("Cannot become session id leader, so I won't try to clean kprobe_events.\n");
174 }
@@ -147,6 +185,25 @@ static void ebpf_exit(int sig)
185 *
186 *****************************************************************/
187
188 +
189 +/**
190 + * Get a value from a structure.
191 + *
192 + * @param basis it is the first address of the structure
193 + * @param offset it is the offset of the data you want to access.
194 + * @return
195 + */
196 +collected_number get_value_from_structure(char *basis, size_t offset)
197 +{
198 + collected_number *value = (collected_number *)(basis + offset);
199 +
200 + collected_number ret = (collected_number)llabs(*value);
201 + //this reset is necessary to avoid keep a constant value while processing is not executing a task
202 + *value = 0;
203 +
204 + return ret;
205 +}
206 +
207 /**
208 * Write begin command on standard output
209 *
@@ -155,11 +212,17 @@ static void ebpf_exit(int sig)
212 */
213 void write_begin_chart(char *family, char *name)
214 {
158 - int ret = printf( "BEGIN %s.%s\n"
159 - , family
160 - , name);
215 + printf( "BEGIN %s.%s\n"
216 + , family
217 + , name);
218 +}
219
162 - (void)ret;
220 +/**
221 + * Write END command on stdout.
222 + */
223 +inline void write_end_chart()
224 +{
225 + printf("END\n");
226 }
227
228 /**
@@ -181,11 +244,13 @@ void write_chart_dimension(char *dim, long long value)
244 * @param family the chart family
245 * @param move the pointer with the values that will be published
246 * @param end the number of values that will be written on standard output
247 + *
248 + * @return It returns a variable tha maps the charts that did not have zero values.
249 */
185 -void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, int end) {
250 +void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, uint32_t end) {
251 write_begin_chart(family, name);
252
188 - int i = 0;
253 + uint32_t i = 0;
254 while (move && i < end) {
255 write_chart_dimension(move->name, move->ncall);
256
@@ -193,7 +258,7 @@ void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move
258 i++;
259 }
260
196 - printf("END\n");
261 + write_end_chart();
262 }
263
264 /**
@@ -215,7 +280,7 @@ void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move,
280 i++;
281 }
282
218 - printf("END\n");
283 + write_end_chart();
284 }
285
286
@@ -224,37 +289,40 @@ void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move,
289 *
290 * @param family the chart family
291 * @param move the pointer with the values that will be published
292 + *
293 + * @return It returns a variable tha maps the charts that did not have zero values.
294 */
295 void write_io_chart(char *chart, char *family, char *dwrite, char *dread, netdata_publish_vfs_common_t *pvc) {
296 write_begin_chart(family, chart);
297
298 write_chart_dimension(dwrite, (long long) pvc->write);
232 - write_chart_dimension(dread, (long long) pvc->read);
299 + write_chart_dimension(dread, (long long)pvc->read);
300
234 - printf("END\n");
301 + write_end_chart();
302 }
303
304 /**
305 * Write chart cmd on standard output
306 *
240 - * @param type the chart type
241 - * @param id the chart id
242 - * @param axis the axis label
243 - * @param web the group name used to attach the chart on dashaboard
244 - * @param order the chart order
307 + * @param type the chart type
308 + * @param id the chart id
309 + * @param title the chart title
310 + * @param units the units label
311 + * @param family the group name used to attach the chart on dashaboard
312 + * @param charttype the chart type
313 + * @param order the chart order
314 */
246 -void ebpf_write_chart_cmd(char *type
247 - , char *id
248 - , char *axis
249 - , char *web
250 - , int order)
315 +void ebpf_write_chart_cmd(char *type, char *id, char *title, char *units, char *family, char *charttype, int order)
316 {
252 - printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
253 - , type
254 - , id
255 - , axis
256 - , web
257 - , order);
317 + printf("CHART %s.%s '' '%s' '%s' '%s' '' %s %d %d\n",
318 + type,
319 + id,
320 + title,
321 + units,
322 + family,
323 + charttype,
324 + order,
325 + update_every);
326 }
327
328 /**
@@ -290,29 +358,51 @@ void ebpf_create_global_dimension(void *ptr, int end)
358 /**
359 * Call write_chart_cmd to create the charts
360 *
293 - * @param family the chart family
294 - * @param name the chart name
295 - * @param axis the axis label
296 - * @param web the group name used to attach the chart on dashaboard
361 + * @param type the chart type
362 + * @param id the chart id
363 + * @param units the axis label
364 + * @param family the group name used to attach the chart on dashaboard
365 * @param order the order number of the specified chart
366 * @param ncd a pointer to a function called to create dimensions
367 * @param move a pointer for a structure that has the dimensions
368 * @param end number of dimensions for the chart created
369 */
302 -void ebpf_create_chart(char *family
303 - , char *name
304 - , char *axis
305 - , char *web
370 +void ebpf_create_chart(char *type
371 + , char *id
372 + , char *title
373 + , char *units
374 + , char *family
375 , int order
376 , void (*ncd)(void *, int)
377 , void *move
378 , int end)
379 {
311 - ebpf_write_chart_cmd(family, name, axis, web, order);
380 + ebpf_write_chart_cmd(type, id, title, units, family, "line", order);
381
382 ncd(move, end);
383 }
384
385 +/**
386 + * Create charts on apps submenu
387 + *
388 + * @param id the chart id
389 + * @param title the value displayed on vertical axis.
390 + * @param units the value displayed on vertical axis.
391 + * @param family Submenu that the chart will be attached on dashboard.
392 + * @param order the chart order
393 + * @param root structure used to create the dimensions.
394 + */
395 +void ebpf_create_charts_on_apps(char *id, char *title, char *units, char *family, int order, struct target *root)
396 +{
397 + struct target *w;
398 + ebpf_write_chart_cmd(NETDATA_APPS_FAMILY, id, title, units, family, "stacked", order);
399 +
400 + for (w = root; w ; w = w->next) {
401 + if(unlikely(w->exposed))
402 + fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
403 + }
404 +}
405 +
406 /*****************************************************************
407 *
408 * FUNCTIONS TO DEFINE OPTIONS
@@ -465,6 +555,37 @@ void ebpf_print_help() {
555 *
556 *****************************************************************/
557
558 +/**
559 + * Start Ptherad Variable
560 + *
561 + * This function starts all pthread variables.
562 + *
563 + * @return It returns 0 on success and -1.
564 + */
565 +int ebpf_start_pthread_variables()
566 +{
567 + pthread_mutex_init(&lock, NULL);
568 + pthread_mutex_init(&collect_data_mutex, NULL);
569 +
570 + if (pthread_cond_init(&collect_data_cond_var, NULL)) {
571 + thread_finished++;
572 + error("Cannot start conditional variable to control Apps charts.");
573 + return -1;
574 + }
575 +
576 + return 0;
577 +}
578 +
579 +/**
580 + * Allocate the vectors used for all threads.
581 + */
582 +static void ebpf_allocate_common_vectors()
583 +{
584 + all_pids = callocz((size_t) pid_max, sizeof(struct pid_stat *));
585 + pid_index = callocz((size_t)pid_max, sizeof(pid_t));
586 + global_process_stat = callocz((size_t)ebpf_nprocs, sizeof(ebpf_process_stat_t));
587 +}
588 +
589 /**
590 * Fill the ebpf_functions structure with default values
591 *
@@ -514,8 +635,10 @@ static inline int parse_disable_apps(char *ptr)
635
636 /**
637 * Read collector values
638 + *
639 + * @param disable_apps variable to store information related to apps.
640 */
518 -static void read_collector_values() {
641 +static void read_collector_values(int *disable_apps) {
642 // Read global section
643 char *value;
644 if (appconfig_exists(&collector_config, EBPF_GLOBAL_SECTION, "load")) //Backward compatibility
@@ -526,37 +649,38 @@ static void read_collector_values() {
649 how_to_load(value);
650
651 value = appconfig_get(&collector_config, EBPF_GLOBAL_SECTION, "disable apps", "no");
529 - int disable_apps = parse_disable_apps(value);
652 + *disable_apps = parse_disable_apps(value);
653
654 // Read ebpf programs section
655 uint32_t enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION,
656 ebpf_modules[0].config_name, 1);
657 int started = 0;
658 if (enabled) {
536 - ebpf_enable_chart(0, disable_apps);
659 + ebpf_enable_chart(EBPF_MODULE_PROCESS_IDX, *disable_apps);
660 started++;
661 }
662
663 enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION,
664 ebpf_modules[1].config_name, 1);
665 if (enabled) {
543 - ebpf_enable_chart(1, disable_apps);
666 + ebpf_enable_chart(EBPF_MODULE_SOCKET_IDX, *disable_apps);
667 started++;
668 }
669
670 if (!started)
548 - ebpf_enable_all_charts(disable_apps);
671 + ebpf_enable_all_charts(*disable_apps);
672
673 }
674
675 /**
676 * Load collector config
677 *
555 - * @param path the path where the file ebpf.conf is stored.
678 + * @param path the path where the file ebpf.conf is stored.
679 + * @param disable_apps variable to store the information about apps plugin status.
680 *
681 * @return 0 on success and -1 otherwise.
682 */
559 -static int load_collector_config(char *path) {
683 +static int load_collector_config(char *path, int *disable_apps) {
684 char lpath[4096];
685
686 snprintf(lpath, 4095, "%s/%s", path, "ebpf.conf" );
@@ -564,7 +688,7 @@ static int load_collector_config(char *path) {
688 if (!appconfig_load(&collector_config, lpath, 0, NULL))
689 return -1;
690
567 - read_collector_values();
691 + read_collector_values(disable_apps);
692
693 return 0;
694 }
@@ -596,6 +720,7 @@ void set_global_variables() {
720 }
721
722 isrh = get_redhat_release();
723 + pid_max = get_system_pid_max();
724 }
725
726 /**
@@ -609,7 +734,6 @@ static void parse_args(int argc, char **argv)
734 int enabled = 0;
735 int disable_apps = 0;
736 int freq = 0;
612 - int c;
737 int option_index = 0;
738 static struct option long_options[] = {
739 {"help", no_argument, 0, 'h' },
@@ -630,7 +754,7 @@ static void parse_args(int argc, char **argv)
754 }
755
756 while (1) {
633 - c = getopt_long(argc, argv, "hvganpr",long_options, &option_index);
757 + int c = getopt_long(argc, argv, "hvganpr",long_options, &option_index);
758 if (c == -1)
759 break;
760
@@ -660,7 +784,7 @@ static void parse_args(int argc, char **argv)
784 }
785 case 'n': {
786 enabled = 1;
663 - ebpf_enable_chart(1, disable_apps);
787 + ebpf_enable_chart(EBPF_MODULE_SOCKET_IDX, disable_apps);
788 #ifdef NETDATA_INTERNAL_CHECKS
789 info("EBPF enabling \"NET\" charts, because it was started with the option \"--net\" or \"-n\".");
790 #endif
@@ -668,7 +792,7 @@ static void parse_args(int argc, char **argv)
792 }
793 case 'p': {
794 enabled = 1;
671 - ebpf_enable_chart(0, disable_apps);
795 + ebpf_enable_chart(EBPF_MODULE_PROCESS_IDX, disable_apps);
796 #ifdef NETDATA_INTERNAL_CHECKS
797 info("EBPF enabling \"PROCESS\" charts, because it was started with the option \"--process\" or \"-p\".");
798 #endif
@@ -691,10 +815,10 @@ static void parse_args(int argc, char **argv)
815 update_every = freq;
816 }
817
694 - if (load_collector_config(ebpf_user_config_dir)) {
818 + if (load_collector_config(ebpf_user_config_dir, &disable_apps)) {
819 error("Does not have a configuration file inside `%s/ebpf.conf. It will try to load stock file.",
820 ebpf_user_config_dir);
697 - if (load_collector_config(ebpf_stock_config_dir)) {
821 + if (load_collector_config(ebpf_stock_config_dir, &disable_apps)) {
822 error("Does not have a stock file. It is starting with default options.");
823 } else {
824 enabled = 1;
@@ -709,6 +833,24 @@ static void parse_args(int argc, char **argv)
833 info("EBPF running with all charts, because neither \"-n\" or \"-p\" was given.");
834 #endif
835 }
836 +
837 + if (disable_apps)
838 + return;
839 +
840 + //Load apps_groups.conf
841 + if (ebpf_read_apps_groups_conf(&apps_groups_default_target, &apps_groups_root_target,
842 + ebpf_user_config_dir, "groups") ) {
843 + info("Cannot read process groups configuration file '%s/apps_groups.conf'. Will try '%s/apps_groups.conf'",
844 + ebpf_user_config_dir, ebpf_stock_config_dir);
845 + if (ebpf_read_apps_groups_conf(&apps_groups_default_target, &apps_groups_root_target,
846 + ebpf_stock_config_dir, "groups") ) {
847 + error("Cannot read process groups '%s/apps_groups.conf'. There are no internal defaults. Failing.",
848 + ebpf_stock_config_dir);
849 + thread_finished++;
850 + ebpf_exit(1);
851 + }
852 + } else
853 + info("Loaded config file '%s/apps_groups.conf'", ebpf_user_config_dir);
854 }
855
856
@@ -734,7 +876,14 @@ int main(int argc, char **argv)
876 running_on_kernel = get_kernel_version(kernel_string, 63);
877 if(!has_condition_to_run(running_on_kernel)) {
878 error("The current collector cannot run on this kernel.");
737 - return 1;
879 + return 2;
880 + }
881 +
882 + if(!am_i_running_as_root()) {
883 + error("ebpf.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities..",
884 + (unsigned int)getuid(), (unsigned int)geteuid()
885 + );
886 + return 3;
887 }
888
889 //set name
@@ -750,24 +899,28 @@ int main(int argc, char **argv)
899 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
900 if (setrlimit(RLIMIT_MEMLOCK, &r)) {
901 error("Setrlimit(RLIMIT_MEMLOCK)");
753 - return 2;
902 + return 4;
903 }
904
905 signal(SIGINT, ebpf_exit);
906 signal(SIGTERM, ebpf_exit);
907
759 - if (pthread_mutex_init(&lock, NULL)) {
908 + if (ebpf_start_pthread_variables()) {
909 thread_finished++;
761 - error("Cannot start the mutex.");
762 - ebpf_exit(3);
910 + error("Cannot start mutex to control overall charts.");
911 + ebpf_exit(5);
912 }
913
914 + ebpf_allocate_common_vectors();
915 +
916 struct netdata_static_thread ebpf_threads[] = {
917 {"EBPF PROCESS", NULL, NULL, 1, NULL, NULL, ebpf_modules[0].start_routine},
918 {"EBPF SOCKET", NULL, NULL, 1, NULL, NULL, ebpf_modules[1].start_routine},
919 {NULL, NULL, NULL, 0, NULL, NULL, NULL}
920 };
921
922 + clean_loaded_events();
923 +
924 int i;
925 for (i = 0; ebpf_threads[i].name != NULL ; i++) {
926 struct netdata_static_thread *st = &ebpf_threads[i];
collectors/ebpf.plugin/ebpf.conf
+1 -1
@@ -1,6 +1,6 @@
1 [global]
2 ebpf load mode = entry
3 - disable apps = yes
3 + disable apps = no
4
5 [ebpf programs]
6 process = yes
collectors/ebpf.plugin/ebpf.h
+63 -19
@@ -21,13 +21,16 @@
21 # include <dirent.h>
22
23 //From libnetdata.h
24 -# include "../../libnetdata/threads/threads.h"
25 -# include "../../libnetdata/locks/locks.h"
26 -# include "../../libnetdata/avl/avl.h"
27 -# include "../../libnetdata/clocks/clocks.h"
28 -# include "../../libnetdata/config/appconfig.h"
29 -# include "../../libnetdata/ebpf/ebpf.h"
30 -# include "../../daemon/main.h"
24 +# include "libnetdata/threads/threads.h"
25 +# include "libnetdata/locks/locks.h"
26 +# include "libnetdata/avl/avl.h"
27 +# include "libnetdata/clocks/clocks.h"
28 +# include "libnetdata/config/appconfig.h"
29 +# include "libnetdata/ebpf/ebpf.h"
30 +# include "libnetdata/procfile/procfile.h"
31 +# include "daemon/main.h"
32 +
33 +# include "ebpf_apps.h"
34
35 typedef enum {
36 MODE_RETURN = 0, //This attaches kprobe when the function returns
@@ -85,6 +88,20 @@ typedef struct ebpf_module {
88 uint32_t thread_id;
89 } ebpf_module_t;
90
91 +extern ebpf_module_t ebpf_modules[];
92 +#define EBPF_MODULE_PROCESS_IDX 0
93 +#define EBPF_MODULE_SOCKET_IDX 1
94 +
95 +// Copied from musl header
96 +//
97 +#ifndef offsetof
98 +# if __GNUC__ > 3
99 +# define offsetof(type, member) __builtin_offsetof(type, member)
100 +# else
101 +# define offsetof(type, member) ((size_t)( (char *)&(((type *)0)->member) - (char *)0 ))
102 +# endif
103 +#endif
104 +
105 //Chart defintions
106 # define NETDATA_EBPF_FAMILY "ebpf"
107
@@ -99,7 +116,7 @@ typedef struct ebpf_module {
116 # define NETDATA_KERNEL_V5_3 328448
117 # define NETDATA_KERNEL_V4_15 265984
118
102 -
119 +# define EBPF_SYS_CLONE_IDX 11
120 # define EBPF_MAX_MAPS 32
121
122
@@ -117,6 +134,9 @@ extern char kernel_string[64];
134 extern netdata_ebpf_events_t process_probes[];
135 extern netdata_ebpf_events_t socket_probes[];
136
137 +extern pthread_mutex_t collect_data_mutex;
138 +extern pthread_cond_t collect_data_cond_var;
139 +
140 //Common functions
141 extern void ebpf_global_labels(netdata_syscall_stat_t *is,
142 netdata_publish_syscall_t *pio,
@@ -124,20 +144,18 @@ extern void ebpf_global_labels(netdata_syscall_stat_t *is,
144 char **name,
145 int end);
146
127 -extern void ebpf_write_chart_cmd(char *type
128 - , char *id
129 - , char *axis
130 - , char *web
131 - , int order);
147 +extern void ebpf_write_chart_cmd(char *type, char *id, char *title, char *units, char *family,
148 + char *charttype, int order);
149
150 extern void ebpf_write_global_dimension(char *n, char *d);
151
152 extern void ebpf_create_global_dimension(void *ptr, int end);
153
137 -extern void ebpf_create_chart(char *family
138 - , char *name
139 - , char *axis
140 - , char *web
154 +extern void ebpf_create_chart(char *type
155 + , char *id
156 + , char *title
157 + , char *units
158 + , char *family
159 , int order
160 , void (*ncd)(void *, int)
161 , void *move
@@ -147,15 +165,41 @@ extern void write_begin_chart(char *family, char *name);
165
166 extern void write_chart_dimension(char *dim, long long value);
167
150 -extern void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, int end);
168 +extern void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, uint32_t end);
169
170 extern void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end);
171
154 -void write_io_chart(char *chart, char *family, char *dwrite, char *dread, netdata_publish_vfs_common_t *pvc);
172 +extern void write_io_chart(char *chart, char *family, char *dwrite, char *dread, netdata_publish_vfs_common_t *pvc);
173
174 extern void fill_ebpf_functions(ebpf_functions_t *ef);
175
176 +extern void ebpf_create_charts_on_apps(char *name, char *title, char *units, char *family,
177 + int order, struct target *root);
178 +
179 +extern void write_end_chart();
180 +
181 # define EBPF_GLOBAL_SECTION "global"
182 # define EBPF_PROGRAMS_SECTION "ebpf programs"
183
184 +# define EBPF_COMMON_DIMENSION_CALL "Calls"
185 +# define EBPF_COMMON_DIMENSION_BYTESS "bytes/s"
186 +# define EBPF_COMMON_DIMENSION_DIFFERENCE "Difference"
187 +
188 +//Common variables
189 +extern char *ebpf_user_config_dir;
190 +extern char *ebpf_stock_config_dir;
191 +extern pid_t *pid_index;
192 +extern int debug_enabled;
193 +
194 +//Socket functions and variables
195 +//Common functions
196 +extern void ebpf_socket_create_apps_charts(ebpf_module_t *em, struct target *root);
197 +extern collected_number get_value_from_structure(char *basis, size_t offset);
198 +extern struct pid_stat *root_of_pids;
199 +extern ebpf_process_stat_t *global_process_stat;
200 +extern size_t all_pids_count;
201 +extern int update_every;
202 +
203 +# define EBPF_MAX_SYNCHRONIZATION_TIME 300
204 +
205 #endif
collectors/ebpf.plugin/ebpf_apps.c new
+1205
@@ -0,0 +1,1205 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +
4 +#include "ebpf.h"
5 +#include "ebpf_apps.h"
6 +
7 +// ----------------------------------------------------------------------------
8 +// internal flags
9 +// handled in code (automatically set)
10 +
11 +static int
12 + proc_pid_cmdline_is_needed = 0; // 1 when we need to read /proc/cmdline
13 +
14 +/*****************************************************************
15 + *
16 + * FUNCTIONS USED TO READ HASH TABLES
17 + *
18 + *****************************************************************/
19 +
20 +/**
21 + * Read statistic hash table.
22 + *
23 + * @param ep the output structure.
24 + * @param fd the file descriptor mapped from kernel ring.
25 + * @param pid the index used to select the data.
26 + * @param bpf_map_lookup_elem a pointer for the function used to read data.
27 + *
28 + * @return It returns 0 when the data was copied and -1 otherwise
29 + */
30 +#ifndef STATIC
31 +int ebpf_read_hash_table(void *ep, int fd, uint32_t pid,
32 + int (*bpf_map_lookup_elem)(int, const void *, void *))
33 +#else
34 +int ebpf_read_hash_table(void *ep, int fd, pid_t pid)
35 +#endif
36 +{
37 + if (!ep)
38 + return -1;
39 +
40 + if (!bpf_map_lookup_elem(fd, &pid, ep))
41 + return 0;
42 +
43 + return -1;
44 +}
45 +
46 +/**
47 + * Read socket statistic
48 + *
49 + * Read information from kernel ring to user ring.
50 + *
51 + * @param ep the table with all process stats values.
52 + * @param fd the file descriptor mapped from kernel
53 + * @param ef a pointer for the functions mapped from dynamic library
54 + * @param pids the list of pids associated to a target.
55 + *
56 + * @return
57 + */
58 +#ifndef STATIC
59 +size_t read_bandwidth_statistic_using_pid_on_target(ebpf_bandwidth_t **ep, int fd,
60 + ebpf_functions_t *ef, struct pid_on_target *pids)
61 +#else
62 +size_t read_bandwidth_statistic_using_pid_on_target(ebpf_bandwidth_t **ep, int fd,struct pid_on_target *pids)
63 +#endif
64 +{
65 + size_t count = 0;
66 + while(pids) {
67 + uint32_t current_pid = pids->pid;
68 +#ifndef STATIC
69 + if (!ebpf_read_hash_table(ep[current_pid], fd, current_pid, ef->bpf_map_lookup_elem))
70 +#else
71 + if (!ebpf_read_hash_table(ep[current_pid], fd, current_pid))
72 +#endif
73 + count++;
74 +
75 + pids = pids->next;
76 + }
77 +
78 + return count;
79 +}
80 +
81 +/**
82 + * Read bandwidth statistic using hash table
83 + *
84 + * @param out the output tensor that will receive the information.
85 + * @param fd the file descriptor that has the data
86 + * @param bpf_map_lookup_elem a pointer for the function to read the data
87 + * @param bpf_map_get_next_key a pointer fo the function to read the index.
88 + */
89 +#ifndef STATIC
90 +size_t read_bandwidth_statistic_using_hash_table(ebpf_bandwidth_t **out, int fd,
91 + int (*bpf_map_lookup_elem)(int, const void *, void *),
92 + int (*bpf_map_get_next_key)(int, const void *, void *))
93 +#else
94 +size_t read_bandwidth_statistic_using_hash_table(ebpf_bandwidth_t **out, int fd)
95 +#endif
96 +{
97 + size_t count = 0;
98 + uint32_t key =0;
99 + uint32_t next_key = 0;
100 +
101 + while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
102 + ebpf_bandwidth_t *eps = out[next_key];
103 + if (!eps) {
104 + eps = callocz(1, sizeof(ebpf_process_stat_t));
105 + out[next_key] = eps;
106 + }
107 +#ifndef STATIC
108 + ebpf_read_hash_table(eps, fd, next_key, bpf_map_lookup_elem);
109 +#else
110 + ebpf_read_hash_table(eps, fd, next_key);
111 +#endif
112 + }
113 +
114 + return count;
115 +}
116 +
117 +/*****************************************************************
118 + *
119 + * FUNCTIONS CALLED FROM COLLECTORS
120 + *
121 + *****************************************************************/
122 +
123 +/**
124 + * Am I running as Root
125 + *
126 + * Verify the user that is running the collector.
127 + *
128 + * @return It returns 1 for root and 0 otherwise.
129 + */
130 +int am_i_running_as_root() {
131 + uid_t uid = getuid(), euid = geteuid();
132 +
133 + if(uid == 0 || euid == 0) {
134 + return 1;
135 + }
136 +
137 + return 0;
138 +}
139 +
140 +/**
141 + * Reset the target values
142 + *
143 + * @param root the pointer to the chain that will be reseted.
144 + *
145 + * @return it returns the number of structures that was reseted.
146 + */
147 +size_t zero_all_targets(struct target *root) {
148 + struct target *w;
149 + size_t count = 0;
150 +
151 + for (w = root; w ; w = w->next) {
152 + count++;
153 +
154 + /* These variables are not necessary for eBPF collector
155 + w->minflt = 0;
156 + w->majflt = 0;
157 + w->utime = 0;
158 + w->stime = 0;
159 + w->gtime = 0;
160 + w->cminflt = 0;
161 + w->cmajflt = 0;
162 + w->cutime = 0;
163 + w->cstime = 0;
164 + w->cgtime = 0;
165 + w->num_threads = 0;
166 + // w->rss = 0;
167 + w->processes = 0;
168 +
169 + w->status_vmsize = 0;
170 + w->status_vmrss = 0;
171 + w->status_vmshared = 0;
172 + w->status_rssfile = 0;
173 + w->status_rssshmem = 0;
174 + w->status_vmswap = 0;
175 +
176 + w->io_logical_bytes_read = 0;
177 + w->io_logical_bytes_written = 0;
178 + // w->io_read_calls = 0;
179 + // w->io_write_calls = 0;
180 + w->io_storage_bytes_read = 0;
181 + w->io_storage_bytes_written = 0;
182 + // w->io_cancelled_write_bytes = 0;
183 +
184 + // zero file counters
185 + if(w->target_fds) {
186 + memset(w->target_fds, 0, sizeof(int) * w->target_fds_size);
187 + w->openfiles = 0;
188 + w->openpipes = 0;
189 + w->opensockets = 0;
190 + w->openinotifies = 0;
191 + w->openeventfds = 0;
192 + w->opentimerfds = 0;
193 + w->opensignalfds = 0;
194 + w->openeventpolls = 0;
195 + w->openother = 0;
196 + }
197 +
198 + w->collected_starttime = 0;
199 + w->uptime_min = 0;
200 + w->uptime_sum = 0;
201 + w->uptime_max = 0;
202 + */
203 +
204 + if(unlikely(w->root_pid)) {
205 + struct pid_on_target *pid_on_target = w->root_pid;
206 +
207 + while(pid_on_target) {
208 + struct pid_on_target *pid_on_target_to_free = pid_on_target;
209 + pid_on_target = pid_on_target->next;
210 + free(pid_on_target_to_free);
211 + }
212 +
213 + w->root_pid = NULL;
214 + }
215 + }
216 +
217 + return count;
218 +}
219 +
220 +/**
221 + * Clean the allocated structures
222 + *
223 + * @param agrt the pointer to be cleaned.
224 + */
225 +void clean_apps_groups_target(struct target *agrt) {
226 + struct target *current_target;
227 + while (agrt) {
228 + current_target = agrt;
229 + agrt = current_target->target;
230 +
231 + freez(current_target);
232 + }
233 +}
234 +
235 +/**
236 + * Find or create a new target
237 + * there are targets that are just aggregated to other target (the second argument)
238 + *
239 + * @param id
240 + * @param target
241 + * @param name
242 + *
243 + * @return It returns the target on success and NULL otherwise
244 + */
245 +struct target *get_apps_groups_target(struct target **agrt, const char *id,
246 + struct target *target, const char *name) {
247 + int tdebug = 0, thidden = target?target->hidden:0, ends_with = 0;
248 + const char *nid = id;
249 +
250 + // extract the options
251 + while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
252 + if(nid[0] == '-') thidden = 1;
253 + if(nid[0] == '+') tdebug = 1;
254 + if(nid[0] == '*') ends_with = 1;
255 + nid++;
256 + }
257 + uint32_t hash = simple_hash(id);
258 +
259 + // find if it already exists
260 + struct target *w, *last = *agrt;
261 + for(w = *agrt ; w ; w = w->next) {
262 + if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
263 + return w;
264 +
265 + last = w;
266 + }
267 +
268 + // find an existing target
269 + if(unlikely(!target)) {
270 + while(*name == '-') {
271 + if(*name == '-') thidden = 1;
272 + name++;
273 + }
274 +
275 + for(target = *agrt ; target != NULL ; target = target->next) {
276 + if(!target->target && strcmp(name, target->name) == 0)
277 + break;
278 + }
279 + }
280 +
281 + if(target && target->target)
282 + fatal("Internal Error: request to link process '%s' to target '%s' which is linked to target '%s'", id, target->id, target->target->id);
283 +
284 + w = callocz(1, sizeof(struct target));
285 + strncpyz(w->id, nid, MAX_NAME);
286 + w->idhash = simple_hash(w->id);
287 +
288 + if(unlikely(!target))
289 + // copy the name
290 + strncpyz(w->name, name, MAX_NAME);
291 + else
292 + // copy the id
293 + strncpyz(w->name, nid, MAX_NAME);
294 +
295 + strncpyz(w->compare, nid, MAX_COMPARE_NAME);
296 + size_t len = strlen(w->compare);
297 + if(w->compare[len - 1] == '*') {
298 + w->compare[len - 1] = '\0';
299 + w->starts_with = 1;
300 + }
301 + w->ends_with = ends_with;
302 +
303 + if(w->starts_with && w->ends_with)
304 + proc_pid_cmdline_is_needed = 1;
305 +
306 + w->comparehash = simple_hash(w->compare);
307 + w->comparelen = strlen(w->compare);
308 +
309 + w->hidden = thidden;
310 +#ifdef NETDATA_INTERNAL_CHECKS
311 + w->debug_enabled = tdebug;
312 +#else
313 + if(tdebug)
314 + fprintf(stderr, "apps.plugin has been compiled without debugging\n");
315 +#endif
316 + w->target = target;
317 +
318 + // append it, to maintain the order in apps_groups.conf
319 + if(last) last->next = w;
320 + else *agrt = w;
321 +
322 + return w;
323 +}
324 +
325 +/**
326 + * Read the apps_groups.conf file
327 + *
328 + * @param agrt a pointer to apps_group_root_target
329 + * @param path the directory to search apps_%s.conf
330 + * @param file the word to complement the file name.
331 + *
332 + * @return It returns 0 on succcess and -1 otherwise
333 + */
334 +int ebpf_read_apps_groups_conf(struct target **agdt, struct target **agrt,
335 + const char *path, const char *file)
336 +{
337 + char filename[FILENAME_MAX + 1];
338 +
339 + snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", path, file);
340 +
341 + // ----------------------------------------
342 +
343 + procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT);
344 + if(!ff) return -1;
345 +
346 + procfile_set_quotes(ff, "'\"");
347 +
348 + ff = procfile_readall(ff);
349 + if(!ff)
350 + return -1;
351 +
352 + size_t line, lines = procfile_lines(ff);
353 +
354 + for (line = 0; line < lines ;line++) {
355 + size_t word, words = procfile_linewords(ff, line);
356 + if(!words) continue;
357 +
358 + char *name = procfile_lineword(ff, line, 0);
359 + if (!name || !*name) continue;
360 +
361 + // find a possibly existing target
362 + struct target *w = NULL;
363 +
364 + // loop through all words, skipping the first one (the name)
365 + for (word = 0; word < words ;word++) {
366 + char *s = procfile_lineword(ff, line, word);
367 + if (!s || !*s) continue;
368 + if (*s == '#') break;
369 +
370 + // is this the first word? skip it
371 + if (s == name) continue;
372 +
373 + // add this target
374 + struct target *n = get_apps_groups_target(agrt, s, w, name);
375 + if (!n) {
376 + error("Cannot create target '%s' (line %zu, word %zu)", s, line, word);
377 + continue;
378 + }
379 +
380 + // just some optimization
381 + // to avoid searching for a target for each process
382 + if (!w) w = n->target?n->target:n;
383 + }
384 + }
385 +
386 + procfile_close(ff);
387 +
388 + *agdt = get_apps_groups_target(agrt, "p+!o@w#e$i^r&7*5(-i)l-o_",
389 + NULL, "other"); // match nothing
390 + if(!*agdt)
391 + fatal("Cannot create default target");
392 +
393 + struct target *ptr = *agdt;
394 + if (ptr->target)
395 + *agdt = ptr->target;
396 +
397 + return 0;
398 +}
399 +
400 +// the minimum PID of the system
401 +// this is also the pid of the init process
402 +#define INIT_PID 1
403 +
404 +// ----------------------------------------------------------------------------
405 +// string lengths
406 +
407 +#define MAX_COMPARE_NAME 100
408 +#define MAX_NAME 100
409 +#define MAX_CMDLINE 16384
410 +
411 +struct pid_stat **all_pids = NULL; // to avoid allocations, we pre-allocate the
412 + // the entire pid space.
413 +struct pid_stat *root_of_pids = NULL; // global list of all processes running
414 +
415 +size_t
416 + all_pids_count = 0; // the number of processes running
417 +
418 +struct target
419 + *apps_groups_default_target = NULL, // the default target
420 + *apps_groups_root_target = NULL, // apps_groups.conf defined
421 + *users_root_target = NULL, // users
422 + *groups_root_target = NULL; // user groups
423 +
424 +size_t
425 + apps_groups_targets_count = 0; // # of apps_groups.conf targets
426 +
427 +
428 +// ----------------------------------------------------------------------------
429 +// internal counters
430 +
431 +static size_t
432 + // global_iterations_counter = 1,
433 + calls_counter = 0,
434 + // file_counter = 0,
435 + // filenames_allocated_counter = 0,
436 + // inodes_changed_counter = 0,
437 + // links_changed_counter = 0,
438 + targets_assignment_counter = 0;
439 +
440 +// ----------------------------------------------------------------------------
441 +// debugging
442 +
443 +// log each problem once per process
444 +// log flood protection flags (log_thrown)
445 +#define PID_LOG_IO 0x00000001
446 +#define PID_LOG_STATUS 0x00000002
447 +#define PID_LOG_CMDLINE 0x00000004
448 +#define PID_LOG_FDS 0x00000008
449 +#define PID_LOG_STAT 0x00000010
450 +
451 +int debug_enabled = 0;
452 +
453 +#ifdef NETDATA_INTERNAL_CHECKS
454 +
455 +#define debug_log(fmt, args...) do { if(unlikely(debug_enabled)) debug_log_int(fmt, ##args); } while(0)
456 +
457 +#else
458 +
459 +static inline void debug_log_dummy(void) {}
460 +#define debug_log(fmt, args...) debug_log_dummy()
461 +
462 +#endif
463 +
464 +/**
465 + * Managed log
466 + *
467 + * Store log information if it is necessary.
468 + *
469 + * @param p the pid stat structure
470 + * @param log the log id
471 + * @param status the return from a function.
472 + *
473 + * @return It returns the status value.
474 + */
475 +static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
476 + if(unlikely(!status)) {
477 + // error("command failed log %u, errno %d", log, errno);
478 +
479 + if(unlikely(debug_enabled || errno != ENOENT)) {
480 + if(unlikely(debug_enabled || !(p->log_thrown & log))) {
481 + p->log_thrown |= log;
482 + switch(log) {
483 + case PID_LOG_IO:
484 + error("Cannot process %s/proc/%d/io (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
485 + break;
486 +
487 + case PID_LOG_STATUS:
488 + error("Cannot process %s/proc/%d/status (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
489 + break;
490 +
491 + case PID_LOG_CMDLINE:
492 + error("Cannot process %s/proc/%d/cmdline (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
493 + break;
494 +
495 + case PID_LOG_FDS:
496 + error("Cannot process entries in %s/proc/%d/fd (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
497 + break;
498 +
499 + case PID_LOG_STAT:
500 + break;
501 +
502 + default:
503 + error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
504 + break;
505 + }
506 + }
507 + }
508 + errno = 0;
509 + }
510 + else if(unlikely(p->log_thrown & log)) {
511 + // error("unsetting log %u on pid %d", log, p->pid);
512 + p->log_thrown &= ~log;
513 + }
514 +
515 + return status;
516 +}
517 +
518 +/**
519 + * Get PID entry
520 + *
521 + * Get or allocate the PID entry for the specifid pid.
522 + *
523 + * @param pid the pid to search the data.
524 + *
525 + * @return It returns the pid entry structure
526 + */
527 +static inline struct pid_stat *get_pid_entry(pid_t pid) {
528 + if(unlikely(all_pids[pid]))
529 + return all_pids[pid];
530 +
531 + struct pid_stat *p = callocz(1, sizeof(struct pid_stat));
532 +
533 + if(likely(root_of_pids))
534 + root_of_pids->prev = p;
535 +
536 + p->next = root_of_pids;
537 + root_of_pids = p;
538 +
539 + p->pid = pid;
540 +
541 + all_pids[pid] = p;
542 + all_pids_count++;
543 +
544 + return p;
545 +}
546 +
547 +/**
548 + * Assign the PID to a target.
549 + *
550 + * @param p the pid_stat structure to assign for a target.
551 + */
552 +static inline void assign_target_to_pid(struct pid_stat *p) {
553 + targets_assignment_counter++;
554 +
555 + uint32_t hash = simple_hash(p->comm);
556 + size_t pclen = strlen(p->comm);
557 +
558 + struct target *w;
559 + for(w = apps_groups_root_target; w ; w = w->next) {
560 + // if(debug_enabled || (p->target && p->target->debug_enabled)) debug_log_int("\t\tcomparing '%s' with '%s'", w->compare, p->comm);
561 +
562 + // find it - 4 cases:
563 + // 1. the target is not a pattern
564 + // 2. the target has the prefix
565 + // 3. the target has the suffix
566 + // 4. the target is something inside cmdline
567 +
568 + if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
569 + || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
570 + || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
571 + || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && p->cmdline && strstr(p->cmdline, w->compare))
572 + ))) {
573 +
574 + if(w->target) p->target = w->target;
575 + else p->target = w;
576 +
577 + if(debug_enabled || (p->target && p->target->debug_enabled))
578 + debug_log_int("%s linked to target %s", p->comm, p->target->name);
579 +
580 + break;
581 + }
582 + }
583 +}
584 +
585 +// ----------------------------------------------------------------------------
586 +// update pids from proc
587 +
588 +/**
589 + * Read cmd line from /proc/PID/cmdline
590 + *
591 + * @param p the pid_stat_structure.
592 + *
593 + * @return It returns 1 on success and 0 otherwise.
594 + */
595 +static inline int read_proc_pid_cmdline(struct pid_stat *p) {
596 + static char cmdline[MAX_CMDLINE + 1];
597 +
598 + if(unlikely(!p->cmdline_filename)) {
599 + char filename[FILENAME_MAX + 1];
600 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", netdata_configured_host_prefix, p->pid);
601 + p->cmdline_filename = strdupz(filename);
602 + }
603 +
604 + int fd = open(p->cmdline_filename, procfile_open_flags, 0666);
605 + if(unlikely(fd == -1)) goto cleanup;
606 +
607 + ssize_t i, bytes = read(fd, cmdline, MAX_CMDLINE);
608 + close(fd);
609 +
610 + if(unlikely(bytes < 0)) goto cleanup;
611 +
612 + cmdline[bytes] = '\0';
613 + for(i = 0; i < bytes ; i++) {
614 + if(unlikely(!cmdline[i])) cmdline[i] = ' ';
615 + }
616 +
617 + if(p->cmdline) freez(p->cmdline);
618 + p->cmdline = strdupz(cmdline);
619 +
620 + debug_log("Read file '%s' contents: %s", p->cmdline_filename, p->cmdline);
621 +
622 + return 1;
623 +
624 +cleanup:
625 + // copy the command to the command line
626 + if(p->cmdline) freez(p->cmdline);
627 + p->cmdline = strdupz(p->comm);
628 + return 0;
629 +}
630 +
631 +/**
632 + * Read information from /proc/PID/stat and /proc/PID/cmdline
633 + * Assign target to pid
634 + *
635 + * @param p the pid stat structure to store the data.
636 + * @param ptr an useless argument.
637 + */
638 +static inline int read_proc_pid_stat(struct pid_stat *p, void *ptr) {
639 + (void)ptr;
640 +
641 + static procfile *ff = NULL;
642 +
643 + if(unlikely(!p->stat_filename)) {
644 + char filename[FILENAME_MAX + 1];
645 + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", netdata_configured_host_prefix, p->pid);
646 + p->stat_filename = strdupz(filename);
647 + }
648 +
649 + int set_quotes = (!ff)?1:0;
650 +
651 + struct stat statbuf;
652 + if (stat(p->stat_filename, &statbuf)) return 0;
653 +
654 + ff = procfile_reopen(ff, p->stat_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
655 + if(unlikely(!ff)) return 0;
656 +
657 + if(unlikely(set_quotes))
658 + procfile_set_open_close(ff, "(", ")");
659 +
660 + ff = procfile_readall(ff);
661 + if(unlikely(!ff)) return 0;
662 +
663 + p->last_stat_collected_usec = p->stat_collected_usec;
664 + p->stat_collected_usec = now_monotonic_usec();
665 + calls_counter++;
666 +
667 + char *comm = procfile_lineword(ff, 0, 1);
668 + p->ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3));
669 +
670 + if(strcmp(p->comm, comm) != 0) {
671 + if(unlikely(debug_enabled)) {
672 + if(p->comm[0])
673 + debug_log("\tpid %d (%s) changed name to '%s'", p->pid, p->comm, comm);
674 + else
675 + debug_log("\tJust added %d (%s)", p->pid, comm);
676 + }
677 +
678 + strncpyz(p->comm, comm, MAX_COMPARE_NAME);
679 +
680 + // /proc/<pid>/cmdline
681 + if(likely(proc_pid_cmdline_is_needed))
682 + managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
683 +
684 + assign_target_to_pid(p);
685 + }
686 +
687 + if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
688 + debug_log_int(
689 + "READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu)",
690 + netdata_configured_host_prefix, p->pid, p->comm, (p->target) ? p->target->name : "UNSET",
691 + p->stat_collected_usec - p->last_stat_collected_usec);
692 +
693 + return 1;
694 +}
695 +
696 +/**
697 + * Collect data for PID
698 + *
699 + * @param pid the current pid that we are working
700 + * @param ptr a NULL value
701 + *
702 + * @return It returns 1 on succcess and 0 otherwise
703 + */
704 +static inline int collect_data_for_pid(pid_t pid, void *ptr) {
705 + if(unlikely(pid < 0 || pid > pid_max)) {
706 + error("Invalid pid %d read (expected %d to %d). Ignoring process.", pid, 0, pid_max);
707 + return 0;
708 + }
709 +
710 + struct pid_stat *p = get_pid_entry(pid);
711 + if(unlikely(!p || p->read)) return 0;
712 + p->read = 1;
713 +
714 + if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p, ptr))))
715 + // there is no reason to proceed if we cannot get its status
716 + return 0;
717 +
718 +
719 + // check its parent pid
720 + if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
721 + error("Pid %d (command '%s') states invalid parent pid %d. Using 0.", pid, p->comm, p->ppid);
722 + p->ppid = 0;
723 + }
724 +
725 + /*
726 + if(unlikely(debug_enabled && all_pids_count && p->ppid && all_pids[p->ppid] && !all_pids[p->ppid]->read))
727 + 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);
728 + */
729 +
730 + // mark it as updated
731 + p->updated = 1;
732 + p->keep = 0;
733 + p->keeploops = 0;
734 +
735 + return 1;
736 +}
737 +
738 +/**
739 + * Fill link list of parents with children PIDs
740 + */
741 +static inline void link_all_processes_to_their_parents(void) {
742 + struct pid_stat *p, *pp;
743 +
744 + // link all children to their parents
745 + // and update children count on parents
746 + for(p = root_of_pids; p ; p = p->next) {
747 + // for each process found
748 +
749 + p->sortlist = 0;
750 + p->parent = NULL;
751 +
752 + if(unlikely(!p->ppid)) {
753 + p->parent = NULL;
754 + continue;
755 + }
756 +
757 + pp = all_pids[p->ppid];
758 + if(likely(pp)) {
759 + p->parent = pp;
760 + pp->children_count++;
761 +
762 + if(unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
763 + debug_log_int("child %d (%s, %s) on target '%s' has parent %d (%s, %s).", p->pid, p->comm, p->updated?"running":"exited", (p->target)?p->target->name:"UNSET", pp->pid, pp->comm, pp->updated?"running":"exited");
764 + }
765 + else {
766 + p->parent = NULL;
767 + error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
768 + }
769 + }
770 +}
771 +
772 +/**
773 + * Aggregate PIDs to targets.
774 + */
775 +static void apply_apps_groups_targets_inheritance(void) {
776 + struct pid_stat *p = NULL;
777 +
778 + // children that do not have a target
779 + // inherit their target from their parent
780 + int found = 1, loops = 0;
781 + while(found) {
782 + if(unlikely(debug_enabled)) loops++;
783 + found = 0;
784 + for(p = root_of_pids; p ; p = p->next) {
785 + // if this process does not have a target
786 + // and it has a parent
787 + // and its parent has a target
788 + // then, set the parent's target to this process
789 + if(unlikely(!p->target && p->parent && p->parent->target)) {
790 + p->target = p->parent->target;
791 + found++;
792 +
793 + if(debug_enabled || (p->target && p->target->debug_enabled))
794 + debug_log_int("TARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s).", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
795 + }
796 + }
797 + }
798 +
799 + // find all the procs with 0 childs and merge them to their parents
800 + // repeat, until nothing more can be done.
801 + int sortlist = 1;
802 + found = 1;
803 + while(found) {
804 + if(unlikely(debug_enabled)) loops++;
805 + found = 0;
806 +
807 + for(p = root_of_pids; p ; p = p->next) {
808 + if(unlikely(!p->sortlist && !p->children_count))
809 + p->sortlist = sortlist++;
810 +
811 + if(unlikely(
812 + !p->children_count // if this process does not have any children
813 + && !p->merged // and is not already merged
814 + && p->parent // and has a parent
815 + && p->parent->children_count // and its parent has children
816 + // and the target of this process and its parent is the same,
817 + // or the parent does not have a target
818 + && (p->target == p->parent->target || !p->parent->target)
819 + && p->ppid != INIT_PID // and its parent is not init
820 + )) {
821 + // mark it as merged
822 + p->parent->children_count--;
823 + p->merged = 1;
824 +
825 + // the parent inherits the child's target, if it does not have a target itself
826 + if(unlikely(p->target && !p->parent->target)) {
827 + p->parent->target = p->target;
828 +
829 + if(debug_enabled || (p->target && p->target->debug_enabled))
830 + debug_log_int("TARGET INHERITANCE: %s is inherited by %d (%s) from its child %d (%s).", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
831 + }
832 +
833 + found++;
834 + }
835 + }
836 +
837 + debug_log("TARGET INHERITANCE: merged %d processes", found);
838 + }
839 +
840 + // init goes always to default target
841 + if(all_pids[INIT_PID])
842 + all_pids[INIT_PID]->target = apps_groups_default_target;
843 +
844 + // pid 0 goes always to default target
845 + if(all_pids[0])
846 + all_pids[0]->target = apps_groups_default_target;
847 +
848 + // give a default target on all top level processes
849 + if(unlikely(debug_enabled)) loops++;
850 + for(p = root_of_pids; p ; p = p->next) {
851 + // if the process is not merged itself
852 + // then is is a top level process
853 + if(unlikely(!p->merged && !p->target))
854 + p->target = apps_groups_default_target;
855 +
856 + // make sure all processes have a sortlist
857 + if(unlikely(!p->sortlist))
858 + p->sortlist = sortlist++;
859 + }
860 +
861 + if(all_pids[1])
862 + all_pids[1]->sortlist = sortlist++;
863 +
864 + // give a target to all merged child processes
865 + found = 1;
866 + while(found) {
867 + if(unlikely(debug_enabled)) loops++;
868 + found = 0;
869 + for(p = root_of_pids; p ; p = p->next) {
870 + if(unlikely(!p->target && p->merged && p->parent && p->parent->target)) {
871 + p->target = p->parent->target;
872 + found++;
873 +
874 + if(debug_enabled || (p->target && p->target->debug_enabled))
875 + debug_log_int("TARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s) at phase 2.", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
876 + }
877 + }
878 + }
879 +
880 + debug_log("apply_apps_groups_targets_inheritance() made %d loops on the process tree", loops);
881 +}
882 +
883 +/**
884 + * Update target timestamp.
885 + *
886 + * @param root the targets that will be updated.
887 + */
888 +static inline void post_aggregate_targets(struct target *root) {
889 + struct target *w;
890 + for (w = root; w ; w = w->next) {
891 + if(w->collected_starttime) {
892 + if (!w->starttime || w->collected_starttime < w->starttime) {
893 + w->starttime = w->collected_starttime;
894 + }
895 + } else {
896 + w->starttime = 0;
897 + }
898 + }
899 +}
900 +
901 +/**
902 + * Remove PID from the link list.
903 + *
904 + * @param pid the PID that will be removed.
905 + */
906 +static inline void del_pid_entry(pid_t pid) {
907 + struct pid_stat *p = all_pids[pid];
908 +
909 + if(unlikely(!p)) {
910 + error("attempted to free pid %d that is not allocated.", pid);
911 + return;
912 + }
913 +
914 + debug_log("process %d %s exited, deleting it.", pid, p->comm);
915 +
916 + if(root_of_pids == p)
917 + root_of_pids = p->next;
918 +
919 + if(p->next) p->next->prev = p->prev;
920 + if(p->prev) p->prev->next = p->next;
921 +
922 + freez(p->stat_filename);
923 + freez(p->status_filename);
924 + freez(p->io_filename);
925 + freez(p->cmdline_filename);
926 + freez(p->cmdline);
927 + freez(p);
928 +
929 + all_pids[pid] = NULL;
930 + all_pids_count--;
931 +}
932 +
933 +/**
934 + * Remove PIDs when they are not running more.
935 + *
936 + * @param out is the structure where PIDs are stored.
937 + */
938 +void cleanup_exited_pids(ebpf_process_stat_t **out) {
939 + struct pid_stat *p = NULL;
940 +
941 + for(p = root_of_pids; p ;) {
942 + if(!p->updated && (!p->keep || p->keeploops > 0)) {
943 + if(unlikely(debug_enabled && (p->keep || p->keeploops)))
944 + debug_log(" > CLEANUP cannot keep exited process %d (%s) anymore - removing it.", p->pid, p->comm);
945 +
946 + pid_t r = p->pid;
947 + p = p->next;
948 + del_pid_entry(r);
949 +
950 + ebpf_process_stat_t *w = out[r];
951 + if (w) {
952 + freez(w);
953 + out[r] = NULL;
954 + }
955 + }
956 + else {
957 + if(unlikely(p->keep)) p->keeploops++;
958 + p->keep = 0;
959 + p = p->next;
960 + }
961 + }
962 +}
963 +
964 +/**
965 + * Read proc filesystem for the first time.
966 + *
967 + * @return It returns 0 on success and -1 otherwise.
968 + */
969 +static inline void read_proc_filesystem()
970 +{
971 + char dirname[FILENAME_MAX + 1];
972 +
973 + snprintfz(dirname, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
974 + DIR *dir = opendir(dirname);
975 + if(!dir) return;
976 +
977 + struct dirent *de = NULL;
978 +
979 + while((de = readdir(dir))) {
980 + char *endptr = de->d_name;
981 +
982 + if(unlikely(de->d_type != DT_DIR || de->d_name[0] < '0' || de->d_name[0] > '9'))
983 + continue;
984 +
985 + pid_t pid = (pid_t) strtoul(de->d_name, &endptr, 10);
986 +
987 + // make sure we read a valid number
988 + if(unlikely(endptr == de->d_name || *endptr != '\0'))
989 + continue;
990 +
991 + collect_data_for_pid(pid, NULL);
992 + }
993 + closedir(dir);
994 +}
995 +
996 +/**
997 + * Aggregated PID on target
998 + *
999 + * @param w the target output
1000 + * @param p the pid with information to update
1001 + * @param o never used
1002 + */
1003 +static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o)
1004 +{
1005 + (void)o;
1006 +
1007 + if(unlikely(!p->updated)) {
1008 + // the process is not running
1009 + return;
1010 + }
1011 +
1012 + if(unlikely(!w)) {
1013 + error("pid %d %s was left without a target!", p->pid, p->comm);
1014 + return;
1015 + }
1016 +
1017 + /*
1018 + w->cutime += p->cutime;
1019 + w->cstime += p->cstime;
1020 + w->cgtime += p->cgtime;
1021 + w->cminflt += p->cminflt;
1022 + w->cmajflt += p->cmajflt;
1023 +
1024 + w->utime += p->utime;
1025 + w->stime += p->stime;
1026 + w->gtime += p->gtime;
1027 + w->minflt += p->minflt;
1028 + w->majflt += p->majflt;
1029 +
1030 + // w->rss += p->rss;
1031 +
1032 + w->status_vmsize += p->status_vmsize;
1033 + w->status_vmrss += p->status_vmrss;
1034 + w->status_vmshared += p->status_vmshared;
1035 + w->status_rssfile += p->status_rssfile;
1036 + w->status_rssshmem += p->status_rssshmem;
1037 + w->status_vmswap += p->status_vmswap;
1038 +
1039 + w->io_logical_bytes_read += p->io_logical_bytes_read;
1040 + w->io_logical_bytes_written += p->io_logical_bytes_written;
1041 + // w->io_read_calls += p->io_read_calls;
1042 + // w->io_write_calls += p->io_write_calls;
1043 + w->io_storage_bytes_read += p->io_storage_bytes_read;
1044 + w->io_storage_bytes_written += p->io_storage_bytes_written;
1045 + // w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1046 + */
1047 +
1048 + w->processes++;
1049 + struct pid_on_target *pid_on_target = mallocz(sizeof(struct pid_on_target));
1050 + pid_on_target->pid = p->pid;
1051 + pid_on_target->next = w->root_pid;
1052 + w->root_pid = pid_on_target;
1053 + /*
1054 + w->num_threads += p->num_threads;
1055 +
1056 + if(!w->collected_starttime || p->collected_starttime < w->collected_starttime) w->collected_starttime = p->collected_starttime;
1057 + if(!w->uptime_min || p->uptime < w->uptime_min) w->uptime_min = p->uptime;
1058 + w->uptime_sum += p->uptime;
1059 + if(!w->uptime_max || w->uptime_max < p->uptime) w->uptime_max = p->uptime;
1060 +
1061 + if(unlikely(debug_enabled || w->debug_enabled)) {
1062 + //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);
1063 +
1064 + struct pid_on_target *pid_on_target = mallocz(sizeof(struct pid_on_target));
1065 + pid_on_target->pid = p->pid;
1066 + pid_on_target->next = w->root_pid;
1067 + w->root_pid = pid_on_target;
1068 + }
1069 + */
1070 +}
1071 +
1072 +/**
1073 + * Collect data for all process
1074 + *
1075 + * Read data from hash table and store it in appropriate vectors.
1076 + * It also creates the link between targets and PIDs.
1077 + *
1078 + * @param out the output vector where we store data read from hash table.
1079 + * @param index the vector to store the indexes read.
1080 + * @param bpf_map_lookup_elem A pointer to the function that reads the data.
1081 + * @param tbl_pid_stats_fd The mapped file descriptor for the hash table.
1082 + */
1083 +#ifndef STATIC
1084 +void collect_data_for_all_processes(ebpf_process_stat_t **out,
1085 + pid_t *index,
1086 + int (*bpf_map_lookup_elem)(int, const void *, void *),
1087 + int tbl_pid_stats_fd)
1088 +#else
1089 +void collect_data_for_all_processes(ebpf_process_stat_t **out,
1090 + pid_t *index,
1091 + int tbl_pid_stats_fd)
1092 +#endif
1093 +{
1094 + struct pid_stat *pids = root_of_pids; // global list of all processes running
1095 + while (pids) {
1096 + if (pids->updated_twice) {
1097 + pids->read = 0; // mark it as not read, so that collect_data_for_pid() will read it
1098 + pids->updated = 0;
1099 + pids->merged = 0;
1100 + pids->children_count = 0;
1101 + pids->parent = NULL;
1102 + } else {
1103 + if (pids->updated)
1104 + pids->updated_twice = 1;
1105 + }
1106 +
1107 + pids = pids->next;
1108 + }
1109 +
1110 + read_proc_filesystem();
1111 +
1112 + int counter = 0;
1113 + uint32_t key;
1114 + pids = root_of_pids; // global list of all processes running
1115 + //while (bpf_map_get_next_key(tbl_pid_stats_fd, &key, &next_key) == 0) {
1116 + while (pids) {
1117 + key = pids->pid;
1118 + ebpf_process_stat_t *w = out[key];
1119 + if (!w) {
1120 + w = mallocz(sizeof(ebpf_process_stat_t));
1121 + out[key] = w;
1122 + }
1123 +
1124 + if (bpf_map_lookup_elem(tbl_pid_stats_fd, &key, w)) {
1125 + pids = pids->next;
1126 + continue;
1127 + }
1128 +
1129 + index[counter] = key;
1130 + counter++;
1131 +
1132 + pids = pids->next;
1133 + }
1134 +
1135 + link_all_processes_to_their_parents();
1136 +
1137 + apply_apps_groups_targets_inheritance();
1138 +
1139 + /* These lines are not necessary for ebpf plugin
1140 + zero_all_targets(users_root_target);
1141 + zero_all_targets(groups_root_target);
1142 + */
1143 +
1144 + apps_groups_targets_count = zero_all_targets(apps_groups_root_target);
1145 +
1146 + // this has to be done, before the cleanup
1147 + struct pid_stat *p = NULL;
1148 + // struct target *w = NULL, *o = NULL;
1149 +
1150 + // // concentrate everything on the targets
1151 + for(p = root_of_pids; p ; p = p->next) {
1152 +
1153 + // --------------------------------------------------------------------
1154 + // apps_groups target
1155 +
1156 + aggregate_pid_on_target(p->target, p, NULL);
1157 +
1158 +
1159 + // // --------------------------------------------------------------------
1160 + // // user target
1161 +
1162 + // o = p->user_target;
1163 + // if(likely(p->user_target && p->user_target->uid == p->uid))
1164 + // w = p->user_target;
1165 + // else {
1166 + // if(unlikely(debug_enabled && p->user_target))
1167 + // debug_log("pid %d (%s) switched user from %u (%s) to %u.", p->pid, p->comm, p->user_target->uid, p->user_target->name, p->uid);
1168 +
1169 + // w = p->user_target = get_users_target(p->uid);
1170 + // }
1171 +
1172 + // aggregate_pid_on_target(w, p, o);
1173 +
1174 +
1175 + // // --------------------------------------------------------------------
1176 + // // user group target
1177 +
1178 + // o = p->group_target;
1179 + // if(likely(p->group_target && p->group_target->gid == p->gid))
1180 + // w = p->group_target;
1181 + // else {
1182 + // if(unlikely(debug_enabled && p->group_target))
1183 + // debug_log("pid %d (%s) switched group from %u (%s) to %u.", p->pid, p->comm, p->group_target->gid, p->group_target->name, p->gid);
1184 +
1185 + // w = p->group_target = get_groups_target(p->gid);
1186 + // }
1187 +
1188 + // aggregate_pid_on_target(w, p, o);
1189 +
1190 +
1191 + // // --------------------------------------------------------------------
1192 + // // aggregate all file descriptors
1193 +
1194 + // if(enable_file_charts)
1195 + // aggregate_pid_fds_on_targets(p);
1196 + }
1197 +
1198 + post_aggregate_targets(apps_groups_root_target);
1199 + /* These lines are not necessary for ebpf plugin
1200 + post_aggregate_targets(users_root_target);
1201 + post_aggregate_targets(groups_root_target);
1202 + */
1203 +
1204 + //cleanup_exited_pids(out);
1205 +}
collectors/ebpf.plugin/ebpf_apps.h new
+432
@@ -0,0 +1,432 @@
1 +#ifndef _NETDATA_EBPF_APPS_H
2 +# define _NETDATA_EBPF_APPS_H 1
3 +
4 +# include "libnetdata/threads/threads.h"
5 +# include "libnetdata/locks/locks.h"
6 +# include "libnetdata/avl/avl.h"
7 +# include "libnetdata/clocks/clocks.h"
8 +# include "libnetdata/config/appconfig.h"
9 +# include "libnetdata/ebpf/ebpf.h"
10 +
11 +# define NETDATA_APPS_FAMILY "apps"
12 +# define NETDATA_APPS_SYSCALL_GROUP "ebpf syscall"
13 +# define NETDATA_APPS_NET_GROUP "ebpf net"
14 +
15 +# define MAX_COMPARE_NAME 100
16 +# define MAX_NAME 100
17 +
18 +// ----------------------------------------------------------------------------
19 +// process_pid_stat
20 +//
21 +// Fields read from the kernel ring for a specific PID
22 +//
23 +typedef struct process_pid_stat {
24 + uint64_t pid_tgid; //Unique identifier
25 + uint32_t pid; //process id
26 +
27 + //Count number of calls done for specific function
28 + uint32_t open_call;
29 + uint32_t write_call;
30 + uint32_t writev_call;
31 + uint32_t read_call;
32 + uint32_t readv_call;
33 + uint32_t unlink_call;
34 + uint32_t exit_call;
35 + uint32_t release_call;
36 + uint32_t fork_call;
37 + uint32_t clone_call;
38 + uint32_t close_call;
39 +
40 + //Count number of bytes written or read
41 + uint64_t write_bytes;
42 + uint64_t writev_bytes;
43 + uint64_t readv_bytes;
44 + uint64_t read_bytes;
45 +
46 + //Count number of errors for the specified function
47 + uint32_t open_err;
48 + uint32_t write_err;
49 + uint32_t writev_err;
50 + uint32_t read_err;
51 + uint32_t readv_err;
52 + uint32_t unlink_err;
53 + uint32_t fork_err;
54 + uint32_t clone_err;
55 + uint32_t close_err;
56 +} process_pid_stat_t;
57 +
58 +// ----------------------------------------------------------------------------
59 +// socket_bandwidth
60 +//
61 +// Fields read from the kernel ring for a specific PID
62 +//
63 +typedef struct socket_bandwidth {
64 + uint64_t first;
65 + uint64_t ct;
66 + uint64_t sent;
67 + uint64_t received;
68 + unsigned char removed;
69 +} socket_bandwidth_t;
70 +
71 +
72 +// ----------------------------------------------------------------------------
73 +// pid_stat
74 +//
75 +// structure to store data for each process running
76 +// see: man proc for the description of the fields
77 +
78 +struct pid_fd {
79 + int fd;
80 +
81 +#ifndef __FreeBSD__
82 + ino_t inode;
83 + char *filename;
84 + uint32_t link_hash;
85 + size_t cache_iterations_counter;
86 + size_t cache_iterations_reset;
87 +#endif
88 +};
89 +
90 +struct target {
91 + char compare[MAX_COMPARE_NAME + 1];
92 + uint32_t comparehash;
93 + size_t comparelen;
94 +
95 + char id[MAX_NAME + 1];
96 + uint32_t idhash;
97 +
98 + char name[MAX_NAME + 1];
99 +
100 + uid_t uid;
101 + gid_t gid;
102 +
103 + /* These variables are not necessary for eBPF collector
104 + kernel_uint_t minflt;
105 + kernel_uint_t cminflt;
106 + kernel_uint_t majflt;
107 + kernel_uint_t cmajflt;
108 + kernel_uint_t utime;
109 + kernel_uint_t stime;
110 + kernel_uint_t gtime;
111 + kernel_uint_t cutime;
112 + kernel_uint_t cstime;
113 + kernel_uint_t cgtime;
114 + kernel_uint_t num_threads;
115 + // kernel_uint_t rss;
116 +
117 + kernel_uint_t status_vmsize;
118 + kernel_uint_t status_vmrss;
119 + kernel_uint_t status_vmshared;
120 + kernel_uint_t status_rssfile;
121 + kernel_uint_t status_rssshmem;
122 + kernel_uint_t status_vmswap;
123 +
124 + kernel_uint_t io_logical_bytes_read;
125 + kernel_uint_t io_logical_bytes_written;
126 + // kernel_uint_t io_read_calls;
127 + // kernel_uint_t io_write_calls;
128 + kernel_uint_t io_storage_bytes_read;
129 + kernel_uint_t io_storage_bytes_written;
130 + // kernel_uint_t io_cancelled_write_bytes;
131 +
132 + int *target_fds;
133 + int target_fds_size;
134 +
135 + kernel_uint_t openfiles;
136 + kernel_uint_t openpipes;
137 + kernel_uint_t opensockets;
138 + kernel_uint_t openinotifies;
139 + kernel_uint_t openeventfds;
140 + kernel_uint_t opentimerfds;
141 + kernel_uint_t opensignalfds;
142 + kernel_uint_t openeventpolls;
143 + kernel_uint_t openother;
144 + */
145 +
146 + kernel_uint_t starttime;
147 + kernel_uint_t collected_starttime;
148 +
149 + /*
150 + kernel_uint_t uptime_min;
151 + kernel_uint_t uptime_sum;
152 + kernel_uint_t uptime_max;
153 + */
154 +
155 + unsigned int processes; // how many processes have been merged to this
156 + int exposed; // if set, we have sent this to netdata
157 + int hidden; // if set, we set the hidden flag on the dimension
158 + int debug_enabled;
159 + int ends_with;
160 + int starts_with; // if set, the compare string matches only the
161 + // beginning of the command
162 +
163 + struct pid_on_target *root_pid; // list of aggregated pids for target debugging
164 +
165 + struct target *target; // the one that will be reported to netdata
166 + struct target *next;
167 +};
168 +
169 +extern struct target *apps_groups_default_target;
170 +extern struct target *apps_groups_root_target;
171 +extern struct target *users_root_target;
172 +extern struct target *groups_root_target;
173 +
174 +struct pid_stat {
175 + int32_t pid;
176 + char comm[MAX_COMPARE_NAME + 1];
177 + char *cmdline;
178 +
179 + uint32_t log_thrown;
180 +
181 + // char state;
182 + int32_t ppid;
183 +
184 + // int32_t pgrp;
185 + // int32_t session;
186 + // int32_t tty_nr;
187 + // int32_t tpgid;
188 + // uint64_t flags;
189 +
190 + /*
191 + // these are raw values collected
192 + kernel_uint_t minflt_raw;
193 + kernel_uint_t cminflt_raw;
194 + kernel_uint_t majflt_raw;
195 + kernel_uint_t cmajflt_raw;
196 + kernel_uint_t utime_raw;
197 + kernel_uint_t stime_raw;
198 + kernel_uint_t gtime_raw; // guest_time
199 + kernel_uint_t cutime_raw;
200 + kernel_uint_t cstime_raw;
201 + kernel_uint_t cgtime_raw; // cguest_time
202 +
203 + // these are rates
204 + kernel_uint_t minflt;
205 + kernel_uint_t cminflt;
206 + kernel_uint_t majflt;
207 + kernel_uint_t cmajflt;
208 + kernel_uint_t utime;
209 + kernel_uint_t stime;
210 + kernel_uint_t gtime;
211 + kernel_uint_t cutime;
212 + kernel_uint_t cstime;
213 + kernel_uint_t cgtime;
214 +
215 + // int64_t priority;
216 + // int64_t nice;
217 + int32_t num_threads;
218 + // int64_t itrealvalue;
219 + kernel_uint_t collected_starttime;
220 + // kernel_uint_t vsize;
221 + // kernel_uint_t rss;
222 + // kernel_uint_t rsslim;
223 + // kernel_uint_t starcode;
224 + // kernel_uint_t endcode;
225 + // kernel_uint_t startstack;
226 + // kernel_uint_t kstkesp;
227 + // kernel_uint_t kstkeip;
228 + // uint64_t signal;
229 + // uint64_t blocked;
230 + // uint64_t sigignore;
231 + // uint64_t sigcatch;
232 + // uint64_t wchan;
233 + // uint64_t nswap;
234 + // uint64_t cnswap;
235 + // int32_t exit_signal;
236 + // int32_t processor;
237 + // uint32_t rt_priority;
238 + // uint32_t policy;
239 + // kernel_uint_t delayacct_blkio_ticks;
240 +
241 + uid_t uid;
242 + gid_t gid;
243 +
244 + kernel_uint_t status_vmsize;
245 + kernel_uint_t status_vmrss;
246 + kernel_uint_t status_vmshared;
247 + kernel_uint_t status_rssfile;
248 + kernel_uint_t status_rssshmem;
249 + kernel_uint_t status_vmswap;
250 +#ifndef __FreeBSD__
251 + ARL_BASE *status_arl;
252 +#endif
253 +
254 + kernel_uint_t io_logical_bytes_read_raw;
255 + kernel_uint_t io_logical_bytes_written_raw;
256 + // kernel_uint_t io_read_calls_raw;
257 + // kernel_uint_t io_write_calls_raw;
258 + kernel_uint_t io_storage_bytes_read_raw;
259 + kernel_uint_t io_storage_bytes_written_raw;
260 + // kernel_uint_t io_cancelled_write_bytes_raw;
261 +
262 + kernel_uint_t io_logical_bytes_read;
263 + kernel_uint_t io_logical_bytes_written;
264 + // kernel_uint_t io_read_calls;
265 + // kernel_uint_t io_write_calls;
266 + kernel_uint_t io_storage_bytes_read;
267 + kernel_uint_t io_storage_bytes_written;
268 + // kernel_uint_t io_cancelled_write_bytes;
269 + */
270 +
271 + struct pid_fd *fds; // array of fds it uses
272 + size_t fds_size; // the size of the fds array
273 +
274 + int children_count; // number of processes directly referencing this
275 + unsigned char keep:1; // 1 when we need to keep this process in memory even after it exited
276 + int keeploops; // increases by 1 every time keep is 1 and updated 0
277 + unsigned char updated:1; // 1 when the process is currently running
278 + unsigned char updated_twice:1; // 1 when the process was running in the previous iteration
279 + unsigned char merged:1; // 1 when it has been merged to its parent
280 + unsigned char read:1; // 1 when we have already read this process for this iteration
281 +
282 + int sortlist; // higher numbers = top on the process tree
283 +
284 + // each process gets a unique number
285 +
286 + struct target *target; // app_groups.conf targets
287 + struct target *user_target; // uid based targets
288 + struct target *group_target; // gid based targets
289 +
290 + usec_t stat_collected_usec;
291 + usec_t last_stat_collected_usec;
292 +
293 + usec_t io_collected_usec;
294 + usec_t last_io_collected_usec;
295 +
296 + kernel_uint_t uptime;
297 +
298 + char *fds_dirname; // the full directory name in /proc/PID/fd
299 +
300 + char *stat_filename;
301 + char *status_filename;
302 + char *io_filename;
303 + char *cmdline_filename;
304 +
305 + struct pid_stat *parent;
306 + struct pid_stat *prev;
307 + struct pid_stat *next;
308 +};
309 +
310 +// ----------------------------------------------------------------------------
311 +// target
312 +//
313 +// target is the structure that processes are aggregated to be reported
314 +// to netdata.
315 +//
316 +// - Each entry in /etc/apps_groups.conf creates a target.
317 +// - Each user and group used by a process in the system, creates a target.
318 +struct pid_on_target {
319 + int32_t pid;
320 + struct pid_on_target *next;
321 +};
322 +
323 +// ----------------------------------------------------------------------------
324 +// Structures used to read information from kernel ring
325 +typedef struct ebpf_process_stat {
326 + uint64_t pid_tgid;
327 + uint32_t pid;
328 +
329 + //Counter
330 + uint32_t open_call;
331 + uint32_t write_call;
332 + uint32_t writev_call;
333 + uint32_t read_call;
334 + uint32_t readv_call;
335 + uint32_t unlink_call;
336 + uint32_t exit_call;
337 + uint32_t release_call;
338 + uint32_t fork_call;
339 + uint32_t clone_call;
340 + uint32_t close_call;
341 +
342 + //Accumulator
343 + uint64_t write_bytes;
344 + uint64_t writev_bytes;
345 + uint64_t readv_bytes;
346 + uint64_t read_bytes;
347 +
348 + //Counter
349 + uint32_t open_err;
350 + uint32_t write_err;
351 + uint32_t writev_err;
352 + uint32_t read_err;
353 + uint32_t readv_err;
354 + uint32_t unlink_err;
355 + uint32_t fork_err;
356 + uint32_t clone_err;
357 + uint32_t close_err;
358 +
359 + uint8_t removeme;
360 +} ebpf_process_stat_t;
361 +
362 +typedef struct ebpf_bandwidth {
363 + uint32_t pid;
364 +
365 + uint64_t first; //First timestamp
366 + uint64_t ct; //Last timestamp
367 + uint64_t sent; //Bytes sent
368 + uint64_t received; //Bytes received
369 + unsigned char removed; //Remove the PID from table
370 +} ebpf_bandwidth_t;
371 +
372 +/**
373 + * Internal function used to write debug messages.
374 + *
375 + * @param fmt the format to create the message.
376 + * @param ... the arguments to fill the format.
377 + */
378 +static inline void debug_log_int(const char *fmt, ... ) {
379 + va_list args;
380 +
381 + fprintf( stderr, "apps.plugin: ");
382 + va_start( args, fmt );
383 + vfprintf( stderr, fmt, args );
384 + va_end( args );
385 +
386 + fputc('\n', stderr);
387 +}
388 +
389 +// ----------------------------------------------------------------------------
390 +// Exported variabled and functions
391 +//
392 +extern struct pid_stat **all_pids;
393 +
394 +
395 +extern int ebpf_read_apps_groups_conf(struct target **apps_groups_default_target,
396 + struct target **apps_groups_root_target, const char *path, const char *file);
397 +
398 +extern void clean_apps_groups_target(struct target *apps_groups_root_target);
399 +
400 +extern size_t zero_all_targets(struct target *root);
401 +
402 +extern int am_i_running_as_root();
403 +
404 +extern void cleanup_exited_pids(ebpf_process_stat_t **out);
405 +
406 +#ifndef STATIC
407 +extern int ebpf_read_hash_table(void *ep, int fd, uint32_t pid,
408 + int (*bpf_map_lookup_elem)(int, const void *, void *));
409 +
410 +extern size_t read_processes_statistic_using_pid_on_target(ebpf_process_stat_t **ep, int fd, ebpf_functions_t *ef,
411 + struct pid_on_target *pids);
412 +
413 +extern size_t read_bandwidth_statistic_using_pid_on_target(ebpf_bandwidth_t **ep, int fd,
414 + ebpf_functions_t *ef, struct pid_on_target *pids);
415 +
416 +extern void collect_data_for_all_processes(ebpf_process_stat_t **out,
417 + pid_t *index,
418 + int (*bpf_map_lookup_elem)(int, const void *, void *),
419 + int tbl_pid_stats_fd);
420 +
421 +#else
422 +extern int ebpf_read_hash_table(void *ep, int fd, pid_t pid);
423 +
424 +extern size_t read_processes_statistic_using_pid_on_target(ebpf_process_stat_t **ep, int fd,struct pid_on_target *pids);
425 +
426 +extern void collect_data_for_all_processes(ebpf_process_stat_t **out,
427 + pid_t *index,
428 + int tbl_pid_stats_fd);
429 +
430 +#endif
431 +
432 +#endif
collectors/ebpf.plugin/ebpf_process.c
+687 -144
@@ -23,13 +23,20 @@ static netdata_publish_syscall_t *process_publish_aggregated = NULL;
23
24 static ebpf_functions_t process_functions;
25
26 +static ebpf_process_stat_t **local_process_stats = NULL;
27 +static ebpf_process_publish_apps_t **current_apps_data = NULL;
28 +static ebpf_process_publish_apps_t **prev_apps_data = NULL;
29 +
30 +int process_enabled = 0;
31 +
32 #ifndef STATIC
33 /**
34 * Pointers used when collector is dynamically linked
35 */
36
37 //Libbpf (It is necessary to have at least kernel 4.10)
32 -static int (*bpf_map_lookup_elem)(int, const void *, void *);
38 +static int (*bpf_map_lookup_elem)(int, const void *, void *) = NULL;
39 +static int (*bpf_map_delete_elem)(int fd, const void *key) = NULL;
40
41 static int *map_fd = NULL;
42 /**
@@ -50,7 +57,7 @@ static int *map_fd = NULL;
57 * @param pvc the second output structure with correlated dimensions
58 * @param input the structure with the input data.
59 */
53 -static void ebpf_update_publish(netdata_publish_syscall_t *publish,
60 +static void ebpf_update_global_publish(netdata_publish_syscall_t *publish,
61 netdata_publish_vfs_common_t *pvc,
62 netdata_syscall_stat_t *input) {
63
@@ -89,6 +96,34 @@ static void ebpf_update_publish(netdata_publish_syscall_t *publish,
96 pvc->zombie = (long)publish[5].ncall + (long)publish[6].ncall;
97 }
98
99 +/**
100 + * Update apps dimension to publish.
101 + *
102 + * @param curr Last values read from memory.
103 + * @param prev Previous values read from memory.
104 + * @param first was it allocated now?
105 + */
106 +static void ebpf_process_update_apps_publish(ebpf_process_publish_apps_t *curr,
107 + ebpf_process_publish_apps_t *prev,int first)
108 +{
109 + if (first)
110 + return;
111 +
112 + curr->publish_open = curr->call_sys_open - prev->call_sys_open;
113 + curr->publish_closed = curr->call_close_fd - prev->call_close_fd;
114 + curr->publish_deleted = curr->call_vfs_unlink - prev->call_vfs_unlink;
115 + curr->publish_write_call = curr->call_write - prev->call_write;
116 + curr->publish_write_bytes = curr->bytes_written - prev->bytes_written;
117 + curr->publish_read_call = curr->call_read - prev->call_read;
118 + curr->publish_read_bytes = curr->bytes_read - prev->bytes_read;
119 + curr->publish_process = curr->call_do_fork - prev->call_do_fork;
120 + curr->publish_thread = curr->call_sys_clone - prev->call_sys_clone;
121 + curr->publish_task = curr->call_release_task - prev->call_release_task;
122 + curr->publish_open_error = curr->ecall_sys_open - prev->ecall_sys_open;
123 + curr->publish_close_error = curr->ecall_close_fd - prev->ecall_close_fd;
124 + curr->publish_write_error = curr->ecall_write - prev->ecall_write;
125 + curr->publish_read_error = curr->ecall_read - prev->ecall_read;
126 +}
127
128 /**
129 * Call the necessary functions to create a chart.
@@ -102,7 +137,7 @@ static void write_status_chart(char *family, netdata_publish_vfs_common_t *pvc)
137 write_chart_dimension(status[0], (long long) pvc->running);
138 write_chart_dimension(status[1], (long long) pvc->zombie);
139
105 - printf("END\n");
140 + write_end_chart();
141 }
142
143 /**
@@ -112,25 +147,28 @@ static void write_status_chart(char *family, netdata_publish_vfs_common_t *pvc)
147 */
148 static void ebpf_process_send_data(ebpf_module_t *em) {
149 netdata_publish_vfs_common_t pvc;
115 - ebpf_update_publish(process_publish_aggregated, &pvc, process_aggregated_data);
150 + ebpf_update_global_publish(process_publish_aggregated, &pvc, process_aggregated_data);
151
152 write_count_chart(NETDATA_FILE_OPEN_CLOSE_COUNT, NETDATA_EBPF_FAMILY, process_publish_aggregated, 2);
153 +
154 write_count_chart(NETDATA_VFS_FILE_CLEAN_COUNT,
119 - NETDATA_EBPF_FAMILY,
120 - &process_publish_aggregated[NETDATA_DEL_START],
121 - 1);
155 + NETDATA_EBPF_FAMILY,
156 + &process_publish_aggregated[NETDATA_DEL_START],
157 + 1);
158 +
159 write_count_chart(NETDATA_VFS_FILE_IO_COUNT,
123 - NETDATA_EBPF_FAMILY,
124 - &process_publish_aggregated[NETDATA_IN_START_BYTE],
125 - 2);
160 + NETDATA_EBPF_FAMILY,
161 + &process_publish_aggregated[NETDATA_IN_START_BYTE],
162 + 2);
163 +
164 write_count_chart(NETDATA_EXIT_SYSCALL,
127 - NETDATA_EBPF_FAMILY,
128 - &process_publish_aggregated[NETDATA_EXIT_START],
129 - 2);
165 + NETDATA_EBPF_FAMILY,
166 + &process_publish_aggregated[NETDATA_EXIT_START],
167 + 2);
168 write_count_chart(NETDATA_PROCESS_SYSCALL,
131 - NETDATA_EBPF_FAMILY,
132 - &process_publish_aggregated[NETDATA_PROCESS_START],
133 - 2);
169 + NETDATA_EBPF_FAMILY,
170 + &process_publish_aggregated[NETDATA_PROCESS_START],
171 + 2);
172
173 write_status_chart(NETDATA_EBPF_FAMILY, &pvc);
174 if(em->mode < MODE_ENTRY) {
@@ -143,10 +181,207 @@ static void ebpf_process_send_data(ebpf_module_t *em) {
181 NETDATA_EBPF_FAMILY,
182 &process_publish_aggregated[NETDATA_PROCESS_START],
183 2);
184 + }
185 +
186 + write_io_chart(NETDATA_VFS_IO_FILE_BYTES, NETDATA_EBPF_FAMILY, process_id_names[3],
187 + process_id_names[4], &pvc);
188 +}
189 +
190 +/**
191 + * Sum values for pid
192 + *
193 + * @param root the structure with all available PIDs
194 + *
195 + * @param offset the address that we are reading
196 + *
197 + * @return it returns the sum of all PIDs
198 + */
199 +long long ebpf_process_sum_values_for_pids(struct pid_on_target *root, size_t offset)
200 +{
201 + long long ret = 0;
202 + while (root) {
203 + int32_t pid = root->pid;
204 + ebpf_process_publish_apps_t *w = current_apps_data[pid];
205 + if (w) {
206 + ret += get_value_from_structure((char *)w, offset);
207 + }
208 +
209 + root = root->next;
210 + }
211 +
212 + return ret;
213 +}
214 +
215 +/**
216 + * Remove process pid
217 + *
218 + * Remove from PID task table when task_release was called.
219 + */
220 +void ebpf_process_remove_pids()
221 +{
222 + struct pid_stat *pids = root_of_pids;
223 + int pid_fd = map_fd[0];
224 + while (pids) {
225 + uint32_t pid = pids->pid;
226 + ebpf_process_stat_t *w = local_process_stats[pid];
227 + if (w) {
228 + if (w->removeme) {
229 + freez(w);
230 + local_process_stats[pid] = NULL;
231 + bpf_map_delete_elem(pid_fd, &pid);
232 + }
233 + }
234 +
235 + pids = pids->next;
236 + }
237 +}
238 +
239 +/**
240 + * Send data to Netdata calling auxiliar functions.
241 + *
242 + * @param em the structure with thread information
243 + * @param root the target list.
244 + */
245 +void ebpf_process_send_apps_data(ebpf_module_t *em, struct target *root)
246 +{
247 + struct target *w;
248 + collected_number value;
249 +
250 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_FILE_OPEN);
251 + for (w = root; w ; w = w->next) {
252 + if (unlikely(w->exposed && w->processes)) {
253 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_open));
254 + write_chart_dimension(w->name, value);
255 + }
256 + }
257 + write_end_chart();
258 +
259 + if (em->mode < MODE_ENTRY) {
260 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_FILE_OPEN_ERROR);
261 + for (w = root; w ; w = w->next) {
262 + if (unlikely(w->exposed && w->processes)) {
263 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_open_error));
264 + write_chart_dimension(w->name, value);
265 + }
266 + }
267 + write_end_chart();
268 + }
269 +
270 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_FILE_CLOSED);
271 + for (w = root; w ; w = w->next) {
272 + if (unlikely(w->exposed && w->processes)) {
273 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_closed));
274 + write_chart_dimension(w->name, value);
275 + }
276 + }
277 + write_end_chart();
278 +
279 + if (em->mode < MODE_ENTRY) {
280 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR);
281 + for (w = root; w ; w = w->next) {
282 + if (unlikely(w->exposed && w->processes)) {
283 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_close_error));
284 + write_chart_dimension(w->name, value);
285 + }
286 + }
287 + write_end_chart();
288 + }
289 +
290 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_FILE_DELETED);
291 + for (w = root; w ; w = w->next) {
292 + if (unlikely(w->exposed && w->processes)) {
293 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_deleted));
294 + write_chart_dimension(w->name, value);
295 + }
296 + }
297 + write_end_chart();
298 +
299 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_VFS_WRITE_CALLS);
300 + for (w = root; w ; w = w->next) {
301 + if (unlikely(w->exposed && w->processes)) {
302 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_write_call));
303 + write_chart_dimension(w->name, value);
304 + }
305 + }
306 + write_end_chart();
307 +
308 + if (em->mode < MODE_ENTRY) {
309 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_VFS_WRITE_CALLS_ERROR);
310 + for (w = root; w ; w = w->next) {
311 + if (unlikely(w->exposed && w->processes)) {
312 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_write_error));
313 + write_chart_dimension(w->name, value);
314 + }
315 + }
316 + write_end_chart();
317 + }
318 +
319 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_VFS_READ_CALLS);
320 + for (w = root; w ; w = w->next) {
321 + if (unlikely(w->exposed && w->processes)) {
322 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_read_call));
323 + write_chart_dimension(w->name, value);
324 + }
325 + }
326 + write_end_chart();
327 +
328 + if (em->mode < MODE_ENTRY) {
329 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_VFS_READ_CALLS_ERROR);
330 + for (w = root; w ; w = w->next) {
331 + if (unlikely(w->exposed && w->processes)) {
332 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_read_error));
333 + write_chart_dimension(w->name, value);
334 + }
335 + }
336 + write_end_chart();
337 + }
338 +
339 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_VFS_WRITE_BYTES);
340 + for (w = root; w ; w = w->next) {
341 + if (unlikely(w->exposed && w->processes)) {
342 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_write_bytes));
343 + write_chart_dimension(w->name, value);
344 + }
345 + }
346 + write_end_chart();
347 +
348 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_VFS_READ_BYTES);
349 + for (w = root; w ; w = w->next) {
350 + if (unlikely(w->exposed && w->processes)) {
351 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_read_bytes));
352 + write_chart_dimension(w->name, value);
353 + }
354 + }
355 + write_end_chart();
356 +
357 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_TASK_PROCESS);
358 + for (w = root; w ; w = w->next) {
359 + if (unlikely(w->exposed && w->processes)) {
360 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_process));
361 + write_chart_dimension(w->name, value);
362 + }
363 + }
364 + write_end_chart();
365 +
366 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_TASK_THREAD);
367 + for (w = root; w ; w = w->next) {
368 + if (unlikely(w->exposed && w->processes)) {
369 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_thread));
370 + write_chart_dimension(w->name, value);
371 + }
372 + }
373 + write_end_chart();
374
147 - write_io_chart(NETDATA_VFS_IO_FILE_BYTES, NETDATA_EBPF_FAMILY, process_id_names[3],
148 - process_id_names[4], &pvc);
375 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SYSCALL_APPS_TASK_CLOSE);
376 + for (w = root; w ; w = w->next) {
377 + if (unlikely(w->exposed && w->processes)) {
378 + value = ebpf_process_sum_values_for_pids(w->root_pid, offsetof(ebpf_process_publish_apps_t, publish_task));
379 + write_chart_dimension(w->name, value);
380 + }
381 }
382 + write_end_chart();
383 +
384 + ebpf_process_remove_pids();
385 }
386
387 /*****************************************************************
@@ -202,34 +437,58 @@ static void read_hash_global_tables()
437 (uint64_t)res[NETDATA_KEY_BYTES_VFS_READV];
438 }
439
205 -/*****************************************************************
206 - *
207 - * FUNCTIONS WITH THE MAIN LOOP
208 - *
209 - *****************************************************************/
210 -
211 -
440 /**
213 - * Main loop for this collector.
214 - *
215 - * @param step the number of microseconds used with heart beat
216 - * @param em the structure with thread information
441 + * Read the hash table and store data to allocated vectors.
442 */
218 -static void process_collector(usec_t step, ebpf_module_t *em)
443 +static void ebpf_process_update_apps_data()
444 {
220 - heartbeat_t hb;
221 - heartbeat_init(&hb);
222 - while(!close_ebpf_plugin) {
223 - usec_t dt = heartbeat_next(&hb, step);
224 - (void)dt;
225 -
226 - read_hash_global_tables();
227 -
228 - pthread_mutex_lock(&lock);
229 - ebpf_process_send_data(em);
230 - pthread_mutex_unlock(&lock);
445 + size_t i;
446 + for ( i = 0 ; i < all_pids_count; i++) {
447 + uint32_t current_pid = pid_index[i];
448 + ebpf_process_stat_t *ps = local_process_stats[current_pid];
449 + if (!ps)
450 + continue;
451 +
452 + ebpf_process_publish_apps_t *cad = current_apps_data[current_pid];
453 + ebpf_process_publish_apps_t *pad = prev_apps_data[current_pid];
454 + int lstatus;
455 + if (!cad) {
456 + ebpf_process_publish_apps_t *ptr = callocz(2, sizeof(ebpf_process_publish_apps_t));
457 + cad = &ptr[0];
458 + current_apps_data[current_pid] = cad;
459 + pad = &ptr[1];
460 + prev_apps_data[current_pid] = pad;
461 + lstatus = 1;
462 + } else {
463 + memcpy(pad, cad, sizeof(ebpf_process_publish_apps_t));
464 + lstatus = 0;
465 + }
466
232 - fflush(stdout);
467 + //Read data
468 + cad->call_sys_open = ps->open_call;
469 + cad->call_close_fd = ps->close_call;
470 + cad->call_vfs_unlink = ps->unlink_call;
471 + cad->call_read = ps->read_call + ps->readv_call;
472 + cad->call_write = ps->write_call + ps->writev_call;
473 + cad->call_do_exit = ps->exit_call;
474 + cad->call_release_task = ps->release_call;
475 + cad->call_do_fork = ps->fork_call;
476 + cad->call_sys_clone = ps->clone_call;
477 +
478 + cad->ecall_sys_open = ps->open_err;
479 + cad->ecall_close_fd = ps->close_err;
480 + cad->ecall_vfs_unlink = ps->unlink_err;
481 + cad->ecall_read = ps->read_err + ps->readv_err;
482 + cad->ecall_write = ps->write_err + ps->writev_err;
483 + cad->ecall_do_fork = ps->fork_err;
484 + cad->ecall_sys_clone = ps->clone_err;
485 +
486 + cad->bytes_written = (uint64_t)ps->write_bytes +
487 + (uint64_t)ps->write_bytes;
488 + cad->bytes_read = (uint64_t)ps->read_bytes +
489 + (uint64_t)ps->readv_bytes;
490 +
491 + ebpf_process_update_apps_publish(cad, pad, lstatus);
492 }
493 }
494
@@ -249,12 +508,13 @@ static void process_collector(usec_t step, ebpf_module_t *em)
508 * @param order the order number of the specified chart
509 */
510 static void ebpf_create_io_chart(char *family, char *name, char *axis, char *web, int order) {
252 - printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
253 - , family
254 - , name
255 - , axis
256 - , web
257 - , order);
511 + printf("CHART %s.%s '' 'Bytes written and read' '%s' '%s' '' line %d %d\n",
512 + family,
513 + name,
514 + axis,
515 + web,
516 + order,
517 + update_every);
518
519 printf("DIMENSION %s %s absolute 1 1\n", process_id_names[3], NETDATA_VFS_DIM_OUT_FILE_BYTES);
520 printf("DIMENSION %s %s absolute 1 1\n", process_id_names[4], NETDATA_VFS_DIM_IN_FILE_BYTES);
@@ -270,12 +530,13 @@ static void ebpf_create_io_chart(char *family, char *name, char *axis, char *web
530 * @param order the order number of the specified chart
531 */
532 static void ebpf_process_status_chart(char *family, char *name, char *axis, char *web, int order) {
273 - printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
274 - , family
275 - , name
276 - , axis
277 - , web
278 - , order);
533 + printf("CHART %s.%s '' 'Process not closed' '%s' '%s' '' line %d %d ''\n",
534 + family,
535 + name,
536 + axis,
537 + web,
538 + order,
539 + update_every);
540
541 printf("DIMENSION %s '' absolute 1 1\n", status[0]);
542 printf("DIMENSION %s '' absolute 1 1\n", status[1]);
@@ -288,98 +549,325 @@ static void ebpf_process_status_chart(char *family, char *name, char *axis, char
549 *
550 * @param em a pointer to the structure with the default values.
551 */
291 -static void ebpf_create_global_charts(ebpf_module_t *em) {
292 - ebpf_create_chart(NETDATA_EBPF_FAMILY
293 - , NETDATA_FILE_OPEN_CLOSE_COUNT
294 - , "Calls"
295 - , NETDATA_FILE_GROUP
296 - , 970
297 - , ebpf_create_global_dimension
298 - , process_publish_aggregated
299 - , 2);
552 +static void ebpf_create_global_charts(ebpf_module_t *em)
553 +{
554 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
555 + NETDATA_FILE_OPEN_CLOSE_COUNT,
556 + "Open and close calls",
557 + EBPF_COMMON_DIMENSION_CALL,
558 + NETDATA_FILE_GROUP,
559 + 21000,
560 + ebpf_create_global_dimension,
561 + process_publish_aggregated,
562 + 2);
563
564 if (em->mode < MODE_ENTRY) {
302 - ebpf_create_chart(NETDATA_EBPF_FAMILY
303 - , NETDATA_FILE_OPEN_ERR_COUNT
304 - , "Calls"
305 - , NETDATA_FILE_GROUP
306 - , 971
307 - , ebpf_create_global_dimension
308 - , process_publish_aggregated
309 - , 2);
310 - }
311 -
312 - ebpf_create_chart(NETDATA_EBPF_FAMILY
313 - , NETDATA_VFS_FILE_CLEAN_COUNT
314 - , "Calls"
315 - , NETDATA_VFS_GROUP
316 - , 972
317 - , ebpf_create_global_dimension
318 - , &process_publish_aggregated[NETDATA_DEL_START]
319 - , 1);
320 -
321 - ebpf_create_chart(NETDATA_EBPF_FAMILY
322 - , NETDATA_VFS_FILE_IO_COUNT
323 - , "Calls"
324 - , NETDATA_VFS_GROUP
325 - , 973
326 - , ebpf_create_global_dimension
327 - , &process_publish_aggregated[NETDATA_IN_START_BYTE]
328 - , 2);
565 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
566 + NETDATA_FILE_OPEN_ERR_COUNT,
567 + "Open fails",
568 + EBPF_COMMON_DIMENSION_CALL,
569 + NETDATA_FILE_GROUP,
570 + 21001,
571 + ebpf_create_global_dimension,
572 + process_publish_aggregated,
573 + 2);
574 + }
575 +
576 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
577 + NETDATA_VFS_FILE_CLEAN_COUNT,
578 + "Remove files",
579 + EBPF_COMMON_DIMENSION_CALL,
580 + NETDATA_VFS_GROUP,
581 + 21002,
582 + ebpf_create_global_dimension,
583 + &process_publish_aggregated[NETDATA_DEL_START],
584 + 1);
585 +
586 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
587 + NETDATA_VFS_FILE_IO_COUNT,
588 + "Calls to IO",
589 + EBPF_COMMON_DIMENSION_CALL,
590 + NETDATA_VFS_GROUP,
591 + 21003,
592 + ebpf_create_global_dimension,
593 + &process_publish_aggregated[NETDATA_IN_START_BYTE],
594 + 2);
595 +
596 + ebpf_create_io_chart(NETDATA_EBPF_FAMILY,
597 + NETDATA_VFS_IO_FILE_BYTES,
598 + EBPF_COMMON_DIMENSION_BYTESS,
599 + NETDATA_VFS_GROUP,
600 + 21004);
601 +
602 + if (em->mode < MODE_ENTRY) {
603 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
604 + NETDATA_VFS_FILE_ERR_COUNT,
605 + "Fails to write or read",
606 + EBPF_COMMON_DIMENSION_CALL,
607 + NETDATA_VFS_GROUP,
608 + 21005,
609 + ebpf_create_global_dimension,
610 + &process_publish_aggregated[2],
611 + NETDATA_VFS_ERRORS);
612 + }
613 +
614 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
615 + NETDATA_PROCESS_SYSCALL,
616 + "Start process",
617 + EBPF_COMMON_DIMENSION_CALL,
618 + NETDATA_PROCESS_GROUP,
619 + 21006,
620 + ebpf_create_global_dimension,
621 + &process_publish_aggregated[NETDATA_PROCESS_START],
622 + 2);
623 +
624 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
625 + NETDATA_EXIT_SYSCALL,
626 + "Exit process",
627 + EBPF_COMMON_DIMENSION_CALL,
628 + NETDATA_PROCESS_GROUP,
629 + 21007,
630 + ebpf_create_global_dimension,
631 + &process_publish_aggregated[NETDATA_EXIT_START],
632 + 2);
633 +
634 + ebpf_process_status_chart(NETDATA_EBPF_FAMILY,
635 + NETDATA_PROCESS_STATUS_NAME,
636 + EBPF_COMMON_DIMENSION_DIFFERENCE,
637 + NETDATA_PROCESS_GROUP,
638 + 21008);
639
640 if (em->mode < MODE_ENTRY) {
331 - ebpf_create_io_chart(NETDATA_EBPF_FAMILY
332 - , NETDATA_VFS_IO_FILE_BYTES
333 - , "bytes/s"
334 - , NETDATA_VFS_GROUP
335 - , 974);
336 -
337 - ebpf_create_chart(NETDATA_EBPF_FAMILY
338 - , NETDATA_VFS_FILE_ERR_COUNT
339 - , "Calls"
340 - , NETDATA_VFS_GROUP
341 - , 975
342 - , ebpf_create_global_dimension
343 - , &process_publish_aggregated[2]
344 - , NETDATA_VFS_ERRORS);
345 -
346 - }
347 -
348 - ebpf_create_chart(NETDATA_EBPF_FAMILY
349 - , NETDATA_PROCESS_SYSCALL
350 - , "Calls"
351 - , NETDATA_PROCESS_GROUP
352 - , 976
353 - , ebpf_create_global_dimension
354 - , &process_publish_aggregated[NETDATA_PROCESS_START]
355 - , 2);
356 -
357 - ebpf_create_chart(NETDATA_EBPF_FAMILY
358 - , NETDATA_EXIT_SYSCALL
359 - , "Calls"
360 - , NETDATA_PROCESS_GROUP
361 - , 977
362 - , ebpf_create_global_dimension
363 - , &process_publish_aggregated[NETDATA_EXIT_START]
364 - , 2);
365 -
366 - ebpf_process_status_chart(NETDATA_EBPF_FAMILY
367 - , NETDATA_PROCESS_STATUS_NAME
368 - , "Total"
369 - , NETDATA_PROCESS_GROUP
370 - , 978);
641 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
642 + NETDATA_PROCESS_ERROR_NAME,
643 + "Fails to create process",
644 + EBPF_COMMON_DIMENSION_CALL,
645 + NETDATA_PROCESS_GROUP,
646 + 21009,
647 + ebpf_create_global_dimension,
648 + &process_publish_aggregated[NETDATA_PROCESS_START],
649 + 2);
650 + }
651 +
652 +}
653 +
654 +/**
655 + * Create process apps charts
656 + *
657 + * Call ebpf_create_chart to create the charts on apps submenu.
658 + *
659 + * @param em a pointer to the structure with the default values.
660 + * @param root a pointer for the targets.
661 + */
662 +static void ebpf_process_create_apps_charts(ebpf_module_t *em, struct target *root)
663 +{
664 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_FILE_OPEN,
665 + "Number of open files",
666 + EBPF_COMMON_DIMENSION_CALL,
667 + NETDATA_APPS_SYSCALL_GROUP,
668 + 20061,
669 + root);
670
671 if (em->mode < MODE_ENTRY) {
373 - ebpf_create_chart(NETDATA_EBPF_FAMILY
374 - , NETDATA_PROCESS_ERROR_NAME
375 - , "Calls"
376 - , NETDATA_PROCESS_GROUP
377 - , 979
378 - , ebpf_create_global_dimension
379 - , &process_publish_aggregated[NETDATA_PROCESS_START]
380 - , 2);
672 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_FILE_OPEN_ERROR,
673 + "Fails to open files",
674 + EBPF_COMMON_DIMENSION_CALL,
675 + NETDATA_APPS_SYSCALL_GROUP,
676 + 20062,
677 + root);
678 + }
679 +
680 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_FILE_CLOSED,
681 + "Files closed",
682 + EBPF_COMMON_DIMENSION_CALL,
683 + NETDATA_APPS_SYSCALL_GROUP,
684 + 20063,
685 + root);
686 +
687 + if (em->mode < MODE_ENTRY) {
688 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR,
689 + "Fails to close files",
690 + EBPF_COMMON_DIMENSION_CALL,
691 + NETDATA_APPS_SYSCALL_GROUP,
692 + 20064,
693 + root);
694 + }
695 +
696 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_FILE_DELETED,
697 + "Files deleted",
698 + EBPF_COMMON_DIMENSION_CALL,
699 + NETDATA_APPS_SYSCALL_GROUP,
700 + 20065,
701 + root);
702 +
703 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_VFS_WRITE_CALLS,
704 + "Write to disk",
705 + EBPF_COMMON_DIMENSION_CALL,
706 + NETDATA_APPS_SYSCALL_GROUP,
707 + 20066,
708 + apps_groups_root_target);
709 +
710 + if (em->mode < MODE_ENTRY) {
711 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_VFS_WRITE_CALLS_ERROR,
712 + "Fails to write",
713 + EBPF_COMMON_DIMENSION_CALL,
714 + NETDATA_APPS_SYSCALL_GROUP,
715 + 20067,
716 + root);
717 + }
718 +
719 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_VFS_READ_CALLS,
720 + "Read from disk",
721 + EBPF_COMMON_DIMENSION_CALL,
722 + NETDATA_APPS_SYSCALL_GROUP,
723 + 20068,
724 + root);
725 +
726 + if (em->mode < MODE_ENTRY) {
727 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_VFS_READ_CALLS_ERROR,
728 + "Fails to read",
729 + EBPF_COMMON_DIMENSION_CALL,
730 + NETDATA_APPS_SYSCALL_GROUP,
731 + 20069,
732 + root);
733 + }
734 +
735 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_VFS_WRITE_BYTES,
736 + "Bytes written on disk",
737 + EBPF_COMMON_DIMENSION_BYTESS,
738 + NETDATA_APPS_SYSCALL_GROUP,
739 + 20070,
740 + root);
741 +
742 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_VFS_READ_BYTES,
743 + "Bytes read from disk",
744 + EBPF_COMMON_DIMENSION_BYTESS,
745 + NETDATA_APPS_SYSCALL_GROUP,
746 + 20071,
747 + root);
748 +
749 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_TASK_PROCESS,
750 + "Process started",
751 + EBPF_COMMON_DIMENSION_CALL,
752 + NETDATA_APPS_SYSCALL_GROUP,
753 + 20072,
754 + root);
755 +
756 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_TASK_THREAD,
757 + "Threads started",
758 + EBPF_COMMON_DIMENSION_CALL,
759 + NETDATA_APPS_SYSCALL_GROUP,
760 + 20073,
761 + root);
762 +
763 + ebpf_create_charts_on_apps(NETDATA_SYSCALL_APPS_TASK_CLOSE,
764 + "Tasks closed",
765 + EBPF_COMMON_DIMENSION_CALL,
766 + NETDATA_APPS_SYSCALL_GROUP,
767 + 20074,
768 + root);
769 +}
770 +
771 +/**
772 + * Create apps charts
773 + *
774 + * Call ebpf_create_chart to create the charts on apps submenu.
775 + *
776 + * @param em a pointer to the structure with the default values.
777 + * @param root a pointer for the targets.
778 + */
779 +static void ebpf_create_apps_charts(ebpf_module_t *em, struct target *root)
780 +{
781 + struct target *w;
782 + int newly_added = 0;
783 +
784 + for(w = root ; w ; w = w->next) {
785 + if (w->target) continue;
786 +
787 + if(unlikely(w->processes && (debug_enabled || w->debug_enabled))) {
788 + struct pid_on_target *pid_on_target;
789 +
790 + fprintf(stderr, "ebpf.plugin: target '%s' has aggregated %u process%s:", w->name, w->processes, (w->processes == 1)?"":"es");
791 +
792 + for(pid_on_target = w->root_pid; pid_on_target; pid_on_target = pid_on_target->next) {
793 + fprintf(stderr, " %d", pid_on_target->pid);
794 + }
795 +
796 + fputc('\n', stderr);
797 + }
798 +
799 + if (!w->exposed && w->processes) {
800 + newly_added++;
801 + w->exposed = 1;
802 + if (debug_enabled || w->debug_enabled)
803 + debug_log_int("%s just added - regenerating charts.", w->name);
804 + }
805 }
806
807 + if (!newly_added) return;
808 +
809 + if (ebpf_modules[EBPF_MODULE_PROCESS_IDX].apps_charts)
810 + ebpf_process_create_apps_charts(em, root);
811 +
812 + if (ebpf_modules[EBPF_MODULE_SOCKET_IDX].apps_charts)
813 + ebpf_socket_create_apps_charts(NULL, root);
814 +}
815 +
816 +/*****************************************************************
817 + *
818 + * FUNCTIONS WITH THE MAIN LOOP
819 + *
820 + *****************************************************************/
821 +
822 +/**
823 + * Main loop for this collector.
824 + *
825 + * @param step the number of microseconds used with heart beat
826 + * @param em the structure with thread information
827 + */
828 +static void process_collector(usec_t step, ebpf_module_t *em)
829 +{
830 + heartbeat_t hb;
831 + heartbeat_init(&hb);
832 + int publish_global = em->global_charts;
833 + int apps_enabled = em->apps_charts;
834 + int pid_fd = map_fd[0];
835 + while (!close_ebpf_plugin) {
836 + usec_t dt = heartbeat_next(&hb, step);
837 + (void)dt;
838 +
839 + read_hash_global_tables();
840 +
841 + pthread_mutex_lock(&collect_data_mutex);
842 + cleanup_exited_pids(local_process_stats);
843 + collect_data_for_all_processes(local_process_stats,
844 + pid_index,
845 + process_functions.bpf_map_lookup_elem,
846 + pid_fd);
847 +
848 + ebpf_create_apps_charts(em, apps_groups_root_target);
849 +
850 + pthread_cond_broadcast(&collect_data_cond_var);
851 + pthread_mutex_unlock(&collect_data_mutex);
852 +
853 + int publish_apps = 0;
854 + if (apps_enabled && all_pids_count > 0){
855 + publish_apps = 1;
856 + ebpf_process_update_apps_data();
857 + }
858 +
859 + pthread_mutex_lock(&lock);
860 + if (publish_global) {
861 + ebpf_process_send_data(em);
862 + }
863 +
864 + if (publish_apps) {
865 + ebpf_process_send_apps_data(em, apps_groups_root_target);
866 + }
867 + pthread_mutex_unlock(&lock);
868 +
869 + fflush(stdout);
870 + }
871 }
872
873 /*****************************************************************
@@ -388,6 +876,18 @@ static void ebpf_create_global_charts(ebpf_module_t *em) {
876 *
877 *****************************************************************/
878
879 +/**
880 + * Clean the allocated process stat structure
881 + */
882 +static void clean_process_stat()
883 +{
884 + size_t i;
885 + for (i = 0 ; i < all_pids_count ; i++) {
886 + ebpf_process_stat_t *w = local_process_stats[pid_index[i]];
887 + freez(w);
888 + }
889 +}
890 +
891 /**
892 * Clean up the main thread.
893 *
@@ -396,15 +896,21 @@ static void ebpf_create_global_charts(ebpf_module_t *em) {
896 static void ebpf_process_cleanup(void *ptr)
897 {
898 (void)ptr;
899 +
900 freez(process_aggregated_data);
901 freez(process_publish_aggregated);
902 freez(process_hash_values);
903
904 + clean_process_stat();
905 + freez(local_process_stats);
906 +
907 if (process_functions.libnetdata) {
908 dlclose(process_functions.libnetdata);
909 }
910
911 freez(process_functions.map_fd);
912 + freez(current_apps_data);
913 + freez(prev_apps_data);
914 }
915
916 /*****************************************************************
@@ -424,12 +930,16 @@ static void ebpf_process_allocate_global_vectors(size_t length) {
930 process_aggregated_data = callocz(length, sizeof(netdata_syscall_stat_t));
931 process_publish_aggregated = callocz(length, sizeof(netdata_publish_syscall_t));
932 process_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
933 +
934 + local_process_stats = callocz((size_t)pid_max, sizeof(ebpf_process_stat_t *));
935 + current_apps_data = callocz((size_t)pid_max, sizeof(ebpf_process_publish_apps_t *));
936 + prev_apps_data = callocz((size_t)pid_max, sizeof(ebpf_process_publish_apps_t *));
937 }
938
939 static void change_collector_event() {
940 int i;
941 if (running_on_kernel < NETDATA_KERNEL_V5_3)
432 - process_probes[10].name = NULL;
942 + process_probes[EBPF_SYS_CLONE_IDX].name = NULL;
943
944 for (i = 0; process_probes[i].name ; i++ ) {
945 process_probes[i].type = 'p';
@@ -443,12 +953,14 @@ static void change_syscalls() {
953 }
954
955 /**
446 - * Set local function pointers, this function will never be compiled with static libraries
956 + * Set local variables
957 + *
958 */
959 static void set_local_pointers(ebpf_module_t *em) {
960 #ifndef STATIC
961 bpf_map_lookup_elem = process_functions.bpf_map_lookup_elem;
962
963 + bpf_map_delete_elem = process_functions.bpf_map_delete_elem;
964 #endif
965
966 map_fd = process_functions.map_fd;
@@ -467,6 +979,34 @@ static void set_local_pointers(ebpf_module_t *em) {
979 *
980 *****************************************************************/
981
982 +/**
983 + *
984 + */
985 +static void wait_for_all_threads_die()
986 +{
987 + ebpf_modules[EBPF_MODULE_PROCESS_IDX].enabled = 0;
988 +
989 + heartbeat_t hb;
990 + heartbeat_init(&hb);
991 +
992 + int max = 10;
993 + int i;
994 + for (i = 0; i < max ; i++) {
995 + heartbeat_next(&hb, 200000);
996 +
997 + size_t j, counter = 0, compare = 0;
998 + for (j = 0; ebpf_modules[j].thread_name; j++) {
999 + if (!ebpf_modules[j].enabled)
1000 + counter++;
1001 +
1002 + compare++;
1003 + }
1004 +
1005 + if (counter == compare)
1006 + break;
1007 + }
1008 +}
1009 +
1010 /**
1011 * Process thread
1012 *
@@ -481,11 +1021,9 @@ void *ebpf_process_thread(void *ptr)
1021 netdata_thread_cleanup_push(ebpf_process_cleanup, ptr);
1022
1023 ebpf_module_t *em = (ebpf_module_t *)ptr;
1024 + process_enabled = em->enabled;
1025 fill_ebpf_functions(&process_functions);
1026
486 - if (!em->enabled)
487 - goto endprocess;
488 -
1027 pthread_mutex_lock(&lock);
1028 ebpf_process_allocate_global_vectors(NETDATA_MAX_MONITOR_VECTOR);
1029
@@ -504,11 +1042,16 @@ void *ebpf_process_thread(void *ptr)
1042 ebpf_global_labels(process_aggregated_data, process_publish_aggregated, process_dimension_names,
1043 process_id_names, NETDATA_MAX_MONITOR_VECTOR);
1044
507 - ebpf_create_global_charts(em);
1045 + if (process_enabled) {
1046 + ebpf_create_global_charts(em);
1047 + }
1048 +
1049 pthread_mutex_unlock(&lock);
1050 +
1051 process_collector((usec_t)(em->update_time*USEC_PER_SEC), em);
1052
1053 endprocess:
1054 + wait_for_all_threads_die();
1055 netdata_thread_cleanup_pop(1);
1056 return NULL;
1057 }
collectors/ebpf.plugin/ebpf_process.h
+65
@@ -1,19 +1,23 @@
1 #ifndef _NETDATA_EBPF_PROCESS_H_
2 # define _NETDATA_EBPF_PROCESS_H_ 1
3
4 +//Groups used on Dashboard
5 # define NETDATA_FILE_GROUP "File"
6 # define NETDATA_VFS_GROUP "VFS"
7 # define NETDATA_PROCESS_GROUP "Process"
8
9 +//Internal constants
10 # define NETDATA_GLOBAL_VECTOR 24
11 # define NETDATA_MAX_MONITOR_VECTOR 9
12 # define NETDATA_VFS_ERRORS 3
13
14 +//Map index
15 # define NETDATA_DEL_START 2
16 # define NETDATA_IN_START_BYTE 3
17 # define NETDATA_EXIT_START 5
18 # define NETDATA_PROCESS_START 7
19
20 +// Global chart name
21 # define NETDATA_FILE_OPEN_CLOSE_COUNT "file_descriptor"
22 # define NETDATA_FILE_OPEN_ERR_COUNT "file_error"
23 # define NETDATA_VFS_FILE_CLEAN_COUNT "deleted_objects"
@@ -29,6 +33,26 @@
33 # define NETDATA_VFS_DIM_IN_FILE_BYTES "write"
34 # define NETDATA_VFS_DIM_OUT_FILE_BYTES "read"
35
36 +// Charts created on Apps submenu
37 +# define NETDATA_SYSCALL_APPS_FILE_OPEN "file_open"
38 +# define NETDATA_SYSCALL_APPS_FILE_CLOSED "file_closed"
39 +# define NETDATA_SYSCALL_APPS_FILE_DELETED "file_deleted"
40 +# define NETDATA_SYSCALL_APPS_VFS_WRITE_CALLS "vfs_write_call"
41 +# define NETDATA_SYSCALL_APPS_VFS_READ_CALLS "vfs_read_call"
42 +# define NETDATA_SYSCALL_APPS_VFS_WRITE_BYTES "vfs_write_bytes"
43 +# define NETDATA_SYSCALL_APPS_VFS_READ_BYTES "vfs_read_bytes"
44 +# define NETDATA_SYSCALL_APPS_TASK_PROCESS "process_create"
45 +# define NETDATA_SYSCALL_APPS_TASK_THREAD "thread_create"
46 +# define NETDATA_SYSCALL_APPS_TASK_CLOSE "task_close"
47 +
48 +// Charts created on Apps submenu, if and only if, the return mode is active
49 +
50 +# define NETDATA_SYSCALL_APPS_FILE_OPEN_ERROR "file_open_error"
51 +# define NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR "file_close_error"
52 +# define NETDATA_SYSCALL_APPS_VFS_WRITE_CALLS_ERROR "vfs_write_error"
53 +# define NETDATA_SYSCALL_APPS_VFS_READ_CALLS_ERROR "vfs_read_error"
54 +
55 +
56 //Index from kernel
57 typedef enum ebpf_process_index {
58 NETDATA_KEY_CALLS_DO_SYS_OPEN,
@@ -68,4 +92,45 @@ typedef enum ebpf_process_index {
92
93 } ebpf_process_index_t;
94
95 +typedef struct ebpf_process_publish_apps {
96 + //Number of calls during the last read
97 + uint64_t call_sys_open;
98 + uint64_t call_close_fd;
99 + uint64_t call_vfs_unlink;
100 + uint64_t call_read;
101 + uint64_t call_write;
102 + uint64_t call_do_exit;
103 + uint64_t call_release_task;
104 + uint64_t call_do_fork;
105 + uint64_t call_sys_clone;
106 +
107 + //Number of errors during the last read
108 + uint64_t ecall_sys_open;
109 + uint64_t ecall_close_fd;
110 + uint64_t ecall_vfs_unlink;
111 + uint64_t ecall_read;
112 + uint64_t ecall_write;
113 + uint64_t ecall_do_fork;
114 + uint64_t ecall_sys_clone;
115 +
116 + //Number of bytes during the last read
117 + uint64_t bytes_written;
118 + uint64_t bytes_read;
119 +
120 + //Dimensions sent to chart
121 + uint64_t publish_open ;
122 + uint64_t publish_closed ;
123 + uint64_t publish_deleted ;
124 + uint64_t publish_write_call ;
125 + uint64_t publish_write_bytes ;
126 + uint64_t publish_read_call ;
127 + uint64_t publish_read_bytes ;
128 + uint64_t publish_process;
129 + uint64_t publish_thread ;
130 + uint64_t publish_task ;
131 + uint64_t publish_open_error ;
132 + uint64_t publish_close_error ;
133 + uint64_t publish_write_error ;
134 + uint64_t publish_read_error ;
135 +}ebpf_process_publish_apps_t;
136 # endif
collectors/ebpf.plugin/ebpf_socket.c
+272 -66
@@ -11,15 +11,21 @@
11 *
12 *****************************************************************/
13
14 -static ebpf_functions_t socket_functions;
14 +static char *socket_dimension_names[NETDATA_MAX_SOCKET_VECTOR] = { "sent", "received", "close", "sent", "received" };
15 +static char *socket_id_names[NETDATA_MAX_SOCKET_VECTOR] = { "tcp_sendmsg", "tcp_cleanup_rbuf", "tcp_close", "udp_sendmsg",
16 + "udp_recvmsg" };
17
18 static netdata_idx_t *socket_hash_values = NULL;
19 static netdata_syscall_stat_t *socket_aggregated_data = NULL;
20 static netdata_publish_syscall_t *socket_publish_aggregated = NULL;
21
20 -static char *socket_dimension_names[NETDATA_MAX_SOCKET_VECTOR] = { "sent", "received", "close", "sent", "received" };
21 -static char *socket_id_names[NETDATA_MAX_SOCKET_VECTOR] = { "tcp_sendmsg", "tcp_cleanup_rbuf", "tcp_close", "udp_sendmsg",
22 - "udp_recvmsg" };
22 +static ebpf_functions_t socket_functions;
23 +
24 +static ebpf_socket_publish_apps_t **socket_bandwidth_curr = NULL;
25 +static ebpf_socket_publish_apps_t **socket_bandwidth_prev = NULL;
26 +static ebpf_bandwidth_t *bandwidth_vector = NULL;
27 +
28 +static int socket_apps_created = 0;
29
30 #ifndef STATIC
31 /**
@@ -50,10 +56,10 @@ static int *map_fd = NULL;
56 * @param udp structure to store IO from udp sockets
57 * @param input the structure with the input data.
58 */
53 -static void ebpf_update_publish(netdata_publish_syscall_t *publish,
54 - netdata_publish_vfs_common_t *tcp,
55 - netdata_publish_vfs_common_t *udp,
56 - netdata_syscall_stat_t *input) {
59 +static void ebpf_update_global_publish(netdata_publish_syscall_t *publish,
60 + netdata_publish_vfs_common_t *tcp,
61 + netdata_publish_vfs_common_t *udp,
62 + netdata_syscall_stat_t *input) {
63
64 netdata_publish_syscall_t *move = publish;
65 while(move) {
@@ -89,15 +95,28 @@ static void ebpf_update_publish(netdata_publish_syscall_t *publish,
95 udp->read = (long)publish[4].nbyte;
96 }
97
98 +/**
99 + * Update the publish strctures to create the dimenssions
100 + *
101 + * @param curr Last values read from memory.
102 + * @param prev Previous values read from memory.
103 + */
104 +static void ebpf_socket_update_apps_publish(ebpf_socket_publish_apps_t *curr,
105 + ebpf_socket_publish_apps_t *prev)
106 +{
107 + curr->publish_recv = curr->received - prev->received;
108 + curr->publish_sent = curr->sent - prev->sent;
109 +}
110 +
111 /**
112 * Send data to Netdata calling auxiliar functions.
113 *
114 * @param em the structure with thread information
115 */
97 -static void ebpf_process_send_data(ebpf_module_t *em) {
116 +static void ebpf_socket_send_data(ebpf_module_t *em) {
117 netdata_publish_vfs_common_t common_tcp;
118 netdata_publish_vfs_common_t common_udp;
100 - ebpf_update_publish(socket_publish_aggregated, &common_tcp, &common_udp, socket_aggregated_data);
119 + ebpf_update_global_publish(socket_publish_aggregated, &common_tcp, &common_udp, socket_aggregated_data);
120
121 write_count_chart(NETDATA_TCP_FUNCTION_COUNT, NETDATA_EBPF_FAMILY, socket_publish_aggregated, 3);
122 write_io_chart(NETDATA_TCP_FUNCTION_BYTES, NETDATA_EBPF_FAMILY, socket_id_names[0], socket_id_names[1], &common_tcp);
@@ -114,6 +133,65 @@ static void ebpf_process_send_data(ebpf_module_t *em) {
133 }
134 }
135
136 +/**
137 + * Sum values for pid
138 + *
139 + * @param root the structure with all available PIDs
140 + *
141 + * @param offset the address that we are reading
142 + *
143 + * @return it returns the sum of all PIDs
144 + */
145 +long long ebpf_socket_sum_values_for_pids(struct pid_on_target *root, size_t offset)
146 +{
147 + long long ret = 0;
148 + while (root) {
149 + int32_t pid = root->pid;
150 + ebpf_socket_publish_apps_t *w = socket_bandwidth_curr[pid];
151 + if (w) {
152 + ret += get_value_from_structure((char *)w, offset);
153 + }
154 +
155 + root = root->next;
156 + }
157 +
158 + return ret;
159 +}
160 +
161 +/**
162 + * Send data to Netdata calling auxiliar functions.
163 + *
164 + * @param em the structure with thread information
165 + * @param root the target list.
166 + */
167 +void ebpf_socket_send_apps_data(ebpf_module_t *em, struct target *root)
168 +{
169 + (void)em;
170 + if (!socket_apps_created)
171 + return;
172 +
173 + struct target *w;
174 + collected_number value;
175 +
176 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_NET_APPS_BANDWIDTH_SENT);
177 + for (w = root; w ; w = w->next) {
178 + if (unlikely(w->exposed && w->processes)) {
179 + value = ebpf_socket_sum_values_for_pids(w->root_pid, offsetof(ebpf_socket_publish_apps_t, publish_sent));
180 + write_chart_dimension(w->name, value);
181 + }
182 + }
183 + write_end_chart();
184 +
185 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_NET_APPS_BANDWIDTH_RECV);
186 + for (w = root; w ; w = w->next) {
187 + if (unlikely(w->exposed && w->processes)) {
188 + value = ebpf_socket_sum_values_for_pids(w->root_pid, offsetof(ebpf_socket_publish_apps_t, publish_recv));
189 + write_chart_dimension(w->name, value);
190 + }
191 + }
192 + write_end_chart();
193 +}
194 +
195 /*****************************************************************
196 *
197 * FUNCTIONS TO CREATE CHARTS
@@ -128,65 +206,98 @@ static void ebpf_process_send_data(ebpf_module_t *em) {
206 * @param em a pointer to the structure with the default values.
207 */
208 static void ebpf_create_global_charts(ebpf_module_t *em) {
131 - ebpf_create_chart(NETDATA_EBPF_FAMILY
132 - , NETDATA_TCP_FUNCTION_COUNT
133 - , "Calls"
134 - , NETDATA_SOCKET_GROUP
135 - , 950
136 - , ebpf_create_global_dimension
137 - , socket_publish_aggregated
138 - , 3);
139 -
140 - ebpf_create_chart(NETDATA_EBPF_FAMILY
141 - , NETDATA_TCP_FUNCTION_BYTES
142 - , "bytes/s"
143 - , NETDATA_SOCKET_GROUP
144 - , 951
145 - , ebpf_create_global_dimension
146 - , socket_publish_aggregated
147 - , 3);
209 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
210 + NETDATA_TCP_FUNCTION_COUNT,
211 + "Calls to internal functions",
212 + EBPF_COMMON_DIMENSION_CALL,
213 + NETDATA_SOCKET_GROUP,
214 + 21070,
215 + ebpf_create_global_dimension,
216 + socket_publish_aggregated,
217 + 3);
218 +
219 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
220 + NETDATA_TCP_FUNCTION_BYTES,
221 + "TCP bandwidth",
222 + EBPF_COMMON_DIMENSION_BYTESS,
223 + NETDATA_SOCKET_GROUP,
224 + 21071,
225 + ebpf_create_global_dimension,
226 + socket_publish_aggregated,
227 + 3);
228
229 if (em->mode < MODE_ENTRY) {
150 - ebpf_create_chart(NETDATA_EBPF_FAMILY
151 - , NETDATA_TCP_FUNCTION_ERROR
152 - , "Calls"
153 - , NETDATA_SOCKET_GROUP
154 - , 952
155 - , ebpf_create_global_dimension
156 - , socket_publish_aggregated
157 - , 2);
230 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
231 + NETDATA_TCP_FUNCTION_ERROR,
232 + "TCP errors",
233 + EBPF_COMMON_DIMENSION_CALL,
234 + NETDATA_SOCKET_GROUP,
235 + 21072,
236 + ebpf_create_global_dimension,
237 + socket_publish_aggregated,
238 + 2);
239 }
240
160 - ebpf_create_chart(NETDATA_EBPF_FAMILY
161 - , NETDATA_UDP_FUNCTION_COUNT
162 - , "Calls"
163 - , NETDATA_SOCKET_GROUP
164 - , 953
165 - , ebpf_create_global_dimension
166 - , &socket_publish_aggregated[NETDATA_UDP_START]
167 - , 2);
168 -
169 - ebpf_create_chart(NETDATA_EBPF_FAMILY
170 - , NETDATA_UDP_FUNCTION_BYTES
171 - , "bytes/s"
172 - , NETDATA_SOCKET_GROUP
173 - , 954
174 - , ebpf_create_global_dimension
175 - , &socket_publish_aggregated[NETDATA_UDP_START]
176 - , 2);
241 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
242 + NETDATA_UDP_FUNCTION_COUNT,
243 + "UDP calls",
244 + EBPF_COMMON_DIMENSION_CALL,
245 + NETDATA_SOCKET_GROUP,
246 + 21073,
247 + ebpf_create_global_dimension,
248 + &socket_publish_aggregated[NETDATA_UDP_START],
249 + 2);
250 +
251 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
252 + NETDATA_UDP_FUNCTION_BYTES,
253 + "UDP bandwidth",
254 + EBPF_COMMON_DIMENSION_BYTESS,
255 + NETDATA_SOCKET_GROUP,
256 + 21074,
257 + ebpf_create_global_dimension,
258 + &socket_publish_aggregated[NETDATA_UDP_START],
259 + 2);
260
261 if (em->mode < MODE_ENTRY) {
179 - ebpf_create_chart(NETDATA_EBPF_FAMILY
180 - , NETDATA_UDP_FUNCTION_ERROR
181 - , "Calls"
182 - , NETDATA_SOCKET_GROUP
183 - , 955
184 - , ebpf_create_global_dimension
185 - , &socket_publish_aggregated[NETDATA_UDP_START]
186 - , 2);
262 + ebpf_create_chart(NETDATA_EBPF_FAMILY,
263 + NETDATA_UDP_FUNCTION_ERROR,
264 + "UDP errors",
265 + EBPF_COMMON_DIMENSION_CALL,
266 + NETDATA_SOCKET_GROUP,
267 + 21075,
268 + ebpf_create_global_dimension,
269 + &socket_publish_aggregated[NETDATA_UDP_START],
270 + 2);
271 }
272 }
273
274 +/**
275 + * Create apps charts
276 + *
277 + * Call ebpf_create_chart to create the charts on apps submenu.
278 + *
279 + * @param em a pointer to the structure with the default values.
280 + */
281 +void ebpf_socket_create_apps_charts(ebpf_module_t *em, struct target *root)
282 +{
283 + (void)em;
284 + ebpf_create_charts_on_apps(NETDATA_NET_APPS_BANDWIDTH_SENT,
285 + "Bytes sent",
286 + EBPF_COMMON_DIMENSION_BYTESS,
287 + NETDATA_APPS_NET_GROUP,
288 + 20080,
289 + root);
290 +
291 + ebpf_create_charts_on_apps(NETDATA_NET_APPS_BANDWIDTH_RECV,
292 + "bytes received",
293 + EBPF_COMMON_DIMENSION_BYTESS,
294 + NETDATA_APPS_NET_GROUP,
295 + 20081,
296 + root);
297 +
298 + socket_apps_created = 1;
299 +}
300 +
301 /*****************************************************************
302 *
303 * READ INFORMATION FROM KERNEL RING
@@ -202,8 +313,9 @@ static void read_hash_global_tables()
313 netdata_idx_t res[NETDATA_SOCKET_COUNTER];
314
315 netdata_idx_t *val = socket_hash_values;
316 + int fd = map_fd[4];
317 for (idx = 0; idx < NETDATA_SOCKET_COUNTER ; idx++) {
206 - if (!bpf_map_lookup_elem(map_fd[4], &idx, val)) {
318 + if (!bpf_map_lookup_elem(fd, &idx, val)) {
319 uint64_t total = 0;
320 int i;
321 int end = (running_on_kernel < NETDATA_KERNEL_V4_15) ? 1 : ebpf_nprocs;
@@ -233,6 +345,76 @@ static void read_hash_global_tables()
345 socket_aggregated_data[4].bytes = res[NETDATA_KEY_BYTES_UDP_SENDMSG];
346 }
347
348 +/**
349 + * Fill publish apps when necessary.
350 + *
351 + * @param current_pid the PID that I am updating
352 + * @param eb the structure with data read from memory.
353 + */
354 +void ebpf_socket_fill_publish_apps(uint32_t current_pid, ebpf_bandwidth_t *eb)
355 +{
356 + ebpf_socket_publish_apps_t *curr= socket_bandwidth_curr[current_pid];
357 + ebpf_socket_publish_apps_t *prev = socket_bandwidth_prev[current_pid];
358 + if (!curr) {
359 + ebpf_socket_publish_apps_t *ptr = callocz(2, sizeof(ebpf_socket_publish_apps_t));
360 + curr = &ptr[0];
361 + socket_bandwidth_curr[current_pid] = curr;
362 + prev = &ptr[1];
363 + socket_bandwidth_prev[current_pid] = prev;
364 + } else {
365 + memcpy(prev, curr, sizeof(ebpf_socket_publish_apps_t));
366 + }
367 +
368 + curr->sent = eb->sent;
369 + curr->received = eb->received;
370 +
371 + ebpf_socket_update_apps_publish(curr, prev);
372 +}
373 +
374 +/**
375 + * Bandwidth accumulator.
376 + *
377 + * @param out the vector with the values to sum
378 + */
379 +void ebpf_socket_bandwidth_accumulator(ebpf_bandwidth_t *out)
380 +{
381 + int i, end = (running_on_kernel >= NETDATA_KERNEL_V4_15)?ebpf_nprocs:1;
382 + ebpf_bandwidth_t *total = &out[0];
383 + for (i = 1; i < end; i++) {
384 + ebpf_bandwidth_t *move = &out[i];
385 + total->sent += move->sent;
386 + total->received += move->received;
387 + }
388 +}
389 +
390 +/**
391 + * Update the apps data reading information from the hash table
392 + */
393 +static void ebpf_socket_update_apps_data()
394 +{
395 + int fd = map_fd[0];
396 + ebpf_bandwidth_t *eb = bandwidth_vector;
397 + uint32_t key;
398 + struct pid_stat *pids = root_of_pids;
399 + while (pids) {
400 + key = pids->pid;
401 +
402 + if (bpf_map_lookup_elem(fd, &key, eb)) {
403 + pids = pids->next;
404 + continue;
405 + }
406 +
407 + ebpf_socket_bandwidth_accumulator(eb);
408 +
409 + ebpf_socket_fill_publish_apps(key, eb);
410 +
411 + if (eb[0].removed)
412 + bpf_map_delete_elem(fd, &key);
413 +
414 + pids = pids->next;
415 + }
416 +}
417 +
418 /*****************************************************************
419 *
420 * FUNCTIONS WITH THE MAIN LOOP
@@ -249,16 +431,31 @@ static void read_hash_global_tables()
431 static void socket_collector(usec_t step, ebpf_module_t *em)
432 {
433 (void)em;
434 + (void)step;
435 heartbeat_t hb;
436 heartbeat_init(&hb);
437 +
438 + int socket_apps_enabled = ebpf_modules[EBPF_MODULE_SOCKET_IDX].apps_charts;
439 + int socket_global_enabled = ebpf_modules[EBPF_MODULE_SOCKET_IDX].global_charts;
440 while(!close_ebpf_plugin) {
255 - usec_t dt = heartbeat_next(&hb, step);
256 - (void)dt;
441 + pthread_mutex_lock(&collect_data_mutex);
442 + pthread_cond_wait(&collect_data_cond_var, &collect_data_mutex);
443 +
444 + if (socket_global_enabled)
445 + read_hash_global_tables();
446
258 - read_hash_global_tables();
447 + if (socket_apps_enabled)
448 + ebpf_socket_update_apps_data();
449
450 pthread_mutex_lock(&lock);
261 - ebpf_process_send_data(em);
451 + if (socket_global_enabled)
452 + ebpf_socket_send_data(em);
453 +
454 + if (socket_apps_enabled)
455 + ebpf_socket_send_apps_data(em, apps_groups_root_target);
456 +
457 + pthread_mutex_unlock(&collect_data_mutex);
458 +
459 pthread_mutex_unlock(&lock);
460
461 fflush(stdout);
@@ -279,7 +476,6 @@ static void socket_collector(usec_t step, ebpf_module_t *em)
476 static void ebpf_socket_cleanup(void *ptr)
477 {
478 (void)ptr;
282 -
479 freez(socket_aggregated_data);
480 freez(socket_publish_aggregated);
481 freez(socket_hash_values);
@@ -289,6 +485,11 @@ static void ebpf_socket_cleanup(void *ptr)
485 }
486
487 freez(socket_functions.map_fd);
488 + freez(socket_bandwidth_curr);
489 + freez(socket_bandwidth_prev);
490 + freez(bandwidth_vector);
491 +
492 + ebpf_modules[EBPF_MODULE_SOCKET_IDX].enabled = 0;
493 }
494
495 /*****************************************************************
@@ -308,6 +509,10 @@ static void ebpf_socket_allocate_global_vectors(size_t length) {
509 socket_aggregated_data = callocz(length, sizeof(netdata_syscall_stat_t));
510 socket_publish_aggregated = callocz(length, sizeof(netdata_publish_syscall_t));
511 socket_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
512 +
513 + socket_bandwidth_curr = callocz((size_t)pid_max, sizeof(ebpf_socket_publish_apps_t *));
514 + socket_bandwidth_prev = callocz((size_t)pid_max, sizeof(ebpf_socket_publish_apps_t *));
515 + bandwidth_vector = callocz((size_t) ebpf_nprocs, sizeof(ebpf_bandwidth_t));
516 }
517
518 static void change_collector_event() {
@@ -377,6 +582,7 @@ void *ebpf_socket_thread(void *ptr)
582 socket_id_names, NETDATA_MAX_SOCKET_VECTOR);
583
584 ebpf_create_global_charts(em);
585 +
586 pthread_mutex_unlock(&lock);
587
588 socket_collector((usec_t)(em->update_time*USEC_PER_SEC), em);
collectors/ebpf.plugin/ebpf_socket.h
+14
@@ -29,6 +29,7 @@ typedef enum ebpf_socket_idx {
29
30 # define NETDATA_SOCKET_GROUP "Socket"
31
32 +//Global chart name
33 # define NETDATA_TCP_FUNCTION_COUNT "tcp_functions"
34 # define NETDATA_TCP_FUNCTION_BYTES "tcp_bandwidth"
35 # define NETDATA_TCP_FUNCTION_ERROR "tcp_error"
@@ -36,5 +37,18 @@ typedef enum ebpf_socket_idx {
37 # define NETDATA_UDP_FUNCTION_BYTES "udp_bandwidth"
38 # define NETDATA_UDP_FUNCTION_ERROR "udp_error"
39
40 +// Charts created on Apps submenu
41 +# define NETDATA_NET_APPS_BANDWIDTH_SENT "bandwidth_sent"
42 +# define NETDATA_NET_APPS_BANDWIDTH_RECV "bandwidth_recv"
43 +
44 +typedef struct ebpf_socket_publish_apps {
45 + //Data read
46 + uint64_t sent;
47 + uint64_t received;
48 +
49 + //Publish information.
50 + uint64_t publish_sent;
51 + uint64_t publish_recv;
52 +} ebpf_socket_publish_apps_t;
53
54 #endif
libnetdata/ebpf/ebpf.c
+6
@@ -225,6 +225,12 @@ int ebpf_load_libraries(ebpf_functions_t *ef, char *libbase, char *pluginsdir)
225 return -1;
226 }
227
228 + ef->bpf_map_get_next_key = dlsym(libnetdata, "bpf_map_get_next_key");
229 + if ((err = dlerror()) != NULL) {
230 + error("Cannot find bpf_map_delete_elem: %s", err);
231 + return -1;
232 + }
233 +
234 return 0;
235 }
236
libnetdata/ebpf/ebpf.h
+1
@@ -58,6 +58,7 @@ typedef struct ebpf_functions {
58 //Libbpf (It is necessary to have at least kernel 4.10)
59 int (*bpf_map_lookup_elem)(int, const void *, void *);
60 int (*bpf_map_delete_elem)(int fd, const void *key);
61 + int (*bpf_map_get_next_key)(int fd, const void *key, void *next_key);
62
63 int *map_fd;
64
packaging/ebpf.checksums
+3 -3
@@ -1,3 +1,3 @@
1 -b52aac730f2d76b0fe924a04b145471f4d50b926fd12aa608fccb05a050d0557 netdata-kernel-collector-glibc-v0.4.0.tar.xz
2 -be3886d7b0af671b96c8a8bd5e4efc83ecf6ea16be23e258d5d45527563599c3 netdata-kernel-collector-musl-v0.4.0.tar.xz
3 -cf8c644105b0b527c3e2478b4ef61f578a98a21ca84db730ca5840c77afd431a netdata-kernel-collector-static-v0.4.0.tar.xz
1 +fbee50759ebede5f8b6d4cb43ed28f4877ab813b2700f25f560a854186cc53af netdata-kernel-collector-glibc-v0.4.3.tar.xz
2 +2f9eea8821fac1324b634edb6ef973096ffb20c8f959675887885844068f5461 netdata-kernel-collector-musl-v0.4.3.tar.xz
3 +2fc3dfdcf6efd4c75a3fee0c9e4898bf596cf6e2d58adf3933b3ed519ef8c287 netdata-kernel-collector-static-v0.4.3.tar.xz
packaging/ebpf.version
+1 -1
@@ -1 +1 @@
1 -v0.4.0
1 +v0.4.3
web/gui/dashboard_info.js
+64
@@ -1085,6 +1085,70 @@ netdataDashboard.context = {
1085 info: 'Carried over process group uptime since the Netdata restart. The period of time within which at least one process in the group was running.'
1086 },
1087
1088 + 'apps.file_open': {
1089 + height: 2.0
1090 + },
1091 +
1092 + 'apps.file_open_error': {
1093 + height: 2.0
1094 + },
1095 +
1096 + 'apps.file_closed': {
1097 + height: 2.0
1098 + },
1099 +
1100 + 'apps.file_close_error': {
1101 + height: 2.0
1102 + },
1103 +
1104 + 'apps.file_deleted': {
1105 + height: 2.0
1106 + },
1107 +
1108 + 'apps.vfs_write_call': {
1109 + height: 2.0
1110 + },
1111 +
1112 + 'apps.vfs_write_error': {
1113 + height: 2.0
1114 + },
1115 +
1116 + 'apps.vfs_read_call': {
1117 + height: 2.0
1118 + },
1119 +
1120 + 'apps.vfs_read_error': {
1121 + height: 2.0
1122 + },
1123 +
1124 + 'apps.vfs_write_bytes': {
1125 + height: 2.0
1126 + },
1127 +
1128 + 'apps.vfs_read_bytes': {
1129 + height: 2.0
1130 + },
1131 +
1132 + 'apps.process_create': {
1133 + height: 2.0
1134 + },
1135 +
1136 + 'apps.thread_create': {
1137 + height: 2.0
1138 + },
1139 +
1140 + 'apps.task_close': {
1141 + height: 2.0
1142 + },
1143 +
1144 + 'apps.bandwidth_sent': {
1145 + height: 2.0
1146 + },
1147 +
1148 + 'apps.bandwidth_recv': {
1149 + height: 2.0
1150 + },
1151 +
1152 // ------------------------------------------------------------------------
1153 // USERS
1154