Расширенный пакетный фильтр Беркли (eBPF) — это виртуальная машина в ядре, которая запускает пользовательские программы eBPF для расширения функциональных возможностей ядра. Эти программы можно подключить к зондам или событиям в ядре и использовать для сбора полезной статистики ядра, мониторинга и отладки. Программа загружается в ядро с помощью системного вызова bpf(2) и предоставляется пользователем в виде бинарного блока машинных инструкций eBPF. Система сборки Android поддерживает компиляцию программ C в eBPF с использованием простого синтаксиса файла сборки, описанного в этом документе.
Дополнительную информацию о внутреннем устройстве и архитектуре eBPF можно найти на странице eBPF Брендана Грегга .
Android включает в себя загрузчик eBPF и библиотеку, которая загружает программы eBPF во время загрузки.
Android-загрузчик BPF
 Во время загрузки Android загружаются все программы eBPF, расположенные в /system/etc/bpf/ . Эти программы представляют собой двоичные объекты, созданные системой сборки Android из программ C и сопровождаемые файлами Android.bp в дереве исходного кода Android. Система сборки сохраняет сгенерированные объекты в /system/etc/bpf , и эти объекты становятся частью образа системы.
Формат программы Android eBPF C
Программа eBPF C должна иметь следующий формат:
#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
Где:
-  name_of_my_map— это имя вашей переменной карты. Это имя информирует загрузчик BPF о типе создаваемой карты и параметрах. Это определение структуры предоставляется включенным заголовкомbpf_helpers.h.
- PROGTYPE/PROGNAMEпредставляет тип программы и имя программы. Тип программы может быть любым из перечисленных в следующей таблице. Если тип программы не указан, для программы не существует строгого соглашения об именах; имя просто должно быть известно процессу, который прикрепляет программу.
- PROGFUNC— это функция, которая при компиляции помещается в раздел результирующего файла.
| kprobe | PROGFUNCк инструкции ядра, используя инфраструктуру kprobe.PROGNAMEдолжно быть именем функции ядра, для которой выполняется kprobed. Обратитесь к документации ядра kprobe для получения дополнительной информации о kprobes. | 
|---|---|
| точка трассировки | PROGFUNCк точке трассировки.PROGNAMEдолжен иметь форматSUBSYSTEM/EVENT. Например, раздел точки трассировки для присоединения функций к событиям переключения контекста планировщика будет иметьSEC("tracepoint/sched/sched_switch"), гдеsched— имя подсистемы трассировки, аsched_switch— имя события трассировки. Дополнительные сведения о точках трассировки см. в документации по ядру событий трассировки. | 
| скфильтр | Программа работает как фильтр сетевых сокетов. | 
| расписание | Программа выполняет функции классификатора сетевого трафика. | 
| cgroupskb, cgroupsock | Программа запускается всякий раз, когда процессы в CGroup создают сокет AF_INET или AF_INET6. | 
Дополнительные типы можно найти в исходном коде загрузчика .
 Например, следующая программа myschedtp.c добавляет информацию о последнем PID задачи, запущенной на конкретном ЦП. Эта программа достигает своей цели, создавая карту и определяя функцию tp_sched_switch , которую можно присоединить к событию трассировки sched:sched_switch . Дополнительные сведения см. в разделе Присоединение программ к точкам трассировки .
#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 используется для проверки совместимости программы с лицензией ядра, когда программа использует вспомогательные функции BPF, предоставляемые ядром. Укажите название лицензии вашей программы в строковой форме, например LICENSE("GPL") или LICENSE("Apache 2.0") .
Формат файла Android.bp
 Чтобы система сборки Android могла создать программу eBPF .c , необходимо создать запись в файле Android.bp проекта. Например, чтобы создать программу eBPF C с именем bpf_test.c , сделайте следующую запись в файле Android.bp вашего проекта:
bpf {
    name: "bpf_test.o",
    srcs: ["bpf_test.c"],
    cflags: [
        "-Wall",
        "-Werror",
    ],
}
 Эта запись компилирует программу C, в результате чего получается объект /system/etc/bpf/bpf_test.o . При загрузке система Android автоматически загружает в ядро программу bpf_test.o .
