BPF学习笔记2-基于BCC的BPF示例程序

1390阅读 0评论2022-04-01 frankzfz
分类:LINUX

结合前面一篇针对BPF的学习,本篇文章重点介绍编写一个对内核系统调用exec的例子。本测试例子基本上包含了全部的,syscall类别系统调用的BPF的框架。

点击(此处)折叠或打开

  1. #!/usr/bin/python
  2. from __future__ import print_function
  3. from bcc import BPF
  4. from collections import defaultdict

  5. bpf_text = """
  6. #include
  7. #include
  8. #include

  9. #define ARGSIZE 256
  10. struct data_t {
  11.       u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
  12.       char comm[TASK_COMM_LEN];
  13.       char argv[ARGSIZE];
  14.  };

  15. BPF_PERF_OUTPUT(events);

  16. int syscall__execve(struct pt_regs *ctx,
  17.                    const char __user *filename,
  18.                    const char __user *const __user *__argv,
  19.                    const char __user *const __user *__envp)
  20. {
  21.     struct data_t data = {};


  22.     bpf_trace_printk("Hello, World!222%s\\n",filename);
  23.     data.pid = bpf_get_current_pid_tgid();
  24.     bpf_get_current_comm(&data.comm,sizeof(data.comm));

  25.     bpf_probe_read_user(data.argv, sizeof(data.argv), filename);

  26.     events.perf_submit(ctx, &data, sizeof(struct data_t));

  27.     return 0;
  28. }

  29. """
  30. b = BPF(text=bpf_text)
  31. execve_fnname = b.get_syscall_fnname("execve")
  32. b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve")
  33. print("%-18s %-16s %-14s" % ("COMM", "PID","ARGS"))


  34. argv = defaultdict(list)

  35. def print_event(cpu, data, size):
  36.     event = b["events"].event(data)
  37.     argv[event.pid].append(event.argv)

  38.     argv_text = b' '.join(argv[event.pid]).replace(b'\n', b'\\n')

  39.     print("%-18s %-16d %-14s" % (event.comm, event.pid,argv_text))


  40. b["events"].open_perf_buffer(print_event)
  41. while 1:
  42.     try:
  43.         b.perf_buffer_poll()
  44.     except KeyboardInterrupt:
  45.         exit()
 主要注意点:

1. BPF_PERF_OUTPUT   创建BPFtable,通过Perf 的环形缓存区,把用户定义的event事件的数据推送到用户空间,这是把事件数据推送到用户空间的首选的方式。 也就是如果把从内核中获取到的数据,push到用户空间进行处理和展示,在代码中添加该宏。

2. b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve") 这里的fn_name="syscal__execve"函数名,保持格式的绝对一致,也就是syscall__ 后面两个下划线,execve名字和内核中syscall保持一致。
3.  
在内核中插桩syscall__execve 函数时,这里的第一个参数struct pt_regs *ctx 保持固定,其余的参数是syscall类函数保持一致的,下面是调用execve的参数。

 int execve(const char *pathname, char *const argv[], char *const envp[]);

4.  其实整个程序包含了两个部分,一个是python的客户端,一个是以字符串的形式bpf_text出现的插桩函数,使用C语言编写,整个测试程序包含了,用户空间和内核空间的代码。

5  bpf_trace_printk函数,类似于C语言中printf,编写bpf程序时,可以用来调试打印相关信息。注意该打印函数会将信息输出到下面的文件中:/sys/kernel/debug/tracing/trace_pipe,通过跟踪该文件进行查看。

6. print_even 函数时对内核中获取的数据和信息的统一展示。

 
上一篇:BPF学习笔记1
下一篇:BPF学习笔记3-BPF示例程序