MemoryUsageMetric


@ExperimentalMetricApi
class MemoryUsageMetric : TraceMetric


Metric for tracking the memory usage of the target application.

Summary

Public constructors

MemoryUsageMetric(
    mode: MemoryUsageMetric.Mode,
    subMetrics: List<MemoryUsageMetric.SubMetric>,
    processNameSuffix: String,
    metricNameSuffix: String
)

Public functions

open List<Metric.Measurement>
getMeasurements(
    captureInfo: Metric.CaptureInfo,
    traceSession: TraceProcessor.Session
)

Get the metric result for a given iteration given information about the target process and a TraceProcessor session

Public constructors

MemoryUsageMetric

MemoryUsageMetric(
    mode: MemoryUsageMetric.Mode,
    subMetrics: List<MemoryUsageMetric.SubMetric> = listOf(SubMetric.HeapSize, SubMetric.RssAnon, SubMetric.RssFile, SubMetric.Gpu),
    processNameSuffix: String = "",
    metricNameSuffix: String = processNameSuffix.replace(oldValue = ":", newValue = "_")
)
Parameters
mode: MemoryUsageMetric.Mode

There are two modes for measurement - Last, which represents the last observed value during an iteration, and Max, which represents the largest sample observed per measurement.

subMetrics: List<MemoryUsageMetric.SubMetric> = listOf(SubMetric.HeapSize, SubMetric.RssAnon, SubMetric.RssFile, SubMetric.Gpu)

By default, reports:

  • memoryRssAnonKb - Anonymous resident/allocated memory owned by the process, not including memory mapped files or shared memory.

  • memoryRssAnonFileKb - Memory allocated by the process to map files.

  • memoryHeapSizeKb - Heap memory allocations from the Android Runtime, sampled after each GC.

  • memoryGpuKb - GPU Memory allocated for the process.

By passing a custom subMetrics list, you can enable other SubMetrics.

processNameSuffix: String = ""

A suffix appended to the app's package name for subprocesses. This is useful when there are separate subprocesses of the app.

metricNameSuffix: String = processNameSuffix.replace(oldValue = ":", newValue = "_")

A suffix appended to the metric names. Use this to distinguish metrics collected from different subprocesses in the app. Defaults to processNameSuffix with ":" replaced by "_".

Public functions

getMeasurements

open fun getMeasurements(
    captureInfo: Metric.CaptureInfo,
    traceSession: TraceProcessor.Session
): List<Metric.Measurement>

Get the metric result for a given iteration given information about the target process and a TraceProcessor session

import androidx.benchmark.macro.ExperimentalMetricApi
import androidx.benchmark.macro.TraceMetric
import androidx.benchmark.traceprocessor.TraceProcessor

/**
 * Calculates latency of sections where begin and end trace points are in different processes.
 *
 * @param beginPointName Name of begin tracepoint.
 * @param endPointName Name of end tracepoint.
 * @param eventName Name of the final metric that is spit out after the test run. The metric
 *   name will be {$eventName}LatencyMillis.
 */
@OptIn(ExperimentalMetricApi::class)
class CrossProcessLatencyMetricSample(
    private val beginPointName: String,
    private val endPointName: String,
    private val eventName: String,
) : TraceMetric() {
    @OptIn(ExperimentalMetricApi::class)
    override fun getMeasurements(
        captureInfo: CaptureInfo,
        traceSession: TraceProcessor.Session,
    ): List<Measurement> {
        val query =
            """
         INCLUDE perfetto module slices.with_context;
         SELECT
             event,
             ts2-ts1 AS latency_in_nanos
         FROM
         (
           (SELECT
               ts AS ts1,
               "event" AS event,
               LOWER(TRIM(process_name)) as process_name_1
               FROM thread_slice WHERE name = '${beginPointName}'
           )
           LEFT JOIN
           (SELECT
               ts AS ts2,
               "event" AS event,
               LOWER(TRIM(process_name)) as process_name_2
               FROM thread_slice WHERE name = '${endPointName}'
           )
           USING (event)
         )
           """
                .trimIndent() // maybe add checks for process name etc too in query

        val rowSequence = traceSession.query(query)
        // First row (or null) is returned.
        val latencyResultNanos = rowSequence.firstOrNull()?.long("latency_in_nanos")
        return if (latencyResultNanos != null) {
            listOf(Measurement(eventName + "LatencyMillis", latencyResultNanos / 1_000_000.0))
        } else {
            emptyList()
        }
    }
}