eBPF-এর মাধ্যমে কার্নেল টেলিমেট্রি সংগ্রহ করা

এই পৃষ্ঠায় Android-এ এক্সটেন্ডেড বার্কলে প্যাকেট ফিল্টার (eBPF) কার্নেল টেলিমেট্রি ফ্রেমওয়ার্কের ওভারভিউ দেওয়া হয়েছে। এতে ফ্রেমওয়ার্ক আর্কিটেকচার, বিল্ট-ইন মেট্রিক এবং কীভাবে নতুন কার্নেল মেট্রিক যোগ করতে হয় তা বর্ণনা করা হয়েছে।

Android-এ কম ওভারহেড কার্নেল মেট্রিক সংগ্রহ করতে এবং statsd-এর মতো ইউজারস্পেস পরিষেবায় সেইসব মেট্রিক এক্সপোজ করতে একটি ইউনিফায়েড eBPF কার্নেল টেলিমেট্রি ফ্রেমওয়ার্ক অন্তর্ভুক্ত থাকে। কার্নেল ট্রেসপয়েন্ট ও kprobes-এ পোর্টেবল eBPF প্রোগ্রাম অ্যাটাচ করার মাধ্যমে, ফ্রেমওয়ার্কটি হাই-ফ্রিকোয়েন্সি কার্নেল ইভেন্টগুলিকে একত্রিত করে এবং জেনেরিক কার্নেল ইমেজ (GKI) পরিবর্তন না করে বা কার্নেল মডিউল ইন্টারফেস (KMI) না ভেঙে কম্প্যাক্ট হিস্টোগ্রাম বা কাউন্টার এক্সপোর্ট করে।

কার্নেল টেলিমেট্রির প্রথাগত পদ্ধতি কাস্টম procfs বা sysfs নোড, আউট-অফ-ট্রি কার্নেল প্যাচ বা ভেন্ডর হুকের উপর নির্ভর করে যার জন্য কার্নেল, SELinux, জাভা নেটিভ ইন্টারফেস (JNI) এবং ইউজারস্পেস পরিষেবা জুড়ে কাস্টম প্লাম্বিং প্রয়োজন। eBPF কার্নেল টেলিমেট্রি ফ্রেমওয়ার্ক Android সাধারণ কার্নেল (ACK) জুড়ে এই পাইপলাইনকে স্ট্যান্ডার্ডাইজ করে এবং নিম্নলিখিত সুবিধা প্রদান করে:

  • কার্নেল ABI ও KMI চর্ন এড়িয়ে যায়: কার্নেল সোর্স কোড পরিবর্তন না করে, কার্নেল অ্যাপ্লিকেশন বাইনারি ইন্টারফেস (ABI) পরিবর্তন না করে অথবা TRACE_ANDROID_VH_*-এর মতো Android ভেন্ডর হুক যোগ না করে, আগে থেকে থাকা কার্নেল ট্রেসপয়েন্ট বা kprobes-এর সাথে eBPF প্রোগ্রাম অ্যাটাচ করে। ডিভাইস প্রস্তুতকারক ও সিস্টেম-অন-চিপ (SoC) পার্টনাররা নতুন টেলিমেট্রি ভেন্ডর হুক যোগ করার পরিবর্তে বা আগে থেকে থাকা ভেন্ডর হুক সরিয়ে দেওয়ার জন্য eBPF টেলিমেট্রি ব্যবহার করতে পারবেন।
  • android14-6.1 এবং তার পরবর্তী ভার্সনের মতো GKI ভার্সন জুড়ে ইন্টার্নাল কার্নেল স্ট্রাক্ট লেআউট পরিবর্তনের সাথে একটি কম্পাইল করা eBPF প্রোগ্রাম যাতে অটোমেটিক মানিয়ে নিতে পারে, তার জন্য BPF Compile Once - Run Everywhere (CO-RE) এবং BPF Type Format (BTF) ব্যবহার করে।
  • সিস্টেম হেলথ ওভারহেড কম করে: সোর্স থেকে সরাসরি হাই-ফ্রিকোয়েন্সি কার্নেল ইভেন্ট কমপ্যাক্ট ৩২-বাকেট বেস-২ লগারিদমিক (log2) হিস্টোগ্রামে ফিল্টার ও অ্যাগ্রিগেট করে, সাধারণত প্রতি BPF ম্যাপে ১ কেবির কম। Userspace দিনে একবার করে একটি BPF_MAP_LOOKUP_BATCH ব্যাচ syscall ব্যবহার করে একত্রিত ম্যাপ এন্ট্রি রিট্রিভ করে।
  • SELinux ও JNI ইন্টিগ্রেশনকে সহজ করে: ডিরেক্টরি-লেভেল fs_bpf_kernelstats SELinux টাইপ ইনহেরিটেন্স প্রয়োগ করতে /sys/fs/bpf/kernelstats/-এর অধীনে টেলিমেট্রি ম্যাপ পিন করে এবং নতুন মেট্রিক যোগ করার সময় কাস্টম C++ পার্সিং কোড বাদ দিতে BTF মেটাডেটা ব্যবহার করে JNI-তে রান টাইমে স্ট্রাক্ট মেম্বার অফসেট সমাধান করে।

