Extended Berkeley Packet Filter (eBPF) คือเครื่องเสมือนในเคอร์เนลที่เรียกใช้โปรแกรม eBPF ที่ผู้ใช้ระบุเพื่อขยายฟังก์ชันการทำงานของเคอร์เนล โปรแกรมเหล่านี้
สามารถเชื่อมต่อกับโพรบหรือเหตุการณ์ในเคอร์เนล และใช้เพื่อรวบรวมสถิติเคอร์เนลที่เป็นประโยชน์
ตรวจสอบ และแก้ไขข้อบกพร่อง โปรแกรมจะ
โหลดลงในเคอร์เนลโดยใช้ bpf(2) syscall และผู้ใช้จะระบุ
เป็น Blob แบบไบนารีของคำสั่งเครื่อง eBPF ระบบบิลด์ของ Android รองรับการคอมไพล์โปรแกรม C เป็น eBPF โดยใช้ไวยากรณ์ไฟล์บิลด์อย่างง่ายที่อธิบายไว้ในเอกสารนี้
ดูข้อมูลเพิ่มเติมเกี่ยวกับรายละเอียดภายในและสถาปัตยกรรมของ eBPF ได้ที่หน้า eBPF ของ Brendan Gregg
Android มีโปรแกรมโหลดและไลบรารี eBPF ที่โหลดโปรแกรม eBPF ในเวลาบูต
โปรแกรมโหลด BPF ของ Android
ในระหว่างการบูต Android ระบบจะโหลดโปรแกรม eBPF ทั้งหมดที่อยู่ใน /system/etc/bpf/
โปรแกรมเหล่านี้เป็นออบเจ็กต์ไบนารีที่สร้างขึ้นโดยระบบบิลด์ของ Android
จากโปรแกรม C และมาพร้อมกับไฟล์ Android.bp ในซอร์สของ Android
ระบบบิลด์จะจัดเก็บออบเจ็กต์ที่สร้างขึ้นไว้ที่ /system/etc/bpf และ
ออบเจ็กต์เหล่านั้นจะกลายเป็นส่วนหนึ่งของอิมเมจระบบ
รูปแบบของโปรแกรม C สำหรับ eBPF ใน Android
โปรแกรม C ของ eBPF ต้องมีรูปแบบต่อไปนี้
#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 | Hooks PROGFUNC onto at a kernel instruction using the
kprobe infrastructure. PROGNAME ต้องเป็นชื่อของฟังก์ชันเคอร์เนล
ที่กำลังใช้ kprobe ดูข้อมูลเพิ่มเติมเกี่ยวกับ Kprobe ได้ที่เอกสารประกอบของเคอร์เนล kprobe
|
|---|---|
| จุดติดตาม | ฮุก PROGFUNC ไปยัง Tracepoint PROGNAME ต้องอยู่ในรูปแบบ SUBSYSTEM/EVENT ตัวอย่างเช่น ส่วน Tracepoint
สําหรับการแนบฟังก์ชันกับเหตุการณ์การสลับบริบทของตัวจัดตารางเวลาจะเป็น
SEC("tracepoint/sched/sched_switch") โดยที่ sched คือ
ชื่อของระบบย่อยการติดตาม และ sched_switch คือชื่อ
ของเหตุการณ์การติดตาม ดูข้อมูลเพิ่มเติมเกี่ยวกับ Tracepoint ได้ที่เอกสารประกอบเกี่ยวกับเคอร์เนลของเหตุการณ์การติดตาม
|
| skfilter | โปรแกรมจะทําหน้าที่เป็นตัวกรองซ็อกเก็ตเครือข่าย |
| schedcls | โปรแกรมทําหน้าที่เป็นตัวแยกประเภทการรับส่งข้อมูลเครือข่าย |
| cgroupskb, cgroupsock | โปรแกรมจะทํางานเมื่อใดก็ตามที่กระบวนการใน CGroup สร้างซ็อกเก็ต AF_INET หรือ AF_INET6 |
ดูประเภทเพิ่มเติมได้ในซอร์สโค้ด ของ Loader
ตัวอย่างเช่น โปรแกรม 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");
มาโคร LICENSE ใช้เพื่อยืนยันว่าโปรแกรมเข้ากันได้กับ
ใบอนุญาตของเคอร์เนลหรือไม่ เมื่อโปรแกรมใช้ฟังก์ชันตัวช่วย BPF ที่เคอร์เนล
จัดเตรียมไว้ให้ ระบุชื่อใบอนุญาตของโปรแกรมในรูปแบบสตริง เช่น
LICENSE("GPL") หรือ LICENSE("Apache 2.0")
รูปแบบของไฟล์ Android.bp
หากต้องการให้ระบบบิลด์ Android สร้างโปรแกรม eBPF .c คุณต้องสร้างรายการในไฟล์ Android.bp ของโปรเจ็กต์ เช่น หากต้องการ
สร้างโปรแกรม C แบบ eBPF ชื่อ 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 ลงในเคอร์เนลโดยอัตโนมัติ
ไฟล์ที่พร้อมใช้งานใน sysfs
ในระหว่างการบูต ระบบ Android จะโหลดออบเจ็กต์ eBPF ทั้งหมดจาก
/system/etc/bpf/ โดยอัตโนมัติ สร้างแมปที่โปรแกรมต้องการ และปักหมุดโปรแกรมที่โหลด
พร้อมแมปไปยังระบบไฟล์ BPF จากนั้นจะใช้ไฟล์เหล่านี้เพื่อ
โต้ตอบกับโปรแกรม eBPF หรืออ่านแผนที่ต่อไปได้ ส่วนนี้
อธิบายรูปแบบที่ใช้ในการตั้งชื่อไฟล์เหล่านี้และตำแหน่งของไฟล์ใน
sysfs
ระบบจะสร้างและปักหมุดไฟล์ต่อไปนี้
สำหรับโปรแกรมที่โหลด สมมติว่า
PROGNAMEคือชื่อโปรแกรมและFILENAMEคือชื่อไฟล์ C ของ eBPF ตัวโหลด Android จะสร้างและ ปักหมุดแต่ละโปรแกรมที่/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAMEเช่น สำหรับ
sched_switchตัวอย่าง Tracepoint ก่อนหน้าในmyschedtp.cระบบจะสร้างไฟล์โปรแกรมและปักหมุดไว้ที่/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switchสำหรับแผนที่ที่สร้างขึ้น สมมติว่า
MAPNAMEคือชื่อของแผนที่และFILENAMEคือชื่อของไฟล์ C ของ eBPF ตัวโหลด Android จะสร้างและ ปักหมุดแต่ละแผนที่ไปยัง/sys/fs/bpf/map_FILENAME_MAPNAMEเช่น สำหรับ
sched_switchตัวอย่าง Tracepoint ก่อนหน้าในmyschedtp.cระบบจะสร้างไฟล์แผนที่และปักหมุดไว้ที่/sys/fs/bpf/map_myschedtp_cpu_pid_mapbpf_obj_get()ในไลบรารี BPF ของ Android จะแสดงตัวอธิบายไฟล์จากไฟล์ที่/sys/fs/bpfตรึงไว้ คุณสามารถใช้ตัวอธิบายไฟล์นี้สำหรับการดำเนินการเพิ่มเติม เช่น การอ่านแมปหรือการแนบโปรแกรมกับจุดติดตาม
ไลบรารี BPF ของ Android
ไลบรารี BPF ของ Android มีชื่อว่า libbpf_android.so และเป็นส่วนหนึ่งของอิมเมจระบบ
ไลบรารีนี้ช่วยให้ผู้ใช้มีขีดความสามารถ eBPF ระดับต่ำที่จำเป็น
สำหรับการสร้างและอ่านแผนที่ สร้างโพรบ จุดติดตาม และบัฟเฟอร์ perf
แนบโปรแกรมกับจุดติดตาม
ระบบจะโหลดโปรแกรม Tracepoint โดยอัตโนมัติเมื่อบูต หลังจากโหลดแล้ว ต้องเปิดใช้งานโปรแกรม Tracepoint โดยทำตามขั้นตอนต่อไปนี้
- เรียกใช้
bpf_obj_get()เพื่อรับโปรแกรมfdจากตำแหน่งของไฟล์ที่ปักหมุด ดูข้อมูลเพิ่มเติมได้ที่ไฟล์ที่พร้อมใช้งานใน sysfs - เรียกใช้
bpf_attach_tracepoint()ในไลบรารี BPF โดยส่งโปรแกรมfdและชื่อจุดติดตาม
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีแนบ sched_switch tracepoint
ที่กำหนดไว้ในไฟล์ต้นฉบับ 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
netdโปรแกรม eBPF C ใช้โดย Daemon เครือข่าย (netd) ใน Android เพื่อวัตถุประสงค์ต่างๆ เช่น การกรองซ็อกเก็ตและการรวบรวมสถิติ หากต้องการดูวิธีใช้โปรแกรมนี้ ให้ตรวจสอบแหล่งที่มาของเครื่องมือตรวจสอบการรับส่งข้อมูล eBPFtime_in_stateโปรแกรม eBPF C จะคำนวณระยะเวลาที่แอป Android ใช้ที่ความถี่ CPU ต่างๆ ซึ่งใช้ในการคำนวณกำลังใน Android 12
gpu_memโปรแกรม eBPF C จะติดตามการใช้หน่วยความจำ GPU ทั้งหมดสำหรับแต่ละกระบวนการและทั้งระบบ โปรแกรมนี้ใช้สำหรับการสร้างโปรไฟล์หน่วยความจำ GPU