একজন ব্যবহারকারী একটি টিকিট ওপেন করলেন: মোবাইল অ্যাপটি মন্থর। আপনি আপনার ড্যাশবোর্ডগুলো পরীক্ষা করলেন। CPU ব্যবহার স্থিতিশীল। এরর রেট শূন্য। আপনার APM লাইটগুলো আশ্বস্তকরভাবে সবুজ দেখাচ্ছে। দৃশ্যমান প্রতিটি মাপকাঠিতে ব্যাকএন্ডটি সুস্থ। কিন্তু ব্যবহারকারী ভুল নন। ধীরগতিটি বাস্তব, এবং এটি একটি অ্যান্ড্রয়েড ডিভাইস এবং আপনার সার্ভারের মধ্যবর্তী দীর্ঘ, অস্পষ্ট পথের কোথাও ঘটছে।

সমস্যাটি হলো বেশিরভাগ অবজারভেবিলিটি টুলস অ্যাপ্লিকেশন বাউন্ডারিতে এসে থেমে যায়। আপনার ফ্রেমওয়ার্ক একটি রিকোয়েস্ট পার্স করার পর যা ঘটে, তারা কেবল তা-ই পরিমাপ করে। তারা ডাটাবেস কুয়েরি, ক্যাশ হিট এবং ডাউনস্ট্রিম সার্ভিস কল ট্র্যাক করে। কিন্তু তারা রিকোয়েস্টের মেকানিক্স বা কার্যপদ্ধতি মিস করে: একটি OkHttp কল অ্যান্ড্রয়েড HTTP স্ট্যাকের ভেতরে কতটা সময় ব্যয় করে, একটি অস্থির মোবাইল নেটওয়ার্কের মধ্য দিয়ে যাতায়াত, TLS হ্যান্ডশেক নেগোসিয়েশন, এবং আপনার কোড চলার আগে কার্নেল কিউতে যে নীরব অপেক্ষা ঘটে। এই ফাঁকগুলো মিলিসেকেন্ড—অথবা পুরো সেকেন্ড—গিলে ফেলে, যখন আপনার APM নীরব থাকে।

এনক্রিপশন এই অন্ধত্বকে পূর্ণতা দেয়। আধুনিক অ্যান্ড্রয়েড অ্যাপ সবকিছু BoringSSL-এর মাধ্যমে রাউট করে। একটি প্যাকেট কার্নেল নেটওয়ার্ক স্ট্যাকে পৌঁছানোর আগেই HTTP হেডারগুলো এনক্রিপ্ট হয়ে যায়। একটি স্ট্যান্ডার্ড tcpdump বা নেটওয়ার্ক হুক কেবল অস্পষ্ট TLS রেকর্ড দেখতে পায়। আপনি দেখতে পারেন যে ট্রাফিক প্রবাহিত হচ্ছে, কিন্তু আপনি এটি পড়তে পারেন না। আপনি নিশ্চিতভাবে একটি নির্দিষ্ট কার্নেল-লেভেল TCP সেগমেন্টকে কোনো নির্দিষ্ট ব্যবহারকারীর API কলের সাথে যুক্ত করতে পারবেন না। আপনার প্রয়োজনীয় ট্রেস কনটেক্সটটি সাইফারটেক্সটের (ciphertext) ভেতরে আটকা পড়ে থাকে।

eBPF সমীকরণটি বদলে দেয় কারণ এটি আপনার অ্যাপ্লিকেশন কোড পরিবর্তন না করেই সিস্টেমকে ভেতর থেকে ইনস্ট্রুমেন্ট করতে দেয়। আপনার অ্যাপকে তার নিজস্ব ল্যাটেন্সি রিপোর্ট করতে বলার পরিবর্তে, আপনি সরাসরি কার্নেল এবং গুরুত্বপূর্ণ ইউজারস্পেস লাইব্রেরিগুলোতে ছোট ছোট প্রোগ্রাম যুক্ত করতে পারেন। এই প্রোগ্রামগুলো ঘটনা ঘটার সাথে সাথে তা পর্যবেক্ষণ করে, আপনার প্রয়োজনীয় তথ্য সংগ্রহ করে এবং একটি রিং বাফারে পাঠিয়ে দেয়। একটি হালকা হেডার ছাড়া আপনার অ্যান্ড্রয়েড APK-এর ভেতরে কোনো SDK bloat থাকে না, এবং আপনার ব্যাকএন্ড ক্লাসগুলো পুনরায় লেখার জন্য কোনো ইনস্ট্রুমেন্টেশন এজেন্টও প্রয়োজন হয় না।

চার-স্তরীয় সেটআপ

এই পাইপলাইনটি তৈরি করতে পর্যবেক্ষণের চারটি স্বতন্ত্র স্তর প্রয়োজন।

