Genişletilmiş Berkeley Paket Filtresi (eBPF), bir çekirdek içi sanal makinedir.
çekirdek işlevlerini artırmak için kullanıcı tarafından sağlanan eBPF programlarını çalıştırır. 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 dahili bileşenleri ve mimarisi hakkında daha fazla bilgiyi Brendan'da bulabilirsiniz. Gregg'in eBPF sayfası.
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üklendi. 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ç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ükleyicisine oluşturulacak harita türünü ve kullanılacak harita türünü bildirir parametreleridir. Bu yapı tanımı, dahil edilenbpf_helpers.h
üstbilgisi tarafından sağlanır.PROGTYPE/PROGNAME
, programın türünü temsil eder ve program adı. Program türü, aşağıdaki tabloda bulabilirsiniz. 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 yerleştirilen elde edilecek dosyanın bir bölümüne yerleştirilmelidir.
kprobe | PROGFUNC öğesini,
kprobe altyapısı var. PROGNAME , çekirdeğin adı olmalıdır
fonksiyonunun kprobunu yapılır. Kprobe'ler 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, izleme noktası bölümü
planlayıcı bağlam değiştirme etkinliklerine işlev eklemek için
SEC("tracepoint/sched/sched_switch") , burada sched :
izleme alt sisteminin adı, sched_switch ise addır
izleme etkinliğinden kaynaklanı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 | Ağ trafiği sınıflandırıcısı olarak programlama işlevleri. |
cgroupskb, cgroupsock | Bir CGroup işlemleri AF_INET veya AF_INET6 oluşturduğunda program çalışır soket. |
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 amacına ulaşıyor
harita oluşturup oluşturabileceğiniz bir tp_sched_switch
sched:sched_switch
izleme etkinliğine ekli. 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");
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 lisansınızın adını aşağıdaki gibi bir dize biçiminde belirtin:
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 nesneyle oluşturur
/system/etc/bpf/bpf_test.o
Android sistemi başlatıldığında otomatik olarak yüklenir
bpf_test.o
programını çekirdeğe aktarmanızı sağlar.
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:
Programın adı
PROGNAME
olduğu varsayıldığında, yüklenen tüm programlar içinFILENAME
, eBPF C dosyasının adıdır. Android yükleyicisi, her programı/sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME
konumuna sabitler.Örneğin, önceki
sched_switch
izleme noktası örneği içinmyschedtp.c
, bir program dosyası oluşturuldu ve şu konuma sabitlendi:/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch
.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, önceki
sched_switch
izleme noktası örneği içinmyschedtp.c
, bir harita dosyası oluşturuldu ve şu konuma sabitlendi:/sys/fs/bpf/map_myschedtp_cpu_pid_map
.Android BPF kitaplığındaki
bpf_obj_get()
,/sys/fs/bpf
dosya sabitlendi. 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ığı
Sistemin bir parçası olan Android BPF kitaplığının adı libbpf_android.so
şeklindedir
görüntüsüdür. Bu kitaplık, kullanıcıya gereken düşük düzey eBPF özelliklerini sunar.
harita oluşturmak ve okumak, sondalar, izleme noktaları ve perf tamponları oluşturmak için geliştirilmiştir.
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.
Böylece şablon haline getirilmiş BpfMap
sınıfı özel bir BpfMap
tanımlamanıza olanak tanır.
söz konusu harita için uygun bir nesnedir. 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ı.
Hata ayıklama sorunları
Başlatma süresinde, BPF yüklemesi ile ilgili çeşitli mesajlar günlüğe kaydedilir. Öğe
Yükleme işlemi herhangi bir nedenle başarısız olursa ayrıntılı bir günlük mesajı verilir.
inceleyebilirsiniz. Logcat günlüklerini bpf
etiketine göre filtrelemek, tüm iletileri
Yükleme süresinde eBPF doğrulayıcı hataları gibi ayrıntılı hatalar.
Android'de eBPF örnekleri
AOSP'deki aşağıdaki programlar, eBPF'nin kullanımına dair ek örnekler sağlar:
netd
eBPF C programına Android'deki ağ arka plan programı (netd) tarafından çeşitli amaçlar için kullanılır. Örneğin: ve istatistik toplama gibi işlevlere sahiptir. Programın nasıl kullanıldığını görmek için eBPF trafiğini kontrol edin izleme kaynakları.time_in_state
eBPF C programına bir Android uygulamasının farklı bir ekranda geçirdiği süreyi hesaplar Gücü hesaplamak için kullanılan CPU frekansları.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.