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 durum kontrollerine veya etkinliklere bağlanabilir ve faydalı çekirdek istatistiklerini toplama, izleme ve hata ayıklama 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 dahili 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ükleyici ve kitaplığı içerir.
Android BPF yükleyici
Android başlatma sırasında, /system/etc/bpf/
konumunda bulunan tüm eBPF programları yüklenir. Bu programlar, C programlarından Android derleme sistemi tarafından oluşturulan ikili nesnelerdir ve Android kaynak ağacındaki Android.bp
dosyalarıyla birlikte sunulur. 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çimde 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. Program türü belirtilmemişse program için kesin bir adlandırma kuralı yoktur. Adın yalnızca programı ekleyen işlem tarafından bilinmesi gerekir.PROGFUNC
, derlendiğinde elde edilen dosyanın bir bölümüne yerleştirilen bir işlevdir.
kprobe | kprobe altyapısını kullanarak PROGFUNC 'ü bir çekirdek talimatına ekler. PROGNAME , kprobe yapılan çekirdek işlevinin adıdır. Kprobe'lar hakkında daha fazla bilgi için kprobe çekirdek dokümanlarına bakın.
|
---|---|
izleme noktası | PROGFUNC öğesini bir izleme noktasına bağlar. 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. |
Schedcl'ler | Program, ağ trafiği sınıflandırıcısı olarak çalışır. |
cgroupskb, cgroupsock | Bir CGroup işlemleri her AF_INET veya AF_INET6 yuvası oluşturduğunda program çalışır. |
Diğer türleri Loader kaynak kodunda bulabilirsiniz.
Örneğin, aşağıdaki myschedtp.c
programı belirli bir CPU'da çalıştırılan 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şlevlerinden yararlandığında programın çekirdek 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.
Syfs'ta kullanılabilen 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 kabul edilir) Android yükleyici her programı/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME
adresinde oluşturur ve sabitler.Örneğin,
myschedtp.c
bölgesindeki öncekisched_switch
izleme noktası örneğinde, bir program dosyası oluşturulmuş ve/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch
konumuna sabitlenmiştir.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üklemenin ardından izleme noktası programı şu 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ğini 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, şablonlu BpfMap
sınıfı belirli bir harita için uygun olan özel bir BpfMap
nesnesi tanımlamanızı sağlar. 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.
Sorunları ayıklama
Başlatma süresinde, BPF yüklemesi ile 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 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ı, bir Android uygulamasının farklı CPU frekanslarında geçirdiği süreyi hesaplar. 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.