Android কীভাবে eBPF প্রোগ্রাম ও ম্যাপ লোড ও পিন করে সেই সম্পর্কে আরও তথ্য পেতে, eBPF-এর মাধ্যমে কার্নেল এক্সটেন্ড করুন দেখুন।

আর্কিটেকচার ওভারভিউ

eBPF কার্নেল টেলিমেট্রি পাইপলাইন, মেট্রিক-নির্দিষ্ট সংজ্ঞা থেকে সাধারণ ইনফ্রাস্ট্রাকচার কম্পোনেন্টকে আলাদা করে, যাতে নতুন কার্নেল মেট্রিক যোগ করার জন্য ন্যূনতম কোড পরিবর্তন করতে হয়। নিচের সারণীতে পাইপলাইনের কম্পোনেন্টগুলি বর্ণনা করা হয়েছে:

কম্পোনেন্টপাইপলাইনে ভূমিকাসুযোগ
BPF লোডার, bpfloaderচলমান কার্নেলের সাথে মানানসই হলে, বুট করার সময় /system/etc/bpf/kernelstats/ থেকে কম্পাইল করা eBPF প্রোগ্রাম লোড করে এবং /sys/fs/bpf/kernelstats/-এ ম্যাপ পিন করে। বন্ধ করা থাকলে বা কাজ না করলে, লোডিং এড়িয়ে যায়।সাধারণ
SELinux নীতি, fs_bpf_kernelstats/sys/fs/bpf/kernelstats/-এ ডিরেক্টরি-লেভেল SELinux লেবেল প্রয়োগ করে, যাতে system_server প্রতি-মেট্রিক SELinux নীতি আপডেট ছাড়াই টেলিমেট্রি ম্যাপ পড়তে পারে।সাধারণ
system/bpfprogs/telemetry/-এ eBPF প্রোগ্রামএটি কার্নেল ট্রেসপয়েন্ট বা kprobes-এর সাথে অ্যাটাচ হয়, ইভেন্ট ফিল্টার করে এবং BPF ম্যাপে মেট্রিক একত্রিত করে।প্রতিটি মেট্রিকের জন্য কাস্টমাইজ করা
JNI BTF রিডাররান টাইমে কম্পাইল করা BPF এক্সিকিউটেবল এবং লিঙ্কেবল ফর্ম্যাট (ELF) বাইনারি থেকে BTF মেটাডেটা পার্স করে, স্ট্রাক্ট মেম্বার অফসেট ক্যাশে করে এবং প্রিমিটিভ জাভা অ্যারেতে ব্যাচ ম্যাপ রিড করে।সাধারণ
statsd-এ ইউজারস্পেস পুলারপ্রতিদিনের শিডিউল অনুযায়ী, কিউমুলেটিভ হিস্টোগ্রাম ম্যাপ পুল করে, ক্যাশে করা ইতিহাসের সাথে ডেল্টা গণনা করে এবং স্ট্রংলি টাইপ করা StatsEvent পুল অ্যাটম তৈরি করে।প্রতিটি অ্যাটমে একটি case এন্ট্রি সহ জেনেরিক হেল্পার
টেলিমেট্রি স্কিমাস্ট্রংলি টাইপ করা statsd পুল অ্যাটম প্রোটোবাফ এবং সার্ভার-সাইড হিস্টোগ্রাম বা ভ্যালু মেট্রিক অ্যাগ্রিগেশন নির্ধারণ করে।প্রতি মেট্রিক অনুযায়ী কাস্টমাইজ করা

