Extended Berkeley Packet Filter (eBPF) คือเครื่องเสมือนในเคอร์เนลที่รันโปรแกรม eBPF ที่ผู้ใช้ระบุเพื่อขยายฟังก์ชันของเคิร์นัล โปรแกรมเหล่านี้สามารถเชื่อมต่อกับโปรเบสหรือเหตุการณ์ในเคอร์เนล และใช้เพื่อรวบรวมสถิติ เคอร์เนลที่มีประโยชน์ ตรวจสอบ และแก้ไขข้อบกพร่อง ระบบจะโหลดโปรแกรมลงในเคอร์เนลโดยใช้ bpf(2)
syscall และผู้ใช้เป็นผู้ระบุเป็นบล็อกไบนารีของคำสั่งเครื่อง eBPF ระบบบิลด์ของ Android รองรับการคอมไพล์โปรแกรม C เป็น eBPF โดยใช้ไวยากรณ์ไฟล์บิลด์แบบง่ายที่อธิบายไว้ในเอกสารนี้
ดูข้อมูลเพิ่มเติมเกี่ยวกับโครงสร้างภายในและสถาปัตยกรรมของ eBPF ได้ที่ Brendan หน้า eBPF ของ Gregg
Android มีตัวโหลด eBPF และไลบรารีที่โหลด eBPF เมื่อเปิดเครื่อง
ตัวโหลด Android BPF
ระหว่างการบูต Android ระบบจะโหลดโปรแกรม eBPF ทั้งหมดที่อยู่ใน /system/etc/bpf/
โปรแกรมเหล่านี้เป็นออบเจ็กต์ไบนารีที่สร้างโดยระบบบิลด์ของ Android
จากโปรแกรม C และมาพร้อมกับไฟล์ Android.bp
ในซอร์สโค้ดของ Android
ต้นไม้ ระบบบิลด์จะจัดเก็บออบเจ็กต์ที่สร้างขึ้นที่ /system/etc/bpf
และออบเจ็กต์เหล่านั้นจะกลายเป็นส่วนหนึ่งของอิมเมจระบบ
รูปแบบของโปรแกรม Android eBPF C
โปรแกรม eBPF C ต้องมีรูปแบบต่อไปนี้
#include <bpf_helpers.h>
/* Define one or more maps in the maps section, for example
* define a map of type array int -> uint32_t, with 10 entries
*/
DEFINE_BPF_MAP(name_of_my_map
, ARRAY, int, uint32_t, 10);
/* this also defines type-safe accessors:
* value * bpf_name_of_my_map_lookup_elem(&key);
* int bpf_name_of_my_map_update_elem(&key, &value, flags);
* int bpf_name_of_my_map_delete_elem(&key);
* as such it is heavily suggested to use lowercase *_map names.
* Also note that due to compiler deficiencies you cannot use a type
* of 'struct foo' but must instead use just 'foo'. As such structs
* must not be defined as 'struct foo {}' and must instead be
* 'typedef struct {} foo'.
*/
DEFINE_BPF_PROG("PROGTYPE/PROGNAME", AID_*, AID_*, PROGFUNC)(..args..) {
<body-of-code
... read or write to MY_MAPNAME
... do other things
>
}
LICENSE("GPL"); // or other license
สถานที่:
name_of_my_map
คือชื่อของตัวแปรแผนที่ ช่วงเวลานี้ จะบอกตัวโหลด BPF ถึงประเภทของแผนที่ที่จะสร้าง พารามิเตอร์ คําจํากัดความของโครงสร้างนี้มาจากbpf_helpers.h
ส่วนหัวที่รวมอยู่ด้วยPROGTYPE/PROGNAME
แสดงถึงประเภทโปรแกรม และชื่อโปรแกรม ประเภทของโปรแกรมอาจเป็นรายการใดก็ได้ที่ระบุไว้ใน ตารางต่อไปนี้ หากไม่มีประเภทรายการอยู่ในรายการ จะไม่มีการตั้งชื่อที่แน่นอน ที่กำหนดสำหรับโปรแกรมนี้ จะต้องรู้จักชื่อในกระบวนการที่ แนบโปรแกรมมาด้วยPROGFUNC
คือฟังก์ชันที่เมื่อคอมไพล์แล้วจะวางไว้ในส่วนหนึ่งของไฟล์ที่ได้
kprobe | ผูก PROGFUNC ไว้ที่คำสั่งของเคอร์เนลโดยใช้
Kprobe PROGNAME ต้องเป็นชื่อของฟังก์ชันเคอร์เนลที่กำลังทำการเฝ้าติดตาม ดูข้อมูลเพิ่มเติมเกี่ยวกับ kprobe ได้จากเอกสารประกอบเคอร์เนล kprobe
|
---|---|
จุดติดตาม | ดึง PROGFUNC เข้ากับจุดติดตาม PROGNAME ต้องอยู่ในรูปแบบ SUBSYSTEM/EVENT ตัวอย่างเช่น ส่วนการติดตามจุดสําหรับการแนบฟังก์ชันกับเหตุการณ์การเปลี่ยนบริบทของโปรแกรมจัดตารางเวลาจะเป็น SEC("tracepoint/sched/sched_switch") โดยที่ sched คือชื่อของส่วนย่อยการติดตาม และ sched_switch คือชื่อของเหตุการณ์การติดตาม ดูข้อมูลเพิ่มเติมเกี่ยวกับจุดติดตามได้ในเอกสารประกอบเกี่ยวกับเคอร์เนลเหตุการณ์การติดตาม
|
Skfilter | โปรแกรมทําหน้าที่เป็นตัวกรองซ็อกเก็ตเครือข่าย |
schedcls | โปรแกรมทำหน้าที่เป็นตัวแยกประเภทการจราจรของข้อมูลในเครือข่าย |
กลุ่มก๊อกบ๊อก | โปรแกรมจะทำงานทุกครั้งที่กระบวนการใน CGroup สร้าง AF_INET หรือ AF_INET6 socket |
ประเภทเพิ่มเติมอาจ จะอยู่ในเครื่องมือโหลด ซอร์สโค้ด
ตัวอย่างเช่น โปรแกรม myschedtp.c
ต่อไปนี้เพิ่มข้อมูลเกี่ยวกับ
PID ของงานล่าสุดที่ทำงานบน CPU ตัวใดตัวหนึ่ง โปรแกรมนี้บรรลุเป้าหมาย
โดยการสร้างแผนที่และกำหนดฟังก์ชัน tp_sched_switch
ซึ่งสามารถ
แนบอยู่กับเหตุการณ์การติดตาม sched:sched_switch
ดูข้อมูลเพิ่มเติมได้ที่การแนบโปรแกรมกับจุดติดตาม
#include <linux/bpf.h> #include <stdbool.h> #include <stdint.h> #include <bpf_helpers.h> DEFINE_BPF_MAP(cpu_pid_map, ARRAY, int, uint32_t, 1024); struct switch_args { unsigned long long ignore; char prev_comm[16]; int prev_pid; int prev_prio; long long prev_state; char next_comm[16]; int next_pid; int next_prio; }; DEFINE_BPF_PROG("tracepoint/sched/sched_switch", AID_ROOT, AID_SYSTEM, tp_sched_switch) (struct switch_args *args) { int key; uint32_t val; key = bpf_get_smp_processor_id(); val = args->next_pid; bpf_cpu_pid_map_update_elem(&key, &val, BPF_ANY); return 1; // return 1 to avoid blocking simpleperf from receiving events } LICENSE("GPL");
ระบบจะใช้มาโครใบอนุญาตเพื่อตรวจสอบว่าโปรแกรมสามารถทำงานร่วมกับ
สัญญาอนุญาตของเคอร์เนลเมื่อโปรแกรมใช้ฟังก์ชันตัวช่วย BPF ที่ให้บริการโดย
เคอร์เนล ระบุชื่อใบอนุญาตของโปรแกรมในรูปแบบสตริง เช่น LICENSE("GPL")
หรือ LICENSE("Apache 2.0")
รูปแบบของไฟล์ Android.bp
คุณต้องอนุญาตให้ระบบบิลด์ของ Android สร้างโปรแกรม eBPF .c
สร้างรายการในไฟล์ Android.bp
ของโปรเจ็กต์ ตัวอย่างเช่น หากต้องการ
สร้างโปรแกรม eBPF C ชื่อ bpf_test.c
โปรดทำดังนี้
ข้อมูลในไฟล์ Android.bp
ของโครงการของคุณ:
bpf { name: "bpf_test.o", srcs: ["bpf_test.c"], cflags: [ "-Wall", "-Werror", ], }
รายการนี้จะคอมไพล์โปรแกรม C เพื่อสร้างออบเจ็กต์ /system/etc/bpf/bpf_test.o
เมื่อเปิดเครื่อง ระบบ Android จะโหลดโดยอัตโนมัติ
โปรแกรม bpf_test.o
ลงในเคอร์เนล
ไฟล์ที่มีอยู่ใน sysf
ในระหว่างการบูต ระบบ Android จะโหลดออบเจ็กต์ eBPF ทั้งหมดจาก /system/etc/bpf/
โดยอัตโนมัติ สร้างแผนที่ที่โปรแกรมต้องการ และปักหมุดโปรแกรมที่โหลดไว้ด้วยแผนที่ไปยังระบบไฟล์ BPF จากนั้นจะใช้ไฟล์เหล่านี้เพื่อโต้ตอบกับโปรแกรม eBPF หรืออ่านแผนที่เพิ่มเติมได้ ส่วนนี้
อธิบายแบบแผนที่ใช้ในการตั้งชื่อไฟล์เหล่านี้ และตำแหน่งของไฟล์ใน
ของช่วง
สร้างและปักหมุดไฟล์ต่อไปนี้
สำหรับโปรแกรมที่โหลด ให้สมมติว่า
PROGNAME
เป็นชื่อของโปรแกรมและFILENAME
คือชื่อของไฟล์ eBPF C ซึ่งโปรแกรมโหลด Android จะสร้าง ปักหมุดแต่ละโปรแกรมที่/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME
เช่น สำหรับตัวอย่าง Tracepoint ของ
sched_switch
ก่อนหน้าในmyschedtp.c
ระบบจะสร้างไฟล์โปรแกรมและปักหมุดไว้ที่/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch
สำหรับแผนที่ที่สร้างขึ้น สมมติว่า
MAPNAME
คือชื่อของแผนที่และFILENAME
คือชื่อของไฟล์ eBPF C ซึ่งโปรแกรมโหลด Android จะสร้าง ปักหมุดแผนที่แต่ละชุดไว้ที่/sys/fs/bpf/map_FILENAME_MAPNAME
เช่น สำหรับตัวอย่าง Tracepoint ของ
sched_switch
ก่อนหน้าในmyschedtp.c
ระบบจะสร้างไฟล์แผนที่และปักหมุดไว้ที่/sys/fs/bpf/map_myschedtp_cpu_pid_map
bpf_obj_get()
ในไลบรารี Android BPF แสดงผลข้อบ่งชี้ไฟล์จาก ปักหมุด/sys/fs/bpf
ไฟล์แล้ว ใช้ข้อบ่งชี้ไฟล์นี้ในการดำเนินการเพิ่มเติมได้ เช่น การอ่านแผนที่หรือการติดตั้งโปรแกรมเข้ากับจุดติดตาม
ไลบรารี BPF ของ Android
ไลบรารี BPF ของ Android มีชื่อว่า libbpf_android.so
และเป็นส่วนหนึ่งของระบบไฟล์ ไลบรารีนี้มอบความสามารถระดับ eBPF ในระดับต่ำให้แก่ผู้ใช้
สำหรับการสร้างและอ่านแผนที่ การสร้างโพรบ จุดติดตาม และบัฟเฟอร์ Perf
แนบโปรแกรมกับจุดติดตาม
โปรแกรม Tracepoint จะโหลดโดยอัตโนมัติเมื่อเปิดเครื่อง หลังจากโหลดแล้ว ต้องเปิดใช้งานโปรแกรมการติดตามจุดโดยใช้ขั้นตอนต่อไปนี้
- โทรหา
bpf_obj_get()
เพื่อขอรับโปรแกรมfd
จากตำแหน่งของไฟล์ที่ปักหมุดไว้ ดูข้อมูลเพิ่มเติมได้ที่ไฟล์ที่มีอยู่ใน sysfs - โทรหา
bpf_attach_tracepoint()
ในคลัง BPF เพื่อส่งให้ผ่านโปรแกรมfd
และชื่อการติดตาม
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีแนบ Trackpoint ของ sched_switch
ที่กำหนดไว้ในไฟล์แหล่งที่มา myschedtp.c
ก่อนหน้า (ระบบจะไม่แสดงการตรวจสอบข้อผิดพลาด):
char *tp_prog_path = "/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch"; char *tp_map_path = "/sys/fs/bpf/map_myschedtp_cpu_pid"; // Attach tracepoint and wait for 4 seconds int mProgFd = bpf_obj_get(tp_prog_path); int mMapFd = bpf_obj_get(tp_map_path); int ret = bpf_attach_tracepoint(mProgFd, "sched", "sched_switch"); sleep(4); // Read the map to find the last PID that ran on CPU 0 android::bpf::BpfMap<int, int> myMap(mMapFd); printf("last PID running on CPU %d is %d\n", 0, myMap.readValue(0));
อ่านจากแผนที่
แมป BPF รองรับโครงสร้างหรือประเภทคีย์และค่าที่ซับซ้อน ไลบรารี BPF ของ Android มีคลาส android::BpfMap
ที่ใช้เทมเพลต C++ เพื่อสร้างอินสแตนซ์ BpfMap
โดยอิงตามคีย์และประเภทค่าสำหรับแผนที่ที่เป็นปัญหา ตัวอย่างโค้ดก่อนหน้านี้สาธิตการใช้ BpfMap
ที่มีคีย์
และเป็นจำนวนเต็ม จำนวนเต็มอาจเป็นโครงสร้างแบบกำหนดเองได้เช่นกัน
ดังนั้นคลาส BpfMap
ที่ใช้เทมเพลตจึงช่วยให้คุณกำหนดออบเจ็กต์ BpfMap
ที่กําหนดเองซึ่งเหมาะกับแผนที่หนึ่งๆ ได้ จากนั้นคุณจะเข้าถึงแผนที่ได้โดยใช้ฟังก์ชันที่สร้างขึ้นเองซึ่งจะรับรู้ประเภท ส่งผลให้โค้ดสะอาดขึ้น
ดูข้อมูลเพิ่มเติมเกี่ยวกับ BpfMap
ได้ที่
แหล่งที่มาของ Android
แก้ไขข้อบกพร่อง
ในระหว่างการเปิดเครื่อง ระบบจะบันทึกข้อความหลายข้อความที่เกี่ยวข้องกับการโหลด BPF หาก
กระบวนการโหลดล้มเหลวไม่ว่าจะด้วยเหตุผลใดก็ตาม มีการแสดงข้อความบันทึกโดยละเอียด
ใน Logcat การกรองบันทึก Logcat ตาม bpf
จะพิมพ์ข้อความทั้งหมดและ
ข้อผิดพลาดโดยละเอียดในระหว่างการโหลด เช่น ข้อผิดพลาดเกี่ยวกับตัวตรวจสอบ eBPF
ตัวอย่างของ eBPF ใน Android
โปรแกรมต่อไปนี้ใน AOSP มีตัวอย่างเพิ่มเติมในการใช้ eBPF
eBPF C
netd
โปรแกรม ถูกใช้งานโดย Daemon เครือข่าย (netd) ใน Android เพื่อวัตถุประสงค์ต่างๆ เช่น ในการกรองซ็อกเก็ต และการรวบรวมสถิติ หากต้องการดูวิธีใช้โปรแกรมนี้ ให้ตรวจสอบแหล่งที่มาของเครื่องมือตรวจสอบการเข้าชม eBPFtime_in_state
โปรแกรม eBPF C จะคำนวณระยะเวลาที่แอป Android ทำงานที่ความถี่ CPU ต่างๆ ซึ่งจะใช้คำนวณพลังงานใน Android 12
gpu_mem
eBPF C โปรแกรม ติดตามการใช้งานหน่วยความจำ GPU ทั้งหมดสำหรับแต่ละกระบวนการและสำหรับทั้งระบบ ช่วงเวลานี้ ที่ใช้ทำโปรไฟล์หน่วยความจำ GPU