• Android中eBPF使用原理以及 kprobe dmabuf_setup实例


    目录

    eBPF in Android

    Android eBPF kprobe dma代码

    定义一个MAP

    定义一个 PROG

    bpfprogs/Android.bp

    测试程序

    bpfprogs/memstats/Android.bp

    bpfprogs/memstats/MemStats.h

    bpfprogs/memstats/MemStats.cpp

    bpfprogs/memstats/MemStatsMain.cpp

    编译运行

    结果分析

    小结


    eBPF in Android

    官网:https://source.android.com/docs/core/architecture/kernel/bpf?hl=zh-cn#tracepoints

    Andoid官方网站有比较详细的介绍,在Android中使用eBPF的官方示例,可以参考以上链接。

    Android eBPF kprobe dma代码

    在 源目录/system/bpfprogs,可以开发我们自己的 eBPF程序。

    在system/bpfprogs 源码目录下,新建一个 memStats.c 示例程序,在次程序中有几个关键点,下面我逐一进行讲解。

    memStats.c

    1. #include
    2. #include
    3. struct pt_regs {
    4. unsigned long long regs[31];
    5. unsigned long long sp;
    6. unsigned long long pc;
    7. unsigned long long pstate;
    8. };
    9. #define DMABUF_MAP_SIZE 4096
    10. struct dmabuf_info {
    11. unsigned long pid;
    12. uint64_t inode;
    13. char comm[16];
    14. uint64_t size;
    15. };
    16. DEFINE_BPF_MAP_GRW(dmabuf_mem_map, HASH, uint64_t, struct dmabuf_info, DMABUF_MAP_SIZE,
    17. AID_SYSTEM);
    18. DEFINE_BPF_PROG("kprobe/dmabuf_setup", AID_ROOT, AID_SYSTEM, kp_dmabuf_setup)
    19. (struct pt_regs* regs) {
    20. const int ALLOW = 1;
    21. struct dmabuf_info cur_val = { 0 };
    22. unsigned long long tempAddr;
    23. size_t size;
    24. unsigned long inode;
    25. pid_t pid = (bpf_get_current_pid_tgid() >> 32) & 0xffffffff;
    26. bpf_probe_read(&size, sizeof(size), (void*)regs->regs[0]); // size = [x0]; dmabuf->size
    27. bpf_probe_read(&tempAddr, sizeof(tempAddr), ((void*)regs->regs[1] + 32)); // tempAddr = [x1 + 32];
    28. bpf_probe_read(&inode, sizeof(inode), ((void*)tempAddr + 64)); // inode = [[x1 + 32] + 64];
    29. cur_val.pid = pid;
    30. cur_val.size = size;
    31. cur_val.inode = inode;
    32. bpf_get_current_comm(cur_val.comm, sizeof(cur_val.comm));
    33. bpf_dmabuf_mem_map_update_elem(&(cur_val.inode), &cur_val, BPF_ANY);
    34. return ALLOW;
    35. }
    36. LICENSE("GPL");

    定义一个MAP

    1. DEFINE_BPF_MAP_GRW(dmabuf_mem_map, HASH, uint64_t, struct dmabuf_info, DMABUF_MAP_SIZE,
    2. AID_SYSTEM);

    其中:

    1. MAP的名字为:dmabuf_mem_map
    2. MAP使用HASH进行存储,常见的还有 ARRAY。
    3. MAP的key的类型为 uint64_t,value的类型为 struct dmabuf_info。后续会使用 这个函数 bpf_dmabuf_mem_map_update_elem(&key, &value, flags) 进行key和value的映射。
    4. MAP的大小 DMABUF_MAP_SIZE 设置为 4096

    5. 权限是 AID_SYSTEM

    定义一个 PROG

    1. DEFINE_BPF_PROG("kprobe/dmabuf_setup", AID_ROOT, AID_SYSTEM, kp_dmabuf_setup)
    2. (struct pt_regs* regs) {
    3. const int ALLOW = 1;
    4. struct dmabuf_info cur_val = { 0 };
    5. unsigned long long tempAddr;
    6. size_t size;
    7. unsigned long inode;
    8. pid_t pid = (bpf_get_current_pid_tgid() >> 32) & 0xffffffff;
    9. bpf_probe_read(&size, sizeof(size), (void*)regs->regs[0]); // size = [x0]; dmabuf->size
    10. bpf_probe_read(&tempAddr, sizeof(tempAddr), ((void*)regs->regs[1] + 32)); // tempAddr = [x1 + 32];
    11. bpf_probe_read(&inode, sizeof(inode), ((void*)tempAddr + 64)); // inode = [[x1 + 32] + 64];
    12. cur_val.pid = pid;
    13. cur_val.size = size;
    14. cur_val.inode = inode;
    15. bpf_get_current_comm(cur_val.comm, sizeof(cur_val.comm));
    16. bpf_dmabuf_mem_map_update_elem(&(cur_val.inode), &cur_val, BPF_ANY);
    17. return ALLOW;
    18. }

    其中:

    1. pid_t pid = (bpf_get_current_pid_tgid() >> 32) & 0xffffffff; 获取进程的 pid号

    2. 三个 bpf_probe_read(...) 获取对应的 dmabuf的 size 和 inode。这个比较那难理解,我这边详细解释一下。首先需要知道 关于aarch64调用传入参数规则:

      怎样获取kprobe对应函数的参数? ARM64中参数1~参数8 分别保存到 X0~X7 寄存器中 即x0存储参数1.....                                                                                                           详细参考这篇文章:第16部分- Linux ARM汇编 ARM64调用标准 - 掘金

    3. 那么寄存器中的参数偏移是怎么样计算得到的呢?主要是通过 gdb 调试得到的。

    1. 怎么获取函数输入变量偏移?
    2. aarch64-linux-android-gdb vmlinux
    3. (gdb) ptype/T struct dma_buf
    4. type = struct dma_buf {
    5. size_t size;
    6. struct file *file;
    7. struct list_head attachments;
    8. const struct dma_buf_ops *ops;
    9. struct mutex lock;
    10. unsigned int vmapping_counter;
    11. struct iosys_map vmap_ptr;
    12. const char *exp_name;
    13. const char *name;
    14. spinlock_t name_lock;
    15. struct module *owner;
    16. struct list_head list_node;
    17. void *priv;
    18. struct dma_resv *resv;
    19. wait_queue_head_t poll;
    20. struct dma_buf_poll_cb_t cb_in;
    21. struct dma_buf_poll_cb_t cb_out;
    22. struct dma_buf_sysfs_entry *sysfs_entry;
    23. u64 android_kabi_reserved1;
    24. u64 android_kabi_reserved2;
    25. }
    26. (gdb) print (int)&((struct dma_buf *)0)->name
    27. $2 = 120
    28. (gdb) print (int)&((struct dma_buf *)0)->exp_name
    29. $3 = 112
    30. (gdb) print (int)&((struct dma_buf *)0)->file
    31. $6 = 8

    bpf_get_current_comm(cur_val.comm, sizeof(cur_val.comm)); 获得进程的名字。

    bpf_dmabuf_mem_map_update_elem(&(cur_val.inode), &cur_val, BPF_ANY); 建立 key 和 value的 MAP映射。

    bpfprogs/Android.bp

    需要在 Android.bp中添加以下代码:

    1. bpf {
    2. name: "memStats.o",
    3. srcs: ["memStats.c"],
    4. btf: true,
    5. cflags: [
    6. "-Wall",
    7. "-Werror",
    8. ],
    9. }

    怎么样写一个测试程序,验证上面的 eBPF程序是没有没有问题的呢?

    测试程序

    主要参考安卓开源代码:  frameworks/native/services/gpuservice/ 中的实现部分。

    bpfprogs/memstats/Android.bp

    1. // Copyright 2020 The Android Open Source Project
    2. //
    3. // Licensed under the Apache License, Version 2.0 (the "License");
    4. // you may not use this file except in compliance with the License.
    5. // You may obtain a copy of the License at
    6. //
    7. // http://www.apache.org/licenses/LICENSE-2.0
    8. //
    9. // Unless required by applicable law or agreed to in writing, software
    10. // distributed under the License is distributed on an "AS IS" BASIS,
    11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12. // See the License for the specific language governing permissions and
    13. // limitations under the License.
    14. package {
    15. default_applicable_licenses: ["system_bpfprogs_license"],
    16. }
    17. cc_binary {
    18. name: "memStats",
    19. srcs: [
    20. "MemStatsMain.cpp",
    21. "MemStats.cpp",
    22. ],
    23. header_libs: ["bpf_headers"],
    24. shared_libs: [
    25. "libbase",
    26. "libbpf_bcc",
    27. "libcutils",
    28. "liblog",
    29. "libutils",
    30. ],
    31. export_header_lib_headers: ["bpf_headers"],
    32. export_shared_lib_headers: ["libbase"],
    33. cppflags: [
    34. "-Wall",
    35. "-Werror",
    36. "-Wformat",
    37. "-Wthread-safety",
    38. "-Wunused",
    39. "-Wunreachable-code",
    40. ],
    41. }

    bpfprogs/memstats/MemStats.h

    1. /*
    2. * Copyright 2020 The Android Open Source Project
    3. *
    4. * Licensed under the Apache License, Version 2.0 (the "License");
    5. * you may not use this file except in compliance with the License.
    6. * You may obtain a copy of the License at
    7. *
    8. * http://www.apache.org/licenses/LICENSE-2.0
    9. *
    10. * Unless required by applicable law or agreed to in writing, software
    11. * distributed under the License is distributed on an "AS IS" BASIS,
    12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13. * See the License for the specific language governing permissions and
    14. * limitations under the License.
    15. */
    16. #pragma once
    17. #include
    18. #include
    19. #include
    20. #include
    21. class MemStats {
    22. public:
    23. MemStats() = default;
    24. ~MemStats();
    25. // initialize eBPF program and map
    26. void initialize();
    27. bool isInitialized() { return mInitialized.load(); }
    28. private:
    29. std::atomic<bool> mInitialized = false;
    30. // tracepoint event category
    31. // tracepoint event category
    32. static constexpr char * kMemStatsKprobeTraceGroup[] = {"kprobes"};
    33. // tracepoint
    34. static constexpr char * kMemStatsDmabufKprobe[] ={"dmabuf_setup"};
    35. // pinned bpf c program path in bpf sysfs
    36. static constexpr char * kMemStatsDmabufProgPath[] =
    37. {"/sys/fs/bpf/prog_memStats_kprobe_dmabuf_setup"};
    38. // 30 seconds timeout for trying to attach bpf program to tracepoint
    39. static constexpr int kWaitTimeout = 30;
    40. };

    bpfprogs/memstats/MemStats.cpp

    1. /*
    2. * Copyright 2020 The Android Open Source Project
    3. *
    4. * Licensed under the Apache License, Version 2.0 (the "License");
    5. * you may not use this file except in compliance with the License.
    6. * You may obtain a copy of the License at
    7. *
    8. * http://www.apache.org/licenses/LICENSE-2.0
    9. *
    10. * Unless required by applicable law or agreed to in writing, software
    11. * distributed under the License is distributed on an "AS IS" BASIS,
    12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13. * See the License for the specific language governing permissions and
    14. * limitations under the License.
    15. */
    16. #undef LOG_TAG
    17. #define LOG_TAG "MemStats"
    18. #define ATRACE_TAG ATRACE_TAG_GRAPHICSS3
    19. #include "MemStats.h"
    20. #include
    21. #include
    22. #include
    23. #include
    24. #include
    25. #include
    26. #include
    27. #include
    28. #include
    29. #include
    30. MemStats::~MemStats() {
    31. for (int i = 0; i < sizeof(kMemStatsDmabufKprobe)/sizeof(char *); i++)
    32. {
    33. bpf_detach_tracepoint(kMemStatsKprobeTraceGroup[0], kMemStatsDmabufKprobe[i]);
    34. }
    35. }
    36. void MemStats::initialize() {
    37. int count = 0;
    38. int fd = 0;
    39. // Make sure bpf programs are loaded
    40. android::bpf::waitForProgsLoaded();
    41. errno = 0;
    42. android::base::WriteStringToFile("p:dmabuf_setup dma_buf_stats_setup","/sys/kernel/debug/tracing/kprobe_events");
    43. for (int i = 0; i < sizeof(kMemStatsDmabufProgPath)/sizeof(char *); i++)
    44. {
    45. fd = android::bpf::retrieveProgram(kMemStatsDmabufProgPath[i]);
    46. if (fd < 0) {
    47. ALOGE("Failed to retrieve pinned program from %s [%d(%s)]", kMemStatsDmabufProgPath[i], errno,
    48. strerror(errno));
    49. }
    50. // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
    51. errno = 0;
    52. count = 0;
    53. while (bpf_attach_tracepoint(fd, kMemStatsKprobeTraceGroup[0], kMemStatsDmabufKprobe[i]) < 0) {
    54. if (++count > kWaitTimeout) {
    55. ALOGE("Failed to attach bpf program to %s/%s tracepoint [%d(%s)]", kMemStatsKprobeTraceGroup[0],
    56. kMemStatsDmabufKprobe[i], errno, strerror(errno));
    57. }
    58. // Retry until loaded or timeout.
    59. sleep(1);
    60. }
    61. }
    62. mInitialized.store(true);
    63. }

    其中:

    android::base::WriteStringToFile("p:dmabuf_setup dma_buf_stats_setup","/sys/kernel/debug/tracing/kprobe_events");

    在 bpf_attach_tracepoint(...)之前,将 "p:dmabuf_setup dma_buf_stats_setup" 写入到 "/sys/kernel/debug/tracing/kprobe_events" 这个事件中去。如果不使用这行代码,也可以在 Linux终端使用,adb shell; echo 'p:dmabuf_setup dma_buf_stats_setup' > /sys/kernel/debug/tracing/kprobe_events

    bpfprogs/memstats/MemStatsMain.cpp

    1. #include "MemStats.h"
    2. #include
    3. int main()
    4. {
    5. class MemStats *memHandle = new MemStats;
    6. memHandle->initialize();
    7. while(1)
    8. {
    9. sleep(10);
    10. }
    11. return 0;
    12. }

    编译运行

    1. make memStats.o
    2. adb push system/etc/bpf/memStats.o /system/etc/bpf/

    3. adb reboot; #重启之后就可以在 /sys/fs/bpf/ 目录下,看到生成了 这个文件 map_memStats_dmabuf_mem_map

    4. make memStats

    5. adb push memStats /data/local/tmp/

    6. adb shell ; /data/local/tmp/memStats

    7. 在另外一个Linux终端:cat /sys/fs/bpf/map_memStats_dmabuf_mem_map

    可以看到输出以下信息:

    1. / # cat /sys/fs/bpf/map_memStats_dmabuf_mem_map
    2. # WARNING!! The output is for debug purpose only
    3. # WARNING!! The output format will change
    4. 697: {1512,697,['b','i','n','d','e','r',':','1','5','1','2','_','3',],188416,}
    5. 1344: {1512,1344,['P','r','e','v','i','e','w','_','4',],22020096,}
    6. 752: {1512,752,['P','r','e','v','i','e','w','_','0',],4096,}
    7. 589: {1525,589,['b','i','n','d','e','r',':','1','5','2','5','_','1',],10522624,}
    8. 1175: {1512,1175,['P','r','e','v','i','e','w','_','2',],462848,}
    9. 1498: {1512,1498,['P','r','e','v','i','e','w','_','4',],4096,}
    10. 1539: {1525,1539,['b','i','n','d','e','r',':','1','5','2','5','_','1',],73728,}
    11. 712: {1512,712,['P','r','e','v','i','e','w','_','2',],4096,}
    12. 1469: {1525,1469,['b','i','n','d','e','r',':','1','5','2','5','_','1',],2506752,}
    13. 1520: {2553,1520,['.','v','o','r','b','i','s','.','d','e','c','o','d','e','r',],32768,}
    14. 745: {1512,745,['P','r','e','v','i','e','w','_','1',],4096,}
    15. 1137: {1512,1137,['P','r','e','v','i','e','w','_','7',],4096,}
    16. 1186: {1512,1186,['b','i','n','d','e','r',':','1','5','1','2','_','3',],6451200,}
    17. 724: {1512,724,['P','r','e','v','i','e','w','_','5',],4096,}
    18. 791: {1512,791,['P','r','e','v','i','e','w','_','7',],4096,}
    19. 1159: {1512,1159,['P','r','e','v','i','e','w','_','4',],462848,}
    20. 810: {1512,810,['P','r','e','v','i','e','w','_','7',],4096,}
    21. 1285: {1512,1285,['P','r','e','v','i','e','w','_','0',],4096,}
    22. 761: {1512,761,['P','r','e','v','i','e','w','_','8',],4096,}
    23. 735: {1512,735,['P','r','e','v','i','e','w','_','1',],4096,}
    24. 630: {1512,630,['b','i','n','d','e','r',':','1','5','1','2','_','3',],4096,}
    25. 1012: {1512,1012,['b','i','n','d','e','r',':','1','5','1','2','_','3',],45056,}

    结果分析

    解释以下输出结果,以这个为例子:697: {1512,697,['b','i','n','d','e','r',':','1','5','1','2','_','3',],188416,}

    其中 697 是 inode的值,{1512,697,['b','i','n','d','e','r',':','1','5','1','2','_','3',],188416,} 是struct dmabuf_info 结构体的输出。具体输出每一项为对应结构体中的值,如下所示:

    1. struct dmabuf_info {
    2. unsigned long pid; //1512
    3. uint64_t inode; //697
    4. char comm[16]; //['b','i','n','d','e','r',':','1','5','1','2','_','3',]
    5. uint64_t size; //188416
    6. };

    小结

    以上是一个具体的 eBPF实例程序。通过 kprobe 监控内核中的 dma_buf_stats_setup 接口。

    kernel_platform/common/drivers/dma-buf/dma-buf-sysfs-stats.c

    1. int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file)
    2. 172 {
    3. 173 struct dma_buf_sysfs_entry *sysfs_entry;
    4. 174 int ret;
    5. 175
    6. 176 if (!dmabuf->exp_name) {
    7. 177 pr_err("exporter name must not be empty if stats needed\n");
    8. 178 return -EINVAL;
    9. 179 }
    10. 180
    11. 181 sysfs_entry = kzalloc(sizeof(struct dma_buf_sysfs_entry), GFP_KERNEL);
    12. 182 if (!sysfs_entry)
    13. 183 return -ENOMEM;
    14. 184
    15. 185 sysfs_entry->kobj.kset = dma_buf_per_buffer_stats_kset;
    16. 186 sysfs_entry->dmabuf = dmabuf;
    17. 187
    18. 188 dmabuf->sysfs_entry = sysfs_entry;
    19. 189
    20. 190 /* create the directory for buffer stats */
    21. 191 ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_ktype, NULL,
    22. 192 "%lu", file_inode(file)->i_ino);
    23. 193 if (ret)
    24. 194 goto err_sysfs_dmabuf;
    25. 195
    26. 196 return 0;
    27. 197
    28. 198 err_sysfs_dmabuf:
    29. 199 kobject_put(&sysfs_entry->kobj);
    30. 200 dmabuf->sysfs_entry = NULL;
    31. 201 return ret;
    32. 202 }

    后续将在 Android上输出更多的 eBPF demo程序。

  • 相关阅读:
    武汉智能网联道路智能化建设规范
    SpringCloud 简介
    nodejs+vue+elementui图书馆销售网上书城管理系统express228
    【JavaSe笔记】——类和对象、this引用、构造方法、封装、static、代码块
    HPC 工作负载管理 —— IBM Spectrum LSF Suite
    项目管理之低效合作
    年薪30万,达到人生巅峰,入职字节一个月,我却被无情碾压
    什么是langchain
    原生CSS嵌套简介
    ELK【elasticsearch+logstash+kibana】企业级日志分析系统
  • 原文地址:https://blog.csdn.net/weixin_42136255/article/details/134402162