১. traceparent অ্যাঙ্কর। অ্যান্ড্রয়েড ডিভাইসে, আপনি একটি OkHttp ইন্টারসেপ্টর যোগ করবেন যা প্রতিটি আউটবাউন্ড রিকোয়েস্টে একটি W3C traceparent হেডার ইনজেক্ট করে। মোবাইল সাইডে এটিই একমাত্র প্রয়োজনীয় পরিবর্তন, এবং এটি অত্যন্ত নগণ্য। হেডারটি এনক্রিপ্টেড পেলোডের ভেতরে আপনার ব্যাকএন্ড পর্যন্ত ভ্রমণ করে। যেহেতু এটি HTTP লেয়ারের ভেতরে থাকে, তাই TLS ইঞ্জিন যা প্লেইনটেক্সট হিসেবে প্রকাশ করে, এটি তার ভেতরেই টিকে থাকে।

২. TCP আগমন সময় (TCP arrival timing)। ব্যাকএন্ড হোস্টের ওপর, আপনি নেটওয়ার্ক ইন্টারফেসে সংযুক্ত eBPF Traffic Control হুক ব্যবহার করবেন। এই প্রোগ্রামগুলো প্রতিটি পৃথক TCP সেগমেন্ট আসার সাথে সাথে সক্রিয় হয়। তারা এজ (edge) প্রান্তে সিকোয়েন্স নম্বর এবং টাইমস্ট্যাম্প ক্যাপচার করে। এখন আপনি সুনির্দিষ্টভাবে জানতে পারবেন কখন বিটগুলো তার (wire) থেকে বেরিয়ে আপনার মেশিনে প্রবেশ করেছে, আপনার অ্যাপ্লিকেশন একটি মাত্র বাইট পড়ার অনেক আগেই।

৩. ডিক্রিপশন প্রোব (Decryption probes)। আপনার ব্যাকএন্ড OpenSSL বা BoringSSL ব্যবহার করে TLS টার্মিনেট করে। এখানেই আর্কিটেকচারটি আকর্ষণীয় হয়ে ওঠে। uprobes—ডায়নামিক ইউজারস্পেস প্রোব—ব্যবহার করে আপনি TLS লাইব্রেরির ভেতরে SSL_write এবং SSL_read-এর সাথে যুক্ত হতে পারেন। এই ফাংশনগুলো প্লেইনটেক্সট ডেটা এনক্রিপশন ইঞ্জিনের মধ্য দিয়ে যাওয়ার সাথে সাথেই সক্রিয় হয়। আপনার eBPF প্রোগ্রাম সেই ডিক্রিপ্ট করা বাফারটি পড়ে, traceparent হেডারটি স্ক্যান করে এবং এটি এক্সট্র্যাক্ট করে। কার্নেল এখন একটি র-TCP ফ্লো এবং একটি নির্দিষ্ট মোবাইল রিকোয়েস্টের মধ্যে সরাসরি ম্যাপিং তৈরি করতে পারে, যেখানে আপনাকে কাস্টম টুলে সার্টিফিকেট বা কী (key) নিয়ে কাজ করতে হয় না।

৪. কার্নেল কিউয়িং (Kernel queueing)। ডেটা ডিক্রিপ্ট হওয়ার এবং প্রস্তুত হওয়ার পরেও, আপনার অ্যাপ্লিকেশন হয়তো এটি সাথে সাথে গ্রহণ নাও করতে পারে। আপনি সকেট বাফার এবং শিডিউলিং ইভেন্ট হ্যান্ডেল করা প্রাসঙ্গিক কার্নেল ফাংশনগুলোতে kprobes যুক্ত করতে পারেন। এটি কিউয়িং ল্যাটেন্সি পরিমাপ করে: রিকোয়েস্টগুলো কার্নেল ল্যান্ডে কতক্ষণ অপেক্ষা করে কারণ আপনার প্রসেসটি CPU-র জন্য প্রতিযোগিতা করছে বা কেবল read() কল করেনি।

এই চারটি সিগন্যাল সোর্সই একটি eBPF রিং বাফারে ইভেন্টগুলো লেখে। ইউজারস্পেসে চলা একটি সাইডকার প্রসেস এই বাফার থেকে ডেটা সংগ্রহ করে, traceparent ID দ্বারা ইভেন্টগুলোকে কোরিলেট করে এবং প্রতিটি রিকোয়েস্টের জন্য একটি একক, সুসংগত টাইমলাইন পুনর্গঠন করে। যা আগে বিচ্ছিন্ন কার্নেল নয়েজের সমষ্টি ছিল, তা এখন একটি স্ট্রাকচার্ড ট্রেসে পরিণত হয়।

সম্পূর্ণ পথটি পড়া

সংগৃহীত আউটপুটটি সাধারণত একটি ফ্লেম গ্রাফ (flame graph) বা একটি স্ট্রাকচার্ড স্প্যান ট্রি (structured 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