Çekirdeği eBPF ile genişletme

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 edilen bpf_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çin FILENAME, 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çin myschedtp.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çin myschedtp.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:

  1. Sabitlenmiş dosyanın konumundan fd programını almak için bpf_obj_get() numaralı telefonu arayın. Daha fazla bilgi için sysfs'te bulunan dosyalar başlıklı makaleyi inceleyin.
  2. BPF kitaplığında bpf_attach_tracepoint() işlevini çağırarak programa fd 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.