প্রক্রিয়াধীন ট্রেসিং (পরীক্ষামূলক)

নতুন androidx.tracing:tracing:2.0.0-alpha01 লাইব্রেরি হল একটি লো-ওভারহেড কোটলিন API যা ইন-প্রসেস ট্রেস ইভেন্টগুলি ক্যাপচার করার অনুমতি দেয়। এই ইভেন্টগুলি সময়ের স্লাইস এবং তাদের প্রেক্ষাপট ক্যাপচার করতে পারে। লাইব্রেরিটি অতিরিক্তভাবে কোটলিন কোরোটিনের জন্য প্রেক্ষাপট প্রচারকে সমর্থন করে।

লাইব্রেরিটি একই Perfetto ট্রেস প্যাকেট ফর্ম্যাট ব্যবহার করে যা অ্যান্ড্রয়েড ডেভেলপাররা জানেন। এছাড়াও, Tracing 2.0 ( 1.0.0-* API গুলির বিপরীতে) প্লাগেবল ট্রেসিং ব্যাকএন্ড এবং সিঙ্কের ধারণাকে সমর্থন করে, যাতে অন্যান্য ট্রেসিং লাইব্রেরিগুলি আউটপুট ট্রেসিং ফর্ম্যাট এবং তাদের বাস্তবায়নে প্রসঙ্গ প্রচার কীভাবে কাজ করে তা কাস্টমাইজ করতে পারে।

নির্ভরতা

ট্রেসিং শুরু করতে, আপনার build.gradle.kts এ নিম্নলিখিত নির্ভরতাগুলি সংজ্ঞায়িত করতে হবে।

kotlin {
  androidLibrary {
    namespace = "com.example.library"
    // ...
  }
  sourceSets {
    androidMain {
      dependencies {
        api("androidx.tracing:tracing-wire-android:2.0.0-alpha01")
        // ...
      }
    }
    jvmMain {
      dependencies {
        api("androidx.tracing:tracing-wire-desktop:2.0.0-alpha01")
        // ...
      }
    }
  }
}

যদি আপনি কোন অ্যান্ড্রয়েড লাইব্রেরি বা অ্যাপ্লিকেশনকে টার্গেট করেন, তাহলে androidx.tracing:tracing-wire-android:2.0.0-alpha01 এর উপর নির্ভরতা ঘোষণা করুন। যদি আপনি JVM কে টার্গেট করেন, তাহলে আপনি androidx.tracing:tracing-wire-desktop:2.0.0-alpha01 উপর নির্ভরতা ব্যবহার করতে পারেন।

মৌলিক ব্যবহার

TraceSink নির্ধারণ করে কিভাবে ট্রেস প্যাকেটগুলি সিরিয়ালাইজ করা হয়। ট্রেসিং 2.0.0 একটি সিঙ্কের বাস্তবায়নের সাথে আসে যা Perfetto ট্রেস প্যাকেট ফর্ম্যাট ব্যবহার করে। একটি TraceDriver Tracer একটি হ্যান্ডেল সরবরাহ করে এবং একটি ট্রেস চূড়ান্ত করতে ব্যবহার করা যেতে পারে।

আপনি যদি কিছু অ্যাপ্লিকেশন ভেরিয়েন্টে একেবারেই ট্রেস না করতে চান, তাহলে অ্যাপ্লিকেশনের সমস্ত ট্রেস পয়েন্ট অক্ষম করতে TraceDriver ব্যবহার করতে পারেন। TraceDriver-এর ভবিষ্যতের API গুলি ডেভেলপারদের কোন ট্রেস বিভাগগুলি ক্যাপচার করতে আগ্রহী তা নিয়ন্ত্রণ করার অনুমতি দেবে (অথবা যখন কোনও বিভাগ শব্দ করে তখন অক্ষম করবে)।

শুরু করতে, একটি TraceSink এবং একটি TraceDriver এর একটি উদাহরণ তৈরি করুন।

/**
 * A [TraceSink] defines how traces are serialized.
 *
 * [androidx.tracing.wire.TraceSink] uses the `Perfetto` trace packet format.
 */
fun createSink(): TraceSink {
    val outputDirectory = File(/* path = */ "/tmp/perfetto")
    if (!outputDirectory.exists()) {
        outputDirectory.mkdirs()
    }
    // We are using the factory function defined in androidx.tracing.wire
    return TraceSink(
        sequenceId = 1,
        directory = outputDirectory
    )
}
/**
 * Creates a new instance of [androidx.tracing.TraceDriver].
 */
