MemoryUsageMetric


@ExperimentalMetricApi
public final class MemoryUsageMetric extends TraceMetric


Metric for tracking the memory usage of the target application.

Summary

Nested types

public enum MemoryUsageMetric.Mode extends Enum
public enum MemoryUsageMetric.SubMetric extends Enum

Public constructors

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

Public methods

@NonNull List<@NonNull Metric.Measurement>
getMeasurements(
    @NonNull Metric.CaptureInfo captureInfo,
    @NonNull TraceProcessor.Session traceSession
)

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

Public constructors

MemoryUsageMetric

public MemoryUsageMetric(
    @NonNull MemoryUsageMetric.Mode mode,
    @NonNull List<@NonNull MemoryUsageMetric.SubMetric> subMetrics,
    @NonNull String processNameSuffix,
    @NonNull String metricNameSuffix
)
Parameters
@NonNull MemoryUsageMetric.Mode 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.

@NonNull List<@NonNull MemoryUsageMetric.SubMetric> subMetrics

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.

@NonNull String processNameSuffix

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

@NonNull String metricNameSuffix

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 methods

getMeasurements

public @NonNull List<@NonNull Metric.MeasurementgetMeasurements(
    @NonNull Metric.CaptureInfo captureInfo,
    @NonNull TraceProcessor.Session traceSession
)

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()
        }
    }
}