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