ट्रिगर के आधार पर प्रोफ़ाइलिंग

ProfilingManager, सिस्टम ट्रिगर के आधार पर प्रोफ़ाइलें कैप्चर करने की सुविधा देता है. सिस्टम, रिकॉर्डिंग की प्रोसेस को मैनेज करता है और आपके ऐप्लिकेशन को प्रोफ़ाइल उपलब्ध कराता है.

ट्रिगर, परफ़ॉर्मेंस से जुड़े अहम इवेंट से जुड़े होते हैं. सिस्टम से रिकॉर्ड की गई प्रोफ़ाइलें, इन ट्रिगर से जुड़ी क्रिटिकल यूज़र जर्नी (सीयूजे) के लिए, डीबग करने से जुड़ी पूरी जानकारी देती हैं.

पुराना डेटा कैप्चर करना

कई ट्रिगर के लिए, इवेंट से पहले के पुराने डेटा का विश्लेषण करना ज़रूरी होता है. ट्रिगर, अक्सर किसी समस्या की वजह से होता है, न कि समस्या की असली वजह. अगर ट्रिगर होने के बाद ही प्रोफ़ाइल बनाना शुरू किया जाता है, तो समस्या की असली वजह का पता नहीं चल पाता.

उदाहरण के लिए, यूज़र इंटरफ़ेस (यूआई) थ्रेड पर लंबे समय तक चलने वाली किसी कार्रवाई की वजह से, ऐप्लिकेशन काम नहीं कर रहा है (एएनआर) वाली गड़बड़ी होती है. सिस्टम को एएनआर का पता चलने और ऐप्लिकेशन को सिग्नल भेजने तक, हो सकता है कि कार्रवाई पूरी हो चुकी हो. ऐसे में, प्रोफ़ाइल बनाने पर, ब्लॉक करने वाली असली कार्रवाई का पता नहीं चल पाता.

कुछ ट्रिगर कब होंगे, इसका सटीक अनुमान लगाना मुमकिन नहीं है. इसलिए, मैन्युअल तरीके से पहले से प्रोफ़ाइल बनाना मुमकिन नहीं है.

ट्रिगर के आधार पर कैप्चर करने की सुविधा का इस्तेमाल क्यों करें?

प्रोफ़ाइलिंग ट्रिगर का इस्तेमाल करने की मुख्य वजह, ऐसे अनचाहे इवेंट का डेटा कैप्चर करना है जिनके लिए, ऐप्लिकेशन मैन्युअल तरीके से रिकॉर्डिंग शुरू नहीं कर सकता. प्रोफ़ाइलिंग ट्रिगर का इस्तेमाल इन कामों के लिए किया जा सकता है:

  • परफ़ॉर्मेंस से जुड़ी समस्याओं को डीबग करना: एएनआर, मेमोरी लीक, और स्थिरता से जुड़ी अन्य समस्याओं का विश्लेषण करना.
  • क्रिटिकल यूज़र जर्नी को ऑप्टिमाइज़ करना: फ़्लो का विश्लेषण करना और उन्हें बेहतर बनाना. उदाहरण के लिए, ऐप्लिकेशन का स्टार्टअप.
  • उपयोगकर्ताओं के व्यवहार को समझना: इवेंट के बारे में जानकारी पाना. उदाहरण के लिए, उपयोगकर्ता की ओर से ऐप्लिकेशन बंद करना.

ट्रिगर सेट अप करना

यहां दिए गए कोड से पता चलता है कि TRIGGER_TYPE_APP_FULLY_DRAWN ट्रिगर के लिए रजिस्टर कैसे करें और इस पर रेट लिमिट कैसे लागू करें.

Kotlin

fun recordWithTrigger() {
    val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java)

    val triggers = ArrayList<ProfilingTrigger>()

    val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN)
        .setRateLimitingPeriodHours(1)

    triggers.add(triggerBuilder.build())

    val mainExecutor: Executor = Executors.newSingleThreadExecutor()

    val resultCallback = Consumer<ProfilingResult> { profilingResult ->
        if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) {
            Log.d(
                "ProfileTest",
                "Received profiling result file=" + profilingResult.resultFilePath
            )
            setupProfileUploadWorker(profilingResult.resultFilePath)
        } else {
            Log.e(
                "ProfileTest",
                "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage
            )
        }
    }

    profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback)
    profilingManager.addProfilingTriggers(triggers)

}

Java