Файлы доступны в sysfs
 Во время загрузки система Android автоматически загружает все объекты eBPF из /system/etc/bpf/ , создает карты, которые нужны программе, и прикрепляет загруженную программу со своими картами к файловой системе BPF. Эти файлы затем можно использовать для дальнейшего взаимодействия с программой eBPF или чтения карт. В этом разделе описываются соглашения, используемые для именования этих файлов и их расположения в sysfs.
Создаются и закрепляются следующие файлы:
- Для любых загруженных программ, предполагая, что - PROGNAME— это имя программы, а- FILENAME— это имя файла eBPF C, загрузчик Android создает и закрепляет каждую программу в- /sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME.- Например, для предыдущего примера точки - sched_switchв- myschedtp.cсоздается программный файл, который прикрепляется к- /sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch.
- Для любых созданных карт, предполагая, что - MAPNAME— это имя карты, а- FILENAME— это имя файла eBPF C, загрузчик Android создает и прикрепляет каждую карту к- /sys/fs/bpf/map_FILENAME_MAPNAME.- Например, для предыдущего примера точки - sched_switchв- myschedtp.cфайл карты создается и прикрепляется к- /sys/fs/bpf/map_myschedtp_cpu_pid_map.
- bpf_obj_get()в библиотеке Android BPF возвращает дескриптор файла из закрепленного файла- /sys/fs/bpf. Этот файловый дескриптор можно использовать для дальнейших операций, таких как чтение карт или присоединение программы к точке трассировки.
Библиотека Android BPF
 Библиотека Android BPF называется libbpf_android.so и является частью образа системы. Эта библиотека предоставляет пользователю низкоуровневую функциональность eBPF, необходимую для создания и чтения карт, создания зондов, точек трассировки и буферов производительности.
Прикрепление программ к точкам трассировки
Программы tracepoint загружаются автоматически при загрузке. После загрузки программу tracepoint необходимо активировать, выполнив следующие действия:
-  Вызовите bpf_obj_get(), чтобы получить программуfdиз расположения закрепленного файла. Для получения дополнительной информации обратитесь к файлам, доступным в sysfs .
-  Вызовите bpf_attach_tracepoint()в библиотеке BPF, передав ей программуfdи имя точки трассировки.
 В следующем примере кода показано, как подключить sched_switch , определенную в предыдущем исходном файле myschedtp.c (проверка ошибок не показана):
  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));
Чтение с карт
 Карты BPF поддерживают произвольные сложные структуры или типы ключей и значений. Библиотека Android BPF включает класс android::BpfMap , который использует шаблоны C++ для создания экземпляра BpfMap на основе ключа и типа значения для рассматриваемой карты. В предыдущем примере кода показано использование BpfMap с ключом и значением в виде целых чисел. Целые числа также могут быть произвольными структурами.
 Таким образом, шаблонный класс BpfMap упрощает определение пользовательского объекта BpfMap подходящего для конкретной карты. Затем к карте можно получить доступ с помощью специально созданных функций, которые учитывают типы, что приводит к более чистому коду.
 Дополнительные сведения о BpfMap см. в источниках для Android .
Проблемы отладки
Во время загрузки регистрируется несколько сообщений, связанных с загрузкой BPF. Если процесс загрузки по какой-либо причине завершается сбоем, в logcat отображается подробное сообщение журнала. Фильтрация журналов logcat с помощью «bpf» выводит все сообщения и все подробные ошибки во время загрузки, например ошибки проверки eBPF.
Примеры eBPF в Android
Следующие программы в AOSP предоставляют дополнительные примеры использования eBPF:
- Программа - netdeBPF C используется сетевым демоном (netd) в Android для различных целей, таких как фильтрация сокетов и сбор статистики. Чтобы увидеть, как используется эта программа, проверьте источники монитора трафика eBPF .
- Программа - time_in_stateeBPF C вычисляет количество времени, которое приложение Android проводит на разных частотах ЦП, что используется для расчета мощности.
- В Android 12 программа - gpu_memeBPF C отслеживает общее использование памяти графического процессора для каждого процесса и для всей системы. Эта программа используется для профилирования памяти графического процессора.
