Genişletilmiş Berkeley Paket Filtresi (eBPF), çekirdek işlevselliğini genişletmek için kullanıcı tarafından sağlanan eBPF programlarını çalıştıran çekirdek içi bir sanal makinedir. Bu programlar çekirdekteki araştırmalara veya olaylara bağlanabilir ve yararlı çekirdek istatistikleri toplamak, izlemek ve hata ayıklamak için kullanılabilir. Bir program bpf(2)
sistem çağrısını kullanarak çekirdeğe yüklenir ve kullanıcı tarafından eBPF makine talimatlarının ikili bloğu olarak sağlanır. Android derleme sistemi, bu belgede açıklanan basit derleme dosyası sözdizimini kullanarak C programlarını eBPF'ye derleme desteğine sahiptir.
eBPF'nin iç bileşenleri ve mimarisi hakkında daha fazla bilgiyi Brendan Gregg'in eBPF sayfasında bulabilirsiniz.
Android, eBPF programlarını önyükleme sırasında yükleyen bir eBPF yükleyicisi ve kitaplığı içerir.
Android BPF yükleyici
Android önyüklemesi sırasında /system/etc/bpf/
konumunda bulunan tüm eBPF programları yüklenir. Bu programlar, Android derleme sistemi tarafından C programlarından oluşturulan ikili nesnelerdir ve Android kaynak ağacında Android.bp
dosyalarıyla birlikte sunulur. Yapı sistemi, oluşturulan nesneleri /system/etc/bpf
konumunda saklar ve bu nesneler sistem görüntüsünün parçası haline gelir.
Android eBPF C programının formatı
Bir eBPF C programı aşağıdaki formatta olmalıdır:
#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 will also define 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
Nerede:
-
name_of_my_map
harita değişkeninizin adıdır. Bu ad, BPF yükleyicisine haritanın türü ve hangi parametrelerle oluşturulacağı konusunda bilgi verir. Bu yapı tanımı, dahil edilenbpf_helpers.h
başlığı tarafından sağlanır. PROGTYPE/PROGNAME
programın türünü ve program adını temsil eder. Programın türü aşağıdaki tabloda listelenenlerden herhangi biri olabilir. Bir program türü listelenmediğinde, program için kesin bir adlandırma kuralı yoktur; adın yalnızca programı ekleyen süreç tarafından bilinmesi gerekir.PROGFUNC
, derlendiğinde ortaya çıkan dosyanın bir bölümüne yerleştirilen bir işlevdir.
kprobe | PROGFUNC kprobe altyapısını kullanarak bir çekirdek talimatına bağlar. PROGNAME kproblanan çekirdek fonksiyonunun adı olmalıdır. Kprobe'lar hakkında daha fazla bilgi için kprobe çekirdek belgelerine bakın. |
---|---|
izleme noktası | PROGFUNC bir izleme noktasına bağlar. PROGNAME ALT SUBSYSTEM/EVENT biçiminde olmalıdır. Örneğin, zamanlayıcı bağlam anahtarı olaylarına işlevler eklemek için bir izleme noktası bölümü SEC("tracepoint/sched/sched_switch") olacaktır; burada sched , izleme alt sisteminin adıdır ve sched_switch , izleme olayının adıdır. İzleme noktaları hakkında daha fazla bilgi için izleme olayları çekirdeği belgelerine bakın. |
sk filtresi | Program bir ağ soketi filtresi olarak işlev görür. |
programlar | Program bir ağ trafiği sınıflandırıcısı olarak işlev görür. |
cgroupskb, cgroupsock | Program, bir CGruptaki işlemler bir AF_INET veya AF_INET6 soketi oluşturduğunda çalışır. |
Ek türler Yükleyici kaynak kodunda bulunabilir.
Örneğin, aşağıdaki myschedtp.c
programı, belirli bir CPU üzerinde çalışan en son görev PID'si hakkında bilgi ekler. Bu program amacına bir harita oluşturarak ve sched:sched_switch
trace olayına eklenebilecek bir tp_sched_switch
fonksiyonu tanımlayarak ulaşır. Daha fazla bilgi için bkz. İzleme noktalarına program ekleme .
#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");
LİSANS makrosu, program çekirdek tarafından sağlanan BPF yardımcı işlevlerini kullandığında programın çekirdeğin lisansıyla uyumlu olup olmadığını doğrulamak için kullanılır. Program lisansınızın adını LICENSE("GPL")
veya LICENSE("Apache 2.0")
gibi dize biçiminde belirtin.
Android.bp dosyasının biçimi
Android derleme sisteminin bir eBPF .c
programı oluşturması için projenin Android.bp
dosyasında bir giriş oluşturmanız gerekir. Örneğin, bpf_test.c
adında bir eBPF C programı oluşturmak için projenizin Android.bp
dosyasına aşağıdaki girişi yapın:
bpf { name: "bpf_test.o", srcs: ["bpf_test.c"], cflags: [ "-Wall", "-Werror", ], }
Bu girdi, /system/etc/bpf/bpf_test.o
nesnesiyle sonuçlanan C programını derler. Önyükleme sırasında Android sistemi bpf_test.o
programını çekirdeğe otomatik olarak yükler.
Sysfs'de bulunan dosyalar
Önyükleme sırasında, Android sistemi tüm eBPF nesnelerini /system/etc/bpf/
adresinden otomatik olarak yükler, programın ihtiyaç duyduğu haritaları oluşturur ve yüklenen programı haritalarıyla birlikte BPF dosya sistemine sabitler. Bu dosyalar daha sonra eBPF programıyla daha fazla etkileşim kurmak veya haritaları okumak için kullanılabilir. Bu bölümde bu dosyaları adlandırmak için kullanılan kurallar ve bunların sysfs'deki konumları açıklanmaktadır.
Aşağıdaki dosyalar oluşturulur ve sabitlenir:
Yüklenen tüm programlar için
PROGNAME
programın adı veFILENAME
eBPF C dosyasının adı olduğunu varsayarsak, Android yükleyici her programı/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME
konumunda oluşturur ve sabitler.Örneğin,
myschedtp.c
dosyasındaki öncekisched_switch
izleme noktası örneği için, bir program dosyası oluşturulur ve/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch
dosyasına sabitlenir.Oluşturulan tüm haritalar için,
MAPNAME
haritanın adı veFILENAME
eBPF C dosyasının adı olduğunu varsayarsak, Android yükleyici her haritayı oluşturur ve/sys/fs/bpf/map_FILENAME_MAPNAME
sabitler.Örneğin,
myschedtp.c
dosyasındaki öncekisched_switch
izleme noktası örneği için bir harita dosyası oluşturulur ve/sys/fs/bpf/map_myschedtp_cpu_pid_map
dosyasına sabitlenir.Android BPF kitaplığındaki
bpf_obj_get()
sabitlenmiş/sys/fs/bpf
dosyasından bir dosya tanımlayıcı döndürür. Bu dosya tanımlayıcı, haritaları okumak veya bir programı bir izleme noktasına eklemek gibi daha ileri işlemler için kullanılabilir.
Android BPF kitaplığı
Android BPF kitaplığı libbpf_android.so
olarak adlandırılır ve sistem görüntüsünün bir parçasıdır. Bu kitaplık, kullanıcıya haritalar oluşturmak ve okumak, problar, izleme noktaları ve mükemmel tamponlar oluşturmak için gereken düşük seviyeli eBPF işlevselliğini sağlar.
Programları izleme noktalarına ekleme
Tracepoint programları açılışta otomatik olarak yüklenir. Yüklemeden sonra izleme noktası programı aşağıdaki adımlar kullanılarak etkinleştirilmelidir:
- Sabitlenmiş dosyanın konumundan
fd
programını almak içinbpf_obj_get()
ı çağırın. Daha fazla bilgi için sysfs'de bulunan Dosyalara bakın. - BPF kitaplığında
bpf_attach_tracepoint()
çağırın ve onafd
programını ve izleme noktası adını iletin.
Aşağıdaki kod örneği, önceki myschedtp.c
kaynak dosyasında tanımlanan sched_switch
izleme noktasının nasıl ekleneceğini gösterir (hata denetimi gösterilmemiştir):
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));
Haritalardan okuma
BPF haritaları rastgele karmaşık anahtar ve değer yapılarını veya türlerini destekler. Android BPF kitaplığı, söz konusu harita için anahtar ve değer türüne göre BpfMap
başlatmak için C++ şablonlarından yararlanan bir android::BpfMap
sınıfı içerir. Önceki kod örneği, anahtar ve değerin tamsayı olarak kullanıldığı bir BpfMap
kullanımını göstermektedir. Tamsayılar aynı zamanda keyfi yapılar da olabilir.
Böylece şablonlaştırılmış BpfMap
sınıfı, belirli bir haritaya uygun özel bir BpfMap
nesnesinin tanımlanmasını kolaylaştırır. Daha sonra haritaya, tür farkında olan ve daha temiz kod sağlayan özel olarak oluşturulmuş işlevler kullanılarak erişilebilir.
BpfMap
hakkında daha fazla bilgi için Android kaynaklarına bakın.
Hata ayıklama sorunları
Önyükleme sırasında BPF yüklemesiyle ilgili çeşitli mesajlar günlüğe kaydedilir. Yükleme işlemi herhangi bir nedenle başarısız olursa logcat'te ayrıntılı bir log mesajı sağlanır. Logcat günlüklerini "bpf" ile filtrelemek, tüm mesajları ve eBPF doğrulayıcı hataları gibi yükleme süresi sırasındaki tüm ayrıntılı hataları yazdırır.
Android'deki eBPF örnekleri
AOSP'deki aşağıdaki programlar, eBPF kullanımına ilişkin ek örnekler sağlar:
netd
eBPF C programı, Android'deki ağ oluşturma arka plan programı (netd) tarafından soket filtreleme ve istatistik toplama gibi çeşitli amaçlar için kullanılır. Bu programın nasıl kullanıldığını görmek için eBPF trafik izleme kaynaklarını kontrol edin.time_in_state
eBPF C programı, bir Android uygulamasının gücü hesaplamak için kullanılan farklı CPU frekanslarında harcadığı süreyi hesaplar.Android 12'de
gpu_mem
eBPF C programı , her işlem ve sistemin tamamı için toplam GPU bellek kullanımını izler. Bu program GPU bellek profili oluşturmak için kullanılır.