ব্যবহারকারীর স্পেস কনজাম্পশন মডেল

কার্নেল টেলিমেট্রি মেট্রিক ডিজাইন করার সময়, মেট্রিকটি কি কিউমুলেটিভ স্টেট নাকি অর্ডার্ড ইভেন্ট সিকোয়েন্স তা দেখে দুটি ইউজারস্পেস কনজাম্পশন মডেলের মধ্যে একটি বেছে নিন। নিম্নলিখিত সারণীতে দুটি মডেলের তুলনা করা হয়েছে:

প্রপার্টিঅ্যাকুমুলেটরস্ট্রিম
সংজ্ঞাস্টেটফুল, একভাবে ক্রমবর্ধমান ক্রমপুঞ্জিত কাউন্টার বা হিস্টোগ্রামটাইমস্ট্যাম্প করা কার্নেল ইভেন্টের ক্রমিক সিকোয়েন্স
অ্যাক্সেস প্যাটার্নstatsd-এর মাধ্যমে প্রতি ২৪ ঘণ্টায় একবারের মতো চাহিদা অনুযায়ী ব্যবহারকারী স্পেস পোলড্রপ করা ইভেন্ট এড়াতে, ব্যবহারকারীর স্পেস একটি রিং বাফার থেকে ক্রমাগত ডেটা নেয়
সাধারণ BPF ম্যাপের ধরনBPF_MAP_TYPE_HASH, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_PERCPU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASHBPF_MAP_TYPE_RINGBUF
কনকারেন্সি মডেলকার্নেল ম্যাপ লুক-আপ অ্যাটমিক হয়; একাধিক রিডার ইউজারস্পেস লকিং ছাড়াই পোল করতে পারেডেস্ট্রাক্টিভ রিড সিম্যান্টিক্সের জন্য গ্রাহকদের কাছে ডিসপ্যাচ করার জন্য একটি রিডার থ্রেড প্রয়োজন
ব্যবহারের উদাহরণমেমরি ম্যানেজমেন্ট ল্যাটেন্সি হিস্টোগ্রাম, ডাইরেক্ট মেমরি অ্যাক্সেস বাফার (DMA-BUF) অ্যালোকেশন সাইজ, ওয়েক-আপ সোর্স ইটারেটরফিল্টার করা শিডিউলার ট্রেস ইভেন্ট, হাই-ফ্রিকোয়েন্সি সাবসিস্টেম পাওয়ার ট্রেস

বিল্ট-ইন কার্নেল টেলিমেট্রি মেট্রিক

Android-এ বিল্ট-ইন eBPF কার্নেল টেলিমেট্রি প্রোগ্রাম থাকে যা GKI 6.1 ও তার পরের যেকোনও কার্নেলে কাজ করে, এর মধ্যে android14-6.1, android15-6.6, android16-6.12 ও android17-6.18 অন্তর্ভুক্ত। প্রতিটি লেটেন্সি ও সাইজ মেট্রিক ২৪-ঘণ্টার উইন্ডো জুড়ে অ্যাগ্রিগেট করা ৩২-বাকেট এক্সপোনেনশিয়াল (log2) হিস্টোগ্রাম ব্যবহার করে। নিম্নলিখিত সারণীতে বিল্ট-ইন কার্নেল টেলিমেট্রি মেট্রিক্সের তালিকা দেওয়া হল:

