1*fae6e9adSlinfeng# eBPF 2*fae6e9adSlinfeng 3*fae6e9adSlinfeng> 作者: 陈林峰 4*fae6e9adSlinfeng> 5*fae6e9adSlinfeng> Email: chenlinfeng25@outlook.com 6*fae6e9adSlinfeng 7*fae6e9adSlinfeng## 概述 8*fae6e9adSlinfeng 9*fae6e9adSlinfengeBPF 是一项革命性的技术,起源于 Linux 内核,它可以在特权上下文中(如操作系统内核)运行沙盒程序。它用于安全有效地扩展内核的功能,而无需通过更改内核源代码或加载内核模块的方式来实现。 10*fae6e9adSlinfeng 11*fae6e9adSlinfeng从历史上看,由于内核具有监督和控制整个系统的特权,操作系统一直是实现可观测性、安全性和网络功能的理想场所。同时,由于操作系统内核的核心地位和对稳定性和安全性的高要求,操作系统内核很难快速迭代发展。因此在传统意义上,与在操作系统本身之外实现的功能相比,操作系统级别的创新速度要慢一些。 12*fae6e9adSlinfeng 13*fae6e9adSlinfengeBPF 从根本上改变了这个方式。通过允许在操作系统中运行沙盒程序的方式,应用程序开发人员可以运行 eBPF 程序,以便在运行时向操作系统添加额外的功能。然后在 JIT 编译器和验证引擎的帮助下,操作系统确保它像本地编译的程序一样具备安全性和执行效率。这引发了一股基于 eBPF 的项目热潮,它们涵盖了广泛的用例,包括下一代网络实现、可观测性和安全功能等领域。 14*fae6e9adSlinfeng 15*fae6e9adSlinfeng## eBPF In DragonOS 16*fae6e9adSlinfeng 17*fae6e9adSlinfeng在一个新的OS上添加eBPF的支持需要了解eBPF的运行过程,通常,eBPF需要用户态工具和内核相关基础设施配合才能发挥其功能。而新的OS通常会兼容Linux上的应用程序,这可以进一步简化对用户态工具的移植工作,只要内核实现相关的系统调用和功能,就可以配合已有的工具完成eBPF的支持。 18*fae6e9adSlinfeng 19*fae6e9adSlinfeng## eBPF的运行流程 20*fae6e9adSlinfeng 21*fae6e9adSlinfeng![image-20240909165945192](./ebpf_flow.png) 22*fae6e9adSlinfeng 23*fae6e9adSlinfeng如图所示,eBPF程序的运行过程分为三个主要步骤: 24*fae6e9adSlinfeng 25*fae6e9adSlinfeng1. 源代码->二进制 26*fae6e9adSlinfeng 1. 用户可以使用python/C/Rust编写eBPF程序,并使用相关的工具链编译源代码到二进制程序 27*fae6e9adSlinfeng 2. 这个步骤中,用户需要合理使用helper函数丰富eBPF程序功能 28*fae6e9adSlinfeng2. 加载eBPF程序 29*fae6e9adSlinfeng 1. 用户态的工具库会封装内核提供的系统调用接口,以简化用户的工作。用户态工具对eBPF程序经过预处理后发出系统调用,请求内核加载eBPF程序。 30*fae6e9adSlinfeng 1. 内核首先会对eBPF程序进行验证,检查程序的正确性和合法性,同时也会对程序做进一步的处理 31*fae6e9adSlinfeng 1. 内核会根据用户请求,将eBPF程序附加到内核的挂载点上(kprobe/uprobe/trace_point) 32*fae6e9adSlinfeng 1. 在内核运行期间,当这些挂载点被特定的事件触发, eBPF程序就会被执行 33*fae6e9adSlinfeng3. 数据交互 34*fae6e9adSlinfeng 1. eBPF程序可以收集内核的信息,用户工具可以选择性的获取这些信息 35*fae6e9adSlinfeng 2. eBPF程序可以直接将信息输出到文件中,用户工具通过读取和解析文件中的内容拿到信息 36*fae6e9adSlinfeng 3. eBPF程序通过Map在内核和用户态之间共享和交换数据 37*fae6e9adSlinfeng 38*fae6e9adSlinfeng 39*fae6e9adSlinfeng 40*fae6e9adSlinfeng## 用户态支持 41*fae6e9adSlinfeng 42*fae6e9adSlinfeng用户态的eBPF工具库有很多,比如C的libbpf,python的bcc, Rust的Aya,总体来说,这些工具的处理流程都大致相同。DragonOS当前支持[Aya](https://github.com/aya-rs/aya)框架编写的eBPF程序,以Aya为例,用户态的工具的处理过程如下: 43*fae6e9adSlinfeng 44*fae6e9adSlinfeng1. 提供eBPF使用的helper函数和Map抽象,方便实现eBPF程序 45*fae6e9adSlinfeng2. 处理编译出来的eBPF程序,调用系统调用创建Map,获得对应的文件描述符 46*fae6e9adSlinfeng3. 根据需要,更新Map的值(.data) 47*fae6e9adSlinfeng4. 根据重定位信息,对eBPF程序的相关指令做修改 48*fae6e9adSlinfeng5. 根据内核版本,对eBPF程序中的bpf to bpf call进行处理 49*fae6e9adSlinfeng6. 加载eBPF程序到内核中 50*fae6e9adSlinfeng7. 对系统调用封装,提供大量的函数帮助访问eBPF的信息并与内核交互 51*fae6e9adSlinfeng 52*fae6e9adSlinfengDragonOS对Aya 库的支持并不完整。通过对Aya库的删减,我们实现了一个较小的[tiny-aya](https://github.com/DragonOS-Community/tiny-aya)。为了确保后期对Aya的兼容,tiny-aya只对Aya中的核心工具aya做了修改**,其中一些函数被禁用,因为这些函数的所需的系统调用或者文件在DragonOS中还未实现**。 53*fae6e9adSlinfeng 54*fae6e9adSlinfeng### Tokio 55*fae6e9adSlinfeng 56*fae6e9adSlinfengAya需要使用异步运行时,通过增加一些系统调用和修复一些错误DragonOS现在已经支持基本的tokio运行时。 57*fae6e9adSlinfeng 58*fae6e9adSlinfeng### 使用Aya创建eBPF程序 59*fae6e9adSlinfeng 60*fae6e9adSlinfeng与Aya官方提供的[文档](https://aya-rs.dev/book/start/development/)所述,只需要根据其流程安装对应的Rust工具链,就可以按照模板创建eBPF项目。以当前实现的`syscall_ebf`为例,这个程序的功能是统计系统调用的次数,并将其存储在一个HashMap中。 61*fae6e9adSlinfeng 62*fae6e9adSlinfeng``` 63*fae6e9adSlinfeng├── Cargo.toml 64*fae6e9adSlinfeng├── README.md 65*fae6e9adSlinfeng├── syscall_ebpf 66*fae6e9adSlinfeng├── syscall_ebpf-common 67*fae6e9adSlinfeng├── syscall_ebpf-ebpf 68*fae6e9adSlinfeng└── xtask 69*fae6e9adSlinfeng``` 70*fae6e9adSlinfeng 71*fae6e9adSlinfeng在user/app目录中,项目结构如上所示: 72*fae6e9adSlinfeng 73*fae6e9adSlinfeng- `syscall_ebpf-ebpf`是 eBPF代码的实现目录,其会被编译到字节码 74*fae6e9adSlinfeng- `syscall_ebpf-common` 是公共库,方便内核和用户态进行信息交互 75*fae6e9adSlinfeng- `syscall_ebpf` 是用户态程序,其负责加载eBPF程序并获取eBPF程序产生的数据 76*fae6e9adSlinfeng- `xtask` 是一个命令行工具,方便用户编译和运行用户态程序 77*fae6e9adSlinfeng 78*fae6e9adSlinfeng为了在DragonOS中运行用户态程序,暂时还不能直接使用模板创建的项目: 79*fae6e9adSlinfeng 80*fae6e9adSlinfeng1. 这个项目不符合DragonOS对用户程序的项目结构要求,当然这可以通过稍加修改完成 81*fae6e9adSlinfeng2. 因为DragonOS对tokio运行时的支持还不是完整体,需要稍微修改一下使用方式 82*fae6e9adSlinfeng 83*fae6e9adSlinfeng``` 84*fae6e9adSlinfeng#[tokio::main(flavor = "current_thread")] 85*fae6e9adSlinfengasync fn main() -> Result<(), Box<dyn Error>> { 86*fae6e9adSlinfeng``` 87*fae6e9adSlinfeng 88*fae6e9adSlinfeng3. 因为对Aya支持不是完整体,因此项目依赖的aya和aya-log需要换成tiny-aya中的实现。 89*fae6e9adSlinfeng 90*fae6e9adSlinfeng``` 91*fae6e9adSlinfeng[dependencies] 92*fae6e9adSlinfengaya = { git = "https://github.com/DragonOS-Community/tiny-aya.git" } 93*fae6e9adSlinfengaya-log = { git = "https://github.com/DragonOS-Community/tiny-aya.git" } 94*fae6e9adSlinfeng``` 95*fae6e9adSlinfeng 96*fae6e9adSlinfeng只需要稍加修改,就可以利用Aya现有的工具完成eBPF程序的实现。 97*fae6e9adSlinfeng 98*fae6e9adSlinfeng## 内核态支持 99*fae6e9adSlinfeng 100*fae6e9adSlinfeng内核态支持主要为三个部分: 101*fae6e9adSlinfeng 102*fae6e9adSlinfeng1. kprobe实现:位于目录`kernel/crates/kprobe` 103*fae6e9adSlinfeng2. rbpf运行时:位于目录`kernel/crates/rbpf` 104*fae6e9adSlinfeng3. 系统调用支持 105*fae6e9adSlinfeng4. helper函数支持 106*fae6e9adSlinfeng 107*fae6e9adSlinfeng### rbpf 108*fae6e9adSlinfeng 109*fae6e9adSlinfeng由于rbpf之前只是用于运行一些简单的eBPF程序,其需要通过一些修改才能运行更复杂的程序。 110*fae6e9adSlinfeng 111*fae6e9adSlinfeng1. 增加bpf to bpf call 的支持:通过增加新的栈抽象和保存和恢复必要的寄存器数据 112*fae6e9adSlinfeng2. 关闭内部不必要的内存检查,这通常由内核的验证器完成 113*fae6e9adSlinfeng3. 增加带所有权的数据结构避免生命周期的限制 114*fae6e9adSlinfeng 115*fae6e9adSlinfeng 116*fae6e9adSlinfeng 117*fae6e9adSlinfeng### 系统调用 118*fae6e9adSlinfeng 119*fae6e9adSlinfengeBPF相关的系统调用都集中在`bpf()` 上,通过参数cmd来进一步区分功能,目前对其支持如下: 120*fae6e9adSlinfeng 121*fae6e9adSlinfeng```rust 122*fae6e9adSlinfengpub fn bpf(cmd: bpf_cmd, attr: &bpf_attr) -> Result<usize> { 123*fae6e9adSlinfeng let res = match cmd { 124*fae6e9adSlinfeng // Map related commands 125*fae6e9adSlinfeng bpf_cmd::BPF_MAP_CREATE => map::bpf_map_create(attr), 126*fae6e9adSlinfeng bpf_cmd::BPF_MAP_UPDATE_ELEM => map::bpf_map_update_elem(attr), 127*fae6e9adSlinfeng bpf_cmd::BPF_MAP_LOOKUP_ELEM => map::bpf_lookup_elem(attr), 128*fae6e9adSlinfeng bpf_cmd::BPF_MAP_GET_NEXT_KEY => map::bpf_map_get_next_key(attr), 129*fae6e9adSlinfeng bpf_cmd::BPF_MAP_DELETE_ELEM => map::bpf_map_delete_elem(attr), 130*fae6e9adSlinfeng bpf_cmd::BPF_MAP_LOOKUP_AND_DELETE_ELEM => map::bpf_map_lookup_and_delete_elem(attr), 131*fae6e9adSlinfeng bpf_cmd::BPF_MAP_LOOKUP_BATCH => map::bpf_map_lookup_batch(attr), 132*fae6e9adSlinfeng bpf_cmd::BPF_MAP_FREEZE => map::bpf_map_freeze(attr), 133*fae6e9adSlinfeng // Program related commands 134*fae6e9adSlinfeng bpf_cmd::BPF_PROG_LOAD => prog::bpf_prog_load(attr), 135*fae6e9adSlinfeng // Object creation commands 136*fae6e9adSlinfeng bpf_cmd::BPF_BTF_LOAD => { 137*fae6e9adSlinfeng error!("bpf cmd {:?} not implemented", cmd); 138*fae6e9adSlinfeng return Err(SystemError::ENOSYS); 139*fae6e9adSlinfeng } 140*fae6e9adSlinfeng ty => { 141*fae6e9adSlinfeng unimplemented!("bpf cmd {:?} not implemented", ty) 142*fae6e9adSlinfeng } 143*fae6e9adSlinfeng }; 144*fae6e9adSlinfeng res 145*fae6e9adSlinfeng} 146*fae6e9adSlinfeng``` 147*fae6e9adSlinfeng 148*fae6e9adSlinfeng其中对创建Map命令会再次细分,以确定具体的Map类型,目前我们对通用的Map基本添加了支持: 149*fae6e9adSlinfeng 150*fae6e9adSlinfeng```rust 151*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_ARRAY 152*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY 153*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY 154*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_HASH 155*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_PERCPU_HASH 156*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_QUEUE 157*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_STACK 158*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_LRU_HASH 159*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_LRU_PERCPU_HASH 160*fae6e9adSlinfeng 161*fae6e9adSlinfengbpf_map_type::BPF_MAP_TYPE_CPUMAP 162*fae6e9adSlinfeng| bpf_map_type::BPF_MAP_TYPE_DEVMAP 163*fae6e9adSlinfeng| bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH => { 164*fae6e9adSlinfeng error!("bpf map type {:?} not implemented", map_meta.map_type); 165*fae6e9adSlinfeng Err(SystemError::EINVAL)? 166*fae6e9adSlinfeng} 167*fae6e9adSlinfeng``` 168*fae6e9adSlinfeng 169*fae6e9adSlinfeng所有的Map都会实现定义好的接口,这个接口参考Linux的实现定义: 170*fae6e9adSlinfeng 171*fae6e9adSlinfeng```rust 172*fae6e9adSlinfengpub trait BpfMapCommonOps: Send + Sync + Debug + CastFromSync { 173*fae6e9adSlinfeng /// Lookup an element in the map. 174*fae6e9adSlinfeng /// 175*fae6e9adSlinfeng /// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_map_lookup_elem/ 176*fae6e9adSlinfeng fn lookup_elem(&mut self, _key: &[u8]) -> Result<Option<&[u8]>> { 177*fae6e9adSlinfeng Err(SystemError::ENOSYS) 178*fae6e9adSlinfeng } 179*fae6e9adSlinfeng /// Update an element in the map. 180*fae6e9adSlinfeng /// 181*fae6e9adSlinfeng /// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_map_update_elem/ 182*fae6e9adSlinfeng fn update_elem(&mut self, _key: &[u8], _value: &[u8], _flags: u64) -> Result<()> { 183*fae6e9adSlinfeng Err(SystemError::ENOSYS) 184*fae6e9adSlinfeng } 185*fae6e9adSlinfeng /// Delete an element from the map. 186*fae6e9adSlinfeng /// 187*fae6e9adSlinfeng /// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_map_delete_elem/ 188*fae6e9adSlinfeng fn delete_elem(&mut self, _key: &[u8]) -> Result<()> { 189*fae6e9adSlinfeng Err(SystemError::ENOSYS) 190*fae6e9adSlinfeng } 191*fae6e9adSlinfeng /// For each element in map, call callback_fn function with map, 192*fae6e9adSlinfeng /// callback_ctx and other map-specific parameters. 193*fae6e9adSlinfeng /// 194*fae6e9adSlinfeng /// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_for_each_map_elem/ 195*fae6e9adSlinfeng fn for_each_elem(&mut self, _cb: BpfCallBackFn, _ctx: *const u8, _flags: u64) -> Result<u32> { 196*fae6e9adSlinfeng Err(SystemError::ENOSYS) 197*fae6e9adSlinfeng } 198*fae6e9adSlinfeng /// Look up an element with the given key in the map referred to by the file descriptor fd, 199*fae6e9adSlinfeng /// and if found, delete the element. 200*fae6e9adSlinfeng fn lookup_and_delete_elem(&mut self, _key: &[u8], _value: &mut [u8]) -> Result<()> { 201*fae6e9adSlinfeng Err(SystemError::ENOSYS) 202*fae6e9adSlinfeng } 203*fae6e9adSlinfeng /// perform a lookup in percpu map for an entry associated to key on cpu. 204*fae6e9adSlinfeng fn lookup_percpu_elem(&mut self, _key: &[u8], cpu: u32) -> Result<Option<&[u8]>> { 205*fae6e9adSlinfeng Err(SystemError::ENOSYS) 206*fae6e9adSlinfeng } 207*fae6e9adSlinfeng /// Get the next key in the map. If key is None, get the first key. 208*fae6e9adSlinfeng /// 209*fae6e9adSlinfeng /// Called from syscall 210*fae6e9adSlinfeng fn get_next_key(&self, _key: Option<&[u8]>, _next_key: &mut [u8]) -> Result<()> { 211*fae6e9adSlinfeng Err(SystemError::ENOSYS) 212*fae6e9adSlinfeng } 213*fae6e9adSlinfeng /// Push an element value in map. 214*fae6e9adSlinfeng fn push_elem(&mut self, _value: &[u8], _flags: u64) -> Result<()> { 215*fae6e9adSlinfeng Err(SystemError::ENOSYS) 216*fae6e9adSlinfeng } 217*fae6e9adSlinfeng /// Pop an element value from map. 218*fae6e9adSlinfeng fn pop_elem(&mut self, _value: &mut [u8]) -> Result<()> { 219*fae6e9adSlinfeng Err(SystemError::ENOSYS) 220*fae6e9adSlinfeng } 221*fae6e9adSlinfeng /// Peek an element value from map. 222*fae6e9adSlinfeng fn peek_elem(&self, _value: &mut [u8]) -> Result<()> { 223*fae6e9adSlinfeng Err(SystemError::ENOSYS) 224*fae6e9adSlinfeng } 225*fae6e9adSlinfeng /// Freeze the map. 226*fae6e9adSlinfeng /// 227*fae6e9adSlinfeng /// It's useful for .rodata maps. 228*fae6e9adSlinfeng fn freeze(&self) -> Result<()> { 229*fae6e9adSlinfeng Err(SystemError::ENOSYS) 230*fae6e9adSlinfeng } 231*fae6e9adSlinfeng /// Get the first value pointer. 232*fae6e9adSlinfeng fn first_value_ptr(&self) -> *const u8 { 233*fae6e9adSlinfeng panic!("value_ptr not implemented") 234*fae6e9adSlinfeng } 235*fae6e9adSlinfeng} 236*fae6e9adSlinfeng``` 237*fae6e9adSlinfeng 238*fae6e9adSlinfeng联通eBPF和kprobe的系统调用是[`perf_event_open`](https://man7.org/linux/man-pages/man2/perf_event_open.2.html),这个系统调用在Linux中非常复杂,因此Dragon中并没有按照Linux进行实现,目前只支持其中两个功能: 239*fae6e9adSlinfeng 240*fae6e9adSlinfeng 241*fae6e9adSlinfeng 242*fae6e9adSlinfeng```rust 243*fae6e9adSlinfengmatch args.type_ { 244*fae6e9adSlinfeng // Kprobe 245*fae6e9adSlinfeng // See /sys/bus/event_source/devices/kprobe/type 246*fae6e9adSlinfeng perf_type_id::PERF_TYPE_MAX => { 247*fae6e9adSlinfeng let kprobe_event = kprobe::perf_event_open_kprobe(args); 248*fae6e9adSlinfeng Box::new(kprobe_event) 249*fae6e9adSlinfeng } 250*fae6e9adSlinfeng perf_type_id::PERF_TYPE_SOFTWARE => { 251*fae6e9adSlinfeng // For bpf prog output 252*fae6e9adSlinfeng assert_eq!(args.config, perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT); 253*fae6e9adSlinfeng assert_eq!( 254*fae6e9adSlinfeng args.sample_type, 255*fae6e9adSlinfeng Some(perf_event_sample_format::PERF_SAMPLE_RAW) 256*fae6e9adSlinfeng ); 257*fae6e9adSlinfeng let bpf_event = bpf::perf_event_open_bpf(args); 258*fae6e9adSlinfeng Box::new(bpf_event) 259*fae6e9adSlinfeng } 260*fae6e9adSlinfeng} 261*fae6e9adSlinfeng``` 262*fae6e9adSlinfeng 263*fae6e9adSlinfeng- 其中一个`PERF_TYPE_SOFTWARE`是用来创建软件定义的事件,`PERF_COUNT_SW_BPF_OUTPUT` 确保这个事件用来采集bpf的输出。 264*fae6e9adSlinfeng- `PERF_TYPE_MAX` 通常指示创建kprobe/uprobe事件,也就是用户程序使用kprobe的途径之一,用户程序可以将eBPF程序绑定在这个事件上 265*fae6e9adSlinfeng 266*fae6e9adSlinfeng同样的,perf不同的事件也实现定义的接口: 267*fae6e9adSlinfeng 268*fae6e9adSlinfeng```rust 269*fae6e9adSlinfengpub trait PerfEventOps: Send + Sync + Debug + CastFromSync + CastFrom { 270*fae6e9adSlinfeng fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<()> { 271*fae6e9adSlinfeng panic!("mmap not implemented for PerfEvent"); 272*fae6e9adSlinfeng } 273*fae6e9adSlinfeng fn set_bpf_prog(&self, _bpf_prog: Arc<File>) -> Result<()> { 274*fae6e9adSlinfeng panic!("set_bpf_prog not implemented for PerfEvent"); 275*fae6e9adSlinfeng } 276*fae6e9adSlinfeng fn enable(&self) -> Result<()> { 277*fae6e9adSlinfeng panic!("enable not implemented"); 278*fae6e9adSlinfeng } 279*fae6e9adSlinfeng fn disable(&self) -> Result<()> { 280*fae6e9adSlinfeng panic!("disable not implemented"); 281*fae6e9adSlinfeng } 282*fae6e9adSlinfeng fn readable(&self) -> bool { 283*fae6e9adSlinfeng panic!("readable not implemented"); 284*fae6e9adSlinfeng } 285*fae6e9adSlinfeng} 286*fae6e9adSlinfeng``` 287*fae6e9adSlinfeng 288*fae6e9adSlinfeng这个接口目前并不稳定。 289*fae6e9adSlinfeng 290*fae6e9adSlinfeng### helper函数支持 291*fae6e9adSlinfeng 292*fae6e9adSlinfeng用户态工具通过系统调用和内核进行通信,完成eBPF数据的设置、交换。在内核中,eBPF程序的运行也需要内核的帮助,单独的eBPF程序并没有什么太大的用处,因此其会调用内核提供的`helper` 函数完成对内核资源的访问。 293*fae6e9adSlinfeng 294*fae6e9adSlinfeng目前已经支持的大多数`helper` 函数是与Map操作相关: 295*fae6e9adSlinfeng 296*fae6e9adSlinfeng```rust 297*fae6e9adSlinfeng/// Initialize the helper functions. 298*fae6e9adSlinfengpub fn init_helper_functions() { 299*fae6e9adSlinfeng let mut map = BTreeMap::new(); 300*fae6e9adSlinfeng unsafe { 301*fae6e9adSlinfeng // Map helpers::Generic map helpers 302*fae6e9adSlinfeng map.insert(1, define_func!(raw_map_lookup_elem)); 303*fae6e9adSlinfeng map.insert(2, define_func!(raw_map_update_elem)); 304*fae6e9adSlinfeng map.insert(3, define_func!(raw_map_delete_elem)); 305*fae6e9adSlinfeng map.insert(164, define_func!(raw_map_for_each_elem)); 306*fae6e9adSlinfeng map.insert(195, define_func!(raw_map_lookup_percpu_elem)); 307*fae6e9adSlinfeng // map.insert(93,define_func!(raw_bpf_spin_lock); 308*fae6e9adSlinfeng // map.insert(94,define_func!(raw_bpf_spin_unlock); 309*fae6e9adSlinfeng // Map helpers::Perf event array helpers 310*fae6e9adSlinfeng map.insert(25, define_func!(raw_perf_event_output)); 311*fae6e9adSlinfeng // Probe and trace helpers::Memory helpers 312*fae6e9adSlinfeng map.insert(4, define_func!(raw_bpf_probe_read)); 313*fae6e9adSlinfeng // Print helpers 314*fae6e9adSlinfeng map.insert(6, define_func!(trace_printf)); 315*fae6e9adSlinfeng 316*fae6e9adSlinfeng // Map helpers::Queue and stack helpers 317*fae6e9adSlinfeng map.insert(87, define_func!(raw_map_push_elem)); 318*fae6e9adSlinfeng map.insert(88, define_func!(raw_map_pop_elem)); 319*fae6e9adSlinfeng map.insert(89, define_func!(raw_map_peek_elem)); 320*fae6e9adSlinfeng } 321*fae6e9adSlinfeng BPF_HELPER_FUN_SET.init(map); 322*fae6e9adSlinfeng} 323*fae6e9adSlinfeng``` 324*fae6e9adSlinfeng 325