| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #ifndef NETDATA_EBPF_APPS_H |
| 4 | #define NETDATA_EBPF_APPS_H 1 |
| 5 | |
| 6 | #include "libnetdata/libnetdata.h" |
| 7 | #include "collectors/collectors-ipc/ebpf-ipc.h" |
| 8 | #include "libbpf_api/ebpf.h" |
| 9 | |
| 10 | #define NETDATA_APPS_FAMILY "apps" |
| 11 | #define NETDATA_APP_FAMILY "app" |
| 12 | #define NETDATA_APPS_FILE_GROUP "file_access" |
| 13 | #define NETDATA_APPS_FILE_FDS "fds" |
| 14 | #define NETDATA_APPS_PROCESS_GROUP "process" |
| 15 | #define NETDATA_APPS_NET_GROUP "net" |
| 16 | #define NETDATA_APPS_IPC_SHM_GROUP "ipc shm" |
| 17 | |
| 18 | #include "ebpf_process.h" |
| 19 | #include "ebpf_dcstat.h" |
| 20 | #include "ebpf_disk.h" |
| 21 | #include "ebpf_fd.h" |
| 22 | #include "ebpf_filesystem.h" |
| 23 | #include "ebpf_functions.h" |
| 24 | #include "ebpf_hardirq.h" |
| 25 | #include "ebpf_cachestat.h" |
| 26 | #include "ebpf_mdflush.h" |
| 27 | #include "ebpf_mount.h" |
| 28 | #include "ebpf_oomkill.h" |
| 29 | #include "ebpf_shm.h" |
| 30 | #include "ebpf_socket.h" |
| 31 | #include "ebpf_softirq.h" |
| 32 | #include "ebpf_sync.h" |
| 33 | #include "ebpf_swap.h" |
| 34 | #include "ebpf_vfs.h" |
| 35 | |
| 36 | #include "ebpf_socket_ipc.h" |
| 37 | |
| 38 | #define EBPF_MAX_COMPARE_NAME 95 |
| 39 | #define EBPF_MAX_NAME 100 |
| 40 | |
| 41 | #define EBPF_CLEANUP_FACTOR 2 |
| 42 | |
| 43 | enum ebpf_main_index { |
| 44 | EBPF_MODULE_PROCESS_IDX, |
| 45 | EBPF_MODULE_SOCKET_IDX, |
| 46 | EBPF_MODULE_CACHESTAT_IDX, |
| 47 | EBPF_MODULE_SYNC_IDX, |
| 48 | EBPF_MODULE_DCSTAT_IDX, |
| 49 | EBPF_MODULE_SWAP_IDX, |
| 50 | EBPF_MODULE_VFS_IDX, |
| 51 | EBPF_MODULE_FILESYSTEM_IDX, |
| 52 | EBPF_MODULE_DISK_IDX, |
| 53 | EBPF_MODULE_MOUNT_IDX, |
| 54 | EBPF_MODULE_FD_IDX, |
| 55 | EBPF_MODULE_HARDIRQ_IDX, |
| 56 | EBPF_MODULE_SOFTIRQ_IDX, |
| 57 | EBPF_MODULE_OOMKILL_IDX, |
| 58 | EBPF_MODULE_SHM_IDX, |
| 59 | EBPF_MODULE_MDFLUSH_IDX, |
| 60 | EBPF_MODULE_FUNCTION_IDX, |
| 61 | /* THREADS MUST BE INCLUDED BEFORE THIS COMMENT */ |
| 62 | EBPF_OPTION_ALL_CHARTS, |
| 63 | EBPF_OPTION_VERSION, |
| 64 | EBPF_OPTION_HELP, |
| 65 | EBPF_OPTION_GLOBAL_CHART, |
| 66 | EBPF_OPTION_RETURN_MODE, |
| 67 | EBPF_OPTION_LEGACY, |
| 68 | EBPF_OPTION_CORE, |
| 69 | EBPF_OPTION_UNITTEST |
| 70 | }; |
| 71 | |
| 72 | // ---------------------------------------------------------------------------- |
| 73 | // pid_stat |
| 74 | // |
| 75 | struct ebpf_target { |
| 76 | char compare[EBPF_MAX_COMPARE_NAME + 1]; |
| 77 | uint32_t comparehash; |
| 78 | size_t comparelen; |
| 79 | |
| 80 | char id[EBPF_MAX_NAME + 1]; |
| 81 | uint32_t idhash; |
| 82 | uint32_t charts_created; |
| 83 | |
| 84 | char name[EBPF_MAX_NAME + 1]; |
| 85 | char clean_name[EBPF_MAX_NAME + 1]; // sanitized name used in chart id (need to replace at least dots) |
| 86 | |
| 87 | // Changes made to simplify integration between apps and eBPF. |
| 88 | netdata_publish_cachestat_t cachestat; |
| 89 | netdata_publish_dcstat_t dcstat; |
| 90 | netdata_publish_swap_t swap; |
| 91 | netdata_publish_vfs_t vfs; |
| 92 | netdata_fd_stat_t fd; |
| 93 | netdata_publish_shm_t shm; |
| 94 | ebpf_process_stat_t process; |
| 95 | ebpf_socket_publish_apps_t socket; |
| 96 | |
| 97 | kernel_uint_t starttime; |
| 98 | kernel_uint_t collected_starttime; |
| 99 | |
| 100 | unsigned int processes; // how many processes have been merged to this |
| 101 | int exposed; // if set, we have sent this to netdata |
| 102 | int hidden; // if set, we set the hidden flag on the dimension |
| 103 | int debug_enabled; |
| 104 | int ends_with; |
| 105 | int starts_with; // if set, the compare string matches only the |
| 106 | // beginning of the command |
| 107 | |
| 108 | struct ebpf_pid_on_target *root_pid; // list of aggregated pids for target debugging |
| 109 | |
| 110 | struct ebpf_target *target; // the one that will be reported to netdata |
| 111 | struct ebpf_target *next; |
| 112 | }; |
| 113 | extern struct ebpf_target *apps_groups_default_target; |
| 114 | extern struct ebpf_target *apps_groups_root_target; |
| 115 | extern struct ebpf_target *users_root_target; |
| 116 | extern struct ebpf_target *groups_root_target; |
| 117 | extern uint64_t collect_pids; |
| 118 | |
| 119 | void ebpf_reset_pid_map_fds(void); |
| 120 | void ebpf_set_pid_map_fd(int idx, int fd); |
| 121 | int ebpf_get_pid_map_fd(int idx); |
| 122 | |
| 123 | // ebpf_pid_data |
| 124 | typedef struct __attribute__((packed)) ebpf_pid_data { |
| 125 | uint32_t pid; |
| 126 | uint32_t ppid; |
| 127 | uint64_t thread_collecting; |
| 128 | |
| 129 | char comm[EBPF_MAX_COMPARE_NAME + 1]; |
| 130 | char *cmdline; |
| 131 | |
| 132 | uint32_t has_proc_file; |
| 133 | uint32_t not_updated; |
| 134 | int children_count; // number of processes directly referencing this |
| 135 | int merged; |
| 136 | int sortlist; // higher numbers = top on the process tree |
| 137 | |
| 138 | struct ebpf_target *target; // the one that will be reported to netdata |
| 139 | struct ebpf_pid_data *parent; |
| 140 | struct ebpf_pid_data *prev; |
| 141 | struct ebpf_pid_data *next; |
| 142 | |
| 143 | netdata_publish_fd_stat_t *fd; |
| 144 | netdata_publish_swap_t *swap; |
| 145 | netdata_publish_shm_t *shm; |
| 146 | netdata_publish_dcstat_t *dc; |
| 147 | netdata_publish_vfs_t *vfs; |
| 148 | netdata_publish_cachestat_t *cachestat; |
| 149 | ebpf_publish_process_t *process; |
| 150 | ebpf_socket_publish_apps_t *socket; |
| 151 | |
| 152 | } ebpf_pid_data_t; |
| 153 | |
| 154 | extern ebpf_pid_data_t *ebpf_pids_link_list; |
| 155 | extern size_t ebpf_all_pids_count; |
| 156 | extern size_t ebpf_hash_table_pids_count; |
| 157 | void ebpf_del_pid_entry(pid_t pid); |
| 158 | |
| 159 | ebpf_pid_data_t *ebpf_find_or_create_pid_data(pid_t pid); |
| 160 | |
| 161 | static inline ebpf_pid_data_t *ebpf_get_pid_data(uint32_t pid, uint32_t tgid, char *name, uint32_t idx) |
| 162 | { |
| 163 | ebpf_pid_data_t *ptr = ebpf_find_or_create_pid_data(pid); |
| 164 | ptr->thread_collecting |= 1 << idx; |
| 165 | // The caller is getting data to work. |
| 166 | if (!name && idx != NETDATA_EBPF_PIDS_PROC_FILE) |
| 167 | return ptr; |
| 168 | |
| 169 | if (ptr->pid == pid) { |
| 170 | return ptr; |
| 171 | } |
| 172 | |
| 173 | ptr->pid = pid; |
| 174 | ptr->ppid = tgid; |
| 175 | |
| 176 | if (name) |
| 177 | strncpyz(ptr->comm, name, EBPF_MAX_COMPARE_NAME); |
| 178 | |
| 179 | if (likely(ebpf_pids_link_list)) |
| 180 | ebpf_pids_link_list->prev = ptr; |
| 181 | |
| 182 | ptr->next = ebpf_pids_link_list; |
| 183 | ebpf_pids_link_list = ptr; |
| 184 | if (idx == NETDATA_EBPF_PIDS_PROC_FILE) { |
| 185 | ebpf_all_pids_count++; |
| 186 | } |
| 187 | |
| 188 | return ptr; |
| 189 | } |
| 190 | |
| 191 | // The only caller of ebpf_get_pid_data() passes NETDATA_EBPF_PIDS_PROC_FILE, |
| 192 | // so `thread_collecting` in an ebpf_pid_data_t only ever has that single high |
| 193 | // bit set. The per-module (idx < PROC_FILE) branch in the old |
| 194 | // ebpf_reset_specific_pid_data() was therefore unreachable. Collapse the |
| 195 | // function to its effective behaviour so a future reader is not confused by |
| 196 | // dead BPF/freez housekeeping that never ran. |
| 197 | static inline void ebpf_reset_specific_pid_data(ebpf_pid_data_t *ptr) |
| 198 | { |
| 199 | ebpf_del_pid_entry(ptr->pid); |
| 200 | } |
| 201 | |
| 202 | typedef struct ebpf_pid_stat { |
| 203 | uint32_t pid; |
| 204 | uint64_t thread_collecting; |
| 205 | char comm[EBPF_MAX_COMPARE_NAME + 1]; |
| 206 | char *cmdline; |
| 207 | |
| 208 | uint32_t log_thrown; |
| 209 | |
| 210 | uint32_t ppid; |
| 211 | |
| 212 | int children_count; // number of processes directly referencing this |
| 213 | unsigned char keep : 1; // 1 when we need to keep this process in memory even after it exited |
| 214 | int keeploops; // increases by 1 every time keep is 1 and updated 0 |
| 215 | unsigned char updated : 1; // 1 when the process is currently running |
| 216 | unsigned char updated_twice : 1; // 1 when the process was running in the previous iteration |
| 217 | unsigned char merged : 1; // 1 when it has been merged to its parent |
| 218 | unsigned char read : 1; // 1 when we have already read this process for this iteration |
| 219 | |
| 220 | int sortlist; // higher numbers = top on the process tree |
| 221 | |
| 222 | // each process gets a unique number |
| 223 | netdata_publish_cachestat_t cachestat; |
| 224 | netdata_publish_dcstat_t dc; |
| 225 | netdata_fd_stat_t fd; |
| 226 | ebpf_process_stat_t process; |
| 227 | netdata_publish_shm_t shm; |
| 228 | netdata_publish_swap_t swap; |
| 229 | ebpf_socket_publish_apps_t socket; |
| 230 | netdata_publish_vfs_t vfs; |
| 231 | |
| 232 | int not_updated; |
| 233 | |
| 234 | struct ebpf_target *target; // app_groups.conf targets |
| 235 | struct ebpf_target *user_target; // uid based targets |
| 236 | struct ebpf_target *group_target; // gid based targets |
| 237 | |
| 238 | usec_t stat_collected_usec; |
| 239 | usec_t last_stat_collected_usec; |
| 240 | |
| 241 | netdata_publish_cachestat_t cache; |
| 242 | |
| 243 | char *stat_filename; |
| 244 | char *status_filename; |
| 245 | char *io_filename; |
| 246 | char *cmdline_filename; |
| 247 | |
| 248 | struct ebpf_pid_stat *parent; |
| 249 | struct ebpf_pid_stat *prev; |
| 250 | struct ebpf_pid_stat *next; |
| 251 | } ebpf_pid_stat_t; |
| 252 | |
| 253 | // ---------------------------------------------------------------------------- |
| 254 | // target |
| 255 | // |
| 256 | // target is the structure that processes are aggregated to be reported |
| 257 | // to netdata. |
| 258 | // |
| 259 | // - Each entry in /etc/apps_groups.conf creates a target. |
| 260 | // - Each user and group used by a process in the system, creates a target. |
| 261 | struct ebpf_pid_on_target { |
| 262 | int32_t pid; |
| 263 | struct ebpf_pid_on_target *next; |
| 264 | }; |
| 265 | |
| 266 | /** |
| 267 | * Internal function used to write debug messages. |
| 268 | * |
| 269 | * @param fmt the format to create the message. |
| 270 | * @param ... the arguments to fill the format. |
| 271 | */ |
| 272 | static inline void debug_log_int(const char *fmt, ...) |
| 273 | { |
| 274 | va_list args; |
| 275 | |
| 276 | fprintf(stderr, "apps.plugin: "); |
| 277 | va_start(args, fmt); |
| 278 | vfprintf(stderr, fmt, args); |
| 279 | va_end(args); |
| 280 | |
| 281 | fputc('\n', stderr); |
| 282 | } |
| 283 | |
| 284 | // ---------------------------------------------------------------------------- |
| 285 | // Exported variabled and functions |
| 286 | // |
| 287 | int ebpf_read_apps_groups_conf( |
| 288 | struct ebpf_target **apps_groups_default_target, |
| 289 | struct ebpf_target **apps_groups_root_target, |
| 290 | const char *path, |
| 291 | const char *file); |
| 292 | |
| 293 | void clean_apps_groups_target(struct ebpf_target *apps_groups_root_target); |
| 294 | |
| 295 | size_t zero_all_targets(struct ebpf_target *root); |
| 296 | |
| 297 | void cleanup_exited_pids(); |
| 298 | |
| 299 | int ebpf_read_hash_table(void *ep, int fd, uint32_t pid); |
| 300 | |
| 301 | int get_pid_comm(pid_t pid, size_t n, char *dest); |
| 302 | |
| 303 | void collect_data_for_all_processes(int tbl_pid_stats_fd, int maps_per_core); |
| 304 | void ebpf_process_apps_accumulator(ebpf_process_stat_t *out, int maps_per_core); |
| 305 | |
| 306 | // The default value is at least 32 times smaller than maximum number of PIDs allowed on system, |
| 307 | // this is only possible because we are using ARAL (https://github.com/netdata/netdata/tree/master/src/libnetdata/aral). |
| 308 | #ifndef NETDATA_EBPF_ALLOC_MAX_PID |
| 309 | #define NETDATA_EBPF_ALLOC_MAX_PID 1024 |
| 310 | #endif |
| 311 | #define NETDATA_EBPF_ALLOC_MIN_ELEMENTS 256 |
| 312 | |
| 313 | // ARAL Sectiion |
| 314 | void ebpf_aral_init(void); |
| 315 | extern ebpf_process_stat_t *process_stat_vector; |
| 316 | |
| 317 | extern ARAL *ebpf_aral_vfs_pid; |
| 318 | void ebpf_vfs_aral_init(); |
| 319 | netdata_publish_vfs_t *ebpf_vfs_get(void); |
| 320 | void ebpf_vfs_release(netdata_publish_vfs_t *stat); |
| 321 | |
| 322 | extern ARAL *ebpf_aral_shm_pid; |
| 323 | void ebpf_shm_aral_init(); |
| 324 | netdata_publish_shm_t *ebpf_shm_stat_get(void); |
| 325 | void ebpf_shm_release(netdata_publish_shm_t *stat); |
| 326 | void ebpf_parse_proc_files(); |
| 327 | |
| 328 | // ARAL Section end |
| 329 | |
| 330 | // Threads integrated with apps |
| 331 | |
| 332 | #include "libnetdata/threads/threads.h" |
| 333 | |
| 334 | #endif /* NETDATA_EBPF_APPS_H */ |