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

#include <bpf_helpers.h>
#include <sys/types.h>struct pt_regs {unsigned long long regs[31];unsigned long long sp;unsigned long long pc;unsigned long long pstate;
};#define DMABUF_MAP_SIZE 4096struct dmabuf_info {unsigned long pid;uint64_t inode;char comm[16];uint64_t size;
};DEFINE_BPF_MAP_GRW(dmabuf_mem_map, HASH, uint64_t, struct dmabuf_info, DMABUF_MAP_SIZE,AID_SYSTEM);DEFINE_BPF_PROG("kprobe/dmabuf_setup", AID_ROOT, AID_SYSTEM, kp_dmabuf_setup)
(struct pt_regs* regs) {const int ALLOW = 1;struct dmabuf_info cur_val = { 0 };unsigned long long tempAddr;size_t size;unsigned long inode;pid_t pid = (bpf_get_current_pid_tgid() >> 32) & 0xffffffff;bpf_probe_read(&size, sizeof(size), (void*)regs->regs[0]); // size = [x0]; dmabuf->sizebpf_probe_read(&tempAddr, sizeof(tempAddr), ((void*)regs->regs[1] + 32)); // tempAddr = [x1 + 32];bpf_probe_read(&inode, sizeof(inode), ((void*)tempAddr + 64)); // inode = [[x1 + 32] + 64];cur_val.pid = pid;cur_val.size = size;cur_val.inode = inode;bpf_get_current_comm(cur_val.comm, sizeof(cur_val.comm));bpf_dmabuf_mem_map_update_elem(&(cur_val.inode), &cur_val, BPF_ANY);return ALLOW;
}LICENSE("GPL");

定义一个MAP

DEFINE_BPF_MAP_GRW(dmabuf_mem_map, HASH, uint64_t, struct dmabuf_info, DMABUF_MAP_SIZE,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

DEFINE_BPF_PROG("kprobe/dmabuf_setup", AID_ROOT, AID_SYSTEM, kp_dmabuf_setup)
(struct pt_regs* regs) {const int ALLOW = 1;struct dmabuf_info cur_val = { 0 };unsigned long long tempAddr;size_t size;unsigned long inode;pid_t pid = (bpf_get_current_pid_tgid() >> 32) & 0xffffffff;bpf_probe_read(&size, sizeof(size), (void*)regs->regs[0]); // size = [x0]; dmabuf->sizebpf_probe_read(&tempAddr, sizeof(tempAddr), ((void*)regs->regs[1] + 32)); // tempAddr = [x1 + 32];bpf_probe_read(&inode, sizeof(inode), ((void*)tempAddr + 64)); // inode = [[x1 + 32] + 64];cur_val.pid = pid;cur_val.size = size;cur_val.inode = inode;bpf_get_current_comm(cur_val.comm, sizeof(cur_val.comm));bpf_dmabuf_mem_map_update_elem(&(cur_val.inode), &cur_val, BPF_ANY);return ALLOW;
}

其中:

  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 调试得到的。

怎么获取函数输入变量偏移?aarch64-linux-android-gdb vmlinux
(gdb) ptype/T struct dma_buf
type = struct dma_buf {size_t size;struct file *file;struct list_head attachments;const struct dma_buf_ops *ops;struct mutex lock;unsigned int vmapping_counter;struct iosys_map vmap_ptr;const char *exp_name;const char *name;spinlock_t name_lock;struct module *owner;struct list_head list_node;void *priv;struct dma_resv *resv;wait_queue_head_t poll;struct dma_buf_poll_cb_t cb_in;struct dma_buf_poll_cb_t cb_out;struct dma_buf_sysfs_entry *sysfs_entry;u64 android_kabi_reserved1;u64 android_kabi_reserved2;
}
(gdb)  print (int)&((struct dma_buf *)0)->name
$2 = 120
(gdb)  print (int)&((struct dma_buf *)0)->exp_name
$3 = 112
(gdb)  print (int)&((struct dma_buf *)0)->file
$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中添加以下代码:

bpf {name: "memStats.o",srcs: ["memStats.c"],btf: true,cflags: ["-Wall","-Werror",],
}

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

测试程序

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

bpfprogs/memstats/Android.bp


// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package {default_applicable_licenses: ["system_bpfprogs_license"],
}
cc_binary {name: "memStats",srcs: ["MemStatsMain.cpp","MemStats.cpp",],header_libs: ["bpf_headers"],shared_libs: ["libbase","libbpf_bcc","libcutils","liblog","libutils",],export_header_lib_headers: ["bpf_headers"],export_shared_lib_headers: ["libbase"],cppflags: ["-Wall","-Werror","-Wformat","-Wthread-safety","-Wunused","-Wunreachable-code",],
}