public void recordWithTrigger() {
  ProfilingManager profilingManager = getApplicationContext().getSystemService(
      ProfilingManager.class);
  List<ProfilingTrigger> triggers = new ArrayList<>();
  ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder(
      ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN);
  triggerBuilder.setRateLimitingPeriodHours(1);
  triggers.add(triggerBuilder.build());

  Executor mainExecutor = Executors.newSingleThreadExecutor();
  Consumer<ProfilingResult> resultCallback =
      new Consumer<ProfilingResult>() {
        @Override
        public void accept(ProfilingResult profilingResult) {
          if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) {
            Log.d(
                "ProfileTest",
                "Received profiling result file=" + profilingResult.getResultFilePath());
            setupProfileUploadWorker(profilingResult.getResultFilePath());
          } else {
            Log.e(
                "ProfileTest",
                "Profiling failed errorcode="
                    + profilingResult.getErrorCode()
                    + " errormsg="
                    + profilingResult.getErrorMessage());
          }
        }
      };
  profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback);
  profilingManager.addProfilingTriggers(triggers);

}

कोड में ये चरण पूरे किए जाते हैं:

  1. मैनेजर वापस लाएं: ProfilingManager सेवा को वापस लाता है.
  2. ट्रिगर तय करना: TRIGGER_TYPE_APP_FULLY_DRAWN के लिए ProfilingTrigger बनाता है. यह इवेंट तब होता है, जब ऐप्लिकेशन यह रिपोर्ट करता है कि उसने स्टार्टअप पूरा कर लिया है और वह इंटरैक्टिव है.
  3. रेट लिमिट सेट करना: इस खास ट्रिगर पर एक घंटे की रेट लिमिट लागू करता है (setRateLimitingPeriodHours(1)). इससे, ऐप्लिकेशन हर घंटे एक से ज़्यादा स्टार्टअप प्रोफ़ाइल रिकॉर्ड नहीं कर पाता.
  4. लिसनर को रजिस्टर करें: यह registerForAllProfilingResults को कॉल करता है, ताकि नतीजे को हैंडल करने वाले कॉलबैक को तय किया जा सके. इस कॉलबैक को, getResultFilePath() के ज़रिए सेव की गई प्रोफ़ाइल का पाथ मिलता है.
  5. ट्रिगर जोड़ना: ट्रिगर की सूची को ProfilingManager के साथ रजिस्टर करता है, जिसमें addProfilingTriggers का इस्तेमाल किया जाता है.
  6. इवेंट शुरू करना: reportFullyDrawn() को कॉल करता है. इससे, सिस्टम को TRIGGER_TYPE_APP_FULLY_DRAWN इवेंट मिलता है. इससे, प्रोफ़ाइल कलेक्शन ट्रिगर होता है. ऐसा माना जाता है कि सिस्टम बैकग्राउंड ट्रेस चल रहा था और रेट लिमिटर कोटा उपलब्ध है. यह ज़रूरी नहीं है. हालांकि, इससे एंड-टू-एंड फ़्लो का पता चलता है, क्योंकि आपके ऐप्लिकेशन को इस ट्रिगर के लिए reportFullyDrawn() को कॉल करना होगा.

ट्रेस वापस पाना

सिस्टम, ट्रिगर के आधार पर बनाई गई प्रोफ़ाइलों को उसी डायरेक्ट्री में सेव करता है जिसमें अन्य प्रोफ़ाइलें सेव की जाती हैं. ट्रिगर किए गए ट्रेस के लिए फ़ाइल का नाम इस फ़ॉर्मैट में होता है:

profile_trigger_<profile_type_code>_<datetime>.<profile-type-name>

एडीबी का इस्तेमाल करके, फ़ाइल को पुल किया जा सकता है. उदाहरण के लिए, एडीबी का इस्तेमाल करके, उदाहरण के तौर पर दिए गए कोड से कैप्चर किए गए सिस्टम ट्रेस को पुल करने के लिए, यह तरीका अपनाया जा सकता है:

adb pull /data/user/0/com.example.sampleapp/files/profiling/profile_trigger_1_2025-05-06-14-12-40.perfetto-trace

इन ट्रेस को विज़ुअलाइज़ करने के बारे में ज़्यादा जानने के लिए, प्रोफ़ाइलिंग डेटा वापस पाना और उसका विश्लेषण करना लेख पढ़ें.

बैकग्राउंड ट्रेसिंग कैसे काम करती है

ट्रिगर से पहले का डेटा कैप्चर करने के लिए, ओएस समय-समय पर बैकग्राउंड ट्रेस शुरू करता है. अगर बैकग्राउंड ट्रेस चालू रहने के दौरान कोई ट्रिगर होता है और आपका ऐप्लिकेशन इसके लिए रजिस्टर है, तो सिस्टम, ट्रेस प्रोफ़ाइल को आपके ऐप्लिकेशन की डायरेक्ट्री में सेव करता है. इसके बाद, प्रोफ़ाइल में वह जानकारी शामिल होगी जिसकी वजह से ट्रिगर हुआ.

