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(&param->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(&param->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, &params[5]);
   186          case 5:
   187              parse_param(ctx, &params[4]);
   188          case 4:
   189              parse_param(ctx, &params[3]);
   190          case 3:
   191              parse_param(ctx, &params[2]);
   192          case 2:
   193              parse_param(ctx, &params[1]);
   194          case 1:
   195              parse_param(ctx, &params[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";