@cryptotaxi247 / netdata-1 / commits / ac3f9b171

Ebpf directory cache (#10855)

Add new thread to ebpf.plugin.

thiagoftsm committed May 3, 2021 at 11:13 UTC ac3f9b171754014126edf6235d7d751f9aaa4ea5
15 files changed +812 -3
CMakeLists.txt
+2
@@ -488,6 +488,8 @@ set(EBPF_PROCESS_PLUGIN_FILES
488 collectors/ebpf.plugin/ebpf.h
489 collectors/ebpf.plugin/ebpf_cachestat.c
490 collectors/ebpf.plugin/ebpf_cachestat.h
491 + collectors/ebpf.plugin/ebpf_dcstat.c
492 + collectors/ebpf.plugin/ebpf_dcstat.h
493 collectors/ebpf.plugin/ebpf_process.c
494 collectors/ebpf.plugin/ebpf_process.h
495 collectors/ebpf.plugin/ebpf_socket.c
Makefile.am
+2
@@ -294,6 +294,8 @@ EBPF_PLUGIN_FILES = \
294 collectors/ebpf.plugin/ebpf.c \
295 collectors/ebpf.plugin/ebpf_cachestat.c \
296 collectors/ebpf.plugin/ebpf_cachestat.h \
297 + collectors/ebpf.plugin/ebpf_dcstat.c \
298 + collectors/ebpf.plugin/ebpf_dcstat.h \
299 collectors/ebpf.plugin/ebpf_process.c \
300 collectors/ebpf.plugin/ebpf_process.h \
301 collectors/ebpf.plugin/ebpf_socket.c \
collectors/ebpf.plugin/Makefile.am
+1
@@ -33,6 +33,7 @@ dist_libconfig_DATA = \
33 dist_ebpfconfig_DATA = \
34 ebpf.d/ebpf_kernel_reject_list.txt \
35 ebpf.d/cachestat.conf \
36 + ebpf.d/dcstat.conf \
37 ebpf.d/network.conf \
38 ebpf.d/process.conf \
39 ebpf.d/sync.conf \
collectors/ebpf.plugin/README.md
+4
@@ -200,6 +200,9 @@ The eBPF collector enables and runs the following eBPF programs by default:
200 - `cachestat`: Netdata's eBPF data collector creates charts about the memory page cache. When the integration with
201 [`apps.plugin`](/collectors/apps.plugin/README.md) is enabled, this collector creates charts for the whole host _and_
202 for each application.
203 +- `dcstat` : This eBPF program creates charts that show information about file access using directory cache. It appends
204 + `kprobes` for `lookup_fast()` and `d_lookup()` to identify if files are inside directory cache, outside and
205 + files are not found.
206 - `process`: This eBPF program creates charts that show information about process creation, VFS IO, and files removed.
207 When in `return` mode, it also creates charts showing errors when these operations are executed.
208 - `network viewer`: This eBPF program creates charts with information about `TCP` and `UDP` functions, including the
@@ -221,6 +224,7 @@ cd /etc/netdata/ # Replace with your Netdata configuration directory, if not /
224 The following configuration files are available:
225
226 - `cachestat.conf`: Configuration for the `cachestat` thread.
227 +- `dcstat.conf`: Configuration for the `dcstat` thread.
228 - `process.conf`: Configuration for the `process` thread.
229 - `network.conf`: Configuration for the `network viewer` thread. This config file overwrites the global options and
230 also lets you specify which network the eBPF collector monitors.
collectors/ebpf.plugin/ebpf.c
+32 -1
@@ -90,6 +90,10 @@ ebpf_module_t ebpf_modules[] = {
90 { .thread_name = "sync", .config_name = "sync", .enabled = 0, .start_routine = ebpf_sync_thread,
91 .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY,
92 .optional = 0, .apps_routine = NULL, .maps = NULL, .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL },
93 + { .thread_name = "dc", .config_name = "dc", .enabled = 0, .start_routine = ebpf_dcstat_thread,
94 + .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY,
95 + .optional = 0, .apps_routine = ebpf_dcstat_create_apps_charts, .maps = NULL,
96 + .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE },
97 { .thread_name = NULL, .enabled = 0, .start_routine = NULL, .update_time = 1,
98 .global_charts = 0, .apps_charts = 1, .mode = MODE_ENTRY,
99 .optional = 0, .apps_routine = NULL, .maps = NULL, .pid_map_size = 0, .names = NULL },
@@ -145,6 +149,12 @@ static void ebpf_exit(int sig)
149 freez(cachestat_pid);
150 }
151
152 + if (ebpf_modules[EBPF_MODULE_DCSTAT_IDX].enabled) {
153 + ebpf_modules[EBPF_MODULE_DCSTAT_IDX].enabled = 0;
154 + clean_dcstat_pid_structures();
155 + freez(dcstat_pid);
156 + }
157 +
158 /*
159 int ret = fork();
160 if (ret < 0) // error
@@ -585,6 +595,8 @@ void ebpf_print_help()
595 "\n"
596 " --cachestat or -c Enable charts related to process run time.\n"
597 "\n"
598 + " --dcstat or -d Enable charts related to directory cache.\n"
599 + "\n"
600 " --net or -n Enable network viewer charts.\n"
601 "\n"
602 " --process or -p Enable charts related to process run time.\n"
@@ -875,6 +887,13 @@ static void read_collector_values(int *disable_apps)
887 started++;
888 }
889
890 + enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "dcstat",
891 + CONFIG_BOOLEAN_NO);
892 + if (enabled) {
893 + ebpf_enable_chart(EBPF_MODULE_DCSTAT_IDX, *disable_apps);
894 + started++;
895 + }
896 +
897 if (!started){
898 ebpf_enable_all_charts(*disable_apps);
899 // Read network viewer section
@@ -958,6 +977,7 @@ static void parse_args(int argc, char **argv)
977 {"global", no_argument, 0, 'g' },
978 {"all", no_argument, 0, 'a' },
979 {"cachestat", no_argument, 0, 'c' },
980 + {"dcstat", no_argument, 0, 'd' },
981 {"net", no_argument, 0, 'n' },
982 {"process", no_argument, 0, 'p' },
983 {"return", no_argument, 0, 'r' },
@@ -976,7 +996,7 @@ static void parse_args(int argc, char **argv)
996 }
997
998 while (1) {
979 - int c = getopt_long(argc, argv, "hvgcanprs", long_options, &option_index);
999 + int c = getopt_long(argc, argv, "hvgacdnprs", long_options, &option_index);
1000 if (c == -1)
1001 break;
1002
@@ -1011,6 +1031,15 @@ static void parse_args(int argc, char **argv)
1031 #ifdef NETDATA_INTERNAL_CHECKS
1032 info(
1033 "EBPF enabling \"CACHESTAT\" charts, because it was started with the option \"--cachestat\" or \"-c\".");
1034 +#endif
1035 + break;
1036 + }
1037 + case 'd': {
1038 + enabled = 1;
1039 + ebpf_enable_chart(EBPF_MODULE_DCSTAT_IDX, disable_apps);
1040 +#ifdef NETDATA_INTERNAL_CHECKS
1041 + info(
1042 + "EBPF enabling \"DCSTAT\" charts, because it was started with the option \"--dcstat\" or \"-d\".");
1043 #endif
1044 break;
1045 }
@@ -1172,6 +1201,8 @@ int main(int argc, char **argv)
1201 NULL, NULL, ebpf_modules[EBPF_MODULE_CACHESTAT_IDX].start_routine},
1202 {"EBPF SYNC" , NULL, NULL, 1,
1203 NULL, NULL, ebpf_modules[EBPF_MODULE_SYNC_IDX].start_routine},
1204 + {"EBPF DCSTAT" , NULL, NULL, 1,
1205 + NULL, NULL, ebpf_modules[EBPF_MODULE_DCSTAT_IDX].start_routine},
1206 {NULL , NULL, NULL, 0,
1207 NULL, NULL, NULL}
1208 };
collectors/ebpf.plugin/ebpf.d.conf
+1
@@ -33,6 +33,7 @@
33 # `sync` : Montitor calls for syscall sync(2).
34 [ebpf programs]
35 cachestat = no
36 + dcstat = no
37 process = yes
38 socket = yes
39 sync = yes
collectors/ebpf.plugin/ebpf.d/dcstat.conf new
+13
@@ -0,0 +1,13 @@
1 +# The `ebpf load mode` option accepts the following values :
2 +# `entry` : The eBPF collector only monitors calls for the functions, and does not show charts related to errors.
3 +# `return : In the `return` mode, the eBPF collector monitors the same kernel functions as `entry`, but also creates
4 +# new charts for the return of these functions, such as errors.
5 +#
6 +# The eBPF collector also creates charts for each running application through an integration with the `apps plugin`.
7 +# If you want to disable the integration with `apps.plugin` along with the above charts, change the setting `apps` to
8 +# 'no'.
9 +#
10 +[global]
11 + ebpf load mode = entry
12 + apps = yes
13 + update every = 2
collectors/ebpf.plugin/ebpf.h
+4 -1
@@ -77,7 +77,8 @@ enum ebpf_module_indexes {
77 EBPF_MODULE_PROCESS_IDX,
78 EBPF_MODULE_SOCKET_IDX,
79 EBPF_MODULE_CACHESTAT_IDX,
80 - EBPF_MODULE_SYNC_IDX
80 + EBPF_MODULE_SYNC_IDX,
81 + EBPF_MODULE_DCSTAT_IDX
82 };
83
84 // Copied from musl header
@@ -91,6 +92,7 @@ enum ebpf_module_indexes {
92
93 // Chart definitions
94 #define NETDATA_EBPF_FAMILY "ebpf"
95 +#define NETDATA_FILESYSTEM_FAMILY "filesystem"
96 #define NETDATA_EBPF_CHART_TYPE_LINE "line"
97 #define NETDATA_EBPF_CHART_TYPE_STACKED "stacked"
98 #define NETDATA_EBPF_MEMORY_GROUP "mem"
@@ -196,6 +198,7 @@ extern void ebpf_cleanup_publish_syscall(netdata_publish_syscall_t *nps);
198 #define EBPF_COMMON_DIMENSION_BYTES "bytes/s"
199 #define EBPF_COMMON_DIMENSION_DIFFERENCE "difference"
200 #define EBPF_COMMON_DIMENSION_PACKETS "packets"
201 +#define EBPF_COMMON_DIMENSION_FILES "files"
202
203 // Common variables
204 extern int debug_enabled;
collectors/ebpf.plugin/ebpf_apps.c
+6
@@ -927,6 +927,12 @@ void cleanup_variables_from_other_threads(uint32_t pid)
927 freez(cachestat_pid[pid]);
928 cachestat_pid[pid] = NULL;
929 }
930 +
931 + // Clean directory cache structure
932 + if (dcstat_pid) {
933 + freez(dcstat_pid[pid]);
934 + dcstat_pid[pid] = NULL;
935 + }
936 }
937
938 /**
collectors/ebpf.plugin/ebpf_apps.h
+5 -1
@@ -16,8 +16,10 @@
16 #define NETDATA_APPS_PROCESS_GROUP "process (eBPF)"
17 #define NETDATA_APPS_NET_GROUP "net (eBPF)"
18 #define NETDATA_APPS_CACHESTAT_GROUP "page cache (eBPF)"
19 +#define NETDATA_APPS_DCSTAT_GROUP "directory cache (eBPF)"
20
21 #include "ebpf_process.h"
22 +#include "ebpf_dcstat.h"
23 #include "ebpf_cachestat.h"
24 #include "ebpf_sync.h"
25
@@ -108,8 +110,9 @@ struct target {
110 uid_t uid;
111 gid_t gid;
112
111 - // Page cache statistic per process
113 + // Changes made to simplify integration between apps and eBPF.
114 netdata_publish_cachestat_t cachestat;
115 + netdata_publish_dcstat_t dcstat;
116
117 /* These variables are not necessary for eBPF collector
118 kernel_uint_t minflt;
@@ -435,5 +438,6 @@ extern void clean_global_memory();
438 extern ebpf_process_stat_t **global_process_stats;
439 extern ebpf_process_publish_apps_t **current_apps_data;
440 extern netdata_publish_cachestat_t **cachestat_pid;
441 +extern netdata_publish_dcstat_t **dcstat_pid;
442
443 #endif /* NETDATA_EBPF_APPS_H */
collectors/ebpf.plugin/ebpf_dcstat.c new
+603
@@ -0,0 +1,603 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "ebpf.h"
4 +#include "ebpf_dcstat.h"
5 +
6 +static char *dcstat_counter_dimension_name[NETDATA_DCSTAT_IDX_END] = { "ratio", "reference", "slow", "miss" };
7 +static netdata_syscall_stat_t dcstat_counter_aggregated_data[NETDATA_DCSTAT_IDX_END];
8 +static netdata_publish_syscall_t dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_END];
9 +
10 +static ebpf_data_t dcstat_data;
11 +
12 +netdata_dcstat_pid_t *dcstat_vector = NULL;
13 +netdata_publish_dcstat_t **dcstat_pid = NULL;
14 +
15 +static struct bpf_link **probe_links = NULL;
16 +static struct bpf_object *objects = NULL;
17 +
18 +static int *map_fd = NULL;
19 +static netdata_idx_t dcstat_hash_values[NETDATA_DCSTAT_IDX_END];
20 +
21 +static int read_thread_closed = 1;
22 +
23 +struct config dcstat_config = { .first_section = NULL,
24 + .last_section = NULL,
25 + .mutex = NETDATA_MUTEX_INITIALIZER,
26 + .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
27 + .rwlock = AVL_LOCK_INITIALIZER } };
28 +
29 +struct netdata_static_thread dcstat_threads = {"DCSTAT KERNEL",
30 + NULL, NULL, 1, NULL,
31 + NULL, NULL};
32 +
33 +static ebpf_local_maps_t dcstat_maps[] = {{.name = "dcstat_pid", .internal_input = ND_EBPF_DEFAULT_PID_SIZE,
34 + .user_input = 0},
35 + {.name = NULL, .internal_input = 0, .user_input = 0}};
36 +
37 +static ebpf_specify_name_t dc_optional_name[] = { {.program_name = "netdata_lookup_fast",
38 + .function_to_attach = "lookup_fast",
39 + .optional = NULL,
40 + .retprobe = CONFIG_BOOLEAN_NO},
41 + {.program_name = NULL}};
42 +
43 +/*****************************************************************
44 + *
45 + * COMMON FUNCTIONS
46 + *
47 + *****************************************************************/
48 +
49 +/**
50 + * Update publish
51 + *
52 + * Update publish values before to write dimension.
53 + *
54 + * @param out strcuture that will receive data.
55 + * @param cache_access number of access to directory cache.
56 + * @param not_found number of files not found on the file system
57 + */
58 +void dcstat_update_publish(netdata_publish_dcstat_t *out, uint64_t cache_access, uint64_t not_found)
59 +{
60 + calculated_number successful_access = (calculated_number) (((long long)cache_access) - ((long long)not_found));
61 + calculated_number ratio = (cache_access) ? successful_access/(calculated_number)cache_access : 0;
62 +
63 + out->ratio = (long long )(ratio*100);
64 +}
65 +
66 +/*****************************************************************
67 + *
68 + * FUNCTIONS TO CLOSE THE THREAD
69 + *
70 + *****************************************************************/
71 +
72 +/**
73 + * Clean PID structures
74 + *
75 + * Clean the allocated structures.
76 + */
77 +void clean_dcstat_pid_structures() {
78 + struct pid_stat *pids = root_of_pids;
79 + while (pids) {
80 + freez(dcstat_pid[pids->pid]);
81 +
82 + pids = pids->next;
83 + }
84 +}
85 +
86 +/**
87 + * Clean names
88 + *
89 + * Clean the optional names allocated during startup.
90 + */
91 +void ebpf_dcstat_clean_names()
92 +{
93 + size_t i = 0;
94 + while (dc_optional_name[i].program_name) {
95 + freez(dc_optional_name[i].optional);
96 + i++;
97 + }
98 +}
99 +
100 +/**
101 + * Clean up the main thread.
102 + *
103 + * @param ptr thread data.
104 + */
105 +static void ebpf_dcstat_cleanup(void *ptr)
106 +{
107 + ebpf_module_t *em = (ebpf_module_t *)ptr;
108 + if (!em->enabled)
109 + return;
110 +
111 + heartbeat_t hb;
112 + heartbeat_init(&hb);
113 + uint32_t tick = 2 * USEC_PER_MS;
114 + while (!read_thread_closed) {
115 + usec_t dt = heartbeat_next(&hb, tick);
116 + UNUSED(dt);
117 + }
118 +
119 + freez(dcstat_vector);
120 +
121 + ebpf_cleanup_publish_syscall(dcstat_counter_publish_aggregated);
122 +
123 + ebpf_dcstat_clean_names();
124 +
125 + struct bpf_program *prog;
126 + size_t i = 0 ;
127 + bpf_object__for_each_program(prog, objects) {
128 + bpf_link__destroy(probe_links[i]);
129 + i++;
130 + }
131 + bpf_object__close(objects);
132 +}
133 +
134 +/*****************************************************************
135 + *
136 + * APPS
137 + *
138 + *****************************************************************/
139 +
140 +/**
141 + * Create apps charts
142 + *
143 + * Call ebpf_create_chart to create the charts on apps submenu.
144 + *
145 + * @param em a pointer to the structure with the default values.
146 + */
147 +void ebpf_dcstat_create_apps_charts(struct ebpf_module *em, void *ptr)
148 +{
149 + UNUSED(em);
150 + struct target *root = ptr;
151 + ebpf_create_charts_on_apps(NETDATA_DC_HIT_CHART,
152 + "Percentage of files listed inside directory cache",
153 + EBPF_COMMON_DIMENSION_PERCENTAGE,
154 + NETDATA_APPS_DCSTAT_GROUP,
155 + NETDATA_EBPF_CHART_TYPE_LINE,
156 + 20100,
157 + ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
158 + root);
159 +
160 + ebpf_create_charts_on_apps(NETDATA_DC_REFERENCE_CHART,
161 + "Count file access.",
162 + EBPF_COMMON_DIMENSION_FILES,
163 + NETDATA_APPS_DCSTAT_GROUP,
164 + NETDATA_EBPF_CHART_TYPE_STACKED,
165 + 20101,
166 + ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
167 + root);
168 +
169 + ebpf_create_charts_on_apps(NETDATA_DC_REQUEST_NOT_CACHE_CHART,
170 + "Access to files that were not present inside directory cache.",
171 + EBPF_COMMON_DIMENSION_FILES,
172 + NETDATA_APPS_DCSTAT_GROUP,
173 + NETDATA_EBPF_CHART_TYPE_STACKED,
174 + 20102,
175 + ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
176 + root);
177 +
178 + ebpf_create_charts_on_apps(NETDATA_DC_REQUEST_NOT_FOUND_CHART,
179 + "Number of requests for files that were not found on filesystem.",
180 + EBPF_COMMON_DIMENSION_FILES,
181 + NETDATA_APPS_DCSTAT_GROUP,
182 + NETDATA_EBPF_CHART_TYPE_STACKED,
183 + 20103,
184 + ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX],
185 + root);
186 +}
187 +
188 +/*****************************************************************
189 + *
190 + * MAIN LOOP
191 + *
192 + *****************************************************************/
193 +
194 +/**
195 + * Apps Accumulator
196 + *
197 + * Sum all values read from kernel and store in the first address.
198 + *
199 + * @param out the vector with read values.
200 + */
201 +static void dcstat_apps_accumulator(netdata_dcstat_pid_t *out)
202 +{
203 + int i, end = (running_on_kernel >= NETDATA_KERNEL_V4_15) ? ebpf_nprocs : 1;
204 + netdata_dcstat_pid_t *total = &out[0];
205 + for (i = 1; i < end; i++) {
206 + netdata_dcstat_pid_t *w = &out[i];
207 + total->cache_access += w->cache_access;
208 + total->file_system += w->file_system;
209 + total->not_found += w->not_found;
210 + }
211 +}
212 +
213 +/**
214 + * Save PID values
215 + *
216 + * Save the current values inside the structure
217 + *
218 + * @param out vector used to plot charts
219 + * @param publish vector with values read from hash tables.
220 + */
221 +static inline void dcstat_save_pid_values(netdata_publish_dcstat_t *out, netdata_dcstat_pid_t *publish)
222 +{
223 + memcpy(&out->curr, &publish[0], sizeof(netdata_dcstat_pid_t));
224 +}
225 +
226 +/**
227 + * Fill PID
228 + *
229 + * Fill PID structures
230 + *
231 + * @param current_pid pid that we are collecting data
232 + * @param out values read from hash tables;
233 + */
234 +static void dcstat_fill_pid(uint32_t current_pid, netdata_dcstat_pid_t *publish)
235 +{
236 + netdata_publish_dcstat_t *curr = dcstat_pid[current_pid];
237 + if (!curr) {
238 + curr = callocz(1, sizeof(netdata_publish_dcstat_t));
239 + dcstat_pid[current_pid] = curr;
240 + }
241 +
242 + dcstat_save_pid_values(curr, publish);
243 +}
244 +
245 +/**
246 + * Read APPS table
247 + *
248 + * Read the apps table and store data inside the structure.
249 + */
250 +static void read_apps_table()
251 +{
252 + netdata_dcstat_pid_t *cv = dcstat_vector;
253 + uint32_t key;
254 + struct pid_stat *pids = root_of_pids;
255 + int fd = map_fd[NETDATA_DCSTAT_PID_STATS];
256 + size_t length = sizeof(netdata_dcstat_pid_t)*ebpf_nprocs;
257 + while (pids) {
258 + key = pids->pid;
259 +
260 + if (bpf_map_lookup_elem(fd, &key, cv)) {
261 + pids = pids->next;
262 + continue;
263 + }
264 +
265 + dcstat_apps_accumulator(cv);
266 +
267 + dcstat_fill_pid(key, cv);
268 +
269 + // We are cleaning to avoid passing data read from one process to other.
270 + memset(cv, 0, length);
271 +
272 + pids = pids->next;
273 + }
274 +}
275 +
276 +/**
277 + * Read global table
278 + *
279 + * Read the table with number of calls for all functions
280 + */
281 +static void read_global_table()
282 +{
283 + uint32_t idx;
284 + netdata_idx_t *val = dcstat_hash_values;
285 + netdata_idx_t stored;
286 + int fd = map_fd[NETDATA_DCSTAT_GLOBAL_STATS];
287 +
288 + for (idx = NETDATA_KEY_DC_REFERENCE; idx < NETDATA_DIRECTORY_CACHE_END; idx++) {
289 + if (!bpf_map_lookup_elem(fd, &idx, &stored)) {
290 + val[idx] = stored;
291 + }
292 + }
293 +}
294 +
295 +/**
296 + * DCstat read hash
297 + *
298 + * This is the thread callback.
299 + * This thread is necessary, because we cannot freeze the whole plugin to read the data.
300 + *
301 + * @param ptr It is a NULL value for this thread.
302 + *
303 + * @return It always returns NULL.
304 + */
305 +void *ebpf_dcstat_read_hash(void *ptr)
306 +{
307 + read_thread_closed = 0;
308 +
309 + heartbeat_t hb;
310 + heartbeat_init(&hb);
311 +
312 + ebpf_module_t *em = (ebpf_module_t *)ptr;
313 +
314 + usec_t step = NETDATA_LATENCY_DCSTAT_SLEEP_MS * em->update_time;
315 + while (!close_ebpf_plugin) {
316 + usec_t dt = heartbeat_next(&hb, step);
317 + (void)dt;
318 +
319 + read_global_table();
320 + }
321 + read_thread_closed = 1;
322 +
323 + return NULL;
324 +}
325 +
326 +/**
327 + * Cachestat sum PIDs
328 + *
329 + * Sum values for all PIDs associated to a group
330 + *
331 + * @param publish output structure.
332 + * @param root structure with listed IPs
333 + */
334 +void ebpf_dcstat_sum_pids(netdata_publish_dcstat_t *publish, struct pid_on_target *root)
335 +{
336 + memset(&publish->curr, 0, sizeof(netdata_dcstat_pid_t));
337 + netdata_dcstat_pid_t *dst = &publish->curr;
338 + while (root) {
339 + int32_t pid = root->pid;
340 + netdata_publish_dcstat_t *w = dcstat_pid[pid];
341 + if (w) {
342 + netdata_dcstat_pid_t *src = &w->curr;
343 + dst->cache_access += src->cache_access;
344 + dst->file_system += src->file_system;
345 + dst->not_found += src->not_found;
346 + }
347 +
348 + root = root->next;
349 + }
350 +}
351 +
352 +/**
353 + * Send data to Netdata calling auxiliar functions.
354 + *
355 + * @param root the target list.
356 +*/
357 +void ebpf_dcache_send_apps_data(struct target *root)
358 +{
359 + struct target *w;
360 + collected_number value;
361 +
362 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_DC_HIT_CHART);
363 + for (w = root; w; w = w->next) {
364 + if (unlikely(w->exposed && w->processes)) {
365 + ebpf_dcstat_sum_pids(&w->dcstat, w->root_pid);
366 +
367 + uint64_t cache = w->dcstat.curr.cache_access;
368 + uint64_t not_found = w->dcstat.curr.not_found;
369 +
370 + dcstat_update_publish(&w->dcstat, cache, not_found);
371 + value = (collected_number) w->dcstat.ratio;
372 + write_chart_dimension(w->name, value);
373 + }
374 + }
375 + write_end_chart();
376 +
377 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_DC_REFERENCE_CHART);
378 + for (w = root; w; w = w->next) {
379 + if (unlikely(w->exposed && w->processes)) {
380 + if (w->dcstat.curr.cache_access < w->dcstat.prev.cache_access) {
381 + w->dcstat.prev.cache_access = 0;
382 + }
383 +
384 + w->dcstat.cache_access = (long long)w->dcstat.curr.cache_access - (long long)w->dcstat.prev.cache_access;
385 + value = (collected_number) w->dcstat.cache_access;
386 + write_chart_dimension(w->name, value);
387 + w->dcstat.prev.cache_access = w->dcstat.curr.cache_access;
388 + }
389 + }
390 + write_end_chart();
391 +
392 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_DC_REQUEST_NOT_CACHE_CHART);
393 + for (w = root; w; w = w->next) {
394 + if (unlikely(w->exposed && w->processes)) {
395 + if (w->dcstat.curr.file_system < w->dcstat.prev.file_system) {
396 + w->dcstat.prev.file_system = 0;
397 + }
398 +
399 + value = (collected_number) (!w->dcstat.cache_access) ? 0 :
400 + (long long )w->dcstat.curr.file_system - (long long)w->dcstat.prev.file_system;
401 + write_chart_dimension(w->name, value);
402 + w->dcstat.prev.file_system = w->dcstat.curr.file_system;
403 + }
404 + }
405 + write_end_chart();
406 +
407 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_DC_REQUEST_NOT_FOUND_CHART);
408 + for (w = root; w; w = w->next) {
409 + if (unlikely(w->exposed && w->processes)) {
410 + if (w->dcstat.curr.not_found < w->dcstat.prev.not_found) {
411 + w->dcstat.prev.not_found = 0;
412 + }
413 + value = (collected_number) (!w->dcstat.cache_access) ? 0 :
414 + (long long)w->dcstat.curr.not_found - (long long)w->dcstat.prev.not_found;
415 + write_chart_dimension(w->name, value);
416 + w->dcstat.prev.not_found = w->dcstat.curr.not_found;
417 + }
418 + }
419 + write_end_chart();
420 +}
421 +
422 +/**
423 + * Send global
424 + *
425 + * Send global charts to Netdata
426 + */
427 +static void dcstat_send_global(netdata_publish_dcstat_t *publish)
428 +{
429 + dcstat_update_publish(publish, dcstat_hash_values[NETDATA_KEY_DC_REFERENCE],
430 + dcstat_hash_values[NETDATA_KEY_DC_MISS]);
431 +
432 + netdata_publish_syscall_t *ptr = dcstat_counter_publish_aggregated;
433 + netdata_idx_t value = dcstat_hash_values[NETDATA_KEY_DC_REFERENCE];
434 + if (value != ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall) {
435 + ptr[NETDATA_DCSTAT_IDX_REFERENCE].ncall = value - ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall;
436 + ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall = value;
437 +
438 + value = dcstat_hash_values[NETDATA_KEY_DC_SLOW];
439 + ptr[NETDATA_DCSTAT_IDX_SLOW].ncall = value - ptr[NETDATA_DCSTAT_IDX_SLOW].pcall;
440 + ptr[NETDATA_DCSTAT_IDX_SLOW].pcall = value;
441 +
442 + value = dcstat_hash_values[NETDATA_KEY_DC_MISS];
443 + ptr[NETDATA_DCSTAT_IDX_MISS].ncall = value - ptr[NETDATA_DCSTAT_IDX_MISS].pcall;
444 + ptr[NETDATA_DCSTAT_IDX_MISS].pcall = value;
445 + } else {
446 + ptr[NETDATA_DCSTAT_IDX_REFERENCE].ncall = 0;
447 + ptr[NETDATA_DCSTAT_IDX_SLOW].ncall = 0;
448 + ptr[NETDATA_DCSTAT_IDX_MISS].ncall = 0;
449 + }
450 +
451 + ebpf_one_dimension_write_charts(NETDATA_FILESYSTEM_FAMILY, NETDATA_DC_HIT_CHART,
452 + ptr[NETDATA_DCSTAT_IDX_RATIO].dimension, publish->ratio);
453 +
454 + write_count_chart(
455 + NETDATA_DC_REFERENCE_CHART, NETDATA_FILESYSTEM_FAMILY,
456 + &dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_REFERENCE], 3);
457 +}
458 +
459 +/**
460 +* Main loop for this collector.
461 +*/
462 +static void dcstat_collector(ebpf_module_t *em)
463 +{
464 + dcstat_threads.thread = mallocz(sizeof(netdata_thread_t));
465 + dcstat_threads.start_routine = ebpf_dcstat_read_hash;
466 +
467 + map_fd = dcstat_data.map_fd;
468 +
469 + netdata_thread_create(dcstat_threads.thread, dcstat_threads.name, NETDATA_THREAD_OPTION_JOINABLE,
470 + ebpf_dcstat_read_hash, em);
471 +
472 + netdata_publish_dcstat_t publish;
473 + memset(&publish, 0, sizeof(publish));
474 + int apps = em->apps_charts;
475 + while (!close_ebpf_plugin) {
476 + pthread_mutex_lock(&collect_data_mutex);
477 + pthread_cond_wait(&collect_data_cond_var, &collect_data_mutex);
478 +
479 + if (apps)
480 + read_apps_table();
481 +
482 + pthread_mutex_lock(&lock);
483 +
484 + dcstat_send_global(&publish);
485 +
486 + if (apps)
487 + ebpf_dcache_send_apps_data(apps_groups_root_target);
488 +
489 + pthread_mutex_unlock(&lock);
490 + pthread_mutex_unlock(&collect_data_mutex);
491 + }
492 +}
493 +
494 +/*****************************************************************
495 + *
496 + * INITIALIZE THREAD
497 + *
498 + *****************************************************************/
499 +
500 +/**
501 + * Create filesystem charts
502 + *
503 + * Call ebpf_create_chart to create the charts for the collector.
504 + */
505 +static void ebpf_create_filesystem_charts()
506 +{
507 + ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, NETDATA_DC_HIT_CHART,
508 + "Percentage of files listed inside directory cache",
509 + EBPF_COMMON_DIMENSION_PERCENTAGE, NETDATA_DIRECTORY_FILESYSTEM_SUBMENU,
510 + NULL,
511 + NETDATA_EBPF_CHART_TYPE_LINE,
512 + 21200,
513 + ebpf_create_global_dimension,
514 + dcstat_counter_publish_aggregated, 1);
515 +
516 + ebpf_create_chart(NETDATA_FILESYSTEM_FAMILY, NETDATA_DC_REFERENCE_CHART,
517 + "Variables used to calculate hit ratio.",
518 + EBPF_COMMON_DIMENSION_FILES, NETDATA_DIRECTORY_FILESYSTEM_SUBMENU,
519 + NULL,
520 + NETDATA_EBPF_CHART_TYPE_LINE,
521 + 21201,
522 + ebpf_create_global_dimension,
523 + &dcstat_counter_publish_aggregated[NETDATA_DCSTAT_IDX_REFERENCE], 3);
524 +
525 + fflush(stdout);
526 +}
527 +
528 +/**
529 + * Allocate vectors used with this thread.
530 + *
531 + * We are not testing the return, because callocz does this and shutdown the software
532 + * case it was not possible to allocate.
533 + *
534 + * @param length is the length for the vectors used inside the collector.
535 + */
536 +static void ebpf_dcstat_allocate_global_vectors(size_t length)
537 +{
538 + dcstat_pid = callocz((size_t)pid_max, sizeof(netdata_publish_dcstat_t *));
539 + dcstat_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_dcstat_pid_t));
540 +
541 + memset(dcstat_counter_aggregated_data, 0, length*sizeof(netdata_syscall_stat_t));
542 + memset(dcstat_counter_publish_aggregated, 0, length*sizeof(netdata_publish_syscall_t));
543 +}
544 +
545 +/*****************************************************************
546 + *
547 + * MAIN THREAD
548 + *
549 + *****************************************************************/
550 +
551 +/**
552 + * Directory Cache thread
553 + *
554 + * Thread used to make dcstat thread
555 + *
556 + * @param ptr a pointer to `struct ebpf_module`
557 + *
558 + * @return It always returns NULL
559 + */
560 +void *ebpf_dcstat_thread(void *ptr)
561 +{
562 + netdata_thread_cleanup_push(ebpf_dcstat_cleanup, ptr);
563 +
564 + ebpf_module_t *em = (ebpf_module_t *)ptr;
565 + em->maps = dcstat_maps;
566 + fill_ebpf_data(&dcstat_data);
567 +
568 + ebpf_update_module(em, &dcstat_config, NETDATA_DIRECTORY_DCSTAT_CONFIG_FILE);
569 + ebpf_update_pid_table(&dcstat_maps[0], em);
570 +
571 + ebpf_update_names(dc_optional_name, em);
572 +
573 + if (!em->enabled)
574 + goto enddcstat;
575 +
576 + ebpf_dcstat_allocate_global_vectors(NETDATA_DCSTAT_IDX_END);
577 +
578 + pthread_mutex_lock(&lock);
579 +
580 + probe_links = ebpf_load_program(ebpf_plugin_dir, em, kernel_string, &objects, dcstat_data.map_fd);
581 + if (!probe_links) {
582 + pthread_mutex_unlock(&lock);
583 + goto enddcstat;
584 + }
585 +
586 + int algorithms[NETDATA_DCSTAT_IDX_END] = {
587 + NETDATA_EBPF_ABSOLUTE_IDX, NETDATA_EBPF_ABSOLUTE_IDX, NETDATA_EBPF_ABSOLUTE_IDX,
588 + NETDATA_EBPF_ABSOLUTE_IDX
589 + };
590 +
591 + ebpf_global_labels(dcstat_counter_aggregated_data, dcstat_counter_publish_aggregated,
592 + dcstat_counter_dimension_name, dcstat_counter_dimension_name,
593 + algorithms, NETDATA_DCSTAT_IDX_END);
594 +
595 + ebpf_create_filesystem_charts();
596 + pthread_mutex_unlock(&lock);
597 +
598 + dcstat_collector(em);
599 +
600 +enddcstat:
601 + netdata_thread_cleanup_pop(1);
602 + return NULL;
603 +}
collectors/ebpf.plugin/ebpf_dcstat.h new
+64
@@ -0,0 +1,64 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_EBPF_DCSTAT_H
4 +#define NETDATA_EBPF_DCSTAT_H 1
5 +
6 +
7 +// charts
8 +#define NETDATA_DC_HIT_CHART "dc_hit_ratio"
9 +#define NETDATA_DC_REFERENCE_CHART "dc_reference"
10 +#define NETDATA_DC_REQUEST_NOT_CACHE_CHART "dc_not_cache"
11 +#define NETDATA_DC_REQUEST_NOT_FOUND_CHART "dc_not_found"
12 +
13 +#define NETDATA_DIRECTORY_CACHE_SUBMENU "directory cache (eBPF)"
14 +#define NETDATA_DIRECTORY_FILESYSTEM_SUBMENU "Directory Cache (eBPF)"
15 +
16 +// configuration file
17 +#define NETDATA_DIRECTORY_DCSTAT_CONFIG_FILE "dcstat.conf"
18 +
19 +#define NETDATA_LATENCY_DCSTAT_SLEEP_MS 700000ULL
20 +
21 +enum directory_cache_indexes {
22 + NETDATA_DCSTAT_IDX_RATIO,
23 + NETDATA_DCSTAT_IDX_REFERENCE,
24 + NETDATA_DCSTAT_IDX_SLOW,
25 + NETDATA_DCSTAT_IDX_MISS,
26 +
27 + // Keep this as last and don't skip numbers as it is used as element counter
28 + NETDATA_DCSTAT_IDX_END
29 +};
30 +
31 +enum directory_cache_tables {
32 + NETDATA_DCSTAT_GLOBAL_STATS,
33 + NETDATA_DCSTAT_PID_STATS
34 +};
35 +
36 +// variables
37 +enum directory_cache_counters {
38 + NETDATA_KEY_DC_REFERENCE,
39 + NETDATA_KEY_DC_SLOW,
40 + NETDATA_KEY_DC_MISS,
41 +
42 + // Keep this as last and don't skip numbers as it is used as element counter
43 + NETDATA_DIRECTORY_CACHE_END
44 +};
45 +
46 +typedef struct netdata_publish_dcstat_pid {
47 + uint64_t cache_access;
48 + uint64_t file_system;
49 + uint64_t not_found;
50 +} netdata_dcstat_pid_t;
51 +
52 +typedef struct netdata_publish_dcstat {
53 + long long ratio;
54 + long long cache_access;
55 +
56 + netdata_dcstat_pid_t curr;
57 + netdata_dcstat_pid_t prev;
58 +} netdata_publish_dcstat_t;
59 +
60 +extern void *ebpf_dcstat_thread(void *ptr);
61 +extern void ebpf_dcstat_create_apps_charts(struct ebpf_module *em, void *ptr);
62 +extern void clean_dcstat_pid_structures();
63 +
64 +#endif // NETDATA_EBPF_DCSTAT_H
libnetdata/ebpf/ebpf.c
+44
@@ -416,6 +416,50 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, char *
416 return ebpf_attach_programs(*obj, count_programs, em->names);
417 }
418
419 +static char *ebpf_update_name(char *search)
420 +{
421 + char filename[FILENAME_MAX + 1];
422 + char *ret = NULL;
423 + snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, NETDATA_KALLSYMS);
424 + procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
425 + if(unlikely(!ff)) {
426 + error("Cannot open %s%s", netdata_configured_host_prefix, NETDATA_KALLSYMS);
427 + return ret;
428 + }
429 +
430 + ff = procfile_readall(ff);
431 + if(unlikely(!ff))
432 + return ret;
433 +
434 + unsigned long i, lines = procfile_lines(ff);
435 + size_t length = strlen(search);
436 + for(i = 0; i < lines ; i++) {
437 + char *cmp = procfile_lineword(ff, i,2);;
438 + if (!strncmp(search, cmp, length)) {
439 + ret = strdupz(cmp);
440 + break;
441 + }
442 + }
443 +
444 + procfile_close(ff);
445 +
446 + return ret;
447 +}
448 +
449 +void ebpf_update_names(ebpf_specify_name_t *opt, ebpf_module_t *em)
450 +{
451 + int mode = em->mode;
452 + em->names = opt;
453 +
454 + size_t i = 0;
455 + while (opt[i].program_name) {
456 + opt[i].retprobe = (mode == MODE_RETURN);
457 + opt[i].optional = ebpf_update_name(opt[i].function_to_attach);
458 +
459 + i++;
460 + }
461 +}
462 +
463 //----------------------------------------------------------------------------------------------------------------------
464
465 void ebpf_mount_config_name(char *filename, size_t length, char *path, char *config)
libnetdata/ebpf/ebpf.h
+2
@@ -7,6 +7,7 @@
7 #include <bpf/libbpf.h>
8
9 #define NETDATA_DEBUGFS "/sys/kernel/debug/tracing/"
10 +#define NETDATA_KALLSYMS "/proc/kallsyms"
11
12 // Config files
13 #define EBPF_GLOBAL_SECTION "global"
@@ -143,5 +144,6 @@ extern void ebpf_mount_config_name(char *filename, size_t length, char *path, ch
144 extern int ebpf_load_config(struct config *config, char *filename);
145 extern void ebpf_update_module_using_config(ebpf_module_t *modules, struct config *cfg);
146 extern void ebpf_update_module(ebpf_module_t *em, struct config *cfg, char *cfg_file);
147 +extern void ebpf_update_names(ebpf_specify_name_t *opt, ebpf_module_t *em);
148
149 #endif /* NETDATA_EBPF_H */
web/gui/dashboard_info.js
+29
@@ -559,6 +559,11 @@ netdataDashboard.menu = {
559 info: 'Monitor system calls, internal functions, bytes read, bytes written and errors using <code>eBPF</code>.'
560 },
561
562 + 'filesystem': {
563 + title: 'Filesystem',
564 + icon: '<i class="fas fa-hdd"></i>',
565 + },
566 +
567 'vernemq': {
568 title: 'VerneMQ',
569 icon: '<i class="fas fa-comments"></i>',
@@ -1079,6 +1084,14 @@ netdataDashboard.context = {
1084 info: 'System calls for <a href="https://man7.org/linux/man-pages/man2/sync_file_range.2.html" target="_blank">sync_file_range()</a> permits fine control when synchronizing the open file referred to by the file descriptor fd with disk. This system call is extremely dangerous and should not be used in portable programs.'
1085 },
1086
1087 + 'filesystem.dc_hit_ratio': {
1088 + info: 'Percentage of file accesses that were present in the directory cache. 100% means that every file that was accessed was present in the directory cache. If files are not present in the directory cache 1) they are not present in the file system, 2) the files were not accessed before. Read more about <a href="https://www.kernel.org/doc/htmldocs/filesystems/the_directory_cache.html" target="_blank">directory cache</a>.'
1089 + },
1090 +
1091 + 'filesystem.dc_reference': {
1092 + info: 'Counters of file accesses. <code>Reference</code> is when there is a file access and the file is not present in the directory cache. <code>Miss</code> is when there is file access and the file is not found in the filesystem. <code>Slow</code> is when there is a file access and the file is present in the filesystem but not in the directory cache. Read more about <a href="https://www.kernel.org/doc/htmldocs/filesystems/the_directory_cache.html" target="_blank">directory cache</a>.'
1093 + },
1094 +
1095 // ------------------------------------------------------------------------
1096 // network interfaces
1097
@@ -1276,6 +1289,22 @@ netdataDashboard.context = {
1289 info: 'Calls for function <code>udp_recvmsg</code>.'
1290 },
1291
1292 + 'apps.dc_hit_ratio': {
1293 + info: 'Percentage of file accesses that were present in the directory cache. 100% means that every file that was accessed was present in the directory cache. If files are not present in the directory cache 1) they are not present in the file system, 2) the files were not accessed before. Read more about <a href="https://www.kernel.org/doc/htmldocs/filesystems/the_directory_cache.html" target="_blank">directory cache</a>.'
1294 + },
1295 +
1296 + 'apps.dc_reference': {
1297 + info: 'Counters of file accesses. <code>Reference</code> is when there is a file access, see the <code>filesystem.dc_reference</code> chart for more context. Read more about <a href="https://www.kernel.org/doc/htmldocs/filesystems/the_directory_cache.html" target="_blank">directory cache</a>.'
1298 + },
1299 +
1300 + 'apps.dc_not_cache': {
1301 + info: 'Counters of file accesses. <code>Slow</code> is when there is a file access and the file is not present in the directory cache, see the <code>filesystem.dc_reference</code> chart for more context. Read more about <a href="https://www.kernel.org/doc/htmldocs/filesystems/the_directory_cache.html" target="_blank">directory cache</a>.'
1302 + },
1303 +
1304 + 'apps.dc_not_found': {
1305 + info: 'Counters of file accesses. <code>Miss</code> is when there is file access and the file is not found in the filesystem, see the <code>filesystem.dc_reference</code> chart for more context. Read more about <a href="https://www.kernel.org/doc/htmldocs/filesystems/the_directory_cache.html" target="_blank">directory cache</a>.'
1306 + },
1307 +
1308 // ------------------------------------------------------------------------
1309 // USERS
1310