Trang này cung cấp thông tin tổng quan về khung đo từ xa của nhân Extended Berkeley Packet Filter (eBPF) trong Android, mô tả cấu trúc khung, các chỉ số tích hợp và cách thêm chỉ số nhân mới.
Android có một khung đo từ xa nhân eBPF hợp nhất để thu thập các chỉ số nhân có mức hao tổn thấp và hiển thị các chỉ số đó cho các dịch vụ không gian người dùng như statsd. Bằng cách đính kèm các chương trình eBPF di động vào các điểm theo dõi nhân và kprobe, khung này tổng hợp các sự kiện nhân có tần suất cao tại chỗ và xuất các biểu đồ hoặc bộ đếm nhỏ gọn mà không sửa đổi Hình ảnh nhân chung (GKI) hoặc làm gián đoạn Giao diện mô-đun nhân (KMI).
Các phương pháp truyền thống để đo từ xa hạt nhân dựa vào các nút procfs hoặc sysfs tuỳ chỉnh, các bản vá hạt nhân ngoài cây hoặc các trình nối của nhà cung cấp yêu cầu hệ thống ống nước tuỳ chỉnh trên hạt nhân, SELinux, Giao diện gốc Java (JNI) và các dịch vụ không gian người dùng.
Khung đo từ xa nhân eBPF chuẩn hoá quy trình này trên các Nhân Android phổ biến (ACK) và mang lại những lợi ích sau:
- Tránh tình trạng biến động ABI và KMI của nhân: Đính kèm các chương trình eBPF vào các điểm theo dõi nhân hoặc kprobe hiện có mà không cần sửa đổi mã nguồn nhân, thay đổi giao diện nhị phân của ứng dụng (ABI) nhân hoặc thêm các trình nối của nhà cung cấp Android như
TRACE_ANDROID_VH_*. Nhà sản xuất thiết bị và đối tác hệ thống trên chip (SoC) có thể sử dụng dữ liệu đo từ xa eBPF thay vì thêm các điểm nối mới của nhà cung cấp dữ liệu đo từ xa hoặc để di chuyển khỏi các điểm nối hiện có của nhà cung cấp. - Duy trì khả năng di chuyển giữa các phiên bản kernel: Sử dụng BPF Biên dịch một lần – Chạy mọi nơi (CO-RE) và Định dạng loại BPF (BTF) để một chương trình eBPF được biên dịch duy nhất tự động thích ứng với các thay đổi về bố cục cấu trúc kernel nội bộ trên các phiên bản GKI, chẳng hạn như
android14-6.1trở lên. - Giảm thiểu mức hao tổn cho tình trạng hệ thống: Lọc và tổng hợp các sự kiện nhân hệ điều hành tần suất cao ngay tại nguồn thành biểu đồ nhật ký cơ số 2 (
log2) gồm 32 nhóm nhỏ gọn, thường dưới 1 KB cho mỗi bản đồ BPF. Không gian người dùng truy xuất các mục nhập bản đồ tổng hợp mỗi ngày một lần bằng cách sử dụng một lệnh gọi hệ thống theo lôBPF_MAP_LOOKUP_BATCH. - Đơn giản hoá việc tích hợp SELinux và JNI: Ghim các bản đồ đo từ xa trong
/sys/fs/bpf/kernelstats/để áp dụng tính năng kế thừa loại SELinux ở cấp thư mụcfs_bpf_kernelstatsvà giải quyết các độ lệch thành phần cấu trúc tại thời gian chạy trong JNI bằng cách sử dụng siêu dữ liệu BTF để loại bỏ mã phân tích cú pháp C++ tuỳ chỉnh khi thêm các chỉ số mới.
Để biết thêm thông tin về cách Android tải và ghim các chương trình và bản đồ eBPF, hãy xem bài viết Mở rộng nhân bằng eBPF.
Tổng quan về cấu trúc
Quy trình đo từ xa của nhân eBPF tách biệt các thành phần cơ sở hạ tầng chung khỏi các định nghĩa dành riêng cho chỉ số, nhờ đó, việc thêm chỉ số nhân mới chỉ cần thay đổi mã ở mức tối thiểu. Bảng sau đây mô tả các thành phần của quy trình:
| Thành phần | Vai trò trong quy trình | Phạm vi |
|---|---|---|
Trình tải BPF, bpfloader | Tải các chương trình eBPF đã biên dịch từ /system/etc/bpf/kernelstats/ khi khởi động nếu tương thích với nhân hệ điều hành đang chạy và ghim bản đồ vào /sys/fs/bpf/kernelstats/. Bỏ qua việc tải một cách rõ ràng khi bị tắt hoặc không được hỗ trợ. | Thông tin chung |
Chính sách SELinux, fs_bpf_kernelstats | Áp dụng nhãn SELinux cấp thư mục cho /sys/fs/bpf/kernelstats/ để system_server có thể đọc bản đồ đo từ xa mà không cần cập nhật chính sách SELinux theo từng chỉ số. | Thông tin chung |
Chương trình eBPF trong system/bpfprogs/telemetry/ | Đính kèm vào các điểm theo dõi hạt nhân hoặc kprobe, lọc sự kiện và tổng hợp các chỉ số vào bản đồ BPF. | Tuỳ chỉnh theo chỉ số |
| Trình đọc BTF JNI | Phân tích cú pháp siêu dữ liệu BTF từ tệp nhị phân Định dạng thực thi và có thể liên kết (ELF) BPF đã biên dịch tại thời gian chạy, lưu vào bộ nhớ đệm các độ lệch thành viên cấu trúc và thực hiện các thao tác đọc bản đồ hàng loạt vào các mảng Java nguyên thuỷ. | Thông tin chung |
Trình lấy dữ liệu không gian người dùng trong statsd | Kéo bản đồ biểu đồ tích luỹ theo lịch biểu hằng ngày, tính toán delta so với nhật ký đã lưu vào bộ nhớ đệm và tạo các nguyên tử kéo StatsEvent được nhập mạnh. | Trình trợ giúp chung có một mục nhập case duy nhất cho mỗi nguyên tử |
| Giản đồ đo từ xa | Xác định protobuf nguyên tử kéo statsd được nhập mạnh và biểu đồ phía máy chủ hoặc tập hợp chỉ số giá trị. | Tuỳ chỉnh theo chỉ số |
Mô hình sử dụng không gian người dùng
Khi thiết kế chỉ số đo từ xa của nhân, hãy chọn giữa hai mô hình sử dụng không gian người dùng tuỳ thuộc vào việc chỉ số đó biểu thị trạng thái tích luỹ hay một chuỗi sự kiện có thứ tự. Bảng sau đây so sánh hai mô hình này:
| Thuộc tính | Bộ tích luỹ | Lượt phát trực tuyến |
|---|---|---|
| Định nghĩa | Các bộ đếm hoặc biểu đồ tích luỹ có trạng thái, tăng đơn điệu | Chuỗi sự kiện kernel được gắn dấu thời gian theo thứ tự |
| Mẫu truy cập | Không gian người dùng thăm dò ý kiến theo yêu cầu, chẳng hạn như 24 giờ một lần thông qua statsd | Không gian người dùng sử dụng liên tục từ vùng đệm vòng để tránh bị mất sự kiện |
| Các loại bản đồ BPF thông thường | BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_PERCPU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH | BPF_MAP_TYPE_RINGBUF |
| Mô hình đồng thời | Các lượt tra cứu bản đồ nhân hệ điều hành là nguyên tử; nhiều trình đọc có thể thăm dò ý kiến mà không cần khoá không gian người dùng | Ngữ nghĩa đọc huỷ bỏ yêu cầu một luồng đọc duy nhất gửi đến người dùng |
| Ví dụ về các trường hợp sử dụng | Biểu đồ tần suất về độ trễ quản lý bộ nhớ, kích thước phân bổ của Vùng đệm truy cập bộ nhớ trực tiếp (DMA-BUF), trình lặp nguồn đánh thức | Sự kiện theo dõi trình lập lịch biểu đã lọc, dấu vết nguồn điện của hệ thống con tần số cao |
Chỉ số đo từ xa tích hợp sẵn của nhân hệ điều hành
Android có các chương trình đo từ xa nhân eBPF tích hợp hoạt động trên các nhân GKI 6.1 trở lên, bao gồm android14-6.1, android15-6.6, android16-6.12 và android17-6.18. Mỗi chỉ số về độ trễ và kích thước đều sử dụng biểu đồ luỹ thừa (log2) 32 nhóm được tổng hợp trong khoảng thời gian 24 giờ. Bảng sau đây liệt kê các chỉ số đo từ xa của nhân tích hợp:
| Thành phần chỉ số | OOB | Mô tả | Khoá phương diện |
|---|---|---|---|
KswapdLatency | Quản lý bộ nhớ | Kernel swap daemon, kswapd, độ trễ thực thi wake-to-sleep tính bằng micro giây | Truy cập bộ nhớ không đồng nhất (NUMA) node_id |
DirectReclaimLatency | Quản lý bộ nhớ | Độ trễ khi thu hồi bộ nhớ trực tiếp đồng bộ tính bằng micrô giây | Trạng thái hết bộ nhớ (OOM) oom_score_adj của quy trình |
CompactionDuration | Quản lý bộ nhớ | Thời lượng thực thi nén bộ nhớ trên toàn hệ thống tính bằng micro giây, thay thế các lệnh gọi của nhà cung cấp dịch vụ nén cũ | Toàn hệ thống |
CmaAllocationLatency | Quản lý bộ nhớ | Độ trễ phân bổ trang của Trình phân bổ bộ nhớ liền kề (CMA) tính bằng micrô giây | Tên vùng nhớ khối xếp CMA |
DmabufRequestSizes | Quản lý bộ nhớ | Kích thước yêu cầu phân bổ DMA-BUF tính bằng byte | Tên heap của trình xuất |
SuspendResumeLatency | Quản lý nguồn | Độ trễ trong các giai đoạn tạm ngưng và tiếp tục của nhân (tính bằng micro giây) | Giai đoạn tạm ngưng hành động |
Thêm chỉ số đo từ xa của nhân
Để thêm chỉ số tích luỹ mới vào quy trình đo từ xa của nhân eBPF, hãy hoàn tất các bước sau theo thứ tự:
- Viết chương trình eBPF trong
system/bpfprogs/telemetry/và tạo tệp nhị phân trong/system/etc/bpf/kernelstats/. - Xác minh khả năng tương thích của bố cục cấu trúc BTF cho trình đọc BTF JNI chung.
- Đăng ký pull atom trong
statsd. - Xác định giản đồ protobuf của nguyên tử kéo.
Các phần sau đây sử dụng DirectReclaimLatency (directReclaim.c) làm ví dụ.
Viết chương trình eBPF
Trong system/bpfprogs/telemetry/, hãy thêm <bpf_telemetry.h> và xác định khoá bản đồ cũng như bản đồ tích luỹ đầu ra. Sử dụng giá trị nhận dạng số nguyên hoặc enum trong khoá bản đồ BPF thay vì chuỗi thô để giảm thiểu mức sử dụng bộ nhớ bản đồ và bảo vệ quyền riêng tư:
#include <android_bpf_defs.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf_telemetry.h>
#include <stdint.h>
struct direct_reclaim_key {
uint32_t process_state;
};
#define DIRECT_RECLAIM_START_MAP_ENTRIES 128
#define DIRECT_RECLAIM_OUTPUT_MAP_ENTRIES 8
// Temporary map to store start time. Key: pid (thread_id), Value: start_time_ns
DEFINE_BPF_MAP_GRO(direct_reclaim_start_map, HASH, uint32_t, uint64_t,
DIRECT_RECLAIM_START_MAP_ENTRIES, AID_SYSTEM);
// Output map. Key: direct_reclaim_key, Value: accumulator_value (32 log2 buckets)
DEFINE_BPF_MAP_GRO(direct_reclaim_latency_map, HASH, struct direct_reclaim_key,
struct accumulator_value, DIRECT_RECLAIM_OUTPUT_MAP_ENTRIES,
AID_SYSTEM);
Trong trình xử lý điểm theo dõi, hãy ghi lại dấu thời gian bắt đầu khi nhập, tính toán bộ chứa độ trễ log2 khi thoát bằng GET_LATENCY_BUCKET và tăng mục nhập bản đồ tích luỹ bằng INCREMENT_ACCUMULATOR:
DEFINE_BPF_PROG("tp_btf/mm_vmscan_direct_reclaim_begin", AID_ROOT, AID_SYSTEM,
tracepoint_vmscan_mm_vmscan_direct_reclaim_begin)(
uint64_t* args) {
(void)args;
uint32_t pid = (uint32_t)bpf_get_current_pid_tgid();
SAVE_FIRST_START_TIME(direct_reclaim_start_map, &pid);
return 0;
}
DEFINE_BPF_PROG("tp_btf/mm_vmscan_direct_reclaim_end", AID_ROOT, AID_SYSTEM,
tracepoint_vmscan_mm_vmscan_direct_reclaim_end)(
uint64_t* args) {
(void)args;
uint32_t pid = (uint32_t)bpf_get_current_pid_tgid();
uint32_t bucket = GET_LATENCY_BUCKET(direct_reclaim_start_map, &pid);
if (bucket == BPF_TELEMETRY_INVALID_BUCKET) return 0;
struct task_struct* task = (struct task_struct*)bpf_get_current_task();
int oom_score_adj = BPF_CORE_READ(task, signal, oom_score_adj);
enum process_state state = get_process_state(oom_score_adj);
struct direct_reclaim_key key = {.process_state = (uint32_t)state};
INCREMENT_ACCUMULATOR(direct_reclaim_latency_map, &key, bucket);
return 0;
}
Xác minh khả năng tương thích của bố cục cấu trúc BTF
Bạn không cần viết mã C++ JNI tuỳ chỉnh khi thêm một bản đồ biểu đồ mới, miễn là các cấu trúc khoá và giá trị bản đồ của bạn sử dụng các loại số nguyên, enum và mảng cố định tiêu chuẩn. Thay vì truyền các mục nhập bản đồ BPF tới các cấu trúc C được mã hoá cứng tại thời gian biên dịch, trình đọc JNI chung sẽ phân giải bố cục cấu trúc một cách linh động trong thời gian chạy theo ba giai đoạn:
- Mối liên kết ELF với bản đồ: Lớp JNI phân tích cú pháp siêu dữ liệu BTF trực tiếp từ tệp ELF BPF đã biên dịch trên ổ đĩa, chẳng hạn như
/system/etc/bpf/kernelstats/directReclaim.bpf, bằng cách sử dụng hàmlibbpfbtf__parse. - Độ phân giải loại BTF: Trong lần truy cập đầu tiên, trình đọc sẽ tạo và lưu vào bộ nhớ đệm một bản trình bày cấu trúc của các cấu trúc C khoá và giá trị của bản đồ, tính toán độ lệch byte tích luỹ cho các thành viên cấu trúc, chiều rộng byte số nguyên và enum, cũng như kích thước phần tử mảng từ con trỏ cơ sở.
- Tra cứu và chuyển đổi tuần tự theo lô: Khi
statsdlấy bản đồ, lớp JNI sẽ phát một lệnhBPF_MAP_LOOKUP_BATCHduy nhất đến lệnh gọi hệ thốngsys_bpfđể đọc tất cả các mục băm theo lô và sao chép trực tiếp các mã nhận dạng khoá và mảng bộ chứa đã phân giải vào mảnglong[]Java gốc bằng hàm JNISetLongArrayRegion.
Nếu cấu trúc BPF C thay đổi trong bản phát hành sau này, thì phần BTF được biên dịch lại sẽ tự động cập nhật và trình đọc JNI sẽ điều chỉnh trong thời gian chạy mà không cần biên dịch lại JNI.
Đăng ký nguyên tử kéo trong statsd
Trong StatsPullAtomService.java, trình trợ giúp pullKernelTelemetry chung sẽ đọc bản đồ BPF được ghim, tính toán delta của bộ chứa so với số lượng được lưu vào bộ nhớ đệm của lần kéo trước đó và phát ra các mục nhập StatsEvent. Để thêm một chỉ số đo từ xa mới cho nhân, bạn chỉ cần thêm 3 phần nhỏ:
// 1. Dispatch the atom tag to pullKernelTelemetry() in onPullAtomLocked():
case FrameworkStatsLog.DIRECT_RECLAIM_LATENCY:
return pullKernelTelemetry(atomTag, data);
// 2. Add the atom tag to the registered atoms list in registerKernelTelemetry():
int[] atoms = {
FrameworkStatsLog.DIRECT_RECLAIM_LATENCY,
// ...
};
// 3. Map the atom tag to its pinned BPF map path:
case FrameworkStatsLog.DIRECT_RECLAIM_LATENCY ->
"/sys/fs/bpf/kernelstats/map_directReclaim_direct_reclaim_latency_map";
Xác định protobuf của nguyên tử kéo
Xác định thông báo nguyên tử kéo được nhập mạnh trong frameworks/proto_logging/ để khớp với khoá phương diện và mảng biểu đồ log2 32 bộ chứa do bản đồ BPF tạo ra:
message DirectReclaimLatency {
enum ProcessState {
PROCESS_STATE_UNSPECIFIED = 0;
STATE_NATIVE = 1;
STATE_SYSTEM = 2;
STATE_PERSISTENT = 3;
STATE_FOREGROUND = 4;
STATE_VISIBLE = 5;
STATE_PERCEPTIBLE = 6;
STATE_SERVICE_BACKGROUND = 7;
STATE_CACHED = 8;
}
optional ProcessState process_state = 1;
repeated int32 duration_log2_buckets_micros = 2;
}