ผู้ใช้เปิดตั๋วแจ้งปัญหา: แอปมือถือทำงานอืด คุณตรวจสอบแดชบอร์ด พบว่าการใช้งาน CPU คงที่ อัตราข้อผิดพลาดเป็นศูนย์ ไฟสถานะ APM ของคุณเป็นสีเขียวที่ดูน่าไว้วางใจ หากวัดจากทุกมาตรวัดที่มองเห็นได้ แบ็กเอนด์ก็ดูปกติ แต่ผู้ใช้ไม่ได้พูดผิด ความล่าช้านั้นมีอยู่จริง และมันกำลังเกิดขึ้นที่ไหนสักแห่งใน "ระเบียงที่มืดมิดและยาวไกล" ระหว่างอุปกรณ์ Android และเซิร์ฟเวอร์ของคุณ
ปัญหาก็คือ เครื่องมือ observability ส่วนใหญ่หยุดอยู่แค่ที่ขอบเขตของแอปพลิเคชัน พวกมันวัดสิ่งที่เกิดขึ้นหลังจากเฟรมเวิร์กของคุณประมวลผลคำขอ (request) แล้ว พวกมันติดตามการคิวรีฐานข้อมูล, cache hits และการเรียกใช้บริการปลายทาง (downstream service calls) แต่สิ่งที่พวกมันพลาดไปคือกลไกของตัวคำขอเอง: เวลาที่การเรียก OkHttp ใช้ภายใน Android HTTP stack, การรับส่งข้อมูลผ่านเครือข่ายมือถือที่ไม่เสถียร, การเจรจา TLS handshake และการรอคอยที่เงียบงันซึ่งเกิดขึ้นภายในคิวของ kernel ก่อนที่โค้ดของคุณจะเริ่มทำงานเสียด้วยซ้ำ ช่องว่างเหล่านี้กลืนกินเวลาเป็นมิลลิวินาที—หรืออาจเป็นวินาทีทั้งวินาที—ในขณะที่ APM ของคุณยังคงเงียบสนิท
การเข้ารหัสทำให้ความบอดสนิทสมบูรณ์ยิ่งขึ้น แอป Android สมัยใหม่จะส่งทุกอย่างผ่าน BoringSSL เมื่อแพ็กเก็ตไปถึง kernel network stack ส่วนหัว HTTP (HTTP headers) ก็จะถูกเข้ารหัสไปแล้ว การใช้ tcpdump มาตรฐานหรือ network hook จะเห็นเพียง TLS records ที่อ่านไม่ออก คุณสามารถสังเกตได้ว่ามีการไหลของข้อมูล แต่คุณไม่สามารถอ่านมันได้ และคุณไม่สามารถเชื่อมโยง TCP segment ในระดับ kernel เข้ากับการเรียก API ของผู้ใช้เฉพาะรายได้อย่างแน่นอน Trace context ที่คุณต้องการนั้นถูกกักขังอยู่ภายใน ciphertext
eBPF เปลี่ยนสมการนี้ เพราะมันช่วยให้คุณสามารถทำ instrumentation ระบบจากภายในสู่ภายนอกได้โดยไม่ต้องแก้ไขโค้ดแอปพลิเคชันของคุณ แทนที่จะขอให้แอปรายงานค่า latency ของตัวเอง คุณกลับแนบโปรแกรมขนาดเล็กเข้ากับ kernel และไลบรารีใน userspace ที่สำคัญโดยตรง โปรแกรมเหล่านี้จะสังเกตเหตุการณ์ที่เกิดขึ้น ดึงข้อมูลที่คุณต้องการ และส่งไปยัง ring buffer ไม่มีการเพิ่ม SDK ที่เทอะทะเข้าไปใน Android APK นอกเหนือจาก header น้ำหนักเบา และไม่มี instrumentation agent ที่ต้องมาเขียนคลาสในแบ็กเอนด์ของคุณใหม่
การตั้งค่าแบบสี่ส่วน
การสร้าง pipeline นี้ประกอบด้วยเลเยอร์ของการสังเกตการณ์สี่ส่วนที่แตกต่างกัน
1. ตัวยึด traceparent. บนอุปกรณ์ Android คุณจะเพิ่ม OkHttp interceptor เพื่อฉีด (inject) W3C traceparent header เข้าไปในทุกคำขอที่ส่งออกไป นี่คือการเปลี่ยนแปลงเพียงอย่างเดียวที่จำเป็นในฝั่งมือถือ และมันก็น้อยมาก หัวข้อนี้จะเดินทางไปพร้อมกับ payload ที่เข้ารหัสจนถึงแบ็กเอนด์ของคุณ เนื่องจากมันอยู่ภายในเลเยอร์ HTTP มันจึงยังคงอยู่ภายใน plaintext ที่ TLS engine จะเปิดเผยออกมาในที่สุด
2. การจับเวลาการมาถึงของ TCP. บนโฮสต์แบ็กเอนด์ คุณจะใช้ eBPF Traffic Control hooks ที่เชื่อมต่อกับ network interface โปรแกรมเหล่านี้จะทำงานเมื่อ TCP segments แต่ละตัวมาถึง พวกมันจะจับหมายเลขลำดับ (sequence numbers) และ timestamps ที่ขอบเครือข่าย ตอนนี้คุณจะรู้ได้อย่างแม่นยำว่าข้อมูล (bits) ออกจากสายและเข้าสู่เครื่องของคุณเมื่อใด ก่อนที่แอปพลิเคชันของคุณจะอ่านข้อมูลแม้แต่ไบต์เดียว
3. ตัวตรวจจับการถอดรหัส. แบ็กเอนด์ของคุณจะสิ้นสุดการทำงานของ TLS โดยใช้ OpenSSL หรือ BoringSSL และนี่คือจุดที่สถาปัตยกรรมเริ่มน่าสนใจ การใช้ uprobes—ซึ่งเป็น dynamic userspace probes—คุณจะเชื่อมต่อเข้ากับ SSL_write และ SSL_read ภายในไลบรารี TLS ฟังก์ชันเหล่านี้จะทำงานทันทีที่ข้อมูล plaintext ไหลผ่าน encryption engine โปรแกรม eBPF ของคุณจะอ่าน buffer ที่ถอดรหัสแล้วนั้น สแกนหา traceparent header และดึงมันออกมา ตอนนี้ kernel จะมีแผนผังการเชื่อมโยงโดยตรงระหว่าง TCP flow ดิบ กับคำขอจากมือถือที่เฉพาะเจาะจง โดยที่คุณไม่ต้องจัดการกับใบรับรอง (certificates) หรือคีย์ (keys) ในเครื่องมือที่คุณสร้างขึ้นเองเลย
4. การจัดคิวใน kernel. แม้ว่าข้อมูลจะถูกถอดรหัสและพร้อมใช้งานแล้ว แต่แอปพลิเคชันของคุณอาจไม่ได้เรียกใช้ข้อมูลนั้นทันที คุณจะแนบ kprobes เข้ากับฟังก์ชัน kernel ที่เกี่ยวข้องซึ่งจัดการ socket buffers และเหตุการณ์การจัดตารางเวลา (scheduling events) สิ่งนี้จะวัดค่า queueing latency: หรือเวลาที่คำขอต้องรออยู่ใน kernel land เนื่องจากโปรเซสของคุณกำลังแย่งชิง CPU หรือเพียงแค่ยังไม่ได้เรียก read()
แหล่งสัญญาณทั้งสี่จะเขียนเหตุการณ์ลงใน eBPF ring buffer โปรเซส sidecar ที่รันอยู่ใน userspace จะดึงข้อมูลออกจาก buffer นี้ นำเหตุการณ์มาเชื่อมโยงกันด้วย traceparent ID และสร้างไทม์ไลน์ที่สอดคล้องกันเพียงหนึ่งเดียวสำหรับทุกคำขอ สิ่งที่เคยเป็นเพียงเสียงรบกวน (noise) ของ kernel ที่ไม่เกี่ยวข้องกัน จะกลายเป็น trace ที่มีโครงสร้างชัดเจน
การอ่านเส้นทางทั้งหมด
ผลลัพธ์ที่ประกอบขึ้นมาแล้วมักจะแสดงผลในรูปแบบ flame graph หรือโครงสร้าง span tree ที่แบ่ง latency ออกเป็นสี่ส่วนที่ชัดเจน:
- 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