bpfprogs/memstats/MemStats.h


/** Copyright 2020 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
#pragma once
#include <bpf/BpfMap.h>
#include <utils/String16.h>
#include <utils/Vector.h>
#include <functional>class MemStats {
public:MemStats() = default;~MemStats();// initialize eBPF program and mapvoid initialize();bool isInitialized() { return mInitialized.load(); }
private:std::atomic<bool> mInitialized = false;// tracepoint event category// tracepoint event categorystatic constexpr char * kMemStatsKprobeTraceGroup[] = {"kprobes"};// tracepointstatic constexpr char * kMemStatsDmabufKprobe[] ={"dmabuf_setup"};// pinned bpf c program path in bpf sysfsstatic constexpr char * kMemStatsDmabufProgPath[] ={"/sys/fs/bpf/prog_memStats_kprobe_dmabuf_setup"};// 30 seconds timeout for trying to attach bpf program to tracepointstatic constexpr int kWaitTimeout = 30;
};

bpfprogs/memstats/MemStats.cpp


/** Copyright 2020 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
#undef LOG_TAG
#define LOG_TAG "MemStats"
#define ATRACE_TAG ATRACE_TAG_GRAPHICSS3
#include "MemStats.h"
#include <android-base/stringprintf.h>
#include <libbpf.h>
#include <bpf/WaitForProgsLoaded.h>
#include <log/log.h>
#include <unistd.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
#include <unordered_map>
#include <vector>#include <android-base/file.h>MemStats::~MemStats() {for (int i = 0; i < sizeof(kMemStatsDmabufKprobe)/sizeof(char *); i++){bpf_detach_tracepoint(kMemStatsKprobeTraceGroup[0], kMemStatsDmabufKprobe[i]);}
}void MemStats::initialize() {int count = 0;int fd = 0;// Make sure bpf programs are loadedandroid::bpf::waitForProgsLoaded();errno = 0;android::base::WriteStringToFile("p:dmabuf_setup dma_buf_stats_setup","/sys/kernel/debug/tracing/kprobe_events");for (int i = 0; i < sizeof(kMemStatsDmabufProgPath)/sizeof(char *); i++){fd = android::bpf::retrieveProgram(kMemStatsDmabufProgPath[i]);if (fd < 0) {ALOGE("Failed to retrieve pinned program from %s [%d(%s)]", kMemStatsDmabufProgPath[i], errno,strerror(errno));}// Attach the program to the tracepoint, and the tracepoint is automatically enabled here.errno = 0;count = 0;while (bpf_attach_tracepoint(fd, kMemStatsKprobeTraceGroup[0], kMemStatsDmabufKprobe[i]) < 0) {if (++count > kWaitTimeout) {ALOGE("Failed to attach bpf program to %s/%s tracepoint [%d(%s)]", kMemStatsKprobeTraceGroup[0],kMemStatsDmabufKprobe[i], errno, strerror(errno));}// Retry until loaded or timeout.sleep(1);}}mInitialized.store(true);
}

其中:

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

#include "MemStats.h"
#include <unistd.h>
int main()
{class MemStats *memHandle = new MemStats;memHandle->initialize();while(1){sleep(10);}return 0;
}

编译运行

  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

可以看到输出以下信息:

/ # cat  /sys/fs/bpf/map_memStats_dmabuf_mem_map
# WARNING!! The output is for debug purpose only
# WARNING!! The output format will change
697: {1512,697,['b','i','n','d','e','r',':','1','5','1','2','_','3',],188416,}
1344: {1512,1344,['P','r','e','v','i','e','w','_','4',],22020096,}
752: {1512,752,['P','r','e','v','i','e','w','_','0',],4096,}
589: {1525,589,['b','i','n','d','e','r',':','1','5','2','5','_','1',],10522624,}
1175: {1512,1175,['P','r','e','v','i','e','w','_','2',],462848,}
1498: {1512,1498,['P','r','e','v','i','e','w','_','4',],4096,}
1539: {1525,1539,['b','i','n','d','e','r',':','1','5','2','5','_','1',],73728,}
712: {1512,712,['P','r','e','v','i','e','w','_','2',],4096,}
1469: {1525,1469,['b','i','n','d','e','r',':','1','5','2','5','_','1',],2506752,}
1520: {2553,1520,['.','v','o','r','b','i','s','.','d','e','c','o','d','e','r',],32768,}
745: {1512,745,['P','r','e','v','i','e','w','_','1',],4096,}
1137: {1512,1137,['P','r','e','v','i','e','w','_','7',],4096,}
1186: {1512,1186,['b','i','n','d','e','r',':','1','5','1','2','_','3',],6451200,}
724: {1512,724,['P','r','e','v','i','e','w','_','5',],4096,}
791: {1512,791,['P','r','e','v','i','e','w','_','7',],4096,}
1159: {1512,1159,['P','r','e','v','i','e','w','_','4',],462848,}
810: {1512,810,['P','r','e','v','i','e','w','_','7',],4096,}
1285: {1512,1285,['P','r','e','v','i','e','w','_','0',],4096,}
761: {1512,761,['P','r','e','v','i','e','w','_','8',],4096,}
735: {1512,735,['P','r','e','v','i','e','w','_','1',],4096,}
630: {1512,630,['b','i','n','d','e','r',':','1','5','1','2','_','3',],4096,}
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 结构体的输出。具体输出每一项为对应结构体中的值,如下所示:

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

小结

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

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

int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file)
172  {
173  	struct dma_buf_sysfs_entry *sysfs_entry;
174  	int ret;
175  
176  	if (!dmabuf->exp_name) {
177  		pr_err("exporter name must not be empty if stats needed\n");
178  		return -EINVAL;
179  	}
180  
181  	sysfs_entry = kzalloc(sizeof(struct dma_buf_sysfs_entry), GFP_KERNEL);
182  	if (!sysfs_entry)
183  		return -ENOMEM;
184  
185  	sysfs_entry->kobj.kset = dma_buf_per_buffer_stats_kset;
186  	sysfs_entry->dmabuf = dmabuf;
187  
188  	dmabuf->sysfs_entry = sysfs_entry;
189  
190  	/* create the directory for buffer stats */
191  	ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_ktype, NULL,
192  				   "%lu", file_inode(file)->i_ino);
193  	if (ret)
194  		goto err_sysfs_dmabuf;
195  
196  	return 0;
197  
198  err_sysfs_dmabuf:
199  	kobject_put(&sysfs_entry->kobj);
200  	dmabuf->sysfs_entry = NULL;
201  	return ret;
202  }

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/145252.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android 13.0 删除连接wifi的配置信息

