PerfettoConfig.kt

/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.benchmark.macro.perfetto

import android.os.Build
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import perfetto.protos.DataSourceConfig
import perfetto.protos.FtraceConfig
import perfetto.protos.MeminfoCounters
import perfetto.protos.ProcessStatsConfig
import perfetto.protos.SysStatsConfig
import perfetto.protos.TraceConfig
import perfetto.protos.TraceConfig.BufferConfig
import perfetto.protos.TraceConfig.BufferConfig.FillPolicy

private val FTRACE_DATA_SOURCE = TraceConfig.DataSource(
    config = DataSourceConfig(
        name = "linux.ftrace",
        target_buffer = 0,
        ftrace_config = FtraceConfig(
            // These parameters affect only the kernel trace buffer size and how
            // frequently it gets moved into the userspace buffer defined above.
            buffer_size_kb = 16384,
            drain_period_ms = 250,
            ftrace_events = listOf(
                // We need to do process tracking to ensure kernel ftrace events targeted at short-lived
                // threads are associated correctly
                "task/task_newtask",
                "task/task_rename",
                "sched/sched_process_exit",
                "sched/sched_process_free",

                // Memory events
                "mm_event/mm_event_record",
                "kmem/rss_stat",
                "kmem/ion_heap_shrink",
                "kmem/ion_heap_grow",
                "ion/ion_stat",
                "oom/oom_score_adj_update",

                // Old (kernel) LMK
                "lowmemorykiller/lowmemory_kill",
            ),
            atrace_categories = listOf(
                AtraceTag.ActivityManager,
                AtraceTag.BinderDriver,
                AtraceTag.Camera,
                AtraceTag.Dalvik,
                AtraceTag.Frequency,
                AtraceTag.Graphics,
                AtraceTag.HardwareModules,
                AtraceTag.Idle,
                AtraceTag.Input,
                AtraceTag.MemReclaim,
                AtraceTag.Resources,
                AtraceTag.Scheduling,
                AtraceTag.Synchronization,
                AtraceTag.View,
                AtraceTag.WindowManager
                // "webview" not included to workaround b/190743595
                // "memory" not included as some Q devices requiring ftrace_event
                // configuration directly to collect this data. See b/171085599
            ).filter {
                // filter to only supported tags on unrooted build
                // TODO: use root-only tags as needed
                it.supported(api = Build.VERSION.SDK_INT, rooted = false)
            }.map {
                it.tag
            },
            // Trace all apps for now
            atrace_apps = listOf("*")
        )
    )
)

private val PROCESS_STATS_DATASOURCE = TraceConfig.DataSource(
    config = DataSourceConfig(
        name = "linux.process_stats",
        target_buffer = 1,
        process_stats_config = ProcessStatsConfig(
            proc_stats_poll_ms = 10000
        )
    )
)

private val LINUX_SYS_STATS_DATASOURCE = TraceConfig.DataSource(
    config = DataSourceConfig(
        name = "linux.sys_stats",
        target_buffer = 1,
        sys_stats_config = SysStatsConfig(
            meminfo_period_ms = 1000,
            meminfo_counters = listOf(
                MeminfoCounters.MEMINFO_MEM_TOTAL,
                MeminfoCounters.MEMINFO_MEM_FREE,
                MeminfoCounters.MEMINFO_MEM_AVAILABLE,
                MeminfoCounters.MEMINFO_BUFFERS,
                MeminfoCounters.MEMINFO_CACHED,
                MeminfoCounters.MEMINFO_SWAP_CACHED,
                MeminfoCounters.MEMINFO_ACTIVE,
                MeminfoCounters.MEMINFO_INACTIVE,
                MeminfoCounters.MEMINFO_ACTIVE_ANON,
                MeminfoCounters.MEMINFO_INACTIVE_ANON,
                MeminfoCounters.MEMINFO_ACTIVE_FILE,
                MeminfoCounters.MEMINFO_INACTIVE_FILE,
                MeminfoCounters.MEMINFO_UNEVICTABLE,
                MeminfoCounters.MEMINFO_SWAP_TOTAL,
                MeminfoCounters.MEMINFO_SWAP_FREE,
                MeminfoCounters.MEMINFO_DIRTY,
                MeminfoCounters.MEMINFO_WRITEBACK,
                MeminfoCounters.MEMINFO_ANON_PAGES,
                MeminfoCounters.MEMINFO_MAPPED,
                MeminfoCounters.MEMINFO_SHMEM,
            )
        )
    )
)

/**
 * Global config for perfetto.
 *
 * Eventually, this should be configurable.
 */
internal val PERFETTO_CONFIG = TraceConfig(
    buffers = listOf(
        BufferConfig(size_kb = 16384, FillPolicy.RING_BUFFER),
        BufferConfig(size_kb = 16384, FillPolicy.RING_BUFFER)
    ),
    data_sources = listOf(
        FTRACE_DATA_SOURCE,
        PROCESS_STATS_DATASOURCE,
        LINUX_SYS_STATS_DATASOURCE,
    ),
)

internal fun TraceConfig.validateAndEncode(): ByteArray {
    val ftraceConfig = data_sources
        .mapNotNull { it.config?.ftrace_config }
        .first()

    // check tags against known-supported tags based on SDK_INT / root status
    val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
    val supportedTags = AtraceTag.supported(
        api = Build.VERSION.SDK_INT,
        rooted = device.isShellSessionRooted()
    ).map { it.tag }.toSet()

    val unsupportedTags = (ftraceConfig.atrace_categories - supportedTags)
    check(unsupportedTags.isEmpty()) {
        "Error - attempted to use unsupported atrace tags: $unsupportedTags"
    }
    return encode()
}