github.com/undoio/delve@v1.9.0/pkg/proc/internal/ebpf/bpf/trace.bpf.c (about) 1 #include "include/trace.bpf.h" 2 #include <string.h> 3 4 #define STRING_KIND 24 5 6 // parse_string_param will parse a string parameter. The parsed value of the string 7 // will be put into param->deref_val. This function expects the string struct 8 // which contains a pointer to the string and the length of the string to have 9 // already been read from memory and passed in as param->val. 10 __always_inline 11 int parse_string_param(struct pt_regs *ctx, function_parameter_t *param) { 12 u64 str_len; 13 size_t str_addr; 14 15 memcpy(&str_addr, param->val, sizeof(str_addr)); 16 memcpy(&str_len, param->val + sizeof(str_addr), sizeof(str_len)); 17 param->daddr = str_addr; 18 19 if (str_addr != 0) { 20 if (str_len > 0x30) { 21 str_len = 0x30; 22 } 23 int ret = bpf_probe_read_user(¶m->deref_val, str_len, (void *)(str_addr)); 24 if (ret < 0) { 25 return 1; 26 } 27 } 28 return 0; 29 } 30 31 __always_inline 32 int parse_param_stack(struct pt_regs *ctx, function_parameter_t *param) { 33 long ret; 34 size_t addr = ctx->sp + param->offset; 35 ret = bpf_probe_read_user(¶m->val, param->size, (void *)(addr)); 36 if (ret < 0) { 37 return 1; 38 } 39 return 0; 40 } 41 42 __always_inline 43 void get_value_from_register(struct pt_regs *ctx, void *dest, int reg_num) { 44 switch (reg_num) { 45 case 0: // RAX 46 memcpy(dest, &ctx->ax, sizeof(ctx->ax)); 47 break; 48 case 1: // RDX 49 memcpy(dest, &ctx->dx, sizeof(ctx->dx)); 50 break; 51 case 2: // RCX 52 memcpy(dest, &ctx->cx, sizeof(ctx->cx)); 53 break; 54 case 3: // RBX 55 memcpy(dest, &ctx->bx, sizeof(ctx->bx)); 56 break; 57 case 4: // RSI 58 memcpy(dest, &ctx->si, sizeof(ctx->si)); 59 break; 60 case 5: // RDI 61 memcpy(dest, &ctx->di, sizeof(ctx->di)); 62 break; 63 case 6: // RBP 64 memcpy(dest, &ctx->bp, sizeof(ctx->bp)); 65 break; 66 case 7: // RSP 67 memcpy(dest, &ctx->sp, sizeof(ctx->sp)); 68 break; 69 case 8: // R8 70 memcpy(dest, &ctx->r8, sizeof(ctx->r8)); 71 break; 72 case 9: // R9 73 memcpy(dest, &ctx->r9, sizeof(ctx->r9)); 74 break; 75 case 10: // R10 76 memcpy(dest, &ctx->r10, sizeof(ctx->r10)); 77 break; 78 case 11: // R11 79 memcpy(dest, &ctx->r11, sizeof(ctx->r11)); 80 break; 81 case 12: // R12 82 memcpy(dest, &ctx->r12, sizeof(ctx->r12)); 83 break; 84 case 13: // R13 85 memcpy(dest, &ctx->r13, sizeof(ctx->r13)); 86 break; 87 case 14: // R14 88 memcpy(dest, &ctx->r14, sizeof(ctx->r14)); 89 break; 90 case 15: // R15 91 memcpy(dest, &ctx->r15, sizeof(ctx->r15)); 92 break; 93 } 94 } 95 96 __always_inline 97 int parse_param_registers(struct pt_regs *ctx, function_parameter_t *param) { 98 switch (param->n_pieces) { 99 case 6: 100 get_value_from_register(ctx, param->val+40, param->reg_nums[5]); 101 case 5: 102 get_value_from_register(ctx, param->val+32, param->reg_nums[4]); 103 case 4: 104 get_value_from_register(ctx, param->val+24, param->reg_nums[3]); 105 case 3: 106 get_value_from_register(ctx, param->val+16, param->reg_nums[2]); 107 case 2: 108 get_value_from_register(ctx, param->val+8, param->reg_nums[1]); 109 case 1: 110 get_value_from_register(ctx, param->val, param->reg_nums[0]); 111 } 112 return 0; 113 } 114 115 __always_inline 116 int parse_param(struct pt_regs *ctx, function_parameter_t *param) { 117 if (param->size > 0x30) { 118 return 0; 119 } 120 121 // Parse the initial value of the parameter. 122 // If the parameter is a basic type, we will be finished here. 123 // If the parameter is a more complex type such as a string or 124 // a slice we will need some further processing below. 125 int ret = 0; 126 if (param->in_reg) { 127 ret = parse_param_registers(ctx, param); 128 } else { 129 ret = parse_param_stack(ctx, param); 130 } 131 if (ret != 0) { 132 return ret; 133 } 134 135 switch (param->kind) { 136 case STRING_KIND: 137 return parse_string_param(ctx, param); 138 } 139 140 return 0; 141 } 142 143 __always_inline 144 int get_goroutine_id(function_parameter_list_t *parsed_args) { 145 // Since eBPF programs have such strict stack requirements 146 // me must implement our own heap using a ringbuffer. 147 // Reserve some memory in our "heap" for the task_struct. 148 struct task_struct *task; 149 task = bpf_ringbuf_reserve(&heap, sizeof(struct task_struct), 0); 150 if (!task) { 151 return 0; 152 } 153 154 // Get the current task. 155 __u64 task_ptr = bpf_get_current_task(); 156 if (!task_ptr) 157 { 158 bpf_ringbuf_discard(task, 0); 159 return 0; 160 } 161 // The bpf_get_current_task helper returns us the address of the task_struct in 162 // kernel memory. Use the bpf_probe_read_kernel helper to read the struct out of 163 // kernel memory. 164 bpf_probe_read_kernel(task, sizeof(struct task_struct), (void*)(task_ptr)); 165 166 // Get the Goroutine ID which is stored in thread local storage. 167 __u64 goid; 168 size_t g_addr; 169 bpf_probe_read_user(&g_addr, sizeof(void *), (void*)(task->thread.fsbase+parsed_args->g_addr_offset)); 170 bpf_probe_read_user(&goid, sizeof(void *), (void*)(g_addr+parsed_args->goid_offset)); 171 parsed_args->goroutine_id = goid; 172 173 // Free back up the memory we reserved for the task_struct. 174 bpf_ringbuf_discard(task, 0); 175 176 return 1; 177 } 178 179 __always_inline 180 void parse_params(struct pt_regs *ctx, unsigned int n_params, function_parameter_t params[6]) { 181 // Since we cannot loop in eBPF programs let's take adavantage of the 182 // fact that in C switch cases will pass through automatically. 183 switch (n_params) { 184 case 6: 185 parse_param(ctx, ¶ms[5]); 186 case 5: 187 parse_param(ctx, ¶ms[4]); 188 case 4: 189 parse_param(ctx, ¶ms[3]); 190 case 3: 191 parse_param(ctx, ¶ms[2]); 192 case 2: 193 parse_param(ctx, ¶ms[1]); 194 case 1: 195 parse_param(ctx, ¶ms[0]); 196 } 197 } 198 199 SEC("uprobe/dlv_trace") 200 int uprobe__dlv_trace(struct pt_regs *ctx) { 201 function_parameter_list_t *args; 202 function_parameter_list_t *parsed_args; 203 uint64_t key = ctx->ip; 204 205 args = bpf_map_lookup_elem(&arg_map, &key); 206 if (!args) { 207 return 1; 208 } 209 210 parsed_args = bpf_ringbuf_reserve(&events, sizeof(function_parameter_list_t), 0); 211 if (!parsed_args) { 212 return 1; 213 } 214 215 // Initialize the parsed_args struct. 216 parsed_args->goid_offset = args->goid_offset; 217 parsed_args->g_addr_offset = args->g_addr_offset; 218 parsed_args->goroutine_id = args->goroutine_id; 219 parsed_args->fn_addr = args->fn_addr; 220 parsed_args->n_parameters = args->n_parameters; 221 parsed_args->n_ret_parameters = args->n_ret_parameters; 222 memcpy(parsed_args->params, args->params, sizeof(args->params)); 223 memcpy(parsed_args->ret_params, args->ret_params, sizeof(args->ret_params)); 224 225 if (!get_goroutine_id(parsed_args)) { 226 bpf_ringbuf_discard(parsed_args, 0); 227 return 1; 228 } 229 230 if (!args->is_ret) { 231 // In uprobe at function entry. 232 233 // Parse input parameters. 234 parse_params(ctx, args->n_parameters, parsed_args->params); 235 } else { 236 // We are now stopped at the RET instruction for this function. 237 238 // Parse output parameters. 239 parse_params(ctx, args->n_ret_parameters, parsed_args->ret_params); 240 } 241 242 bpf_ringbuf_submit(parsed_args, BPF_RB_FORCE_WAKEUP); 243 244 return 0; 245 } 246 247 char _license[] SEC("license") = "Dual MIT/GPL";