يفتح أحد المستخدمين تذكرة دعم: تطبيق الهاتف المحمول بطيء الاستجابة. تتحقق من لوحات التحكم الخاصة بك؛ استهلاك المعالج (CPU) مستقر، ومعدلات الخطأ صفر، وأضواء أدوات مراقبة أداء التطبيقات (APM) تضيء باللون الأخضر المطمئن. تبدو الأنظمة الخلفية (Backend)، وفقًا لكل مقياس مرئي، في حالة صحية ممتازة. لكن المستخدم ليس مخطئًا؛ فالبطء حقيقي، وهو يحدث في مكان ما داخل الممر الغامض الطويل بين جهاز Android وخادمك.
المشكلة تكمن في أن معظم أدوات المراقبة (observability tools) تتوقف عند حدود التطبيق؛ فهي تقيس ما يحدث بعد أن يقوم إطار العمل (framework) الخاص بك بتحليل الطلب. تتبع استعلامات قاعدة البيانات، ونتائج التخزين المؤقت (cache hits)، واستدعاءات الخدمات التابعة. ما يفوتها هو ميكانيكا الطلب نفسه: الوقت الذي تستغرقه استدعاءات OkHttp داخل مكدس HTTP الخاص بـ Android، والعبور عبر شبكة هاتف محمول متقلبة، ومفاوضات مصافحة TLS، والانتظار الصامت الذي يحدث داخل طوابير النواة (kernel queues) قبل أن يتم تنفيذ الكود الخاص بك. هذه الفجوات تبتلع أجزاءً من الثانية — أو ثوانٍ كاملة — بينما تظل أدوات APM الخاصة بك صامتة.
يزيد التشفير من حدة هذه العمى؛ حيث تقوم تطبيقات Android الحديثة بتوجيه كل شيء عبر BoringSSL. وبحلول الوقت الذي تصل فيه الحزمة إلى مكدس شبكة النواة، تكون ترويسات HTTP قد شُفرت بالفعل. لا يرى أمر tcpdump القياسي أو خطاف الشبكة (network hook) سوى سجلات TLS مبهمة. يمكنك ملاحظة أن حركة المرور تتدفق، لكن لا يمكنك قراءتها، وبالتأكيد لا يمكنك ربط جزء TCP محدد على مستوى النواة باستدعاء API لمستخدم معين، لأن سياق التتبع (trace context) الذي تحتاجه محاصر داخل النص المشفر (ciphertext).
يغير eBPF هذه المعادلة لأنه يسمح لك بتزويد النظام بأدوات القياس (instrument) من الداخل إلى الخارج دون تعديل كود التطبيق الخاص بك. بدلاً من مطالبة تطبيقك بالإبلاغ عن زمن الاستجابة الخاص به، تقوم بإرفاق برامج صغيرة مباشرة بالنواة وبمكتبات مساحة المستخدم (userspace libraries) الحيوية. تراقب هذه البرامج الأحداث أثناء وقوعها، وتستخرج ما تحتاجه، وترسله إلى مخزن حلقي (ring buffer). لا يوجد تضخم في حزمة SDK داخل ملف APK الخاص بـ Android سوى ترويسة خفيفة الوزن، ولا يوجد وكيل قياس (instrumentation agent) يعيد كتابة فئات (classes) الأنظمة الخلفية الخاصة بك.
الإعداد المكون من أربعة أجزاء
يتضمن بناء خط الأنابيب هذا أربع طبقات متميزة من المراقبة.
1. مرساة traceparent. في جهاز Android، تضيف OkHttp interceptor يقوم بحقن ترويسة W3C traceparent في كل طلب خارجي. هذا هو التغيير الوحيد المطلوب من جانب الهاتف المحمول، وهو تغيير طفيف. تنتقل الترويسة داخل الحمولة المشفرة وصولاً إلى نظامك الخلفي، ولأنها تقع داخل طبقة HTTP، فإنها تظل موجودة داخل النص الصريح (plaintext) الذي يكشفه محرك TLS في النهاية.
2. توقيت وصول TCP. على المضيف الخلفي (backend host)، تستخدم خطافات eBPF Traffic Control المرتبطة بواجهة الشبكة. تعمل هذه البرامج فور وصول أجزاء TCP الفردية، حيث تلتقط أرقام التسلسل (sequence numbers) والطوابع الزمنية (timestamps) عند الحافة. ستعرف الآن بدقة متى غادرت البيانات السلك ودخلت جهازك، قبل وقت طويل من قراءة تطبيقك لأي بايت واحد.
3. مجسات فك التشفير. يقوم نظامك الخلفي بإنهاء اتصال TLS باستخدام OpenSSL أو BoringSSL. هنا يصبح الهيكل مثيرًا للاهتمام؛ باستخدام uprobes — وهي مجسات ديناميكية في مساحة المستخدم — يمكنك الاتصال بـ SSL_write و SSL_read داخل مكتبة TLS. تعمل هذه الوظائف في اللحظة التي تمر فيها البيانات النصية الصريحة عبر محرك التشفير. يقرأ برنامج eBPF الخاص بك ذلك المخزن المؤقت لفك التشفير، ويبحث عن ترويسة traceparent، ويستخرجها. تمتلك النواة الآن خريطة مباشرة بين تدفق TCP خام وطلب هاتف محمول محدد، دون أن تضطر للتعامل مع الشهادات أو المفاتيح في أداة مخصصة.
4. طوابير النواة. حتى بعد فك تشفير البيانات وجاهزيتها، قد لا يستهلكها تطبيقك على الفور. تقوم بإرفاق kprobes بوظائف النواة ذات الصلة التي تتعامل مع مخازن المقابس المؤقتة (socket buffers) وأحداث الجدولة (scheduling events). يقيس هذا تأخير الطوابير (queueing latency): الوقت الذي تقضيه الطلبات في الانتظار داخل منطقة النواة لأن عمليتك تتنافس على المعالج أو ببساطة لم تستدعِ read() بعد.
تكتب جميع مصادر الإشارات الأربعة الأحداث في eBPF ring buffer. تقوم عملية جانبية (sidecar process) تعمل في مساحة المستخدم بتفريغ هذا المخزن، وتربط الأحداث بواسطة معرف traceparent، وتعيد بناء جدول زمني واحد ومتماسك لكل طلب. ما كان في السابق عبارة عن ضجيج نواة متناثر وغير متصل، يصبح تتبعًا (trace) مهيكلًا.
قراءة المسار الكامل
عادة ما يتم عرض المخرجات المجمعة كمخطط لهب (flame graph) أو شجرة نطاقات (span tree) مهيكلة تقسم زمن الاستجابة إلى أربعة أجزاء ملموسة:
- Network transit time: The duration from the Android radio sending the last byte of the request to the backend NIC receiving it. This is where cellular volatility lives.
- TLS handshake duration: The time spent negotiating the encrypted tunnel. On spotty networks, this can dwarf actual data transfer.
- Kernel queueing latency: Time spent in kernel buffers and scheduler queues after the segment arrives but before userspace consumes it.
- Application processing time: The slice your backend framework and business logic actually consume.
You might discover that an 800ms mobile request spends only 40ms inside your JSON parser. Another 200ms vanishes into a stalled TLS handshake across a lossy connection. Another 300ms disappears into kernel backlog on an oversubscribed host. Your APM was only reporting the 40ms. Without kernel-level visibility, you would have optimized the wrong thing entirely.
Overhead That Actually Scales
Traditional APM agents achieve their visibility by intercepting calls inside your language runtime. They wrap methods, allocate span objects, and serialize telemetry data inside your process heap. Under load, that overhead compounds quickly. Serialization costs rise. Garbage collection pressure increases. You are paying for visibility with your application's own resources.
eBPF programs run inside a kernel virtual machine and are JIT-compiled to native machine instructions. A verifier checks them for safety before they load. Each probe adds microseconds to the path, not milliseconds. The heavy work of matching events and rendering graphs happens in the sidecar, outside your service's hot path. You are not inflating heap allocations. You are not adding serialization taxes inside request handling. For services pushing thousands of requests per second, that distinction matters.
What It Takes to Run
This is not a magic bullet, and it is not a managed SaaS you can toggle on. You need a backend kernel with modern eBPF support, including BTF type information so your probes can safely traverse kernel structures. Your TLS library must expose symbols that uprobes can target; if you ship a statically linked binary with a stripped or heavily customized OpenSSL build, you will need to account for that. You also need to verify that your traceparent header survives any proxies or edge gateways between the mobile client and the TLS termination point.
But for teams exhausted by "mobile is slow" tickets that defy root-cause analysis, this architecture replaces ritual guessing with hard signal. You stop speculating about network weather and start measuring specific requests through specific pipes.
The Real Takeaway
You do not have to treat encrypted mobile traffic as an opaque stream that magically becomes visible once it hits your framework. By combining a simple OkHttp header with strategically placed eBPF probes on the backend, you can follow a single request from an Android device through TLS decryption, kernel queues, and into your application logic—without rewriting your mobile app and without instrumenting your backend code the way traditional APM demands. That is not just incremental improvement. It is end-to-end observability across
