Genişletilmiş Berkeley Paket Filtresi (eBPF), çekirdek işlevini 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 problara veya etkinliklere bağlanabilir ve yararlı çekirdek istatistikleri toplamak, izlemek ve hata ayıklama yapmak için kullanılabilir. Bir program, bpf(2)
sistem çağrısı kullanılarak çekirdeğe yüklenir ve kullanıcı tarafından eBPF makine talimatlarının ikili bir blob'u olarak sağlanır. Android derleme sistemi, bu dokümanda açıklanan basit derleme dosyası söz dizimini kullanarak C programlarını eBPF'ye derleme desteğine sahiptir.
eBPF'nin iç yapısı 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ükleyici ve kitaplığı içerir.
Android BPF yükleyici
Android önyüklemesi sırasında /system/etc/bpf/
adresinde 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 bulunur. Derleme sistemi, oluşturulan nesneleri /system/etc/bpf
konumunda depolar ve bu nesneler sistem görüntüsünün bir parçası olur.
Android eBPF C programının biçimi
eBPF C programı aşağıdaki biçime sahip 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 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
Nerede:
name_of_my_map
, harita değişkeninizin adıdır. Bu ad, BPF yükleyiciye oluşturulacak harita türünü ve hangi parametrelerle oluşturulacağını bildirir. Bu yapı tanımı, dahil edilenbpf_helpers.h
üstbilgisi tarafından sağlanır.PROGTYPE/PROGNAME
, programın türünü ve program adını temsil eder. Program türü, aşağıdaki tabloda listelenenlerden herhangi biri olabilir. Listelenmeyen program türleri için katı bir adlandırma kuralı yoktur. Programın, programa ekleyen işlem tarafından bilinmesi yeterlidir.PROGFUNC
, derlendiğinde ortaya çıkan dosyanın bir bölümüne yerleştirilen bir işlevdir.
kprobe | kprobe altyapısını kullanarak PROGFUNC 'yi bir çekirdek talimatına ekler. PROGNAME , kprobe yapılan çekirdek işlevinin adıdır. Kprobe'ler hakkında daha fazla bilgi için kprobe çekirdek belgelerine bakın.
|
---|---|
izleme noktası | PROGFUNC öğesini bir izleme noktasına ekler. PROGNAME , SUBSYSTEM/EVENT biçiminde olmalıdır. Örneğin, işlevleri planlayıcı bağlam anahtarı değiştirme etkinliklerine eklemek için bir izleme noktası bölümü SEC("tracepoint/sched/sched_switch") olur. Burada sched , izleme alt sisteminin adı, sched_switch ise izleme etkinliğinin adıdır. İzleme noktaları hakkında daha fazla bilgi için izleme etkinlikleri çekirdek belgelerini inceleyin.
|
skfilter | Program, ağ soket filtresi işlevi görür. |
schedcls | Program, ağ trafiği sınıflandırıcısı olarak çalışır. |
cgroupskb, cgroupsock | Program, bir CGroup'daki işlemler AF_INET veya AF_INET6 soketi oluşturduğunda çalışır. |
Diğer türleri Yükleyici kaynak kodunda bulabilirsiniz.
Örneğin, aşağıdaki myschedtp.c
programı belirli bir CPU'da çalıştırılan en son görev PID'si hakkında bilgi ekler. Bu program, bir harita oluşturarak ve sched:sched_switch
izleme etkinliğine eklenebilecek bir tp_sched_switch
işlevi tanımlayarak hedefine ulaşır. Daha fazla bilgi için Programları izleme noktalarına ekleme başlıklı makaleyi inceleyin.
#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 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ınızın lisansının adını dize biçiminde belirtin (ör. LICENSE("GPL")
veya LICENSE("Apache 2.0")
).
Android.bp dosyasının biçimi
Android derleme sisteminin eBPF .c
programı oluşturması için projenin Android.bp
dosyasında bir giriş oluşturmanız gerekir. Örneğin, bpf_test.c
adlı 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 giriş, C programını derleyerek /system/etc/bpf/bpf_test.o
nesnesini oluşturur. Android sistemi, önyükleme sırasında bpf_test.o
programını çekirdeğe otomatik olarak yükler.
sysfs'te bulunan dosyalar
Android sistemi, önyükleme sırasında /system/etc/bpf/
içindeki tüm eBPF nesnelerini otomatik olarak yükler, programın ihtiyaç duyduğu haritaları oluşturur ve yüklü 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ın adlandırması için kullanılan kurallar ve sysfs'teki konumları açıklanmaktadır.
Aşağıdaki dosyalar oluşturulur ve sabitlenir:
Yüklenen tüm programlar için (
PROGNAME
programın adı,FILENAME
ise eBPF C dosyasının adı olarak varsayılır) Android yükleyici her programı/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME
adresinde oluşturur ve sabitler.Örneğin,
myschedtp.c
'daki öncekisched_switch
izleme noktası örneği için bir program dosyası oluşturulur ve/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch
'ye sabitlenir.Oluşturulan tüm haritalar için (
MAPNAME
haritanın adı,FILENAME
ise eBPF C dosyasının adı olarak varsayılır) Android yükleyici her haritayı oluşturup/sys/fs/bpf/map_FILENAME_MAPNAME
'ye sabitler.Örneğin,
myschedtp.c
'daki öncekisched_switch
izleme noktası örneği için bir harita dosyası oluşturulur ve/sys/fs/bpf/map_myschedtp_cpu_pid_map
'ye sabitlenir.Android BPF kitaplığındaki
bpf_obj_get()
, sabitlenmiş/sys/fs/bpf
dosyasından bir dosya tanımlayıcısı döndürür. Bu dosya tanımlayıcısı, haritaları okuma veya bir programı bir izleme noktasına ekleme gibi diğer 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 harita oluşturmak ve okumak, prob, izleme noktası ve performans arabelleği oluşturmak için gereken düşük düzey eBPF özelliklerini sağlar.
Programları izleme noktalarına ekleme
İzleme noktası programları, önyükleme sırasında otomatik olarak yüklenir. Yüklendikten sonra, izleme noktası programı aşağıdaki adımlar uygulanarak etkinleştirilmelidir:
- Sabitlenmiş dosyanın konumundan
fd
programını almak içinbpf_obj_get()
numaralı telefonu arayın. Daha fazla bilgi için sysfs'te bulunan dosyalar başlıklı makaleyi inceleyin. - BPF kitaplığında
bpf_attach_tracepoint()
işlevini çağırarak programafd
ve izleme noktası adını iletin.
Aşağıdaki kod örneğinde, önceki myschedtp.c
kaynak dosyasında tanımlanan sched_switch
izleme noktasının nasıl ekleneceği gösterilmektedir (hata kontrolü 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ı, keyfi karmaşık anahtar ve değer yapılarını veya türlerini destekler. Android BPF kitaplığı, söz konusu haritanın anahtar ve değer türüne göre BpfMap
örneği oluşturmak için C++ şablonlarından yararlanan bir android::BpfMap
sınıfı içerir. Önceki kod örneğinde, anahtar ve değer tam sayı olarak bir BpfMap
kullanımı gösterilmektedir. Tam sayılar, rastgele yapılar da olabilir.
Bu nedenle şablonlaştırılmış BpfMap
sınıfı, belirli bir haritaya uygun özel bir BpfMap
nesnesi tanımlamanıza olanak tanır. Daha sonra, özel olarak oluşturulmuş işlevler kullanılarak haritaya erişilebilir. Bu işlevler tür bilincine sahip olduğundan daha temiz bir kod elde edilir.
BpfMap
hakkında daha fazla bilgi için Android kaynaklarını inceleyin.
Hataları ayıklama
Açılış sırasında BPF yüklemeyle ilgili birkaç mesaj günlüğe kaydedilir. Yükleme işlemi herhangi bir nedenle başarısız olursa logcat'te ayrıntılı bir günlük mesajı sağlanır. Logcat günlüklerini bpf
olarak filtrelemek, tüm mesajları ve yükleme sırasındaki ayrıntılı hataları (ör. eBPF doğrulayıcı hataları) yazdırır.
Android'de eBPF örnekleri
AOSP'deki aşağıdaki programlar, eBPF'nin kullanımına dair ek örnekler sağlar:
netd
eBPF C programı, Android'deki ağ 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 izleyici kaynaklarını kontrol edin.time_in_state
eBPF C programında, bir Android uygulamasının farklı CPU frekanslarında geçirdiği süre hesaplanır. Bu süre, gücü hesaplamak için kullanılır.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 profilleme için kullanılır.