प्रोफ़ाइल सेव होने के बाद, सिस्टम आपके ऐप्लिकेशन को registerForAllProfilingResults को दिए गए कॉलबैक का इस्तेमाल करके सूचना देता है. इस कॉलबैक में, कैप्चर की गई प्रोफ़ाइल का पाथ दिया जाता है. इसे ProfilingResult#getResultFilePath() को कॉल करके ऐक्सेस किया जा सकता है.

बैकग्राउंड ट्रेस स्नैपशॉट के काम करने का तरीका दिखाने वाला डायग्राम. इसमें दिखाया गया है कि ट्रिगर इवेंट से पहले, रिंग बफ़र डेटा कैप्चर करता है.
पहली इमेज: बैकग्राउंड ट्रेस स्नैपशॉट कैसे काम करते हैं.

डिवाइस की परफ़ॉर्मेंस और बैटरी लाइफ़ पर असर कम करने के लिए, सिस्टम लगातार बैकग्राउंड ट्रेस नहीं करता. इसके बजाय, यह सैंपलिंग के तरीके का इस्तेमाल करता है. सिस्टम, तय की गई समयावधि में (कम से कम और ज़्यादा से ज़्यादा अवधि के साथ) रैंडम तरीके से बैकग्राउंड ट्रेस शुरू करता है. इन ट्रेस को रैंडम तरीके से स्पेस करने से, ट्रिगर कवरेज बेहतर होता है.

सिस्टम से ट्रिगर की गई प्रोफ़ाइल का साइज़, सिस्टम के हिसाब से तय की गई ज़्यादा से ज़्यादा सीमा तक हो सकता है. इसलिए, ये रिंग बफ़र का इस्तेमाल करती हैं. बफ़र भर जाने के बाद, नया ट्रेस डेटा, पुराने डेटा को ओवरराइट कर देता है. पहली इमेज में दिखाए गए तरीके के मुताबिक, अगर बफ़र भर जाता है, तो कैप्चर किया गया ट्रेस, बैकग्राउंड रिकॉर्डिंग की पूरी अवधि को कवर नहीं कर सकता. इसके बजाय, यह ट्रिगर से पहले की सबसे हाल की गतिविधि को दिखाता है.

ट्रिगर के हिसाब से रेट लिमिट लागू करना

ज़्यादा फ़्रीक्वेंसी वाले ट्रिगर, आपके ऐप्लिकेशन के रेट लिमिटर कोटा को तेज़ी से खत्म कर सकते हैं. रेट लिमिटर के बारे में ज़्यादा जानने के लिए, हमारा सुझाव है कि रेट लिमिटर कैसे काम करता है लेख पढ़ें. किसी एक तरह के ट्रिगर से आपका कोटा खत्म होने से रोकने के लिए, ट्रिगर के हिसाब से रेट लिमिट लागू की जा सकती है.

ProfilingManager, ऐप्लिकेशन के हिसाब से तय की गई, ट्रिगर के हिसाब से रेट लिमिट लागू करने की सुविधा देता है. इससे, मौजूदा रेट लिमिटर के अलावा, समय के आधार पर थ्रॉटलिंग की एक और लेयर जोड़ी जा सकती है. किसी ट्रिगर के लिए, खास कूलडाउन टाइम सेट करने के लिए, setRateLimitingPeriodHours एपीआई का इस्तेमाल करें. कूलडाउन खत्म होने के बाद, इसे फिर से ट्रिगर किया जा सकता है.

ट्रिगर को बाहर भेजे बिना डीबग करना

बैकग्राउंड ट्रेस रैंडम समय पर चलते हैं. इसलिए, ट्रिगर को बाहर भेजे बिना डीबग करना मुश्किल है. टेस्टिंग के लिए, बैकग्राउंड ट्रेस को फ़ोर्स करने के लिए, एडीबी के इस कमांड का इस्तेमाल करें:

adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>

इस कमांड से, सिस्टम को तय किए गए पैकेज के लिए लगातार बैकग्राउंड ट्रेस शुरू करने के लिए फ़ोर्स किया जाता है. इससे, हर ट्रिगर, प्रोफ़ाइल इकट्ठा कर सकता है. हालांकि, इसके लिए रेट लिमिटर की अनुमति होनी चाहिए.

डीबग करने के अन्य विकल्प भी चालू किए जा सकते हैं. उदाहरण के लिए, बाहर भेजे बिना डीबग करते समय, रेट लिमिटर को बंद करना. ज़्यादा जानकारी के लिए, स्थानीय प्रोफ़ाइलिंग के लिए डीबग करने के कमांड लेख पढ़ें.