Extended Berkeley Packet Filter (eBPF) adalah mesin virtual dalam kernel yang menjalankan program eBPF yang disediakan pengguna untuk memperluas fungsi kernel. Program ini dapat dikaitkan ke probe atau peristiwa di kernel dan digunakan untuk mengumpulkan statistik kernel yang berguna, memantau, dan men-debug. Program dimuat ke dalam kernel menggunakan syscall bpf(2) dan disediakan oleh pengguna sebagai blob biner dari instruksi mesin eBPF. Sistem build Android memiliki dukungan untuk mengompilasi program C ke eBPF menggunakan sintaksis file build sederhana yang dijelaskan dalam dokumen ini.
Informasi selengkapnya tentang internal dan arsitektur eBPF dapat ditemukan di halaman eBPF Brendan Gregg.
Android menyertakan pemuat dan library eBPF yang memuat program eBPF saat waktu booting.
Pemuat BPF Android
Selama booting Android, semua program eBPF yang berada di /system/etc/bpf/ akan dimuat. Program ini adalah objek biner yang dibuat oleh sistem build Android dari program C dan disertai dengan file Android.bp di pohon sumber Android. Sistem build menyimpan objek yang dihasilkan di /system/etc/bpf, dan objek tersebut menjadi bagian dari image sistem.
Format program C eBPF Android
Program C eBPF harus memiliki format berikut:
#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 licenseDengan:
name_of_my_mapadalah nama variabel peta Anda. Nama ini memberi tahu pemuat BPF tentang jenis peta yang akan dibuat dan dengan parameter apa. Definisi struct ini disediakan oleh headerbpf_helpers.hyang disertakan.PROGTYPE/PROGNAMEmewakili jenis program dan nama program. Jenis program dapat berupa salah satu yang tercantum dalam tabel berikut. Jika jenis program tidak tercantum, tidak ada konvensi penamaan yang ketat untuk program tersebut; nama hanya perlu diketahui oleh proses yang melampirkan program.PROGFUNCadalah fungsi yang, saat dikompilasi, ditempatkan di bagian file yang dihasilkan.
| kprobe | Mengaitkan PROGFUNC ke instruksi kernel menggunakan infrastruktur kprobe. PROGNAME harus berupa nama fungsi kernel yang sedang di-kprobe. Lihat dokumentasi kernel kprobe untuk mengetahui informasi selengkapnya tentang
kprobe.
|
|---|---|
| tracepoint | Mengaitkan PROGFUNC ke tracepoint. PROGNAME harus dalam format SUBSYSTEM/EVENT. Misalnya, bagian tracepoint
untuk melampirkan fungsi ke peristiwa pengalihan konteks penjadwal adalah
SEC("tracepoint/sched/sched_switch"), dengan sched adalah
nama subsistem pelacakan, dan sched_switch adalah nama
peristiwa pelacakan. Lihat dokumentasi kernel peristiwa pelacakanuntuk mengetahui informasi selengkapnya tentang tracepoint.
|
| skfilter | Fungsi program sebagai filter soket jaringan. |
| schedcls | Fungsi program sebagai pengklasifikasi traffic jaringan. |
| cgroupskb, cgroupsock | Program berjalan setiap kali proses dalam CGroup membuat soket AF_INET atau AF_INET6. |
Jenis tambahan dapat ditemukan dalam kode sumber Pemuat.
Misalnya, program myschedtp.c berikut menambahkan informasi tentang PID tugas terbaru yang telah berjalan di CPU tertentu. Program ini mencapai tujuannya dengan membuat peta dan menentukan fungsi tp_sched_switch yang dapat dilampirkan ke peristiwa pelacakan sched:sched_switch. Untuk mengetahui informasi selengkapnya, lihat
Melampirkan program ke tracepoint.
#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");
Makro LICENSE digunakan untuk memverifikasi apakah program kompatibel dengan lisensi kernel saat program menggunakan fungsi helper BPF yang disediakan oleh kernel. Tentukan nama lisensi program Anda dalam bentuk string, seperti
LICENSE("GPL") atau LICENSE("Apache 2.0").
Format file Android.bp
Agar sistem build Android dapat membuat program .c eBPF, Anda harus membuat entri di file Android.bp project. Misalnya, untuk membuat program C eBPF bernama bpf_test.c, buat entri berikut di file Android.bp project Anda:
bpf {
name: "bpf_test.o",
srcs: ["bpf_test.c"],
cflags: [
"-Wall",
"-Werror",
],
}Entri ini mengompilasi program C yang menghasilkan objek /system/etc/bpf/bpf_test.o. Saat booting, sistem Android akan otomatis memuat program bpf_test.o ke dalam kernel.
File yang tersedia di sysfs
Selama booting, sistem Android akan otomatis memuat semua objek eBPF dari /system/etc/bpf/, membuat peta yang diperlukan program, dan menyematkan program yang dimuat dengan petanya ke sistem file BPF. File ini kemudian dapat digunakan untuk interaksi lebih lanjut dengan program eBPF atau peta baca. Bagian ini menjelaskan konvensi yang digunakan untuk memberi nama file ini dan lokasinya di sysfs.
File berikut dibuat dan disematkan:
Untuk program yang dimuat, dengan asumsi
PROGNAMEadalah nama program danFILENAMEadalah nama file C eBPF, pemuat Android akan membuat dan menyematkan setiap program di/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME.Misalnya, untuk contoh tracepoint
sched_switchsebelumnya dimyschedtp.c, file program dibuat dan disematkan ke/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch.Untuk peta yang dibuat, dengan asumsi
MAPNAMEadalah nama peta danFILENAMEadalah nama file C eBPF, pemuat Android akan membuat dan menyematkan setiap peta ke/sys/fs/bpf/map_FILENAME_MAPNAME.Misalnya, untuk contoh tracepoint
sched_switchsebelumnya dimyschedtp.c, file peta dibuat dan disematkan ke/sys/fs/bpf/map_myschedtp_cpu_pid_map.bpf_obj_get()di library BPF Android menampilkan deskriptor file dari file/sys/fs/bpfyang disematkan. Deskriptor file ini dapat digunakan untuk operasi lebih lanjut, seperti membaca peta atau melampirkan program ke tracepoint.
Library BPF Android
Library BPF Android diberi nama libbpf_android.so dan merupakan bagian dari image sistem. Library ini menyediakan kemampuan eBPF tingkat rendah yang diperlukan pengguna untuk membuat dan membaca peta, membuat probe, tracepoint, dan buffer performa.
Melampirkan program ke tracepoint
Program tracepoint dimuat secara otomatis saat booting. Setelah dimuat, program tracepoint harus diaktifkan menggunakan langkah-langkah berikut:
- Panggil
bpf_obj_get()untuk mendapatkanfdprogram dari lokasi file yang disematkan. Untuk mengetahui informasi selengkapnya, lihat File yang tersedia di sysfs. - Panggil
bpf_attach_tracepoint()di library BPF, dengan meneruskanfdprogram dan nama tracepoint.
Contoh kode berikut menunjukkan cara melampirkan tracepoint sched_switch yang ditentukan dalam file sumber myschedtp.c sebelumnya (pemeriksaan error tidak ditampilkan):
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));
Membaca dari peta
Peta BPF mendukung struktur atau jenis kunci dan nilai kompleks arbitrer. Library BPF Android menyertakan class android::BpfMap yang menggunakan template C++
untuk membuat instance BpfMap berdasarkan jenis kunci dan nilai untuk peta yang dimaksud. Contoh kode sebelumnya menunjukkan penggunaan BpfMap dengan kunci dan nilai sebagai bilangan bulat. Bilangan bulat juga dapat berupa struktur arbitrer.
Dengan demikian, class BpfMap yang di-templat memungkinkan Anda menentukan objek BpfMap kustom yang sesuai untuk peta tertentu. Peta kemudian dapat diakses menggunakan fungsi yang dibuat kustom, yang memiliki jenis, sehingga menghasilkan kode yang lebih bersih.
Untuk mengetahui informasi selengkapnya tentang BpfMap, lihat
sumber Android.
Men-debug masalah
Selama waktu booting, beberapa pesan terkait pemuatan BPF dicatat. Jika proses pemuatan gagal karena alasan apa pun, pesan log mendetail akan diberikan di logcat. Memfilter logcat log menurut bpf akan mencetak semua pesan dan error mendetail selama waktu pemuatan, seperti error verifikasi eBPF.
Contoh eBPF di Android
Program berikut di AOSP memberikan contoh tambahan penggunaan eBPF:
Program C eBPF
netddigunakan oleh daemon jaringan (netd) di Android untuk berbagai tujuan seperti pemfilteran soket dan pengumpulan statistik. Untuk melihat cara penggunaan program ini, lihat sumber pemantau traffic eBPF.Program C eBPF
time_in_statemenghitung jumlah waktu yang dihabiskan aplikasi Android pada frekuensi CPU yang berbeda, yang digunakan untuk menghitung daya.Di Android 12, program C eBPF
gpu_memmelacak total penggunaan memori GPU untuk setiap proses dan untuk seluruh sistem. Program ini digunakan untuk pembuatan profil memori GPU.
Untuk mengetahui informasi selengkapnya tentang penggunaan eBPF untuk mengumpulkan histogram latensi daya dan pengelolaan memori kernel overhead rendah untuk statsd, lihat Mengumpulkan telemetri kernel
dengan eBPF.