fun createTraceDriver(): TraceDriver {
    // We are using a factory function from androidx.tracing.wire here.
    // `isEnabled` controls whether tracing is enabled for the application.
    val driver = TraceDriver(sink = createSink(), isEnabled = true)
    return driver
}

TraceDriver এর একটি ইনস্ট্যান্স পাওয়ার পর, Tracer টি পান যা সমস্ত ট্রেসিং API-এর জন্য এন্ট্রি পয়েন্ট নির্ধারণ করে।

// Tracing Categories identify subsystems that are responsible
// in generating trace sections. Future APIs in `TraceDriver` will allow the
// application to specify which categories they are interested in tracing.
// This lets the application disable entire trace categories, without
// needing to disable trace instrumentation at the call sites for those
// categories.

internal const val CATEGORY_MAIN = "main"

fun main() {
    val driver = createTraceDriver()
    driver.use {
        driver.tracer.trace(category = CATEGORY_MAIN, name = "basic") {
            Thread.sleep(100L)
        }
    }
}

এটি নিম্নলিখিত ট্রেস তৈরি করে।

একটি বেসিক পারফেটো ট্রেসের স্ক্রিন ক্যাপচার

চিত্র ১. একটি মৌলিক পারফেটো ট্রেসের স্ক্রিন ক্যাপচার।

আপনি দেখতে পাচ্ছেন যে সঠিক প্রক্রিয়া এবং থ্রেড ট্র্যাকগুলি পূর্ণ হয়েছে এবং একটি একক ট্রেস বিভাগ তৈরি করেছে basic , যা 100ms ধরে চলেছিল।

ট্রেস সেকশন (অথবা স্লাইস) একই ট্র্যাকে নেস্ট করা যেতে পারে যাতে ওভারল্যাপিং ইভেন্টগুলি দেখা যায়। এখানে একটি উদাহরণ দেওয়া হল।

fun main() {
    // Initialize the tracing infrastructure to monitor app performance
    val driver = createTraceDriver()
    val tracer = driver.tracer
    driver.use {
        tracer.trace(
            category = CATEGORY_MAIN,
            name = "processImage",
        ) {
            // Load the data first, then apply the sharpen filter
            sharpen(tracer = tracer, output = loadImage(tracer))
        }
    }
}

internal fun loadImage(tracer: Tracer): ByteArray {
    return tracer.trace(CATEGORY_MAIN, "loadImage") {
        // Loads an image
        // ...
        // A placeholder
        ByteArray(0)
    }
}

internal fun sharpen(tracer: Tracer, output: ByteArray) {
    // ...
    tracer.trace(CATEGORY_MAIN, "sharpen") {
        // ...
    }
}

এটি নিম্নলিখিত ট্রেস তৈরি করে।

নেস্টেড বিভাগ সহ একটি বেসিক পারফেটো ট্রেসের স্ক্রিন ক্যাপচার

চিত্র ২। নেস্টেড বিভাগ সহ একটি মৌলিক পারফেটো ট্রেসের স্ক্রিন ক্যাপচার।

তুমি দেখতে পাচ্ছ যে মূল থ্রেড ট্র্যাকে ওভারল্যাপিং ইভেন্ট রয়েছে। এটা খুব স্পষ্ট যে processImage একই থ্রেডে loadImage এবং sharpen কল করে।

ট্রেস বিভাগে অতিরিক্ত মেটাডেটা যোগ করুন

কখনও কখনও, আরও বিশদ জানতে ট্রেস স্লাইসের সাথে অতিরিক্ত প্রাসঙ্গিক মেটাডেটা সংযুক্ত করা কার্যকর হতে পারে। এই ধরনের মেটাডেটার কিছু উদাহরণের মধ্যে থাকতে পারে ব্যবহারকারী যে nav destination আছেন, অথবা input arguments যা একটি ফাংশন কত সময় নেয় তা নির্ধারণ করতে পারে।

fun main() {
    val driver = createTraceDriver()
    driver.use {
        driver.tracer.trace(
            category = CATEGORY_MAIN,
            name = "basicWithContext",
            // Add additional metadata
            metadataBlock = {
                // Add key value pairs.
                addMetadataEntry("key", "value")
                addMetadataEntry("count", 1L)
            }
        ) {
            Thread.sleep(100L)
        }
    }
}

এর ফলে নিম্নলিখিত ফলাফল পাওয়া যাবে। মনে রাখবেন Arguments বিভাগে slice তৈরি করার সময় কী ভ্যালু জোড়া যোগ করা হয়েছে।

অতিরিক্ত মেটাডেটা সহ একটি বেসিক পারফেটো ট্রেসের স্ক্রিন ক্যাপচার