মেট্রিক অ্যাটমসাবসিস্টেমবর্ণনাডাইমেনশন কী
KswapdLatencyমেমোরি ম্যানেজমেন্টকার্নেল সোয়াপ ডেমন, kswapd, মাইক্রোসেকেন্ডে ওয়েক-টু-স্লিপ এক্সিকিউশন লেটেন্সিনন-ইউনিফর্ম মেমরি অ্যাক্সেস (NUMA) node_id
DirectReclaimLatencyমেমোরি ম্যানেজমেন্টমাইক্রোসেকেন্ডে সিঙ্ক্রোনাস ডাইরেক্ট মেমরি রিক্লেইম ল্যাটেন্সিপ্রসেস আউট-অফ-মেমরি (OOM) oom_score_adj স্টেট
CompactionDurationমেমরি ম্যানেজমেন্টপুরনো কম্প্যাকশন ভেন্ডর হুক পরিবর্তন করে, মাইক্রোসেকেন্ডে সিস্টেম-ব্যাপী মেমরি কম্প্যাকশন এক্সিকিউশন সময়সীমাসিস্টেম-ব্যাপী
CmaAllocationLatencyমেমরি ম্যানেজমেন্টমাইক্রোসেকেন্ডে কনটিগুয়াস মেমরি অ্যালোকেটর (CMA) পৃষ্ঠা অ্যালোকেশন ল্যাটেন্সিCMA হিপের নাম
DmabufRequestSizesমেমোরি ম্যানেজমেন্টবাইট হিসেবে DMA-BUF অ্যালোকেশন অনুরোধের সাইজএক্সপোর্টার হিপের নাম
SuspendResumeLatencyশক্তি পরিচালনাকার্নেল সাসপেন্ড এবং মাইক্রোসেকেন্ডে স্টেজ আবার চালু করার লেটেন্সিঅ্যাকশন সাসপেন্ড করার পর্যায়

কার্নেল টেলিমেট্রি মেট্রিক যোগ করুন

eBPF কার্নেল টেলিমেট্রি পাইপলাইনে নতুন অ্যাকুমুলেটর মেট্রিক যোগ করতে, নিম্নলিখিত ধাপগুলি পরপর সম্পূর্ণ করুন:

  1. system/bpfprogs/telemetry/-এর অধীনে eBPF প্রোগ্রাম লিখুন এবং /system/etc/bpf/kernelstats/-এ বাইনারি তৈরি করুন।
  2. জেনেরিক JNI BTF রিডারের জন্য BTF স্ট্রাকচার লেআউট মানানসই কিনা তা যাচাই করুন।
  3. statsd-এ পুল অ্যাটম রেজিস্টার করুন।
  4. পুল অ্যাটম প্রোটোবাফ স্কিমা নির্দিষ্ট করুন।

নিম্নলিখিত বিভাগগুলি উদাহরণ হিসেবে DirectReclaimLatency (directReclaim.c) ব্যবহার করে।

eBPF প্রোগ্রাম লেখা

system/bpfprogs/telemetry/-এ, <bpf_telemetry.h> যোগ করুন এবং ম্যাপ কী ও আউটপুট অ্যাকুমুলেটর ম্যাপকে সংজ্ঞায়িত করুন। ম্যাপ মেমরি ফুটপ্রিন্ট কমাতে এবং গোপনীয়তা রক্ষা করতে র স্ট্রিংয়ের পরিবর্তে BPF ম্যাপ কীতে পূর্ণসংখ্যা শনাক্তকারী বা এনাম ব্যবহার করুন:

#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);

ট্রেসপয়েন্ট হ্যান্ডলারের মধ্যে, এন্ট্রি পয়েন্টে শুরুর টাইমস্ট্যাম্প রেকর্ড করুন, GET_LATENCY_BUCKET ব্যবহার করে log2 লেটেন্সি বাকেট কম্পিউট করুন এবং 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;
}

