ebpf_shm.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "ebpf.h"
  3. #include "ebpf_shm.h"
  4. static char *shm_dimension_name[NETDATA_SHM_END] = { "get", "at", "dt", "ctl" };
  5. static netdata_syscall_stat_t shm_aggregated_data[NETDATA_SHM_END];
  6. static netdata_publish_syscall_t shm_publish_aggregated[NETDATA_SHM_END];
  7. netdata_publish_shm_t *shm_vector = NULL;
  8. static netdata_idx_t shm_hash_values[NETDATA_SHM_END];
  9. static netdata_idx_t *shm_values = NULL;
  10. netdata_publish_shm_t **shm_pid = NULL;
  11. struct config shm_config = { .first_section = NULL,
  12. .last_section = NULL,
  13. .mutex = NETDATA_MUTEX_INITIALIZER,
  14. .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
  15. .rwlock = AVL_LOCK_INITIALIZER } };
  16. static ebpf_local_maps_t shm_maps[] = {{.name = "tbl_pid_shm", .internal_input = ND_EBPF_DEFAULT_PID_SIZE,
  17. .user_input = 0,
  18. .type = NETDATA_EBPF_MAP_RESIZABLE | NETDATA_EBPF_MAP_PID,
  19. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  20. {.name = "shm_ctrl", .internal_input = NETDATA_CONTROLLER_END,
  21. .user_input = 0,
  22. .type = NETDATA_EBPF_MAP_CONTROLLER,
  23. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  24. {.name = "tbl_shm", .internal_input = NETDATA_SHM_END,
  25. .user_input = 0,
  26. .type = NETDATA_EBPF_MAP_STATIC,
  27. .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED},
  28. {.name = NULL, .internal_input = 0, .user_input = 0}};
  29. struct netdata_static_thread shm_threads = {"SHM KERNEL", NULL, NULL, 1,
  30. NULL, NULL, NULL};
  31. static enum ebpf_threads_status ebpf_shm_exited = NETDATA_THREAD_EBPF_RUNNING;
  32. netdata_ebpf_targets_t shm_targets[] = { {.name = "shmget", .mode = EBPF_LOAD_TRAMPOLINE},
  33. {.name = "shmat", .mode = EBPF_LOAD_TRAMPOLINE},
  34. {.name = "shmdt", .mode = EBPF_LOAD_TRAMPOLINE},
  35. {.name = "shmctl", .mode = EBPF_LOAD_TRAMPOLINE},
  36. {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
  37. #ifdef LIBBPF_MAJOR_VERSION
  38. #include "includes/shm.skel.h"
  39. static struct shm_bpf *bpf_obj = NULL;
  40. /*****************************************************************
  41. *
  42. * BTF FUNCTIONS
  43. *
  44. *****************************************************************/
  45. /*
  46. * Disable tracepoint
  47. *
  48. * Disable all tracepoints to use exclusively another method.
  49. *
  50. * @param obj is the main structure for bpf objects.
  51. */
  52. static void ebpf_shm_disable_tracepoint(struct shm_bpf *obj)
  53. {
  54. bpf_program__set_autoload(obj->progs.netdata_syscall_shmget, false);
  55. bpf_program__set_autoload(obj->progs.netdata_syscall_shmat, false);
  56. bpf_program__set_autoload(obj->progs.netdata_syscall_shmdt, false);
  57. bpf_program__set_autoload(obj->progs.netdata_syscall_shmctl, false);
  58. }
  59. /*
  60. * Disable probe
  61. *
  62. * Disable all probes to use exclusively another method.
  63. *
  64. * @param obj is the main structure for bpf objects.
  65. */
  66. static void ebpf_disable_probe(struct shm_bpf *obj)
  67. {
  68. bpf_program__set_autoload(obj->progs.netdata_shmget_probe, false);
  69. bpf_program__set_autoload(obj->progs.netdata_shmat_probe, false);
  70. bpf_program__set_autoload(obj->progs.netdata_shmdt_probe, false);
  71. bpf_program__set_autoload(obj->progs.netdata_shmctl_probe, false);
  72. }
  73. /*
  74. * Disable trampoline
  75. *
  76. * Disable all trampoline to use exclusively another method.
  77. *
  78. * @param obj is the main structure for bpf objects.
  79. */
  80. static void ebpf_disable_trampoline(struct shm_bpf *obj)
  81. {
  82. bpf_program__set_autoload(obj->progs.netdata_shmget_fentry, false);
  83. bpf_program__set_autoload(obj->progs.netdata_shmat_fentry, false);
  84. bpf_program__set_autoload(obj->progs.netdata_shmdt_fentry, false);
  85. bpf_program__set_autoload(obj->progs.netdata_shmctl_fentry, false);
  86. }
  87. /**
  88. * Set trampoline target
  89. *
  90. * Set the targets we will monitor.
  91. *
  92. * @param obj is the main structure for bpf objects.
  93. */
  94. static void ebpf_set_trampoline_target(struct shm_bpf *obj)
  95. {
  96. char syscall[NETDATA_EBPF_MAX_SYSCALL_LENGTH + 1];
  97. ebpf_select_host_prefix(syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH,
  98. shm_targets[NETDATA_KEY_SHMGET_CALL].name, running_on_kernel);
  99. bpf_program__set_attach_target(obj->progs.netdata_shmget_fentry, 0,
  100. syscall);
  101. ebpf_select_host_prefix(syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH,
  102. shm_targets[NETDATA_KEY_SHMAT_CALL].name, running_on_kernel);
  103. bpf_program__set_attach_target(obj->progs.netdata_shmat_fentry, 0,
  104. syscall);
  105. ebpf_select_host_prefix(syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH,
  106. shm_targets[NETDATA_KEY_SHMDT_CALL].name, running_on_kernel);
  107. bpf_program__set_attach_target(obj->progs.netdata_shmdt_fentry, 0,
  108. syscall);
  109. ebpf_select_host_prefix(syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH,
  110. shm_targets[NETDATA_KEY_SHMCTL_CALL].name, running_on_kernel);
  111. bpf_program__set_attach_target(obj->progs.netdata_shmctl_fentry, 0,
  112. syscall);
  113. }
  114. /**
  115. * SHM Attach Probe
  116. *
  117. * Attach probes to target
  118. *
  119. * @param obj is the main structure for bpf objects.
  120. *
  121. * @return It returns 0 on success and -1 otherwise.
  122. */
  123. static int ebpf_shm_attach_probe(struct shm_bpf *obj)
  124. {
  125. char syscall[NETDATA_EBPF_MAX_SYSCALL_LENGTH + 1];
  126. ebpf_select_host_prefix(syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH,
  127. shm_targets[NETDATA_KEY_SHMGET_CALL].name, running_on_kernel);
  128. obj->links.netdata_shmget_probe = bpf_program__attach_kprobe(obj->progs.netdata_shmget_probe,
  129. false, syscall);
  130. int ret = (int)libbpf_get_error(obj->links.netdata_shmget_probe);
  131. if (ret)
  132. return -1;
  133. ebpf_select_host_prefix(syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH,
  134. shm_targets[NETDATA_KEY_SHMAT_CALL].name, running_on_kernel);
  135. obj->links.netdata_shmat_probe = bpf_program__attach_kprobe(obj->progs.netdata_shmat_probe,
  136. false, syscall);
  137. ret = (int)libbpf_get_error(obj->links.netdata_shmat_probe);
  138. if (ret)
  139. return -1;
  140. ebpf_select_host_prefix(syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH,
  141. shm_targets[NETDATA_KEY_SHMDT_CALL].name, running_on_kernel);
  142. obj->links.netdata_shmdt_probe = bpf_program__attach_kprobe(obj->progs.netdata_shmdt_probe,
  143. false, syscall);
  144. ret = (int)libbpf_get_error(obj->links.netdata_shmdt_probe);
  145. if (ret)
  146. return -1;
  147. ebpf_select_host_prefix(syscall, NETDATA_EBPF_MAX_SYSCALL_LENGTH,
  148. shm_targets[NETDATA_KEY_SHMCTL_CALL].name, running_on_kernel);
  149. obj->links.netdata_shmctl_probe = bpf_program__attach_kprobe(obj->progs.netdata_shmctl_probe,
  150. false, syscall);
  151. ret = (int)libbpf_get_error(obj->links.netdata_shmctl_probe);
  152. if (ret)
  153. return -1;
  154. return 0;
  155. }
  156. /**
  157. * Set hash tables
  158. *
  159. * Set the values for maps according the value given by kernel.
  160. */
  161. static void ebpf_shm_set_hash_tables(struct shm_bpf *obj)
  162. {
  163. shm_maps[NETDATA_PID_SHM_TABLE].map_fd = bpf_map__fd(obj->maps.tbl_pid_shm);
  164. shm_maps[NETDATA_SHM_CONTROLLER].map_fd = bpf_map__fd(obj->maps.shm_ctrl);
  165. shm_maps[NETDATA_SHM_GLOBAL_TABLE].map_fd = bpf_map__fd(obj->maps.tbl_shm);
  166. }
  167. /**
  168. * Load and attach
  169. *
  170. * Load and attach the eBPF code in kernel.
  171. *
  172. * @param obj is the main structure for bpf objects.
  173. * @param em structure with configuration
  174. *
  175. * @return it returns 0 on succes and -1 otherwise
  176. */
  177. static inline int ebpf_shm_load_and_attach(struct shm_bpf *obj, ebpf_module_t *em)
  178. {
  179. netdata_ebpf_targets_t *shmt = em->targets;
  180. netdata_ebpf_program_loaded_t test = shmt[NETDATA_KEY_SHMGET_CALL].mode;
  181. // We are testing only one, because all will have the same behavior
  182. if (test == EBPF_LOAD_TRAMPOLINE ) {
  183. ebpf_shm_disable_tracepoint(obj);
  184. ebpf_disable_probe(obj);
  185. ebpf_set_trampoline_target(obj);
  186. } else if (test == EBPF_LOAD_PROBE || test == EBPF_LOAD_RETPROBE ) {
  187. ebpf_shm_disable_tracepoint(obj);
  188. ebpf_disable_trampoline(obj);
  189. } else {
  190. ebpf_disable_probe(obj);
  191. ebpf_disable_trampoline(obj);
  192. }
  193. int ret = shm_bpf__load(obj);
  194. if (!ret) {
  195. if (test != EBPF_LOAD_PROBE && test != EBPF_LOAD_RETPROBE)
  196. shm_bpf__attach(obj);
  197. else
  198. ret = ebpf_shm_attach_probe(obj);
  199. if (!ret)
  200. ebpf_shm_set_hash_tables(obj);
  201. }
  202. return ret;
  203. }
  204. #endif
  205. /*****************************************************************
  206. * FUNCTIONS TO CLOSE THE THREAD
  207. *****************************************************************/
  208. /**
  209. * SHM Exit
  210. *
  211. * Cancel child thread.
  212. *
  213. * @param ptr thread data.
  214. */
  215. static void ebpf_shm_exit(void *ptr)
  216. {
  217. ebpf_module_t *em = (ebpf_module_t *)ptr;
  218. if (!em->enabled) {
  219. em->enabled = NETDATA_MAIN_THREAD_EXITED;
  220. return;
  221. }
  222. ebpf_shm_exited = NETDATA_THREAD_EBPF_STOPPING;
  223. }
  224. /**
  225. * SHM Cleanup
  226. *
  227. * Clean up allocated memory.
  228. *
  229. * @param ptr thread data.
  230. */
  231. static void ebpf_shm_cleanup(void *ptr)
  232. {
  233. ebpf_module_t *em = (ebpf_module_t *)ptr;
  234. if (ebpf_shm_exited != NETDATA_THREAD_EBPF_STOPPED)
  235. return;
  236. ebpf_cleanup_publish_syscall(shm_publish_aggregated);
  237. freez(shm_vector);
  238. freez(shm_values);
  239. #ifdef LIBBPF_MAJOR_VERSION
  240. if (bpf_obj)
  241. shm_bpf__destroy(bpf_obj);
  242. #endif
  243. shm_threads.enabled = NETDATA_MAIN_THREAD_EXITED;
  244. em->enabled = NETDATA_MAIN_THREAD_EXITED;
  245. }
  246. /*****************************************************************
  247. * COLLECTOR THREAD
  248. *****************************************************************/
  249. /**
  250. * Apps Accumulator
  251. *
  252. * Sum all values read from kernel and store in the first address.
  253. *
  254. * @param out the vector with read values.
  255. */
  256. static void shm_apps_accumulator(netdata_publish_shm_t *out)
  257. {
  258. int i, end = (running_on_kernel >= NETDATA_KERNEL_V4_15) ? ebpf_nprocs : 1;
  259. netdata_publish_shm_t *total = &out[0];
  260. for (i = 1; i < end; i++) {
  261. netdata_publish_shm_t *w = &out[i];
  262. total->get += w->get;
  263. total->at += w->at;
  264. total->dt += w->dt;
  265. total->ctl += w->ctl;
  266. }
  267. }
  268. /**
  269. * Fill PID
  270. *
  271. * Fill PID structures
  272. *
  273. * @param current_pid pid that we are collecting data
  274. * @param out values read from hash tables;
  275. */
  276. static void shm_fill_pid(uint32_t current_pid, netdata_publish_shm_t *publish)
  277. {
  278. netdata_publish_shm_t *curr = shm_pid[current_pid];
  279. if (!curr) {
  280. curr = callocz(1, sizeof(netdata_publish_shm_t));
  281. shm_pid[current_pid] = curr;
  282. }
  283. memcpy(curr, publish, sizeof(netdata_publish_shm_t));
  284. }
  285. /**
  286. * Update cgroup
  287. *
  288. * Update cgroup data based in
  289. */
  290. static void ebpf_update_shm_cgroup()
  291. {
  292. netdata_publish_shm_t *cv = shm_vector;
  293. int fd = shm_maps[NETDATA_PID_SHM_TABLE].map_fd;
  294. size_t length = sizeof(netdata_publish_shm_t) * ebpf_nprocs;
  295. ebpf_cgroup_target_t *ect;
  296. memset(cv, 0, length);
  297. pthread_mutex_lock(&mutex_cgroup_shm);
  298. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  299. struct pid_on_target2 *pids;
  300. for (pids = ect->pids; pids; pids = pids->next) {
  301. int pid = pids->pid;
  302. netdata_publish_shm_t *out = &pids->shm;
  303. if (likely(shm_pid) && shm_pid[pid]) {
  304. netdata_publish_shm_t *in = shm_pid[pid];
  305. memcpy(out, in, sizeof(netdata_publish_shm_t));
  306. } else {
  307. if (!bpf_map_lookup_elem(fd, &pid, cv)) {
  308. shm_apps_accumulator(cv);
  309. memcpy(out, cv, sizeof(netdata_publish_shm_t));
  310. // now that we've consumed the value, zero it out in the map.
  311. memset(cv, 0, length);
  312. bpf_map_update_elem(fd, &pid, cv, BPF_EXIST);
  313. }
  314. }
  315. }
  316. }
  317. pthread_mutex_unlock(&mutex_cgroup_shm);
  318. }
  319. /**
  320. * Read APPS table
  321. *
  322. * Read the apps table and store data inside the structure.
  323. */
  324. static void read_apps_table()
  325. {
  326. netdata_publish_shm_t *cv = shm_vector;
  327. uint32_t key;
  328. struct pid_stat *pids = root_of_pids;
  329. int fd = shm_maps[NETDATA_PID_SHM_TABLE].map_fd;
  330. size_t length = sizeof(netdata_publish_shm_t)*ebpf_nprocs;
  331. while (pids) {
  332. key = pids->pid;
  333. if (bpf_map_lookup_elem(fd, &key, cv)) {
  334. pids = pids->next;
  335. continue;
  336. }
  337. shm_apps_accumulator(cv);
  338. shm_fill_pid(key, cv);
  339. // now that we've consumed the value, zero it out in the map.
  340. memset(cv, 0, length);
  341. bpf_map_update_elem(fd, &key, cv, BPF_EXIST);
  342. pids = pids->next;
  343. }
  344. }
  345. /**
  346. * Send global charts to netdata agent.
  347. */
  348. static void shm_send_global()
  349. {
  350. write_begin_chart(NETDATA_EBPF_SYSTEM_GROUP, NETDATA_SHM_GLOBAL_CHART);
  351. write_chart_dimension(
  352. shm_publish_aggregated[NETDATA_KEY_SHMGET_CALL].dimension,
  353. (long long) shm_hash_values[NETDATA_KEY_SHMGET_CALL]
  354. );
  355. write_chart_dimension(
  356. shm_publish_aggregated[NETDATA_KEY_SHMAT_CALL].dimension,
  357. (long long) shm_hash_values[NETDATA_KEY_SHMAT_CALL]
  358. );
  359. write_chart_dimension(
  360. shm_publish_aggregated[NETDATA_KEY_SHMDT_CALL].dimension,
  361. (long long) shm_hash_values[NETDATA_KEY_SHMDT_CALL]
  362. );
  363. write_chart_dimension(
  364. shm_publish_aggregated[NETDATA_KEY_SHMCTL_CALL].dimension,
  365. (long long) shm_hash_values[NETDATA_KEY_SHMCTL_CALL]
  366. );
  367. write_end_chart();
  368. }
  369. /**
  370. * Read global counter
  371. *
  372. * Read the table with number of calls for all functions
  373. */
  374. static void read_global_table()
  375. {
  376. netdata_idx_t *stored = shm_values;
  377. netdata_idx_t *val = shm_hash_values;
  378. int fd = shm_maps[NETDATA_SHM_GLOBAL_TABLE].map_fd;
  379. uint32_t i, end = NETDATA_SHM_END;
  380. for (i = NETDATA_KEY_SHMGET_CALL; i < end; i++) {
  381. if (!bpf_map_lookup_elem(fd, &i, stored)) {
  382. int j;
  383. int last = ebpf_nprocs;
  384. netdata_idx_t total = 0;
  385. for (j = 0; j < last; j++)
  386. total += stored[j];
  387. val[i] = total;
  388. }
  389. }
  390. }
  391. /**
  392. * Shared memory reader thread.
  393. *
  394. * @param ptr It is a NULL value for this thread.
  395. * @return It always returns NULL.
  396. */
  397. void *ebpf_shm_read_hash(void *ptr)
  398. {
  399. netdata_thread_cleanup_push(ebpf_shm_cleanup, ptr);
  400. heartbeat_t hb;
  401. heartbeat_init(&hb);
  402. ebpf_module_t *em = (ebpf_module_t *)ptr;
  403. usec_t step = NETDATA_SHM_SLEEP_MS * em->update_every;
  404. while (ebpf_shm_exited == NETDATA_THREAD_EBPF_RUNNING) {
  405. usec_t dt = heartbeat_next(&hb, step);
  406. (void)dt;
  407. if (ebpf_shm_exited == NETDATA_THREAD_EBPF_STOPPING)
  408. break;
  409. read_global_table();
  410. }
  411. ebpf_shm_exited = NETDATA_THREAD_EBPF_STOPPED;
  412. netdata_thread_cleanup_pop(1);
  413. return NULL;
  414. }
  415. /**
  416. * Sum values for all targets.
  417. */
  418. static void ebpf_shm_sum_pids(netdata_publish_shm_t *shm, struct pid_on_target *root)
  419. {
  420. while (root) {
  421. int32_t pid = root->pid;
  422. netdata_publish_shm_t *w = shm_pid[pid];
  423. if (w) {
  424. shm->get += w->get;
  425. shm->at += w->at;
  426. shm->dt += w->dt;
  427. shm->ctl += w->ctl;
  428. // reset for next collection.
  429. w->get = 0;
  430. w->at = 0;
  431. w->dt = 0;
  432. w->ctl = 0;
  433. }
  434. root = root->next;
  435. }
  436. }
  437. /**
  438. * Send data to Netdata calling auxiliary functions.
  439. *
  440. * @param root the target list.
  441. */
  442. void ebpf_shm_send_apps_data(struct target *root)
  443. {
  444. struct target *w;
  445. for (w = root; w; w = w->next) {
  446. if (unlikely(w->exposed && w->processes)) {
  447. ebpf_shm_sum_pids(&w->shm, w->root_pid);
  448. }
  449. }
  450. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SHMGET_CHART);
  451. for (w = root; w; w = w->next) {
  452. if (unlikely(w->exposed && w->processes)) {
  453. write_chart_dimension(w->name, (long long) w->shm.get);
  454. }
  455. }
  456. write_end_chart();
  457. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SHMAT_CHART);
  458. for (w = root; w; w = w->next) {
  459. if (unlikely(w->exposed && w->processes)) {
  460. write_chart_dimension(w->name, (long long) w->shm.at);
  461. }
  462. }
  463. write_end_chart();
  464. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SHMDT_CHART);
  465. for (w = root; w; w = w->next) {
  466. if (unlikely(w->exposed && w->processes)) {
  467. write_chart_dimension(w->name, (long long) w->shm.dt);
  468. }
  469. }
  470. write_end_chart();
  471. write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_SHMCTL_CHART);
  472. for (w = root; w; w = w->next) {
  473. if (unlikely(w->exposed && w->processes)) {
  474. write_chart_dimension(w->name, (long long) w->shm.ctl);
  475. }
  476. }
  477. write_end_chart();
  478. }
  479. /**
  480. * Sum values for all targets.
  481. */
  482. static void ebpf_shm_sum_cgroup_pids(netdata_publish_shm_t *shm, struct pid_on_target2 *root)
  483. {
  484. netdata_publish_shm_t shmv;
  485. memset(&shmv, 0, sizeof(shmv));
  486. while (root) {
  487. netdata_publish_shm_t *w = &root->shm;
  488. shmv.get += w->get;
  489. shmv.at += w->at;
  490. shmv.dt += w->dt;
  491. shmv.ctl += w->ctl;
  492. root = root->next;
  493. }
  494. memcpy(shm, &shmv, sizeof(shmv));
  495. }
  496. /**
  497. * Create specific shared memory charts
  498. *
  499. * Create charts for cgroup/application.
  500. *
  501. * @param type the chart type.
  502. * @param update_every value to overwrite the update frequency set by the server.
  503. */
  504. static void ebpf_create_specific_shm_charts(char *type, int update_every)
  505. {
  506. ebpf_create_chart(type, NETDATA_SHMGET_CHART,
  507. "Calls to syscall <code>shmget(2)</code>.",
  508. EBPF_COMMON_DIMENSION_CALL,
  509. NETDATA_APPS_IPC_SHM_GROUP,
  510. NETDATA_CGROUP_SHM_GET_CONTEXT,
  511. NETDATA_EBPF_CHART_TYPE_LINE,
  512. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5800,
  513. ebpf_create_global_dimension,
  514. &shm_publish_aggregated[NETDATA_KEY_SHMGET_CALL],
  515. 1,
  516. update_every,
  517. NETDATA_EBPF_MODULE_NAME_SHM);
  518. ebpf_create_chart(type, NETDATA_SHMAT_CHART,
  519. "Calls to syscall <code>shmat(2)</code>.",
  520. EBPF_COMMON_DIMENSION_CALL,
  521. NETDATA_APPS_IPC_SHM_GROUP,
  522. NETDATA_CGROUP_SHM_AT_CONTEXT,
  523. NETDATA_EBPF_CHART_TYPE_LINE,
  524. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5801,
  525. ebpf_create_global_dimension,
  526. &shm_publish_aggregated[NETDATA_KEY_SHMAT_CALL],
  527. 1,
  528. update_every,
  529. NETDATA_EBPF_MODULE_NAME_SHM);
  530. ebpf_create_chart(type, NETDATA_SHMDT_CHART,
  531. "Calls to syscall <code>shmdt(2)</code>.",
  532. EBPF_COMMON_DIMENSION_CALL,
  533. NETDATA_APPS_IPC_SHM_GROUP,
  534. NETDATA_CGROUP_SHM_DT_CONTEXT,
  535. NETDATA_EBPF_CHART_TYPE_LINE,
  536. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5802,
  537. ebpf_create_global_dimension,
  538. &shm_publish_aggregated[NETDATA_KEY_SHMDT_CALL],
  539. 1,
  540. update_every,
  541. NETDATA_EBPF_MODULE_NAME_SHM);
  542. ebpf_create_chart(type, NETDATA_SHMCTL_CHART,
  543. "Calls to syscall <code>shmctl(2)</code>.",
  544. EBPF_COMMON_DIMENSION_CALL,
  545. NETDATA_APPS_IPC_SHM_GROUP,
  546. NETDATA_CGROUP_SHM_CTL_CONTEXT,
  547. NETDATA_EBPF_CHART_TYPE_LINE,
  548. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5803,
  549. ebpf_create_global_dimension,
  550. &shm_publish_aggregated[NETDATA_KEY_SHMCTL_CALL],
  551. 1,
  552. update_every,
  553. NETDATA_EBPF_MODULE_NAME_SHM);
  554. }
  555. /**
  556. * Obsolete specific shared memory charts
  557. *
  558. * Obsolete charts for cgroup/application.
  559. *
  560. * @param type the chart type.
  561. * @param update_every value to overwrite the update frequency set by the server.
  562. */
  563. static void ebpf_obsolete_specific_shm_charts(char *type, int update_every)
  564. {
  565. ebpf_write_chart_obsolete(type, NETDATA_SHMGET_CHART,
  566. "Calls to syscall <code>shmget(2)</code>.",
  567. EBPF_COMMON_DIMENSION_CALL,
  568. NETDATA_APPS_IPC_SHM_GROUP,
  569. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_SHM_GET_CONTEXT,
  570. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5800, update_every);
  571. ebpf_write_chart_obsolete(type, NETDATA_SHMAT_CHART,
  572. "Calls to syscall <code>shmat(2)</code>.",
  573. EBPF_COMMON_DIMENSION_CALL,
  574. NETDATA_APPS_IPC_SHM_GROUP,
  575. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_SHM_AT_CONTEXT,
  576. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5801, update_every);
  577. ebpf_write_chart_obsolete(type, NETDATA_SHMDT_CHART,
  578. "Calls to syscall <code>shmdt(2)</code>.",
  579. EBPF_COMMON_DIMENSION_CALL,
  580. NETDATA_APPS_IPC_SHM_GROUP,
  581. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_SHM_DT_CONTEXT,
  582. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5802, update_every);
  583. ebpf_write_chart_obsolete(type, NETDATA_SHMCTL_CHART,
  584. "Calls to syscall <code>shmctl(2)</code>.",
  585. EBPF_COMMON_DIMENSION_CALL,
  586. NETDATA_APPS_IPC_SHM_GROUP,
  587. NETDATA_EBPF_CHART_TYPE_LINE, NETDATA_CGROUP_SHM_CTL_CONTEXT,
  588. NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5803, update_every);
  589. }
  590. /**
  591. * Create Systemd Swap Charts
  592. *
  593. * Create charts when systemd is enabled
  594. *
  595. * @param update_every value to overwrite the update frequency set by the server.
  596. **/
  597. static void ebpf_create_systemd_shm_charts(int update_every)
  598. {
  599. ebpf_create_charts_on_systemd(NETDATA_SHMGET_CHART,
  600. "Calls to syscall <code>shmget(2)</code>.",
  601. EBPF_COMMON_DIMENSION_CALL,
  602. NETDATA_APPS_IPC_SHM_GROUP,
  603. NETDATA_EBPF_CHART_TYPE_STACKED,
  604. 20191,
  605. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  606. NETDATA_SYSTEMD_SHM_GET_CONTEXT, NETDATA_EBPF_MODULE_NAME_SHM, update_every);
  607. ebpf_create_charts_on_systemd(NETDATA_SHMAT_CHART,
  608. "Calls to syscall <code>shmat(2)</code>.",
  609. EBPF_COMMON_DIMENSION_CALL,
  610. NETDATA_APPS_IPC_SHM_GROUP,
  611. NETDATA_EBPF_CHART_TYPE_STACKED,
  612. 20192,
  613. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  614. NETDATA_SYSTEMD_SHM_AT_CONTEXT, NETDATA_EBPF_MODULE_NAME_SHM, update_every);
  615. ebpf_create_charts_on_systemd(NETDATA_SHMDT_CHART,
  616. "Calls to syscall <code>shmdt(2)</code>.",
  617. EBPF_COMMON_DIMENSION_CALL,
  618. NETDATA_APPS_IPC_SHM_GROUP,
  619. NETDATA_EBPF_CHART_TYPE_STACKED,
  620. 20193,
  621. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  622. NETDATA_SYSTEMD_SHM_DT_CONTEXT, NETDATA_EBPF_MODULE_NAME_SHM, update_every);
  623. ebpf_create_charts_on_systemd(NETDATA_SHMCTL_CHART,
  624. "Calls to syscall <code>shmctl(2)</code>.",
  625. EBPF_COMMON_DIMENSION_CALL,
  626. NETDATA_APPS_IPC_SHM_GROUP,
  627. NETDATA_EBPF_CHART_TYPE_STACKED,
  628. 20193,
  629. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  630. NETDATA_SYSTEMD_SHM_CTL_CONTEXT, NETDATA_EBPF_MODULE_NAME_SHM, update_every);
  631. }
  632. /**
  633. * Send Systemd charts
  634. *
  635. * Send collected data to Netdata.
  636. *
  637. * @return It returns the status for chart creation, if it is necessary to remove a specific dimension, zero is returned
  638. * otherwise function returns 1 to avoid chart recreation
  639. */
  640. static int ebpf_send_systemd_shm_charts()
  641. {
  642. int ret = 1;
  643. ebpf_cgroup_target_t *ect;
  644. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_SHMGET_CHART);
  645. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  646. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  647. write_chart_dimension(ect->name, (long long)ect->publish_shm.get);
  648. } else if (unlikely(ect->systemd))
  649. ret = 0;
  650. }
  651. write_end_chart();
  652. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_SHMAT_CHART);
  653. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  654. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  655. write_chart_dimension(ect->name, (long long)ect->publish_shm.at);
  656. }
  657. }
  658. write_end_chart();
  659. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_SHMDT_CHART);
  660. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  661. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  662. write_chart_dimension(ect->name, (long long)ect->publish_shm.dt);
  663. }
  664. }
  665. write_end_chart();
  666. write_begin_chart(NETDATA_SERVICE_FAMILY, NETDATA_SHMCTL_CHART);
  667. for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
  668. if (unlikely(ect->systemd) && unlikely(ect->updated)) {
  669. write_chart_dimension(ect->name, (long long)ect->publish_shm.ctl);
  670. }
  671. }
  672. write_end_chart();
  673. return ret;
  674. }
  675. /*
  676. * Send Specific Shared memory data
  677. *
  678. * Send data for specific cgroup/apps.
  679. *
  680. * @param type chart type
  681. * @param values structure with values that will be sent to netdata
  682. */
  683. static void ebpf_send_specific_shm_data(char *type, netdata_publish_shm_t *values)
  684. {
  685. write_begin_chart(type, NETDATA_SHMGET_CHART);
  686. write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMGET_CALL].name, (long long)values->get);
  687. write_end_chart();
  688. write_begin_chart(type, NETDATA_SHMAT_CHART);
  689. write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMAT_CALL].name, (long long)values->at);
  690. write_end_chart();
  691. write_begin_chart(type, NETDATA_SHMDT_CHART);
  692. write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMDT_CALL].name, (long long)values->dt);
  693. write_end_chart();
  694. write_begin_chart(type, NETDATA_SHMCTL_CHART);
  695. write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMCTL_CALL].name, (long long)values->ctl);
  696. write_end_chart();
  697. }
  698. /**
  699. * Send data to Netdata calling auxiliary functions.
  700. *
  701. * @param update_every value to overwrite the update frequency set by the server.
  702. */
  703. void ebpf_shm_send_cgroup_data(int update_every)
  704. {
  705. if (!ebpf_cgroup_pids)
  706. return;
  707. pthread_mutex_lock(&mutex_cgroup_shm);
  708. ebpf_cgroup_target_t *ect;
  709. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  710. ebpf_shm_sum_cgroup_pids(&ect->publish_shm, ect->pids);
  711. }
  712. int has_systemd = shm_ebpf_cgroup.header->systemd_enabled;
  713. if (has_systemd) {
  714. static int systemd_charts = 0;
  715. if (!systemd_charts) {
  716. ebpf_create_systemd_shm_charts(update_every);
  717. systemd_charts = 1;
  718. }
  719. systemd_charts = ebpf_send_systemd_shm_charts();
  720. }
  721. for (ect = ebpf_cgroup_pids; ect ; ect = ect->next) {
  722. if (ect->systemd)
  723. continue;
  724. if (!(ect->flags & NETDATA_EBPF_CGROUP_HAS_SHM_CHART) && ect->updated) {
  725. ebpf_create_specific_shm_charts(ect->name, update_every);
  726. ect->flags |= NETDATA_EBPF_CGROUP_HAS_SHM_CHART;
  727. }
  728. if (ect->flags & NETDATA_EBPF_CGROUP_HAS_SHM_CHART) {
  729. if (ect->updated) {
  730. ebpf_send_specific_shm_data(ect->name, &ect->publish_shm);
  731. } else {
  732. ebpf_obsolete_specific_shm_charts(ect->name, update_every);
  733. ect->flags &= ~NETDATA_EBPF_CGROUP_HAS_SWAP_CHART;
  734. }
  735. }
  736. }
  737. pthread_mutex_unlock(&mutex_cgroup_shm);
  738. }
  739. /**
  740. * Main loop for this collector.
  741. */
  742. static void shm_collector(ebpf_module_t *em)
  743. {
  744. shm_threads.thread = mallocz(sizeof(netdata_thread_t));
  745. shm_threads.start_routine = ebpf_shm_read_hash;
  746. netdata_thread_create(
  747. shm_threads.thread,
  748. shm_threads.name,
  749. NETDATA_THREAD_OPTION_DEFAULT,
  750. ebpf_shm_read_hash,
  751. em
  752. );
  753. int cgroups = em->cgroup_charts;
  754. int update_every = em->update_every;
  755. heartbeat_t hb;
  756. heartbeat_init(&hb);
  757. usec_t step = update_every * USEC_PER_SEC;
  758. while (!ebpf_exit_plugin) {
  759. (void)heartbeat_next(&hb, step);
  760. if (ebpf_exit_plugin)
  761. break;
  762. netdata_apps_integration_flags_t apps = em->apps_charts;
  763. pthread_mutex_lock(&collect_data_mutex);
  764. if (apps) {
  765. read_apps_table();
  766. }
  767. if (cgroups) {
  768. ebpf_update_shm_cgroup();
  769. }
  770. pthread_mutex_lock(&lock);
  771. shm_send_global();
  772. if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED) {
  773. ebpf_shm_send_apps_data(apps_groups_root_target);
  774. }
  775. if (cgroups) {
  776. ebpf_shm_send_cgroup_data(update_every);
  777. }
  778. pthread_mutex_unlock(&lock);
  779. pthread_mutex_unlock(&collect_data_mutex);
  780. }
  781. }
  782. /*****************************************************************
  783. * INITIALIZE THREAD
  784. *****************************************************************/
  785. /**
  786. * Create apps charts
  787. *
  788. * Call ebpf_create_chart to create the charts on apps submenu.
  789. *
  790. * @param em a pointer to the structure with the default values.
  791. */
  792. void ebpf_shm_create_apps_charts(struct ebpf_module *em, void *ptr)
  793. {
  794. struct target *root = ptr;
  795. ebpf_create_charts_on_apps(NETDATA_SHMGET_CHART,
  796. "Calls to syscall <code>shmget(2)</code>.",
  797. EBPF_COMMON_DIMENSION_CALL,
  798. NETDATA_APPS_IPC_SHM_GROUP,
  799. NETDATA_EBPF_CHART_TYPE_STACKED,
  800. 20191,
  801. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  802. root, em->update_every, NETDATA_EBPF_MODULE_NAME_SHM);
  803. ebpf_create_charts_on_apps(NETDATA_SHMAT_CHART,
  804. "Calls to syscall <code>shmat(2)</code>.",
  805. EBPF_COMMON_DIMENSION_CALL,
  806. NETDATA_APPS_IPC_SHM_GROUP,
  807. NETDATA_EBPF_CHART_TYPE_STACKED,
  808. 20192,
  809. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  810. root, em->update_every, NETDATA_EBPF_MODULE_NAME_SHM);
  811. ebpf_create_charts_on_apps(NETDATA_SHMDT_CHART,
  812. "Calls to syscall <code>shmdt(2)</code>.",
  813. EBPF_COMMON_DIMENSION_CALL,
  814. NETDATA_APPS_IPC_SHM_GROUP,
  815. NETDATA_EBPF_CHART_TYPE_STACKED,
  816. 20193,
  817. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  818. root, em->update_every, NETDATA_EBPF_MODULE_NAME_SHM);
  819. ebpf_create_charts_on_apps(NETDATA_SHMCTL_CHART,
  820. "Calls to syscall <code>shmctl(2)</code>.",
  821. EBPF_COMMON_DIMENSION_CALL,
  822. NETDATA_APPS_IPC_SHM_GROUP,
  823. NETDATA_EBPF_CHART_TYPE_STACKED,
  824. 20194,
  825. ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
  826. root, em->update_every, NETDATA_EBPF_MODULE_NAME_SHM);
  827. em->apps_charts |= NETDATA_EBPF_APPS_FLAG_CHART_CREATED;
  828. }
  829. /**
  830. * Allocate vectors used with this thread.
  831. *
  832. * We are not testing the return, because callocz does this and shutdown the software
  833. * case it was not possible to allocate.
  834. *
  835. * @param apps is apps enabled?
  836. */
  837. static void ebpf_shm_allocate_global_vectors(int apps)
  838. {
  839. if (apps)
  840. shm_pid = callocz((size_t)pid_max, sizeof(netdata_publish_shm_t *));
  841. shm_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_publish_shm_t));
  842. shm_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t));
  843. memset(shm_hash_values, 0, sizeof(shm_hash_values));
  844. }
  845. /*****************************************************************
  846. * MAIN THREAD
  847. *****************************************************************/
  848. /**
  849. * Create global charts
  850. *
  851. * Call ebpf_create_chart to create the charts for the collector.
  852. *
  853. * @param update_every value to overwrite the update frequency set by the server.
  854. */
  855. static void ebpf_create_shm_charts(int update_every)
  856. {
  857. ebpf_create_chart(
  858. NETDATA_EBPF_SYSTEM_GROUP,
  859. NETDATA_SHM_GLOBAL_CHART,
  860. "Calls to shared memory system calls",
  861. EBPF_COMMON_DIMENSION_CALL,
  862. NETDATA_SYSTEM_IPC_SHM_SUBMENU,
  863. NULL,
  864. NETDATA_EBPF_CHART_TYPE_LINE,
  865. NETDATA_CHART_PRIO_SYSTEM_IPC_SHARED_MEM_CALLS,
  866. ebpf_create_global_dimension,
  867. shm_publish_aggregated,
  868. NETDATA_SHM_END,
  869. update_every, NETDATA_EBPF_MODULE_NAME_SHM
  870. );
  871. fflush(stdout);
  872. }
  873. /*
  874. * Load BPF
  875. *
  876. * Load BPF files.
  877. *
  878. * @param em the structure with configuration
  879. */
  880. static int ebpf_shm_load_bpf(ebpf_module_t *em)
  881. {
  882. int ret = 0;
  883. if (em->load == EBPF_LOAD_LEGACY) {
  884. em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
  885. if (!em->probe_links) {
  886. em->enabled = CONFIG_BOOLEAN_NO;
  887. ret = -1;
  888. }
  889. }
  890. #ifdef LIBBPF_MAJOR_VERSION
  891. else {
  892. bpf_obj = shm_bpf__open();
  893. if (!bpf_obj)
  894. ret = -1;
  895. else
  896. ret = ebpf_shm_load_and_attach(bpf_obj, em);
  897. }
  898. #endif
  899. if (ret)
  900. error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->thread_name);
  901. return ret;
  902. }
  903. /**
  904. * Shared memory thread.
  905. *
  906. * @param ptr a pointer to `struct ebpf_module`
  907. * @return It always return NULL
  908. */
  909. void *ebpf_shm_thread(void *ptr)
  910. {
  911. netdata_thread_cleanup_push(ebpf_shm_exit, ptr);
  912. ebpf_module_t *em = (ebpf_module_t *)ptr;
  913. em->maps = shm_maps;
  914. ebpf_update_pid_table(&shm_maps[NETDATA_PID_SHM_TABLE], em);
  915. if (!em->enabled) {
  916. goto endshm;
  917. }
  918. #ifdef LIBBPF_MAJOR_VERSION
  919. ebpf_adjust_thread_load(em, default_btf);
  920. #endif
  921. if (ebpf_shm_load_bpf(em)) {
  922. em->enabled = CONFIG_BOOLEAN_NO;
  923. goto endshm;
  924. }
  925. ebpf_shm_allocate_global_vectors(em->apps_charts);
  926. int algorithms[NETDATA_SHM_END] = {
  927. NETDATA_EBPF_INCREMENTAL_IDX,
  928. NETDATA_EBPF_INCREMENTAL_IDX,
  929. NETDATA_EBPF_INCREMENTAL_IDX,
  930. NETDATA_EBPF_INCREMENTAL_IDX
  931. };
  932. ebpf_global_labels(
  933. shm_aggregated_data,
  934. shm_publish_aggregated,
  935. shm_dimension_name,
  936. shm_dimension_name,
  937. algorithms,
  938. NETDATA_SHM_END
  939. );
  940. pthread_mutex_lock(&lock);
  941. ebpf_create_shm_charts(em->update_every);
  942. ebpf_update_stats(&plugin_statistics, em);
  943. pthread_mutex_unlock(&lock);
  944. shm_collector(em);
  945. endshm:
  946. if (!em->enabled)
  947. ebpf_update_disabled_plugin_stats(em);
  948. netdata_thread_cleanup_pop(1);
  949. return NULL;
  950. }