চিত্র ৩। অতিরিক্ত মেটাডেটা সহ একটি মৌলিক পারফেটো ট্রেসের স্ক্রিন ক্যাপচার।

প্রসঙ্গ প্রচার

কোটলিন কোরোটিন (অথবা অন্যান্য অনুরূপ ফ্রেমওয়ার্ক যা সমসাময়িক কাজের চাপে সাহায্য করে) ব্যবহার করার সময়, ট্রেসিং 2.0 প্রসঙ্গ প্রচারের ধারণাকে সমর্থন করে। এটি একটি উদাহরণের মাধ্যমে সবচেয়ে ভালোভাবে ব্যাখ্যা করা হয়েছে।

suspend fun taskOne(tracer: Tracer) {
    tracer.traceCoroutine(category = CATEGORY_MAIN, "taskOne") {
        delay(timeMillis = 100L)
    }
}

suspend fun taskTwo(tracer: Tracer) {
    tracer.traceCoroutine(category = CATEGORY_MAIN, "taskTwo") {
        delay(timeMillis = 50L)
    }
}

fun main() = runBlocking(context = Dispatchers.Default) {
    val driver = createTraceDriver()
    val tracer = driver.tracer
    driver.use {
        tracer.traceCoroutine(category = CATEGORY_MAIN, name = "main") {
            coroutineScope {
                launch { taskOne(tracer) }
                launch { taskTwo(tracer) }
            }
        }
        println("All done")
    }
}

এটি নিম্নলিখিত ফলাফল তৈরি করে।

প্রসঙ্গ প্রচার সহ একটি পারফেটো ট্রেসের স্ক্রিন ক্যাপচার

চিত্র ৪. প্রসঙ্গ প্রচার সহ একটি মৌলিক পারফেটো ট্রেসের স্ক্রিন ক্যাপচার।

কনটেক্সট প্রোপাগেশন এক্সিকিউশনের প্রবাহ কল্পনা করা অনেক সহজ করে তোলে। আপনি ঠিক কোন কাজগুলি সম্পর্কিত ছিল (অন্যদের সাথে সংযুক্ত), এবং ঠিক কখন Threads স্থগিত এবং পুনরায় শুরু করা হয়েছিল তা দেখতে পাবেন।

উদাহরণস্বরূপ, আপনি দেখতে পাচ্ছেন যে slice main taskOne এবং taskTwo তৈরি হয়েছে। এরপর উভয় থ্রেডই নিষ্ক্রিয় ছিল (যেহেতু coroutine গুলি স্থগিত করা হয়েছিল - delay ব্যবহারের কারণে)।

ম্যানুয়াল প্রচার

কখনও কখনও যখন আপনি কোটলিন কর্উটিন ব্যবহার করে জাভা Executor উদাহরণের সাথে সমসাময়িক ওয়ার্কলোড মিশ্রিত করেন, তখন একটি থেকে অন্যটিতে প্রসঙ্গটি প্রচার করা কার্যকর হতে পারে। এখানে একটি উদাহরণ দেওয়া হল:

fun executorTask(
    tracer: Tracer,
    token: PropagationToken,
    executor: Executor,
    callback: () -> Unit
) {
    executor.execute {
        tracer.trace(
            category = CATEGORY_MAIN,
            name = "executeTask",
            token = token,
        ) {
            // Do something
            Thread.sleep(100)
            callback()
        }
    }
}

@OptIn(DelicateTracingApi::class)
fun main() = runBlocking(context = Dispatchers.Default) {
    val driver = createTraceDriver()
    val executor = Executors.newSingleThreadExecutor()
    val tracer = driver.tracer
    driver.use {
        tracer.traceCoroutine(category = CATEGORY_MAIN, name = "main") {
            coroutineScope {
                val deferred = CompletableDeferred<Unit>()
                executorTask(
                    tracer = tracer,
                    // Obtain the propagation token from the CoroutineContext
                    token = tracer.tokenFromCoroutineContext(),
                    executor = executor,
                    callback = {
                        deferred.complete(Unit)
                    }
                )
                deferred.await()
            }
        }
        executor.shutdownNow()
    }
}

এটি নিম্নলিখিত ফলাফল তৈরি করে।

ম্যানুয়াল প্রসঙ্গ প্রচারের মাধ্যমে একটি পারফেটো ট্রেসের স্ক্রিন ক্যাপচার

চিত্র ৫। ম্যানুয়াল প্রসঙ্গ প্রচারের মাধ্যমে একটি মৌলিক পারফেটো ট্রেসের স্ক্রিন ক্যাপচার।