BTF স্ট্রাকচার লেআউট মানানসই কিনা তা যাচাই করা

নতুন হিস্টোগ্রাম ম্যাপ যোগ করার সময় আপনাকে কাস্টম JNI C++ কোড লিখতে হবে না, যদি আপনার ম্যাপ কী এবং ভ্যালু স্ট্রাক্ট স্ট্যান্ডার্ড ইন্টিজার, এনাম এবং ফিক্সড অ্যারে টাইপ ব্যবহার করে। BPF ম্যাপ এন্ট্রিকে কম্পাইল টাইমে হার্ডকোডেড C স্ট্রাকচারে কাস্ট করার পরিবর্তে, জেনেরিক JNI রিডার রান টাইমে তিনটি ধাপে ডাইনামিক পদ্ধতিতে স্ট্রাকচার লেআউট সমাধান করে:

  • ELF-এর সাথে ম্যাপের অ্যাসোসিয়েশন: JNI লেয়ার সরাসরি ডিস্কে কম্পাইল করা BPF ELF ফাইল থেকে BTF মেটাডেটা পার্স করে, যেমন /system/etc/bpf/kernelstats/directReclaim.bpf, libbpf btf__parse ফাংশন ব্যবহার করে।
  • BTF টাইপ রেজোলিউশন: প্রথম অ্যাক্সেসে, রিডার ম্যাপের কী ও ভ্যালু C স্ট্রাক্টের একটি স্ট্রাকচারাল উপস্থাপনা তৈরি করে এবং ক্যাশে করে। এটি স্ট্রাক্ট মেম্বার, ইন্টিজার ও এনুম বাইট উইডথ এবং বেস পয়েন্টার থেকে অ্যারে এলিমেন্টের সাইজের জন্য কিউমুলেটিভ বাইট অফসেট গণনা করে।
  • ব্যাচ লুকআপ ও সিরিয়ালাইজেশন: statsd ম্যাপ পুল করলে, JNI লেয়ার sys_bpf syscall-এ একটিমাত্র BPF_MAP_LOOKUP_BATCH কমান্ড ইস্যু করে যাতে সব হ্যাশ এন্ট্রি একসাথে পড়া যায় এবং সমাধান করা কী আইডি ও বাকেট অ্যারে সরাসরি প্রিমিটিভ Java long[] অ্যারেতে কপি করে, SetLongArrayRegion JNI ফাংশন ব্যবহার করে।

ভবিষ্যতে রিলিজ হওয়া কোনও ভার্সনে BPF C স্ট্রাক্ট পরিবর্তন হলে, রিকম্পাইল করা BTF বিভাগ অটোমেটিক আপডেট হয় এবং JNI রিডার রান টাইমে JNI রিকম্পাইলেশন ছাড়াই মানিয়ে নেয়।

statsd-এ পুল অ্যাটম রেজিস্টার করুন

StatsPullAtomService.java-এ, জেনেরিক pullKernelTelemetry হেল্পার পিন করা BPF ম্যাপ পড়ে, আগের পুলের ক্যাশে করা সংখ্যার সাপেক্ষে বাকেট ডেল্টা গণনা করে এবং StatsEvent এন্ট্রি নির্গত করে। নতুন কার্নেল টেলিমেট্রি মেট্রিক যোগ করতে শুধুমাত্র তিনটি ছোট সংযোজন প্রয়োজন:

// 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";

পুল অ্যাটম প্রোটোবাফ নির্ধারণ করুন

BPF ম্যাপের মাধ্যমে তৈরি করা ডাইমেনশন কী এবং ৩২-বাকেট log2 হিস্টোগ্রাম অ্যারের সাথে ম্যাচ করার জন্য frameworks/proto_logging/-এ স্ট্রংলি টাইপড পুল অ্যাটম মেসেজ সংজ্ঞায়িত করুন:

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;
}