1.前言 在13.0的系统rom产品定制化开发中,对于wifi的功能定制需求功能也是挺多的,目前对于wifi模块有这么个需求,要求在 提供接口实现删除已连接wifi的需求,所以需要了解wifi相关的配置情况,然后移除wifi即可,接下来就来实现相关的功能 2.删除连接wifi的配置信息的核心类…

C 语言 sizeof运算符

C 语言 sizeof运算符 sizeof运算符在C语言中使用时&#xff0c;它决定表达式的大小或在char大小的存储单元数量中指定的数据类型。sizeof运算符包含一个操作数&#xff0c;该操作数可以是表达式&#xff0c;也可以是数据类型转换&#xff0c;其中转换是用括号括起来的数据类型…

【fbtft】如何添加fbtft驱动

获取lcd ic的datasheet&#xff0c;或者直接找到其他平台&#xff08;linux&#xff0c;stm32&#xff0c;esp32&#xff09;的驱动 我用的是合宙的esp32驱动&#xff0c;注意是c语言的&#xff0c;合宙上层用lua封装了&#xff0c;需要找到sdk源码。 源码路径&#xff1a; …

File类和IO流

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

设置chunk自动扩展到多大

1. 设置chunk自动扩展 execute function task(modify chunk extendable on,8); 2. 设置dbs扩展到多大合适 execute function task(modify space sp sizes,testdb1024,1024,10240) testdb 初始1MB 下次扩1MB 最大10MB

appium+python自动化测试

获取APP的包名 1、aapt即Android Asset Packaging Tool&#xff0c;在SDK的build-tools目录下。该工具可以查看apk包名和launcherActivity 2、在android-sdk里面双击SDK-manager,下载buidl-tools 3、勾选build-tools&#xff0c;随便选一个版本&#xff0c;我这里选的是24的版…

宏集干货 | 手把手教你通过CODESYS V3进行PLC编程(三)

来源&#xff1a;宏集科技 工业物联网 宏集干货 | 手把手教你通过CODESYS V3进行PLC编程&#xff08;三&#xff09; 教程背景 通过之前的教程&#xff0c;我们已经为大家演示了宏集MC-Prime控制器的连接、试运行和CODESYS的安装&#xff0c;并创建了一个计数器项目。在本期教…

数据分析 - 分散性与变异的量度

