ebpf_shm.c 36 KB

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