strobemeta.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. // SPDX-License-Identifier: GPL-2.0
  2. // Copyright (c) 2019 Facebook
  3. #include <stdint.h>
  4. #include <stddef.h>
  5. #include <stdbool.h>
  6. #include <linux/bpf.h>
  7. #include <linux/ptrace.h>
  8. #include <linux/sched.h>
  9. #include <linux/types.h>
  10. #include <bpf/bpf_helpers.h>
  11. typedef uint32_t pid_t;
  12. struct task_struct {};
  13. #define TASK_COMM_LEN 16
  14. #define PERF_MAX_STACK_DEPTH 127
  15. #define STROBE_TYPE_INVALID 0
  16. #define STROBE_TYPE_INT 1
  17. #define STROBE_TYPE_STR 2
  18. #define STROBE_TYPE_MAP 3
  19. #define STACK_TABLE_EPOCH_SHIFT 20
  20. #define STROBE_MAX_STR_LEN 1
  21. #define STROBE_MAX_CFGS 32
  22. #define STROBE_MAX_PAYLOAD \
  23. (STROBE_MAX_STRS * STROBE_MAX_STR_LEN + \
  24. STROBE_MAX_MAPS * (1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN)
  25. struct strobe_value_header {
  26. /*
  27. * meaning depends on type:
  28. * 1. int: 0, if value not set, 1 otherwise
  29. * 2. str: 1 always, whether value is set or not is determined by ptr
  30. * 3. map: 1 always, pointer points to additional struct with number
  31. * of entries (up to STROBE_MAX_MAP_ENTRIES)
  32. */
  33. uint16_t len;
  34. /*
  35. * _reserved might be used for some future fields/flags, but we always
  36. * want to keep strobe_value_header to be 8 bytes, so BPF can read 16
  37. * bytes in one go and get both header and value
  38. */
  39. uint8_t _reserved[6];
  40. };
  41. /*
  42. * strobe_value_generic is used from BPF probe only, but needs to be a union
  43. * of strobe_value_int/strobe_value_str/strobe_value_map
  44. */
  45. struct strobe_value_generic {
  46. struct strobe_value_header header;
  47. union {
  48. int64_t val;
  49. void *ptr;
  50. };
  51. };
  52. struct strobe_value_int {
  53. struct strobe_value_header header;
  54. int64_t value;
  55. };
  56. struct strobe_value_str {
  57. struct strobe_value_header header;
  58. const char* value;
  59. };
  60. struct strobe_value_map {
  61. struct strobe_value_header header;
  62. const struct strobe_map_raw* value;
  63. };
  64. struct strobe_map_entry {
  65. const char* key;
  66. const char* val;
  67. };
  68. /*
  69. * Map of C-string key/value pairs with fixed maximum capacity. Each map has
  70. * corresponding int64 ID, which application can use (or ignore) in whatever
  71. * way appropriate. Map is "write-only", there is no way to get data out of
  72. * map. Map is intended to be used to provide metadata for profilers and is
  73. * not to be used for internal in-app communication. All methods are
  74. * thread-safe.
  75. */
  76. struct strobe_map_raw {
  77. /*
  78. * general purpose unique ID that's up to application to decide
  79. * whether and how to use; for request metadata use case id is unique
  80. * request ID that's used to match metadata with stack traces on
  81. * Strobelight backend side
  82. */
  83. int64_t id;
  84. /* number of used entries in map */
  85. int64_t cnt;
  86. /*
  87. * having volatile doesn't change anything on BPF side, but clang
  88. * emits warnings for passing `volatile const char *` into
  89. * bpf_probe_read_user_str that expects just `const char *`
  90. */
  91. const char* tag;
  92. /*
  93. * key/value entries, each consisting of 2 pointers to key and value
  94. * C strings
  95. */
  96. struct strobe_map_entry entries[STROBE_MAX_MAP_ENTRIES];
  97. };
  98. /* Following values define supported values of TLS mode */
  99. #define TLS_NOT_SET -1
  100. #define TLS_LOCAL_EXEC 0
  101. #define TLS_IMM_EXEC 1
  102. #define TLS_GENERAL_DYN 2
  103. /*
  104. * structure that universally represents TLS location (both for static
  105. * executables and shared libraries)
  106. */
  107. struct strobe_value_loc {
  108. /*
  109. * tls_mode defines what TLS mode was used for particular metavariable:
  110. * - -1 (TLS_NOT_SET) - no metavariable;
  111. * - 0 (TLS_LOCAL_EXEC) - Local Executable mode;
  112. * - 1 (TLS_IMM_EXEC) - Immediate Executable mode;
  113. * - 2 (TLS_GENERAL_DYN) - General Dynamic mode;
  114. * Local Dynamic mode is not yet supported, because never seen in
  115. * practice. Mode defines how offset field is interpreted. See
  116. * calc_location() in below for details.
  117. */
  118. int64_t tls_mode;
  119. /*
  120. * TLS_LOCAL_EXEC: offset from thread pointer (fs:0 for x86-64,
  121. * tpidr_el0 for aarch64).
  122. * TLS_IMM_EXEC: absolute address of GOT entry containing offset
  123. * from thread pointer;
  124. * TLS_GENERAL_DYN: absolute addres of double GOT entry
  125. * containing tls_index_t struct;
  126. */
  127. int64_t offset;
  128. };
  129. struct strobemeta_cfg {
  130. int64_t req_meta_idx;
  131. struct strobe_value_loc int_locs[STROBE_MAX_INTS];
  132. struct strobe_value_loc str_locs[STROBE_MAX_STRS];
  133. struct strobe_value_loc map_locs[STROBE_MAX_MAPS];
  134. };
  135. struct strobe_map_descr {
  136. uint64_t id;
  137. int16_t tag_len;
  138. /*
  139. * cnt <0 - map value isn't set;
  140. * 0 - map has id set, but no key/value entries
  141. */
  142. int16_t cnt;
  143. /*
  144. * both key_lens[i] and val_lens[i] should be >0 for present key/value
  145. * entry
  146. */
  147. uint16_t key_lens[STROBE_MAX_MAP_ENTRIES];
  148. uint16_t val_lens[STROBE_MAX_MAP_ENTRIES];
  149. };
  150. struct strobemeta_payload {
  151. /* req_id has valid request ID, if req_meta_valid == 1 */
  152. int64_t req_id;
  153. uint8_t req_meta_valid;
  154. /*
  155. * mask has Nth bit set to 1, if Nth metavar was present and
  156. * successfully read
  157. */
  158. uint64_t int_vals_set_mask;
  159. int64_t int_vals[STROBE_MAX_INTS];
  160. /* len is >0 for present values */
  161. uint16_t str_lens[STROBE_MAX_STRS];
  162. /* if map_descrs[i].cnt == -1, metavar is not present/set */
  163. struct strobe_map_descr map_descrs[STROBE_MAX_MAPS];
  164. /*
  165. * payload has compactly packed values of str and map variables in the
  166. * form: strval1\0strval2\0map1key1\0map1val1\0map2key1\0map2val1\0
  167. * (and so on); str_lens[i], key_lens[i] and val_lens[i] determines
  168. * value length
  169. */
  170. char payload[STROBE_MAX_PAYLOAD];
  171. };
  172. struct strobelight_bpf_sample {
  173. uint64_t ktime;
  174. char comm[TASK_COMM_LEN];
  175. pid_t pid;
  176. int user_stack_id;
  177. int kernel_stack_id;
  178. int has_meta;
  179. struct strobemeta_payload metadata;
  180. /*
  181. * makes it possible to pass (<real payload size> + 1) as data size to
  182. * perf_submit() to avoid perf_submit's paranoia about passing zero as
  183. * size, as it deduces that <real payload size> might be
  184. * **theoretically** zero
  185. */
  186. char dummy_safeguard;
  187. };
  188. struct {
  189. __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
  190. __uint(max_entries, 32);
  191. __uint(key_size, sizeof(int));
  192. __uint(value_size, sizeof(int));
  193. } samples SEC(".maps");
  194. struct {
  195. __uint(type, BPF_MAP_TYPE_STACK_TRACE);
  196. __uint(max_entries, 16);
  197. __uint(key_size, sizeof(uint32_t));
  198. __uint(value_size, sizeof(uint64_t) * PERF_MAX_STACK_DEPTH);
  199. } stacks_0 SEC(".maps");
  200. struct {
  201. __uint(type, BPF_MAP_TYPE_STACK_TRACE);
  202. __uint(max_entries, 16);
  203. __uint(key_size, sizeof(uint32_t));
  204. __uint(value_size, sizeof(uint64_t) * PERF_MAX_STACK_DEPTH);
  205. } stacks_1 SEC(".maps");
  206. struct {
  207. __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
  208. __uint(max_entries, 1);
  209. __type(key, uint32_t);
  210. __type(value, struct strobelight_bpf_sample);
  211. } sample_heap SEC(".maps");
  212. struct {
  213. __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
  214. __uint(max_entries, STROBE_MAX_CFGS);
  215. __type(key, pid_t);
  216. __type(value, struct strobemeta_cfg);
  217. } strobemeta_cfgs SEC(".maps");
  218. /* Type for the dtv. */
  219. /* https://github.com/lattera/glibc/blob/master/nptl/sysdeps/x86_64/tls.h#L34 */
  220. typedef union dtv {
  221. size_t counter;
  222. struct {
  223. void* val;
  224. bool is_static;
  225. } pointer;
  226. } dtv_t;
  227. /* Partial definition for tcbhead_t */
  228. /* https://github.com/bminor/glibc/blob/master/sysdeps/x86_64/nptl/tls.h#L42 */
  229. struct tcbhead {
  230. void* tcb;
  231. dtv_t* dtv;
  232. };
  233. /*
  234. * TLS module/offset information for shared library case.
  235. * For x86-64, this is mapped onto two entries in GOT.
  236. * For aarch64, this is pointed to by second GOT entry.
  237. */
  238. struct tls_index {
  239. uint64_t module;
  240. uint64_t offset;
  241. };
  242. #ifdef SUBPROGS
  243. __noinline
  244. #else
  245. __always_inline
  246. #endif
  247. static void *calc_location(struct strobe_value_loc *loc, void *tls_base)
  248. {
  249. /*
  250. * tls_mode value is:
  251. * - -1 (TLS_NOT_SET), if no metavar is present;
  252. * - 0 (TLS_LOCAL_EXEC), if metavar uses Local Executable mode of TLS
  253. * (offset from fs:0 for x86-64 or tpidr_el0 for aarch64);
  254. * - 1 (TLS_IMM_EXEC), if metavar uses Immediate Executable mode of TLS;
  255. * - 2 (TLS_GENERAL_DYN), if metavar uses General Dynamic mode of TLS;
  256. * This schema allows to use something like:
  257. * (tls_mode + 1) * (tls_base + offset)
  258. * to get NULL for "no metavar" location, or correct pointer for local
  259. * executable mode without doing extra ifs.
  260. */
  261. if (loc->tls_mode <= TLS_LOCAL_EXEC) {
  262. /* static executable is simple, we just have offset from
  263. * tls_base */
  264. void *addr = tls_base + loc->offset;
  265. /* multiply by (tls_mode + 1) to get NULL, if we have no
  266. * metavar in this slot */
  267. return (void *)((loc->tls_mode + 1) * (int64_t)addr);
  268. }
  269. /*
  270. * Other modes are more complicated, we need to jump through few hoops.
  271. *
  272. * For immediate executable mode (currently supported only for aarch64):
  273. * - loc->offset is pointing to a GOT entry containing fixed offset
  274. * relative to tls_base;
  275. *
  276. * For general dynamic mode:
  277. * - loc->offset is pointing to a beginning of double GOT entries;
  278. * - (for aarch64 only) second entry points to tls_index_t struct;
  279. * - (for x86-64 only) two GOT entries are already tls_index_t;
  280. * - tls_index_t->module is used to find start of TLS section in
  281. * which variable resides;
  282. * - tls_index_t->offset provides offset within that TLS section,
  283. * pointing to value of variable.
  284. */
  285. struct tls_index tls_index;
  286. dtv_t *dtv;
  287. void *tls_ptr;
  288. bpf_probe_read_user(&tls_index, sizeof(struct tls_index),
  289. (void *)loc->offset);
  290. /* valid module index is always positive */
  291. if (tls_index.module > 0) {
  292. /* dtv = ((struct tcbhead *)tls_base)->dtv[tls_index.module] */
  293. bpf_probe_read_user(&dtv, sizeof(dtv),
  294. &((struct tcbhead *)tls_base)->dtv);
  295. dtv += tls_index.module;
  296. } else {
  297. dtv = NULL;
  298. }
  299. bpf_probe_read_user(&tls_ptr, sizeof(void *), dtv);
  300. /* if pointer has (void *)-1 value, then TLS wasn't initialized yet */
  301. return tls_ptr && tls_ptr != (void *)-1
  302. ? tls_ptr + tls_index.offset
  303. : NULL;
  304. }
  305. #ifdef SUBPROGS
  306. __noinline
  307. #else
  308. __always_inline
  309. #endif
  310. static void read_int_var(struct strobemeta_cfg *cfg,
  311. size_t idx, void *tls_base,
  312. struct strobe_value_generic *value,
  313. struct strobemeta_payload *data)
  314. {
  315. void *location = calc_location(&cfg->int_locs[idx], tls_base);
  316. if (!location)
  317. return;
  318. bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location);
  319. data->int_vals[idx] = value->val;
  320. if (value->header.len)
  321. data->int_vals_set_mask |= (1 << idx);
  322. }
  323. static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg,
  324. size_t idx, void *tls_base,
  325. struct strobe_value_generic *value,
  326. struct strobemeta_payload *data,
  327. void *payload)
  328. {
  329. void *location;
  330. uint64_t len;
  331. data->str_lens[idx] = 0;
  332. location = calc_location(&cfg->str_locs[idx], tls_base);
  333. if (!location)
  334. return 0;
  335. bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location);
  336. len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, value->ptr);
  337. /*
  338. * if bpf_probe_read_user_str returns error (<0), due to casting to
  339. * unsinged int, it will become big number, so next check is
  340. * sufficient to check for errors AND prove to BPF verifier, that
  341. * bpf_probe_read_user_str won't return anything bigger than
  342. * STROBE_MAX_STR_LEN
  343. */
  344. if (len > STROBE_MAX_STR_LEN)
  345. return 0;
  346. data->str_lens[idx] = len;
  347. return len;
  348. }
  349. static __always_inline void *read_map_var(struct strobemeta_cfg *cfg,
  350. size_t idx, void *tls_base,
  351. struct strobe_value_generic *value,
  352. struct strobemeta_payload *data,
  353. void *payload)
  354. {
  355. struct strobe_map_descr* descr = &data->map_descrs[idx];
  356. struct strobe_map_raw map;
  357. void *location;
  358. uint64_t len;
  359. int i;
  360. descr->tag_len = 0; /* presume no tag is set */
  361. descr->cnt = -1; /* presume no value is set */
  362. location = calc_location(&cfg->map_locs[idx], tls_base);
  363. if (!location)
  364. return payload;
  365. bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location);
  366. if (bpf_probe_read_user(&map, sizeof(struct strobe_map_raw), value->ptr))
  367. return payload;
  368. descr->id = map.id;
  369. descr->cnt = map.cnt;
  370. if (cfg->req_meta_idx == idx) {
  371. data->req_id = map.id;
  372. data->req_meta_valid = 1;
  373. }
  374. len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, map.tag);
  375. if (len <= STROBE_MAX_STR_LEN) {
  376. descr->tag_len = len;
  377. payload += len;
  378. }
  379. #ifdef NO_UNROLL
  380. #pragma clang loop unroll(disable)
  381. #else
  382. #pragma unroll
  383. #endif
  384. for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  385. if (i >= map.cnt)
  386. break;
  387. descr->key_lens[i] = 0;
  388. len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN,
  389. map.entries[i].key);
  390. if (len <= STROBE_MAX_STR_LEN) {
  391. descr->key_lens[i] = len;
  392. payload += len;
  393. }
  394. descr->val_lens[i] = 0;
  395. len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN,
  396. map.entries[i].val);
  397. if (len <= STROBE_MAX_STR_LEN) {
  398. descr->val_lens[i] = len;
  399. payload += len;
  400. }
  401. }
  402. return payload;
  403. }
  404. #ifdef USE_BPF_LOOP
  405. enum read_type {
  406. READ_INT_VAR,
  407. READ_MAP_VAR,
  408. READ_STR_VAR,
  409. };
  410. struct read_var_ctx {
  411. struct strobemeta_payload *data;
  412. void *tls_base;
  413. struct strobemeta_cfg *cfg;
  414. void *payload;
  415. /* value gets mutated */
  416. struct strobe_value_generic *value;
  417. enum read_type type;
  418. };
  419. static int read_var_callback(__u32 index, struct read_var_ctx *ctx)
  420. {
  421. switch (ctx->type) {
  422. case READ_INT_VAR:
  423. if (index >= STROBE_MAX_INTS)
  424. return 1;
  425. read_int_var(ctx->cfg, index, ctx->tls_base, ctx->value, ctx->data);
  426. break;
  427. case READ_MAP_VAR:
  428. if (index >= STROBE_MAX_MAPS)
  429. return 1;
  430. ctx->payload = read_map_var(ctx->cfg, index, ctx->tls_base,
  431. ctx->value, ctx->data, ctx->payload);
  432. break;
  433. case READ_STR_VAR:
  434. if (index >= STROBE_MAX_STRS)
  435. return 1;
  436. ctx->payload += read_str_var(ctx->cfg, index, ctx->tls_base,
  437. ctx->value, ctx->data, ctx->payload);
  438. break;
  439. }
  440. return 0;
  441. }
  442. #endif /* USE_BPF_LOOP */
  443. /*
  444. * read_strobe_meta returns NULL, if no metadata was read; otherwise returns
  445. * pointer to *right after* payload ends
  446. */
  447. #ifdef SUBPROGS
  448. __noinline
  449. #else
  450. __always_inline
  451. #endif
  452. static void *read_strobe_meta(struct task_struct *task,
  453. struct strobemeta_payload *data)
  454. {
  455. pid_t pid = bpf_get_current_pid_tgid() >> 32;
  456. struct strobe_value_generic value = {0};
  457. struct strobemeta_cfg *cfg;
  458. void *tls_base, *payload;
  459. cfg = bpf_map_lookup_elem(&strobemeta_cfgs, &pid);
  460. if (!cfg)
  461. return NULL;
  462. data->int_vals_set_mask = 0;
  463. data->req_meta_valid = 0;
  464. payload = data->payload;
  465. /*
  466. * we don't have struct task_struct definition, it should be:
  467. * tls_base = (void *)task->thread.fsbase;
  468. */
  469. tls_base = (void *)task;
  470. #ifdef USE_BPF_LOOP
  471. struct read_var_ctx ctx = {
  472. .cfg = cfg,
  473. .tls_base = tls_base,
  474. .value = &value,
  475. .data = data,
  476. .payload = payload,
  477. };
  478. int err;
  479. ctx.type = READ_INT_VAR;
  480. err = bpf_loop(STROBE_MAX_INTS, read_var_callback, &ctx, 0);
  481. if (err != STROBE_MAX_INTS)
  482. return NULL;
  483. ctx.type = READ_STR_VAR;
  484. err = bpf_loop(STROBE_MAX_STRS, read_var_callback, &ctx, 0);
  485. if (err != STROBE_MAX_STRS)
  486. return NULL;
  487. ctx.type = READ_MAP_VAR;
  488. err = bpf_loop(STROBE_MAX_MAPS, read_var_callback, &ctx, 0);
  489. if (err != STROBE_MAX_MAPS)
  490. return NULL;
  491. #else
  492. #ifdef NO_UNROLL
  493. #pragma clang loop unroll(disable)
  494. #else
  495. #pragma unroll
  496. #endif /* NO_UNROLL */
  497. for (int i = 0; i < STROBE_MAX_INTS; ++i) {
  498. read_int_var(cfg, i, tls_base, &value, data);
  499. }
  500. #ifdef NO_UNROLL
  501. #pragma clang loop unroll(disable)
  502. #else
  503. #pragma unroll
  504. #endif /* NO_UNROLL */
  505. for (int i = 0; i < STROBE_MAX_STRS; ++i) {
  506. payload += read_str_var(cfg, i, tls_base, &value, data, payload);
  507. }
  508. #ifdef NO_UNROLL
  509. #pragma clang loop unroll(disable)
  510. #else
  511. #pragma unroll
  512. #endif /* NO_UNROLL */
  513. for (int i = 0; i < STROBE_MAX_MAPS; ++i) {
  514. payload = read_map_var(cfg, i, tls_base, &value, data, payload);
  515. }
  516. #endif /* USE_BPF_LOOP */
  517. /*
  518. * return pointer right after end of payload, so it's possible to
  519. * calculate exact amount of useful data that needs to be sent
  520. */
  521. return payload;
  522. }
  523. SEC("raw_tracepoint/kfree_skb")
  524. int on_event(struct pt_regs *ctx) {
  525. pid_t pid = bpf_get_current_pid_tgid() >> 32;
  526. struct strobelight_bpf_sample* sample;
  527. struct task_struct *task;
  528. uint32_t zero = 0;
  529. uint64_t ktime_ns;
  530. void *sample_end;
  531. sample = bpf_map_lookup_elem(&sample_heap, &zero);
  532. if (!sample)
  533. return 0; /* this will never happen */
  534. sample->pid = pid;
  535. bpf_get_current_comm(&sample->comm, TASK_COMM_LEN);
  536. ktime_ns = bpf_ktime_get_ns();
  537. sample->ktime = ktime_ns;
  538. task = (struct task_struct *)bpf_get_current_task();
  539. sample_end = read_strobe_meta(task, &sample->metadata);
  540. sample->has_meta = sample_end != NULL;
  541. sample_end = sample_end ? : &sample->metadata;
  542. if ((ktime_ns >> STACK_TABLE_EPOCH_SHIFT) & 1) {
  543. sample->kernel_stack_id = bpf_get_stackid(ctx, &stacks_1, 0);
  544. sample->user_stack_id = bpf_get_stackid(ctx, &stacks_1, BPF_F_USER_STACK);
  545. } else {
  546. sample->kernel_stack_id = bpf_get_stackid(ctx, &stacks_0, 0);
  547. sample->user_stack_id = bpf_get_stackid(ctx, &stacks_0, BPF_F_USER_STACK);
  548. }
  549. uint64_t sample_size = sample_end - (void *)sample;
  550. /* should always be true */
  551. if (sample_size < sizeof(struct strobelight_bpf_sample))
  552. bpf_perf_event_output(ctx, &samples, 0, sample, 1 + sample_size);
  553. return 0;
  554. }
  555. char _license[] SEC("license") = "GPL";