全距 - 极差 处理变异性 方差度量 数值与均值的距离&#xff0c;也就是数据的差异性 标准差描述&#xff1a;典型值 和 均值的距离的方法&#xff0c;数据与均值的分散情况

出行类app如何提升广告变现收益?

出行类APP已经成为越来越多人们出行的首选&#xff0c;出行类app在变现方式上存在以下痛点&#xff1a;APP功能单一、使用场景单一&#xff1b;用户使用时间集中&#xff0c;粘性低...这些痛点使得开发者获取收益的提升面临极大的挑战。 https://www.shenshiads.com 如何让出…

事实 读书笔记

事实 这本书目标是想突破人类的一些本能的直觉惯性&#xff0c;让我们能更加客观的认识这个世界&#xff0c;让我们不显得那么无知。 主体内容以一些直觉陷阱一步一步的展开 二元论 作者认为我们很容易通过简单的对立关系来理解世界&#xff0c;如把这个世界理解为发展中国家…

【ARL灯塔搭建详细教程】

文章目录 前言一、前期准备二、安装docker及docker-compose三、安装ARL灯塔四、登录ARL灯塔 前言 ARL&#xff08;Asset Reconnaissance Lighthouse&#xff09;资产侦查灯塔旨在快速发现并整理企业外网资产并为资产构建基础数据库&#xff0c;无需登录凭证或特殊访问即可主动…

Python ... takes 0 positional arguments but 1 was given

最近&#xff0c;博主在学习python时遇到这么个报错&#xff0c; 系统&#xff1a;windows10 开发环境&#xff1a;VS Code Python版本&#xff1a;3.12 错误重现&#xff1a; class Dog:def __init__(self):passdef eatSomething(self):self.eatBone()def eatBone():prin…

Ubuntu18.04安装Loam保姆级教程

系统环境&#xff1a;Ubuntu18.04.6 LTS 1.Loam的安装前要求&#xff1a; 1.1 ROS安装&#xff1a;参考我的另一篇博客 Ubuntu18.04安装ROS-melodic保姆级教程_灬杨三岁灬的博客-CSDN博客还是那句话&#xff0c;有时候加了这行也不好使&#xff0c;我是疯狂试了20次&#xf…

quartz笔记

Quartz-CSDN博客 上面是Quartz的一些基本知识,如果对quartz的基本API不是很了解的话,建议先看下上面的 和Linux Crontab对比 1.执行粒度: Linux Crontab是进程级 quart是线程级 2.跨平台性: Crontab只能在Linxu运行 quart是java实现,可以跨平台 3.调度集上 Crontab的…

VUE指令、computed计算属性和watch 侦听器(附带详细案例)

文章目录 前言一、指令补充1. 指令修饰符2. v-bind对于样式操作的增强 - class3. 案例 - 京东秒杀 tab 导航高亮4. v-bind对于样式操作的增强 - style5. v-model应用于其他表单元素 二、computed计算属性1. 基础语法2. 计算属性 vS method 方法3. 完整写法4. 成绩案例 三、watc…

Java读取本地文件

import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException;public class Main {public static void main(String[] args) {String filePath "C:/Users/admin/Desktop/知识点记录.md";// 创建一个文件对象File f…

SpringSecurity+jwt使用

参考文章链接 自定义SpringSecurity用户 package com.daben.springsecurityjwt.vo;import com.daben.springsecurityjwt.entity.SysUser; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import j…

关于苏州立讯公司国产替代案例(使用我公司H82409S网络变压器和E1152E01A-YG网口连接器产品)

关于苏州立讯公司国产替代案例&#xff08;使用我们公司的H82409S网络变压器和E1152E01A-YG网口连接器产品&#xff09; 苏州立讯公司是一家专注于通信设备制造的企业&#xff0c;他们在其产品中选择了我们公司的H82409S网络变压器和E1152E01A-YG网口连接器&#xff0c;以实现…

你是想被ChatGPT改变,还是改变软件开发的未来?丨IDCF

人工智能技术的发展&#xff0c;正在深刻地改变着我们的生活和工作方式。在软件工程领域&#xff0c;ChatGPT作为一种新兴的人工智能技术&#xff0c;正在逐渐地被应用到软件开发的各个环节中。那么&#xff0c;ChatGPT对每个人的影响是什么呢&#xff1f; 一、对软件开发人员…

数据结构 链表

单链表&#xff1a;单链表用来写邻接表&#xff0c;邻接表用来存储图和树 双链表&#xff1a;用来优化某些问题 单链表 链式存储 #include<stdio.h> #include<stdlib.h> int cont 0; //结构体 typedef struct List { int data; //数据域 struct List* next; //…