Extended Berkeley Packet Filter (eBPF) adalah virtual machine dalam kernel yang
menjalankan program eBPF yang disediakan pengguna untuk memperluas fungsi kernel. Program ini
dapat dihubungkan ke probe atau peristiwa di kernel dan digunakan untuk mengumpulkan statistik kernel, pemantauan, dan proses debug yang berguna. Program adalah
dimuat ke dalam kernel menggunakan syscall bpf(2)
dan disediakan oleh pengguna
sebagai blob biner
instruksi mesin eBPF. Sistem build Android memiliki
dukungan untuk mengompilasi C
program ke eBPF menggunakan
{i>syntax<i} file {i>build<i} sederhana yang dijelaskan dalam dokumen ini.
Informasi selengkapnya tentang internal dan arsitektur eBPF dapat ditemukan di halaman eBPF Brendan Gregg.
Android menyertakan loader eBPF dan library yang memuat eBPF program pada waktu {i>booting<i}.
Loader BPF Android
Selama booting Android, semua program eBPF yang berada di /system/etc/bpf/
dimuat. Program ini adalah objek biner yang dibangun oleh sistem build Android
dari program C dan disertai dengan file Android.bp
di sumber Android
hierarki. Sistem build menyimpan objek yang dihasilkan di /system/etc/bpf
, dan
objek tersebut menjadi bagian dari {i>image<i} sistem.
Format program eBPF C 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 license
Dalam hal ini:
name_of_my_map
adalah nama variabel peta Anda. Ini nama menginformasikan tipe peta yang akan dibuat dan jenis peta yang akan dibuat ke loader BPF parameter. Definisi struct ini disediakan oleh headerbpf_helpers.h
yang disertakan.PROGTYPE/PROGNAME
merepresentasikan jenis program dan nama program. Jenis program dapat berupa apa saja yang tercantum dalam daftar tabel berikut. Ketika jenis program tidak tercantum, tidak ada penamaan yang ketat yang sesuai untuk program ini; nama hanya perlu diketahui oleh proses yang melampirkan program.PROGFUNC
adalah fungsi yang, saat dikompilasi, ditempatkan di bagian file yang dihasilkan.
kprobe | Menghubungkan PROGFUNC ke instruksi kernel menggunakan
infrastruktur kprobe. PROGNAME harus berupa nama fungsi kernel
yang sedang di-kprobe. Lihat dokumentasi kernel kprobe untuk informasi selengkapnya tentang kprobe.
|
---|---|
{i>tracepoint<i} | Mengaitkan PROGFUNC ke tracepoint. PROGNAME harus
dengan 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 rekaman aktivitas, dan sched_switch adalah nama
peristiwa rekaman aktivitas. Memeriksa kernel peristiwa rekaman aktivitas
dokumentasi untuk informasi selengkapnya tentang tracepoint.
|
skfilter | Program berfungsi sebagai filter soket jaringan. |
schedcls | Program berfungsi sebagai pengklasifikasi traffic jaringan. |
{i>cgroupskb<i}, {i>cgroupsock<i} | Program berjalan setiap kali proses dalam CGroup membuat AF_INET atau AF_INET6 . |
Jenis tambahan dapat ditemukan di Loader kode sumber Anda.
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
disertakan ke peristiwa rekaman aktivitas sched:sched_switch
. Untuk 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 LISENSI digunakan untuk memverifikasi apakah program kompatibel dengan
lisensi {i>kernel<i} ketika program tersebut memanfaatkan
fungsi {i>help <i}BPF yang disediakan oleh
{i>kernel<i}. Menentukan 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
Buat entri di file Android.bp
project. Misalnya, untuk
mem-build program C eBPF bernama bpf_test.c
, buat entri
berikut dalam 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 secara otomatis
memuat semua objek eBPF dari
/system/etc/bpf/
, membuat peta yang diperlukan program, dan menyematkan elemen yang dimuat
dengan petanya
ke sistem file BPF. File ini kemudian dapat digunakan untuk
interaksi lebih lanjut dengan program eBPF atau membaca peta. Bagian ini
menjelaskan konvensi yang digunakan untuk menamai
file-file ini dan lokasinya di
{i>sysfs<i}.
File berikut dibuat dan disematkan:
Untuk semua program yang dimuat, dengan asumsi
PROGNAME
adalah nama program danFILENAME
adalah nama file C eBPF, loader Android yang membuat dan menyematkan setiap program di/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME
.Misalnya, untuk contoh tracepoint
sched_switch
sebelumnya dimyschedtp.c
, file program dibuat dan disematkan ke/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch
.Untuk setiap peta yang dibuat, dengan asumsi
MAPNAME
adalah nama peta danFILENAME
adalah nama file C eBPF, loader Android yang membuat dan menyematkan setiap peta ke/sys/fs/bpf/map_FILENAME_MAPNAME
.Misalnya, untuk contoh tracepoint
sched_switch
sebelumnya dimyschedtp.c
, file peta akan 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/bpf
yang disematkan. Deskriptor file ini dapat digunakan untuk operasi, seperti membaca peta atau melampirkan program ke {i>tracepoint<i}.
Library BPF Android
Library BPF Android diberi nama libbpf_android.so
dan merupakan bagian dari sistem
gambar. Library ini memberikan kemampuan eBPF tingkat rendah yang diperlukan pengguna
untuk membuat dan membaca peta, serta membuat pemeriksaan, tracepoint, dan buffer performa.
Melampirkan program ke tracepoint
Program {i>tracepoint<i} dimuat secara otomatis saat {i>booting<i}. Setelah dimuat, program tracepoint harus diaktifkan menggunakan langkah-langkah berikut:
- Panggil
bpf_obj_get()
untuk mendapatkan programfd
dari file yang disematkan lokasi HTTP/HTTPS. Untuk informasi selengkapnya, lihat File yang tersedia di sysfs. - Panggil
bpf_attach_tracepoint()
di library BPF, dengan meneruskan programfd
dan nama tracepoint.
Contoh kode berikut menunjukkan cara menambahkan tracepoint sched_switch
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 kunci dan struktur nilai kompleks arbitrer. Tujuan
Library BPF Android menyertakan class android::BpfMap
yang menggunakan C++
template untuk membuat instance BpfMap
berdasarkan kunci dan jenis 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
template memungkinkan Anda menentukan BpfMap
kustom
cocok untuk peta tertentu. Peta kemudian dapat diakses menggunakan
fungsi yang dibuat khusus, yang peka terhadap jenisnya, sehingga menghasilkan kode yang lebih bersih.
Untuk informasi selengkapnya tentang BpfMap
, lihat
sumber Android.
Men-debug masalah
Selama waktu booting, beberapa pesan yang terkait dengan pemuatan BPF akan dicatat. Jika
proses pemuatan gagal karena alasan apa pun, pesan log mendetail akan diberikan
di logcat. Memfilter log logcat berdasarkan bpf
akan mencetak semua pesan dan
error mendetail selama waktu muat, seperti error pemverifikasi eBPF.
Contoh eBPF di Android
Program berikut di AOSP memberikan contoh tambahan penggunaan eBPF:
eBPF C
netd
program digunakan oleh {i>daemon<i} jaringan (netd) di Android untuk berbagai tujuan seperti seperti pemfilteran soket dan pengumpulan statistik. Untuk melihat bagaimana program ini digunakan, periksa traffic eBPF memantau sumber.eBPF C
time_in_state
program menghitung jumlah waktu yang dihabiskan aplikasi Android pada Frekuensi CPU, yang digunakan untuk menghitung daya.Di Android 12, program eBPF C
gpu_mem
melacak total penggunaan memori GPU untuk setiap proses dan untuk seluruh sistem. Program ini digunakan untuk pembuatan profil memori GPU.