আপনি দেখতে পাচ্ছেন যে এক্সিকিউশনটি একটি CoroutineContext এ শুরু হয়েছিল এবং পরবর্তীতে একটি Java Executor এ স্যুইচ করা হয়েছিল, কিন্তু আমরা এখনও context propagation ব্যবহার করতে সক্ষম হয়েছি।

সিস্টেম ট্রেসের সাথে একত্রিত করুন

নতুন androidx.tracing CPU সময়সূচী, মেমোরি ব্যবহার এবং অপারেটিং সিস্টেমের সাথে অ্যাপ্লিকেশনের মিথস্ক্রিয়ার মতো তথ্য ক্যাপচার করে না। এর কারণ হল লাইব্রেরিটি খুব কম ওভারহেড ইন-প্রসেস ট্রেসিং সম্পাদন করার একটি উপায় প্রদান করে।

তবে, সিস্টেম ট্রেসগুলিকে ইন-প্রসেস ট্রেসের সাথে একত্রিত করা এবং প্রয়োজনে সেগুলিকে একক ট্রেস হিসাবে কল্পনা করা অত্যন্ত তুচ্ছ। এর কারণ হল Perfetto UI একটি ডিভাইস থেকে একটি ইউনিফাইড টাইমলাইনে একাধিক ট্রেস ফাইল ভিজ্যুয়ালাইজ করা সমর্থন করে।

এটি করার জন্য, আপনি এখানে নির্দেশাবলী অনুসরণ করে Perfetto UI ব্যবহার করে একটি সিস্টেম ট্রেসিং সেশন শুরু করতে পারেন।

সিস্টেম ট্রেসিং চালু থাকা অবস্থায়, আপনি Tracing 2.0 API ব্যবহার করে ইন-প্রসেস ট্রেস ইভেন্ট রেকর্ড করতে পারেন। একবার আপনার দুটি ট্রেস ফাইল হয়ে গেলে আপনি Perfetto তে Open Multiple Trace Files বিকল্পটি ব্যবহার করতে পারেন।

Perfetto UI তে একাধিক ট্রেস ফাইল খোলা হচ্ছে

চিত্র ৬। Perfetto UI তে একাধিক ট্রেস ফাইল খোলা।

উন্নত কর্মপ্রবাহ

স্লাইসগুলি সম্পর্কযুক্ত করুন

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

fun main() {
    val driver = createTraceDriver()
    onEvent(driver, eventId = EVENT_ID)
}

fun onEvent(driver: TraceDriver, eventId: Long) {
    driver.use {
        driver.tracer.trace(
            category = CATEGORY_MAIN,
            name = "step-1",
            metadataBlock = {
                addCorrelationId(eventId)
            }
        ) {
            Thread.sleep(100L)
        }

        Thread.sleep(20)

        driver.tracer.trace(
            category = CATEGORY_MAIN,
            name = "step-2",
            metadataBlock = {
                addCorrelationId(eventId)
            }
        ) {
            Thread.sleep(180)
        }
    }
}

এটি নিম্নলিখিত ফলাফল তৈরি করে।

পারফেটো ট্রেসের স্ক্রিন ক্যাপচার, যার সাথে সম্পর্কযুক্ত স্লাইস রয়েছে

চিত্র ৭। পারফেটো ট্রেসের স্ক্রিন ক্যাপচার, পারস্পরিক সম্পর্কযুক্ত স্লাইস সহ।

কল স্ট্যাকের তথ্য যোগ করুন

হোস্ট সাইড টুলগুলি (কম্পাইলার প্লাগইন, অ্যানোটেশন প্রসেসর ইত্যাদি) অতিরিক্তভাবে একটি ট্রেসে কল স্ট্যাক তথ্য এম্বেড করতে পারে, যাতে একটি ট্রেসে একটি ট্রেস বিভাগ তৈরির জন্য দায়ী ফাইল, ক্লাস বা পদ্ধতিটি সনাক্ত করা সুবিধাজনক হয়।

fun main() {
    val driver = createTraceDriver()
    driver.use {
        driver.tracer.trace(
            category = CATEGORY_MAIN,
            name = "callStackEntry",
            metadataBlock = {
                addCallStackEntry(
                    name = "main",
                    lineNumber = 14,
                    sourceFile = "Basic.kt"
                )
            }
        ) {
            Thread.sleep(100L)
        }
    }
}

এটি নিম্নলিখিত ফলাফল তৈরি করে।

কল স্ট্যাক তথ্য সহ একটি Perfetto ট্রেসের স্ক্রিন ক্যাপচার

চিত্র ৮। কল স্ট্যাক তথ্য সহ একটি পারফেটো ট্রেসের স্ক্রিন ক্যাপচার।