master
c 1,480 lines 44.5 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "ebpf.h"
4 #include "ebpf_shm.h"
5 #include "libbpf_api/ebpf_library.h"
6
7 static char *shm_dimension_name[NETDATA_SHM_END] = {"get", "at", "dt", "ctl"};
8 static netdata_syscall_stat_t shm_aggregated_data[NETDATA_SHM_END];
9 static netdata_publish_syscall_t shm_publish_aggregated[NETDATA_SHM_END];
10
11 netdata_ebpf_shm_t *shm_vector = NULL;
12
13 static netdata_idx_t shm_hash_values[NETDATA_SHM_END];
14 static netdata_idx_t *shm_values = NULL;
15
16 struct config shm_config = APPCONFIG_INITIALIZER;
17
18 static ebpf_local_maps_t shm_maps[] = {
19 {.name = "tbl_pid_shm",
20 .internal_input = ND_EBPF_DEFAULT_PID_SIZE,
21 .user_input = 0,
22 .type = NETDATA_EBPF_MAP_RESIZABLE | NETDATA_EBPF_MAP_PID,
23 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
24 #ifdef LIBBPF_MAJOR_VERSION
25 .map_type = BPF_MAP_TYPE_PERCPU_HASH
26 #endif
27 },
28 {.name = "shm_ctrl",
29 .internal_input = NETDATA_CONTROLLER_END,
30 .user_input = 0,
31 .type = NETDATA_EBPF_MAP_CONTROLLER,
32 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
33 #ifdef LIBBPF_MAJOR_VERSION
34 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
35 #endif
36 },
37 {.name = "tbl_shm",
38 .internal_input = NETDATA_SHM_END,
39 .user_input = 0,
40 .type = NETDATA_EBPF_MAP_STATIC,
41 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
42 #ifdef LIBBPF_MAJOR_VERSION
43 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
44 #endif
45 },
46 {.name = NULL, .internal_input = 0, .user_input = 0}};
47
48 netdata_ebpf_targets_t shm_targets[] = {
49 {.name = "shmget", .mode = EBPF_LOAD_TRAMPOLINE},
50 {.name = "shmat", .mode = EBPF_LOAD_TRAMPOLINE},
51 {.name = "shmdt", .mode = EBPF_LOAD_TRAMPOLINE},
52 {.name = "shmctl", .mode = EBPF_LOAD_TRAMPOLINE},
53 {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
54
55 #ifdef NETDATA_DEV_MODE
56 int shm_disable_priority;
57 #endif
58
59 struct netdata_static_thread ebpf_read_shm = {
60 .name = "EBPF_READ_SHM",
61 .config_section = NULL,
62 .config_name = NULL,
63 .env_name = NULL,
64 .enabled = 1,
65 .thread = NULL,
66 .init_routine = NULL,
67 .start_routine = NULL};
68
69 #ifdef LIBBPF_MAJOR_VERSION
70 /*****************************************************************
71 *
72 * BTF FUNCTIONS
73 *
74 *****************************************************************/
75
76 /*
77 * Disable tracepoint
78 *
79 * Disable all tracepoints to use exclusively another method.
80 *
81 * @param obj is the main structure for bpf objects.
82 */
83 static void ebpf_shm_disable_tracepoint(struct shm_bpf *obj)
84 {
85 bpf_program__set_autoload(obj->progs.netdata_syscall_shmget, false);
86 bpf_program__set_autoload(obj->progs.netdata_syscall_shmat, false);
87 bpf_program__set_autoload(obj->progs.netdata_syscall_shmdt, false);
88 bpf_program__set_autoload(obj->progs.netdata_syscall_shmctl, false);
89 }
90
91 /*
92 * Disable probe
93 *
94 * Disable all probes to use exclusively another method.
95 *
96 * @param obj is the main structure for bpf objects.
97 */
98 static void ebpf_disable_probe(struct shm_bpf *obj)
99 {
100 bpf_program__set_autoload(obj->progs.netdata_shmget_probe, false);
101 bpf_program__set_autoload(obj->progs.netdata_shmat_probe, false);
102 bpf_program__set_autoload(obj->progs.netdata_shmdt_probe, false);
103 bpf_program__set_autoload(obj->progs.netdata_shmctl_probe, false);
104 }
105
106 /*
107 * Disable trampoline
108 *
109 * Disable all trampoline to use exclusively another method.
110 *
111 * @param obj is the main structure for bpf objects.
112 */
113 static void ebpf_disable_trampoline(struct shm_bpf *obj)
114 {
115 bpf_program__set_autoload(obj->progs.netdata_shmget_fentry, false);
116 bpf_program__set_autoload(obj->progs.netdata_shmat_fentry, false);
117 bpf_program__set_autoload(obj->progs.netdata_shmdt_fentry, false);
118 bpf_program__set_autoload(obj->progs.netdata_shmctl_fentry, false);
119 }
120
121 /**
122 * Set trampoline target
123 *
124 * Set the targets we will monitor.
125 *
126 * @param obj is the main structure for bpf objects.
127 */
128 static void ebpf_set_trampoline_target(struct shm_bpf *obj)
129 {
130 char syscall[NETDATA_EBPF_MAX_SYSCALL_LENGTH + 1];
131 ebpf_select_host_prefix(
132 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, shm_targets[NETDATA_KEY_SHMGET_CALL].name, running_on_kernel);
133
134 bpf_program__set_attach_target(obj->progs.netdata_shmget_fentry, 0, syscall);
135
136 ebpf_select_host_prefix(
137 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, shm_targets[NETDATA_KEY_SHMAT_CALL].name, running_on_kernel);
138 bpf_program__set_attach_target(obj->progs.netdata_shmat_fentry, 0, syscall);
139
140 ebpf_select_host_prefix(
141 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, shm_targets[NETDATA_KEY_SHMDT_CALL].name, running_on_kernel);
142 bpf_program__set_attach_target(obj->progs.netdata_shmdt_fentry, 0, syscall);
143
144 ebpf_select_host_prefix(
145 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, shm_targets[NETDATA_KEY_SHMCTL_CALL].name, running_on_kernel);
146 bpf_program__set_attach_target(obj->progs.netdata_shmctl_fentry, 0, syscall);
147 }
148
149 /**
150 * SHM Attach Probe
151 *
152 * Attach probes to target
153 *
154 * @param obj is the main structure for bpf objects.
155 *
156 * @return It returns 0 on success and -1 otherwise.
157 */
158 static int ebpf_shm_attach_probe(struct shm_bpf *obj)
159 {
160 char syscall[NETDATA_EBPF_MAX_SYSCALL_LENGTH + 1];
161 ebpf_select_host_prefix(
162 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, shm_targets[NETDATA_KEY_SHMGET_CALL].name, running_on_kernel);
163
164 obj->links.netdata_shmget_probe = bpf_program__attach_kprobe(obj->progs.netdata_shmget_probe, false, syscall);
165 long ret = libbpf_get_error(obj->links.netdata_shmget_probe);
166 if (ret)
167 return -1;
168
169 ebpf_select_host_prefix(
170 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, shm_targets[NETDATA_KEY_SHMAT_CALL].name, running_on_kernel);
171 obj->links.netdata_shmat_probe = bpf_program__attach_kprobe(obj->progs.netdata_shmat_probe, false, syscall);
172 ret = libbpf_get_error(obj->links.netdata_shmat_probe);
173 if (ret)
174 return -1;
175
176 ebpf_select_host_prefix(
177 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, shm_targets[NETDATA_KEY_SHMDT_CALL].name, running_on_kernel);
178 obj->links.netdata_shmdt_probe = bpf_program__attach_kprobe(obj->progs.netdata_shmdt_probe, false, syscall);
179 ret = libbpf_get_error(obj->links.netdata_shmdt_probe);
180 if (ret)
181 return -1;
182
183 ebpf_select_host_prefix(
184 syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH, shm_targets[NETDATA_KEY_SHMCTL_CALL].name, running_on_kernel);
185 obj->links.netdata_shmctl_probe = bpf_program__attach_kprobe(obj->progs.netdata_shmctl_probe, false, syscall);
186 ret = libbpf_get_error(obj->links.netdata_shmctl_probe);
187 if (ret)
188 return -1;
189
190 return 0;
191 }
192
193 /**
194 * Set hash tables
195 *
196 * Set the values for maps according the value given by kernel.
197 */
198 static void ebpf_shm_set_hash_tables(struct shm_bpf *obj)
199 {
200 shm_maps[NETDATA_PID_SHM_TABLE].map_fd = bpf_map__fd(obj->maps.tbl_pid_shm);
201 shm_maps[NETDATA_SHM_CONTROLLER].map_fd = bpf_map__fd(obj->maps.shm_ctrl);
202 shm_maps[NETDATA_SHM_GLOBAL_TABLE].map_fd = bpf_map__fd(obj->maps.tbl_shm);
203 }
204
205 /**
206 * Adjust Map Size
207 *
208 * Resize maps according input from users.
209 *
210 * @param obj is the main structure for bpf objects.
211 * @param em structure with configuration
212 */
213 static void ebpf_shm_adjust_map(struct shm_bpf *obj, ebpf_module_t *em)
214 {
215 ebpf_update_map_size(
216 obj->maps.tbl_pid_shm, &shm_maps[NETDATA_PID_SHM_TABLE], em, bpf_map__name(obj->maps.tbl_pid_shm));
217
218 ebpf_update_map_type(obj->maps.tbl_shm, &shm_maps[NETDATA_SHM_GLOBAL_TABLE]);
219 ebpf_update_map_type(obj->maps.tbl_pid_shm, &shm_maps[NETDATA_PID_SHM_TABLE]);
220 ebpf_update_map_type(obj->maps.shm_ctrl, &shm_maps[NETDATA_SHM_CONTROLLER]);
221 }
222
223 /**
224 * Load and attach
225 *
226 * Load and attach the eBPF code in kernel.
227 *
228 * @param obj is the main structure for bpf objects.
229 * @param em structure with configuration
230 *
231 * @return it returns 0 on success and -1 otherwise
232 */
233 static inline int ebpf_shm_load_and_attach(struct shm_bpf *obj, ebpf_module_t *em)
234 {
235 netdata_ebpf_targets_t *shmt = em->targets;
236 netdata_ebpf_program_loaded_t mode = shmt[NETDATA_KEY_SHMGET_CALL].mode;
237
238 // We are testing only one, because all will have the same behavior
239 if (mode == EBPF_LOAD_TRAMPOLINE) {
240 ebpf_shm_disable_tracepoint(obj);
241 ebpf_disable_probe(obj);
242
243 ebpf_set_trampoline_target(obj);
244 } else if (mode == EBPF_LOAD_PROBE || mode == EBPF_LOAD_RETPROBE) {
245 ebpf_shm_disable_tracepoint(obj);
246 ebpf_disable_trampoline(obj);
247 } else {
248 ebpf_disable_probe(obj);
249 ebpf_disable_trampoline(obj);
250 }
251
252 ebpf_shm_adjust_map(obj, em);
253
254 int ret = shm_bpf__load(obj);
255 if (!ret) {
256 if (mode != EBPF_LOAD_PROBE && mode != EBPF_LOAD_RETPROBE)
257 shm_bpf__attach(obj);
258 else
259 ret = ebpf_shm_attach_probe(obj);
260
261 if (!ret)
262 ebpf_shm_set_hash_tables(obj);
263 }
264
265 return ret;
266 }
267 #endif
268 /*****************************************************************
269 * FUNCTIONS TO CLOSE THE THREAD
270 *****************************************************************/
271
272 static void ebpf_obsolete_specific_shm_charts(char *type, int update_every);
273
274 /**
275 * Obsolete services
276 *
277 * Obsolete all service charts created
278 *
279 * @param em a pointer to `struct ebpf_module`
280 */
281 static void ebpf_obsolete_shm_services(ebpf_module_t *em, char *id)
282 {
283 ebpf_write_chart_obsolete(
284 id,
285 NETDATA_SHMGET_CHART,
286 "",
287 "Calls to syscall shmget(2).",
288 EBPF_COMMON_UNITS_CALLS_PER_SEC,
289 NETDATA_APPS_IPC_SHM_GROUP,
290 NETDATA_EBPF_CHART_TYPE_STACKED,
291 NETDATA_SYSTEMD_SHM_GET_CONTEXT,
292 20191,
293 em->update_every);
294
295 ebpf_write_chart_obsolete(
296 id,
297 NETDATA_SHMAT_CHART,
298 "",
299 "Calls to syscall shmat(2).",
300 EBPF_COMMON_UNITS_CALLS_PER_SEC,
301 NETDATA_APPS_IPC_SHM_GROUP,
302 NETDATA_EBPF_CHART_TYPE_STACKED,
303 NETDATA_SYSTEMD_SHM_AT_CONTEXT,
304 20192,
305 em->update_every);
306
307 ebpf_write_chart_obsolete(
308 id,
309 NETDATA_SHMDT_CHART,
310 "",
311 "Calls to syscall shmdt(2).",
312 EBPF_COMMON_UNITS_CALLS_PER_SEC,
313 NETDATA_APPS_IPC_SHM_GROUP,
314 NETDATA_EBPF_CHART_TYPE_STACKED,
315 NETDATA_SYSTEMD_SHM_DT_CONTEXT,
316 20193,
317 em->update_every);
318
319 ebpf_write_chart_obsolete(
320 id,
321 NETDATA_SHMCTL_CHART,
322 "",
323 "Calls to syscall shmctl(2).",
324 EBPF_COMMON_UNITS_CALLS_PER_SEC,
325 NETDATA_APPS_IPC_SHM_GROUP,
326 NETDATA_EBPF_CHART_TYPE_STACKED,
327 NETDATA_SYSTEMD_SHM_CTL_CONTEXT,
328 20194,
329 em->update_every);
330 }
331
332 /**
333 * Obsolete cgroup chart
334 *
335 * Send obsolete for all charts created before to close.
336 *
337 * @param em a pointer to `struct ebpf_module`
338 */
339 static inline void ebpf_obsolete_shm_cgroup_charts(ebpf_module_t *em)
340 {
341 netdata_mutex_lock(&mutex_cgroup_shm);
342
343 ebpf_cgroup_target_t *ect;
344 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
345 if (ect->systemd) {
346 ebpf_obsolete_shm_services(em, ect->name);
347
348 continue;
349 }
350
351 ebpf_obsolete_specific_shm_charts(ect->name, em->update_every);
352 }
353 netdata_mutex_unlock(&mutex_cgroup_shm);
354 }
355
356 /**
357 * Obsolette apps charts
358 *
359 * Obsolete apps charts.
360 *
361 * @param em a pointer to the structure with the default values.
362 */
363 void ebpf_obsolete_shm_apps_charts(struct ebpf_module *em)
364 {
365 struct ebpf_target *w;
366 int update_every = em->update_every;
367 netdata_mutex_lock(&collect_data_mutex);
368 for (w = apps_groups_root_target; w; w = w->next) {
369 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_SHM_IDX))))
370 continue;
371
372 ebpf_write_chart_obsolete(
373 NETDATA_APP_FAMILY,
374 w->clean_name,
375 "_ebpf_shmget_call",
376 "Calls to syscall shmget(2).",
377 EBPF_COMMON_UNITS_CALLS_PER_SEC,
378 NETDATA_APPS_IPC_SHM_GROUP,
379 NETDATA_EBPF_CHART_TYPE_STACKED,
380 "app.ebpf_shmget_call",
381 20191,
382 update_every);
383
384 ebpf_write_chart_obsolete(
385 NETDATA_APP_FAMILY,
386 w->clean_name,
387 "_ebpf_shmat_call",
388 "Calls to syscall shmat(2).",
389 EBPF_COMMON_UNITS_CALLS_PER_SEC,
390 NETDATA_APPS_IPC_SHM_GROUP,
391 NETDATA_EBPF_CHART_TYPE_STACKED,
392 "app.ebpf_shmat_call",
393 20192,
394 update_every);
395
396 ebpf_write_chart_obsolete(
397 NETDATA_APP_FAMILY,
398 w->clean_name,
399 "_ebpf_shmdt_call",
400 "Calls to syscall shmdt(2).",
401 EBPF_COMMON_UNITS_CALLS_PER_SEC,
402 NETDATA_APPS_IPC_SHM_GROUP,
403 NETDATA_EBPF_CHART_TYPE_STACKED,
404 "app.ebpf_shmdt_call",
405 20193,
406 update_every);
407
408 ebpf_write_chart_obsolete(
409 NETDATA_APP_FAMILY,
410 w->clean_name,
411 "_ebpf_shmctl_call",
412 "Calls to syscall shmctl(2).",
413 EBPF_COMMON_UNITS_CALLS_PER_SEC,
414 NETDATA_APPS_IPC_SHM_GROUP,
415 NETDATA_EBPF_CHART_TYPE_STACKED,
416 "app.ebpf_shmctl_call",
417 20194,
418 update_every);
419
420 w->charts_created &= ~(1 << EBPF_MODULE_SHM_IDX);
421 }
422 netdata_mutex_unlock(&collect_data_mutex);
423 }
424
425 /**
426 * Obsolete global
427 *
428 * Obsolete global charts created by thread.
429 *
430 * @param em a pointer to `struct ebpf_module`
431 */
432 static void ebpf_obsolete_shm_global(ebpf_module_t *em)
433 {
434 ebpf_write_chart_obsolete(
435 NETDATA_EBPF_SYSTEM_GROUP,
436 NETDATA_SHM_GLOBAL_CHART,
437 "",
438 "Calls to shared memory system calls",
439 EBPF_COMMON_UNITS_CALLS_PER_SEC,
440 NETDATA_SYSTEM_IPC_SHM_SUBMENU,
441 NETDATA_EBPF_CHART_TYPE_LINE,
442 "system.shared_memory_calls",
443 NETDATA_CHART_PRIO_SYSTEM_IPC_SHARED_MEM_CALLS,
444 em->update_every);
445 }
446
447 /**
448 * SHM Exit
449 *
450 * Cancel child thread.
451 *
452 * @param ptr thread data.
453 */
454 void ebpf_shm_unload_bpf(ebpf_module_t *em)
455 {
456 #ifdef LIBBPF_MAJOR_VERSION
457 if (shm_bpf_obj) {
458 shm_bpf__destroy(shm_bpf_obj);
459 shm_bpf_obj = NULL;
460 }
461 #endif
462 if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
463 ebpf_unload_legacy_code(em->objects, em->probe_links);
464 em->objects = NULL;
465 em->probe_links = NULL;
466 }
467 }
468
469 static void ebpf_shm_exit(void *pptr)
470 {
471 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
472 if (!em)
473 return;
474
475 netdata_mutex_lock(&lock);
476 collect_pids &= ~(1 << EBPF_MODULE_SHM_IDX);
477 netdata_mutex_unlock(&lock);
478
479 if (ebpf_read_shm.thread) {
480 nd_thread_signal_cancel(ebpf_read_shm.thread);
481 nd_thread_join(ebpf_read_shm.thread);
482 }
483
484 // Drop this module's bits from the shared PID pool so its slots don't
485 // stay pinned if the plugin keeps running after the module stops.
486 if (integration_shm && ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
487 netdata_ebpf_sweep_shm_for_module_unsafe(NETDATA_EBPF_PIDS_SHM_IDX);
488 sem_post(shm_mutex_ebpf_integration);
489 }
490
491 if (ebpf_module_enabled_get(em) == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
492 netdata_mutex_lock(&lock);
493 if (em->cgroup_charts) {
494 ebpf_obsolete_shm_cgroup_charts(em);
495 fflush(stdout);
496 }
497
498 if (em->apps_charts & NETDATA_EBPF_APPS_FLAG_CHART_CREATED) {
499 ebpf_obsolete_shm_apps_charts(em);
500 }
501
502 ebpf_obsolete_shm_global(em);
503
504 fflush(stdout);
505 netdata_mutex_unlock(&lock);
506 }
507
508 if (!ebpf_plugin_stop() && em->functions.bpf_unload)
509 em->functions.bpf_unload(em);
510
511 netdata_mutex_lock(&ebpf_exit_cleanup);
512 ebpf_module_enabled_set(em, NETDATA_THREAD_EBPF_STOPPED);
513 netdata_mutex_unlock(&ebpf_exit_cleanup);
514 }
515
516 /*****************************************************************
517 * COLLECTOR THREAD
518 *****************************************************************/
519
520 /**
521 * Apps Accumulator
522 *
523 * Sum all values read from kernel and store in the first address.
524 *
525 * @param out the vector with read values.
526 * @param maps_per_core do I need to read all cores?
527 */
528 static void shm_apps_accumulator(netdata_ebpf_shm_t *out, int maps_per_core)
529 {
530 int i, end = (maps_per_core) ? ebpf_nprocs : 1;
531 netdata_ebpf_shm_t *total = &out[0];
532 for (i = 1; i < end; i++) {
533 if (ebpf_plugin_stop())
534 break;
535
536 netdata_ebpf_shm_t *w = &out[i];
537 total->get += w->get;
538 total->at += w->at;
539 total->dt += w->dt;
540 total->ctl += w->ctl;
541
542 if (!total->name[0] && w->name[0])
543 strncpyz(total->name, w->name, sizeof(total->name) - 1);
544 }
545 }
546
547 /**
548 * Update cgroup
549 *
550 * Update cgroup data based in
551 *
552 * @param maps_per_core do I need to read all cores?
553 */
554 static void ebpf_update_shm_cgroup(void)
555 {
556 ebpf_cgroup_target_t *ect;
557
558 memset(shm_vector, 0, sizeof(netdata_ebpf_shm_t));
559
560 netdata_mutex_lock(&mutex_cgroup_shm);
561 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
562 if (ebpf_plugin_stop())
563 break;
564
565 struct pid_on_target2 *pids;
566 for (pids = ect->pids; pids; pids = pids->next) {
567 uint32_t pid = pids->pid;
568 netdata_publish_shm_t *out = &pids->shm;
569 netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_lookup_shm_pointer_unsafe(pid);
570 if (!local_pid || !(local_pid->threads & (1U << (NETDATA_EBPF_PIDS_SHM_IDX << 1))))
571 continue;
572
573 netdata_publish_shm_t *in = &local_pid->shm;
574
575 memcpy(out, in, sizeof(netdata_publish_shm_t));
576 }
577 }
578 netdata_mutex_unlock(&mutex_cgroup_shm);
579 }
580
581 /**
582 * Read APPS table
583 *
584 * Read the apps table and store data inside the structure.
585 *
586 * @param maps_per_core do I need to read all cores?
587 */
588 static void ebpf_read_shm_apps_table(int maps_per_core)
589 {
590 netdata_ebpf_shm_t *cv = shm_vector;
591 int fd = shm_maps[NETDATA_PID_SHM_TABLE].map_fd;
592 size_t length = sizeof(netdata_ebpf_shm_t);
593 if (maps_per_core)
594 length *= ebpf_nprocs;
595
596 uint32_t key = 0, next_key = 0;
597 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
598 if (ebpf_plugin_stop())
599 break;
600
601 if (bpf_map_lookup_elem(fd, &key, cv)) {
602 goto end_shm_loop;
603 }
604
605 shm_apps_accumulator(cv, maps_per_core);
606
607 netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_get_shm_pointer_unsafe(key, NETDATA_EBPF_PIDS_SHM_IDX);
608 if (!local_pid)
609 goto end_shm_loop;
610 netdata_publish_shm_t *publish = &local_pid->shm;
611
612 if (!publish->ct || publish->ct != cv->ct) {
613 memcpy(publish, &cv[0], sizeof(netdata_publish_shm_t));
614 } else {
615 if (kill((pid_t)key, 0) == -1 && errno == ESRCH) {
616 if (netdata_ebpf_reset_shm_pointer_unsafe(fd, key, NETDATA_EBPF_PIDS_SHM_IDX))
617 memset(publish, 0, sizeof(*publish));
618 }
619 }
620
621 end_shm_loop:
622 // now that we've consumed the value, zero it out in the map.
623 memset(cv, 0, length);
624 bpf_map_update_elem(fd, &key, cv, BPF_EXIST);
625
626 key = next_key;
627 }
628 }
629
630 /**
631 * Send global charts to netdata agent.
632 */
633 static void shm_send_global(void)
634 {
635 ebpf_write_begin_chart(NETDATA_EBPF_SYSTEM_GROUP, NETDATA_SHM_GLOBAL_CHART, "");
636 write_chart_dimension(
637 shm_publish_aggregated[NETDATA_KEY_SHMGET_CALL].dimension, (long long)shm_hash_values[NETDATA_KEY_SHMGET_CALL]);
638 write_chart_dimension(
639 shm_publish_aggregated[NETDATA_KEY_SHMAT_CALL].dimension, (long long)shm_hash_values[NETDATA_KEY_SHMAT_CALL]);
640 write_chart_dimension(
641 shm_publish_aggregated[NETDATA_KEY_SHMDT_CALL].dimension, (long long)shm_hash_values[NETDATA_KEY_SHMDT_CALL]);
642 write_chart_dimension(
643 shm_publish_aggregated[NETDATA_KEY_SHMCTL_CALL].dimension, (long long)shm_hash_values[NETDATA_KEY_SHMCTL_CALL]);
644 ebpf_write_end_chart();
645 }
646
647 /**
648 * Read global counter
649 *
650 * Read the table with number of calls for all functions
651 *
652 * @param stats vector used to read data from control table.
653 * @param maps_per_core do I need to read all cores?
654 */
655 static void ebpf_shm_read_global_table(netdata_idx_t *stats, int maps_per_core)
656 {
657 ebpf_read_global_table_stats(
658 shm_hash_values,
659 shm_values,
660 shm_maps[NETDATA_SHM_GLOBAL_TABLE].map_fd,
661 maps_per_core,
662 NETDATA_KEY_SHMGET_CALL,
663 NETDATA_SHM_END);
664
665 ebpf_read_global_table_stats(
666 stats,
667 shm_values,
668 shm_maps[NETDATA_SHM_CONTROLLER].map_fd,
669 maps_per_core,
670 NETDATA_CONTROLLER_PID_TABLE_ADD,
671 NETDATA_CONTROLLER_END);
672 }
673
674 /**
675 * Sum values for all targets.
676 */
677 static void ebpf_shm_sum_pids(netdata_publish_shm_t *shm, struct ebpf_pid_on_target *root)
678 {
679 memset(shm, 0, sizeof(netdata_publish_shm_t));
680 for (; root; root = root->next) {
681 uint32_t pid = root->pid;
682 netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_lookup_shm_pointer_unsafe(pid);
683 if (!local_pid || !(local_pid->threads & (1U << (NETDATA_EBPF_PIDS_SHM_IDX << 1))))
684 continue;
685
686 netdata_publish_shm_t *w = &local_pid->shm;
687
688 shm->get += w->get;
689 shm->at += w->at;
690 shm->dt += w->dt;
691 shm->ctl += w->ctl;
692 }
693 }
694
695 /**
696 * Send data to Netdata calling auxiliary functions.
697 *
698 * @param root the target list.
699 */
700 void ebpf_shm_send_apps_data(struct ebpf_target *root)
701 {
702 struct ebpf_target *w;
703
704 netdata_mutex_lock(&collect_data_mutex);
705 for (w = root; w; w = w->next) {
706 if (ebpf_plugin_stop())
707 break;
708
709 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_SHM_IDX))))
710 continue;
711
712 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_shmget_call");
713 write_chart_dimension("calls", (long long)w->shm.get);
714 ebpf_write_end_chart();
715
716 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_shmat_call");
717 write_chart_dimension("calls", (long long)w->shm.at);
718 ebpf_write_end_chart();
719
720 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_shmdt_call");
721 write_chart_dimension("calls", (long long)w->shm.dt);
722 ebpf_write_end_chart();
723
724 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_shmctl_call");
725 write_chart_dimension("calls", (long long)w->shm.ctl);
726 ebpf_write_end_chart();
727 }
728 netdata_mutex_unlock(&collect_data_mutex);
729 }
730
731 /**
732 * Sum values for all targets.
733 */
734 static void ebpf_shm_sum_cgroup_pids(netdata_publish_shm_t *shm, struct pid_on_target2 *root)
735 {
736 netdata_publish_shm_t shmv;
737 memset(&shmv, 0, sizeof(shmv));
738 while (root) {
739 netdata_publish_shm_t *w = &root->shm;
740 shmv.get += w->get;
741 shmv.at += w->at;
742 shmv.dt += w->dt;
743 shmv.ctl += w->ctl;
744
745 root = root->next;
746 }
747
748 memcpy(shm, &shmv, sizeof(shmv));
749 }
750
751 /**
752 * Create specific shared memory charts
753 *
754 * Create charts for cgroup/application.
755 *
756 * @param type the chart type.
757 * @param update_every value to overwrite the update frequency set by the server.
758 */
759 static void ebpf_create_specific_shm_charts(char *type, int update_every)
760 {
761 char *label = (!strncmp(type, "cgroup_", 7)) ? &type[7] : type;
762 ebpf_create_chart(
763 type,
764 NETDATA_SHMGET_CHART,
765 "Calls to syscall shmget(2).",
766 EBPF_COMMON_UNITS_CALLS_PER_SEC,
767 NETDATA_APPS_IPC_SHM_GROUP,
768 NETDATA_CGROUP_SHM_GET_CONTEXT,
769 NETDATA_EBPF_CHART_TYPE_LINE,
770 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5800,
771 ebpf_create_global_dimension,
772 &shm_publish_aggregated[NETDATA_KEY_SHMGET_CALL],
773 1,
774 update_every,
775 NETDATA_EBPF_MODULE_NAME_SHM);
776 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
777 ebpf_commit_label();
778
779 ebpf_create_chart(
780 type,
781 NETDATA_SHMAT_CHART,
782 "Calls to syscall shmat(2).",
783 EBPF_COMMON_UNITS_CALLS_PER_SEC,
784 NETDATA_APPS_IPC_SHM_GROUP,
785 NETDATA_CGROUP_SHM_AT_CONTEXT,
786 NETDATA_EBPF_CHART_TYPE_LINE,
787 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5801,
788 ebpf_create_global_dimension,
789 &shm_publish_aggregated[NETDATA_KEY_SHMAT_CALL],
790 1,
791 update_every,
792 NETDATA_EBPF_MODULE_NAME_SHM);
793 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
794 ebpf_commit_label();
795
796 ebpf_create_chart(
797 type,
798 NETDATA_SHMDT_CHART,
799 "Calls to syscall shmdt(2).",
800 EBPF_COMMON_UNITS_CALLS_PER_SEC,
801 NETDATA_APPS_IPC_SHM_GROUP,
802 NETDATA_CGROUP_SHM_DT_CONTEXT,
803 NETDATA_EBPF_CHART_TYPE_LINE,
804 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5802,
805 ebpf_create_global_dimension,
806 &shm_publish_aggregated[NETDATA_KEY_SHMDT_CALL],
807 1,
808 update_every,
809 NETDATA_EBPF_MODULE_NAME_SHM);
810 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
811 ebpf_commit_label();
812
813 ebpf_create_chart(
814 type,
815 NETDATA_SHMCTL_CHART,
816 "Calls to syscall shmctl(2).",
817 EBPF_COMMON_UNITS_CALLS_PER_SEC,
818 NETDATA_APPS_IPC_SHM_GROUP,
819 NETDATA_CGROUP_SHM_CTL_CONTEXT,
820 NETDATA_EBPF_CHART_TYPE_LINE,
821 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5803,
822 ebpf_create_global_dimension,
823 &shm_publish_aggregated[NETDATA_KEY_SHMCTL_CALL],
824 1,
825 update_every,
826 NETDATA_EBPF_MODULE_NAME_SHM);
827 ebpf_create_chart_labels("cgroup_name", label, RRDLABEL_SRC_AUTO);
828 ebpf_commit_label();
829 }
830
831 /**
832 * Obsolete specific shared memory charts
833 *
834 * Obsolete charts for cgroup/application.
835 *
836 * @param type the chart type.
837 * @param update_every value to overwrite the update frequency set by the server.
838 */
839 static void ebpf_obsolete_specific_shm_charts(char *type, int update_every)
840 {
841 ebpf_write_chart_obsolete(
842 type,
843 NETDATA_SHMGET_CHART,
844 "",
845 "Calls to syscall shmget(2).",
846 EBPF_COMMON_UNITS_CALLS_PER_SEC,
847 NETDATA_APPS_IPC_SHM_GROUP,
848 NETDATA_EBPF_CHART_TYPE_LINE,
849 NETDATA_CGROUP_SHM_GET_CONTEXT,
850 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5800,
851 update_every);
852
853 ebpf_write_chart_obsolete(
854 type,
855 NETDATA_SHMAT_CHART,
856 "",
857 "Calls to syscall shmat(2).",
858 EBPF_COMMON_UNITS_CALLS_PER_SEC,
859 NETDATA_APPS_IPC_SHM_GROUP,
860 NETDATA_EBPF_CHART_TYPE_LINE,
861 NETDATA_CGROUP_SHM_AT_CONTEXT,
862 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5801,
863 update_every);
864
865 ebpf_write_chart_obsolete(
866 type,
867 NETDATA_SHMDT_CHART,
868 "",
869 "Calls to syscall shmdt(2).",
870 EBPF_COMMON_UNITS_CALLS_PER_SEC,
871 NETDATA_APPS_IPC_SHM_GROUP,
872 NETDATA_EBPF_CHART_TYPE_LINE,
873 NETDATA_CGROUP_SHM_DT_CONTEXT,
874 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5802,
875 update_every);
876
877 ebpf_write_chart_obsolete(
878 type,
879 NETDATA_SHMCTL_CHART,
880 "",
881 "Calls to syscall shmctl(2).",
882 EBPF_COMMON_UNITS_CALLS_PER_SEC,
883 NETDATA_APPS_IPC_SHM_GROUP,
884 NETDATA_EBPF_CHART_TYPE_LINE,
885 NETDATA_CGROUP_SHM_CTL_CONTEXT,
886 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5803,
887 update_every);
888 }
889
890 /**
891 * Create Systemd Swap Charts
892 *
893 * Create charts when systemd is enabled
894 *
895 * @param update_every value to overwrite the update frequency set by the server.
896 **/
897 static void ebpf_create_systemd_shm_charts(int update_every)
898 {
899 static ebpf_systemd_args_t data_shmget = {
900 .title = "Calls to syscall shmget(2).",
901 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
902 .family = NETDATA_APPS_IPC_SHM_GROUP,
903 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
904 .order = 20191,
905 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
906 .context = NETDATA_SYSTEMD_SHM_GET_CONTEXT,
907 .module = NETDATA_EBPF_MODULE_NAME_SHM,
908 .update_every = 0,
909 .suffix = NETDATA_SHMGET_CHART,
910 .dimension = "calls"};
911
912 static ebpf_systemd_args_t data_shmat = {
913 .title = "Calls to syscall shmat(2).",
914 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
915 .family = NETDATA_APPS_IPC_SHM_GROUP,
916 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
917 .order = 20192,
918 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
919 .context = NETDATA_SYSTEMD_SHM_AT_CONTEXT,
920 .module = NETDATA_EBPF_MODULE_NAME_SHM,
921 .update_every = 0,
922 .suffix = NETDATA_SHMAT_CHART,
923 .dimension = "calls"};
924
925 static ebpf_systemd_args_t data_shmdt = {
926 .title = "Calls to syscall shmdt(2).",
927 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
928 .family = NETDATA_APPS_IPC_SHM_GROUP,
929 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
930 .order = 20193,
931 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
932 .context = NETDATA_SYSTEMD_SHM_DT_CONTEXT,
933 .module = NETDATA_EBPF_MODULE_NAME_SHM,
934 .update_every = 0,
935 .suffix = NETDATA_SHMDT_CHART,
936 .dimension = "calls"};
937
938 static ebpf_systemd_args_t data_shmctl = {
939 .title = "Calls to syscall shmctl(2).",
940 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
941 .family = NETDATA_APPS_IPC_SHM_GROUP,
942 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
943 .order = 20194,
944 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
945 .context = NETDATA_SYSTEMD_SHM_CTL_CONTEXT,
946 .module = NETDATA_EBPF_MODULE_NAME_SHM,
947 .update_every = 0,
948 .suffix = NETDATA_SHMCTL_CHART,
949 .dimension = "calls"};
950
951 if (!data_shmget.update_every)
952 data_shmat.update_every = data_shmctl.update_every = data_shmdt.update_every = data_shmget.update_every =
953 update_every;
954
955 ebpf_cgroup_target_t *w;
956 for (w = ebpf_cgroup_pids; w; w = w->next) {
957 if (ebpf_plugin_stop())
958 break;
959
960 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_SHM_CHART))
961 continue;
962
963 data_shmat.id = data_shmctl.id = data_shmdt.id = data_shmget.id = w->name;
964 ebpf_create_charts_on_systemd(&data_shmat);
965
966 ebpf_create_charts_on_systemd(&data_shmctl);
967
968 ebpf_create_charts_on_systemd(&data_shmdt);
969
970 ebpf_create_charts_on_systemd(&data_shmget);
971
972 w->flags |= NETDATA_EBPF_SERVICES_HAS_SHM_CHART;
973 }
974 }
975
976 /**
977 * Send Systemd charts
978 *
979 * Send collected data to Netdata.
980 */
981 static void ebpf_send_systemd_shm_charts(void)
982 {
983 static const char *charts[] = {
984 NETDATA_SHMGET_CHART, NETDATA_SHMAT_CHART, NETDATA_SHMDT_CHART, NETDATA_SHMCTL_CHART};
985
986 ebpf_cgroup_target_t *ect;
987 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
988 if (ebpf_plugin_stop())
989 break;
990
991 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_SHM_CHART))) {
992 continue;
993 }
994
995 ebpf_write_begin_chart(ect->name, charts[NETDATA_KEY_SHMGET_CALL], "");
996 write_chart_dimension("calls", (long long)ect->publish_shm.get);
997 ebpf_write_end_chart();
998
999 ebpf_write_begin_chart(ect->name, charts[NETDATA_KEY_SHMAT_CALL], "");
1000 write_chart_dimension("calls", (long long)ect->publish_shm.at);
1001 ebpf_write_end_chart();
1002
1003 ebpf_write_begin_chart(ect->name, charts[NETDATA_KEY_SHMDT_CALL], "");
1004 write_chart_dimension("calls", (long long)ect->publish_shm.dt);
1005 ebpf_write_end_chart();
1006
1007 ebpf_write_begin_chart(ect->name, charts[NETDATA_KEY_SHMCTL_CALL], "");
1008 write_chart_dimension("calls", (long long)ect->publish_shm.ctl);
1009 ebpf_write_end_chart();
1010 }
1011 }
1012
1013 /*
1014 * Send Specific Shared memory data
1015 *
1016 * Send data for specific cgroup/apps.
1017 *
1018 * @param type chart type
1019 * @param values structure with values that will be sent to netdata
1020 */
1021 static void ebpf_send_specific_shm_data(char *type, netdata_publish_shm_t *values)
1022 {
1023 static const char *charts[] = {
1024 NETDATA_SHMGET_CHART, NETDATA_SHMAT_CHART, NETDATA_SHMDT_CHART, NETDATA_SHMCTL_CHART};
1025
1026 ebpf_write_begin_chart(type, charts[NETDATA_KEY_SHMGET_CALL], "");
1027 write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMGET_CALL].name, (long long)values->get);
1028 ebpf_write_end_chart();
1029
1030 ebpf_write_begin_chart(type, charts[NETDATA_KEY_SHMAT_CALL], "");
1031 write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMAT_CALL].name, (long long)values->at);
1032 ebpf_write_end_chart();
1033
1034 ebpf_write_begin_chart(type, charts[NETDATA_KEY_SHMDT_CALL], "");
1035 write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMDT_CALL].name, (long long)values->dt);
1036 ebpf_write_end_chart();
1037
1038 ebpf_write_begin_chart(type, charts[NETDATA_KEY_SHMCTL_CALL], "");
1039 write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMCTL_CALL].name, (long long)values->ctl);
1040 ebpf_write_end_chart();
1041 }
1042
1043 /**
1044 * Send data to Netdata calling auxiliary functions.
1045 *
1046 * @param update_every value to overwrite the update frequency set by the server.
1047 */
1048 void ebpf_shm_send_cgroup_data(int update_every)
1049 {
1050 netdata_mutex_lock(&mutex_cgroup_shm);
1051 ebpf_cgroup_target_t *ect;
1052 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1053 ebpf_shm_sum_cgroup_pids(&ect->publish_shm, ect->pids);
1054 }
1055
1056 if (ebpf_plugin_stop()) {
1057 netdata_mutex_unlock(&mutex_cgroup_shm);
1058 return;
1059 }
1060
1061 if (ebpf_cgroup_systemd_enabled_get()) {
1062 if (ebpf_send_cgroup_chart_get()) {
1063 ebpf_create_systemd_shm_charts(update_every);
1064 }
1065
1066 ebpf_send_systemd_shm_charts();
1067 }
1068
1069 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1070 if (ebpf_plugin_stop())
1071 break;
1072
1073 if (ect->systemd)
1074 continue;
1075
1076 if (!(ect->flags & NETDATA_EBPF_CGROUP_HAS_SHM_CHART) && ect->updated) {
1077 ebpf_create_specific_shm_charts(ect->name, update_every);
1078 ect->flags |= NETDATA_EBPF_CGROUP_HAS_SHM_CHART;
1079 }
1080
1081 if (ect->flags & NETDATA_EBPF_CGROUP_HAS_SHM_CHART) {
1082 if (ect->updated) {
1083 ebpf_send_specific_shm_data(ect->name, &ect->publish_shm);
1084 } else {
1085 ebpf_obsolete_specific_shm_charts(ect->name, update_every);
1086 ect->flags &= ~NETDATA_EBPF_CGROUP_HAS_SHM_CHART;
1087 }
1088 }
1089 }
1090
1091 netdata_mutex_unlock(&mutex_cgroup_shm);
1092 }
1093
1094 /**
1095 * Resume apps data
1096 */
1097 void ebpf_shm_resume_apps_data()
1098 {
1099 struct ebpf_target *w;
1100 netdata_mutex_lock(&collect_data_mutex);
1101 for (w = apps_groups_root_target; w; w = w->next) {
1102 if (ebpf_plugin_stop())
1103 break;
1104
1105 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_SHM_IDX))))
1106 continue;
1107
1108 ebpf_shm_sum_pids(&w->shm, w->root_pid);
1109 }
1110 netdata_mutex_unlock(&collect_data_mutex);
1111 }
1112
1113 /**
1114 * DCstat thread
1115 *
1116 * Thread used to generate dcstat charts.
1117 *
1118 * @param ptr a pointer to `struct ebpf_module`
1119 *
1120 * @return It always return NULL
1121 */
1122 void ebpf_read_shm_thread(void *ptr)
1123 {
1124 ebpf_module_t *em = (ebpf_module_t *)ptr;
1125
1126 int maps_per_core = em->maps_per_core;
1127 int update_every = em->update_every;
1128 int collect_pid = (em->apps_charts || em->cgroup_charts);
1129 if (!collect_pid)
1130 return;
1131
1132 int counter = update_every - 1;
1133
1134 uint32_t lifetime = em->lifetime;
1135 int cgroups = em->cgroup_charts;
1136 uint32_t running_time = 0;
1137 ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_SHM_IDX, shm_maps[NETDATA_PID_SHM_TABLE].map_fd);
1138 heartbeat_t hb;
1139 heartbeat_init(&hb, USEC_PER_SEC);
1140 while (!ebpf_plugin_stop() && running_time < lifetime) {
1141 if (ebpf_plugin_stop())
1142 break;
1143
1144 (void)heartbeat_next(&hb);
1145 if (ebpf_plugin_stop())
1146 break;
1147
1148 if (++counter != update_every)
1149 continue;
1150
1151 if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
1152 if (errno != ECANCELED)
1153 netdata_log_error("SHM: Failed to wait on semaphore.");
1154 break;
1155 }
1156 ebpf_read_shm_apps_table(maps_per_core);
1157 ebpf_shm_resume_apps_data();
1158 if (ebpf_plugin_stop()) {
1159 if (sem_post(shm_mutex_ebpf_integration))
1160 netdata_log_error("SHM: Failed to post semaphore.");
1161 break;
1162 }
1163
1164 if (cgroups && ebpf_cgroup_integration_active_get())
1165 ebpf_update_shm_cgroup();
1166
1167 if (sem_post(shm_mutex_ebpf_integration)) {
1168 netdata_log_error("SHM: Failed to post semaphore.");
1169 break;
1170 }
1171
1172 counter = 0;
1173
1174 if (ebpf_plugin_stop())
1175 break;
1176
1177 netdata_mutex_lock(&ebpf_exit_cleanup);
1178 running_time += update_every;
1179 em->running_time = running_time;
1180 netdata_mutex_unlock(&ebpf_exit_cleanup);
1181 }
1182 }
1183
1184 /**
1185 * Main loop for this collector.
1186 */
1187 static void shm_collector(ebpf_module_t *em)
1188 {
1189 int cgroups = em->cgroup_charts;
1190 int update_every = em->update_every;
1191 int counter = update_every - 1;
1192 int maps_per_core = em->maps_per_core;
1193 uint32_t running_time = 0;
1194 uint32_t lifetime = em->lifetime;
1195 netdata_idx_t *stats = em->hash_table_stats;
1196 memset(stats, 0, sizeof(em->hash_table_stats));
1197 heartbeat_t hb;
1198 heartbeat_init(&hb, USEC_PER_SEC);
1199 while (!ebpf_plugin_stop() && running_time < lifetime) {
1200 if (ebpf_plugin_stop())
1201 break;
1202
1203 heartbeat_next(&hb);
1204
1205 if (ebpf_plugin_stop())
1206 break;
1207
1208 if (++counter != update_every)
1209 continue;
1210
1211 counter = 0;
1212 netdata_apps_integration_flags_t apps = em->apps_charts;
1213 ebpf_shm_read_global_table(stats, maps_per_core);
1214 netdata_mutex_lock(&lock);
1215
1216 shm_send_global();
1217
1218 if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED) {
1219 ebpf_shm_send_apps_data(apps_groups_root_target);
1220 }
1221
1222 if (ebpf_plugin_stop()) {
1223 netdata_mutex_unlock(&lock);
1224 break;
1225 }
1226
1227 if (cgroups && ebpf_cgroup_integration_active_get()) {
1228 ebpf_shm_send_cgroup_data(update_every);
1229 }
1230
1231 netdata_mutex_unlock(&lock);
1232
1233 if (ebpf_plugin_stop())
1234 break;
1235
1236 netdata_mutex_lock(&ebpf_exit_cleanup);
1237 running_time += update_every;
1238 em->running_time = running_time;
1239 netdata_mutex_unlock(&ebpf_exit_cleanup);
1240 }
1241 }
1242
1243 /*****************************************************************
1244 * INITIALIZE THREAD
1245 *****************************************************************/
1246
1247 /**
1248 * Create apps charts
1249 *
1250 * Call ebpf_create_chart to create the charts on apps submenu.
1251 *
1252 * @param em a pointer to the structure with the default values.
1253 */
1254 void ebpf_shm_create_apps_charts(struct ebpf_module *em, void *ptr)
1255 {
1256 struct ebpf_target *root = ptr;
1257 struct ebpf_target *w;
1258 int update_every = em->update_every;
1259 for (w = root; w; w = w->next) {
1260 if (unlikely(!w->exposed))
1261 continue;
1262
1263 ebpf_write_chart_cmd(
1264 NETDATA_APP_FAMILY,
1265 w->clean_name,
1266 "_ebpf_shmget_call",
1267 "Calls to syscall shmget(2).",
1268 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1269 NETDATA_APPS_IPC_SHM_GROUP,
1270 NETDATA_EBPF_CHART_TYPE_STACKED,
1271 "app.ebpf_shmget_call",
1272 20191,
1273 update_every,
1274 NETDATA_EBPF_MODULE_NAME_SHM);
1275 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
1276 ebpf_commit_label();
1277 fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
1278
1279 ebpf_write_chart_cmd(
1280 NETDATA_APP_FAMILY,
1281 w->clean_name,
1282 "_ebpf_shmat_call",
1283 "Calls to syscall shmat(2).",
1284 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1285 NETDATA_APPS_IPC_SHM_GROUP,
1286 NETDATA_EBPF_CHART_TYPE_STACKED,
1287 "app.ebpf_shmat_call",
1288 20192,
1289 update_every,
1290 NETDATA_EBPF_MODULE_NAME_SHM);
1291 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
1292 ebpf_commit_label();
1293 fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
1294
1295 ebpf_write_chart_cmd(
1296 NETDATA_APP_FAMILY,
1297 w->clean_name,
1298 "_ebpf_shmdt_call",
1299 "Calls to syscall shmdt(2).",
1300 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1301 NETDATA_APPS_IPC_SHM_GROUP,
1302 NETDATA_EBPF_CHART_TYPE_STACKED,
1303 "app.ebpf_shmdt_call",
1304 20193,
1305 update_every,
1306 NETDATA_EBPF_MODULE_NAME_SHM);
1307 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
1308 ebpf_commit_label();
1309 fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
1310
1311 ebpf_write_chart_cmd(
1312 NETDATA_APP_FAMILY,
1313 w->clean_name,
1314 "_ebpf_shmctl_call",
1315 "Calls to syscall shmctl(2).",
1316 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1317 NETDATA_APPS_IPC_SHM_GROUP,
1318 NETDATA_EBPF_CHART_TYPE_STACKED,
1319 "app.ebpf_shmctl_call",
1320 20194,
1321 update_every,
1322 NETDATA_EBPF_MODULE_NAME_SHM);
1323 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
1324 ebpf_commit_label();
1325 fprintf(stdout, "DIMENSION calls '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
1326
1327 w->charts_created |= 1 << EBPF_MODULE_SHM_IDX;
1328 }
1329
1330 em->apps_charts |= NETDATA_EBPF_APPS_FLAG_CHART_CREATED;
1331 }
1332
1333 /**
1334 * Allocate vectors used with this thread.
1335 *
1336 * We are not testing the return, because callocz does this and shutdown the software
1337 * case it was not possible to allocate.
1338 *
1339 * @param apps is apps enabled?
1340 */
1341 static void ebpf_shm_allocate_global_vectors(void)
1342 {
1343 shm_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_ebpf_shm_t));
1344 shm_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t));
1345
1346 memset(shm_hash_values, 0, sizeof(shm_hash_values));
1347 }
1348
1349 /*****************************************************************
1350 * MAIN THREAD
1351 *****************************************************************/
1352
1353 /**
1354 * Create global charts
1355 *
1356 * Call ebpf_create_chart to create the charts for the collector.
1357 *
1358 * @param update_every value to overwrite the update frequency set by the server.
1359 */
1360 static void ebpf_create_shm_charts(int update_every)
1361 {
1362 ebpf_create_chart(
1363 NETDATA_EBPF_SYSTEM_GROUP,
1364 NETDATA_SHM_GLOBAL_CHART,
1365 "Calls to shared memory system calls",
1366 EBPF_COMMON_UNITS_CALLS_PER_SEC,
1367 NETDATA_SYSTEM_IPC_SHM_SUBMENU,
1368 "system.shared_memory_calls",
1369 NETDATA_EBPF_CHART_TYPE_LINE,
1370 NETDATA_CHART_PRIO_SYSTEM_IPC_SHARED_MEM_CALLS,
1371 ebpf_create_global_dimension,
1372 shm_publish_aggregated,
1373 NETDATA_SHM_END,
1374 update_every,
1375 NETDATA_EBPF_MODULE_NAME_SHM);
1376
1377 fflush(stdout);
1378 }
1379
1380 /*
1381 * Load BPF
1382 *
1383 * Load BPF files.
1384 *
1385 * @param em the structure with configuration
1386 */
1387 static int ebpf_shm_load_bpf(ebpf_module_t *em)
1388 {
1389 #ifdef LIBBPF_MAJOR_VERSION
1390 ebpf_define_map_type(em->maps, em->maps_per_core, running_on_kernel);
1391 #endif
1392
1393 int ret = 0;
1394
1395 ebpf_adjust_apps_cgroup(em, em->targets[NETDATA_KEY_SHMGET_CALL].mode);
1396 if (em->load & EBPF_LOAD_LEGACY) {
1397 em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
1398 if (!em->probe_links) {
1399 ret = -1;
1400 }
1401 }
1402 #ifdef LIBBPF_MAJOR_VERSION
1403 else {
1404 shm_bpf_obj = shm_bpf__open();
1405 if (!shm_bpf_obj)
1406 ret = -1;
1407 else {
1408 ret = ebpf_shm_load_and_attach(shm_bpf_obj, em);
1409 if (ret) {
1410 shm_bpf__destroy(shm_bpf_obj);
1411 shm_bpf_obj = NULL;
1412 }
1413 }
1414 }
1415 #endif
1416
1417 if (ret)
1418 netdata_log_error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->info.thread_name);
1419
1420 return ret;
1421 }
1422
1423 /**
1424 * Shared memory thread.
1425 *
1426 * @param ptr a pointer to `struct ebpf_module`
1427 * @return It always return NULL
1428 */
1429 void ebpf_shm_thread(void *ptr)
1430 {
1431 ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_SHM_IDX, -1);
1432 ebpf_module_t *em = (ebpf_module_t *)ptr;
1433
1434 CLEANUP_FUNCTION_REGISTER(ebpf_shm_exit) cleanup_ptr = em;
1435
1436 if (!ebpf_module_thread_has_valid_state(em)) {
1437 goto endshm;
1438 }
1439
1440 em->maps = shm_maps;
1441
1442 ebpf_update_pid_table(&shm_maps[NETDATA_PID_SHM_TABLE], em);
1443
1444 #ifdef LIBBPF_MAJOR_VERSION
1445 ebpf_adjust_thread_load(em, default_btf);
1446 #endif
1447 if (ebpf_shm_load_bpf(em)) {
1448 goto endshm;
1449 }
1450 ebpf_mark_program_loaded();
1451
1452 ebpf_shm_allocate_global_vectors();
1453
1454 int algorithms[NETDATA_SHM_END] = {
1455 NETDATA_EBPF_INCREMENTAL_IDX,
1456 NETDATA_EBPF_INCREMENTAL_IDX,
1457 NETDATA_EBPF_INCREMENTAL_IDX,
1458 NETDATA_EBPF_INCREMENTAL_IDX};
1459 ebpf_global_labels(
1460 shm_aggregated_data,
1461 shm_publish_aggregated,
1462 shm_dimension_name,
1463 shm_dimension_name,
1464 algorithms,
1465 NETDATA_SHM_END);
1466
1467 netdata_mutex_lock(&lock);
1468 ebpf_create_shm_charts(em->update_every);
1469 ebpf_update_stats(&plugin_statistics, em);
1470 ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_ADD);
1471 netdata_mutex_unlock(&lock);
1472
1473 ebpf_read_shm.thread =
1474 nd_thread_create(ebpf_read_shm.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_shm_thread, em);
1475
1476 shm_collector(em);
1477
1478 endshm:
1479 ebpf_update_disabled_plugin_stats(em);
1480 }