FrameStatsResult.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
internal data class FrameStatsResult(
/**
* Unique name of the window, from WindowManagerGlobal.getWindowName()
*
* e.g. "com.pkg/com.pkg.MyActivity/android.view.ViewRootImpl@ade24ea"
*/
val uniqueName: String,
/**
* Most recent clock monotonic (e.g. System.nanoTime()) timestamp of a frame's vsync.
*/
val lastFrameNs: Long?
) {
companion object {
private val NAME_REGEX = Regex("(\S+) \(visibility=[0-9]+\)")
fun parse(frameStatsOutput: String): List<FrameStatsResult> {
return frameStatsOutput
.split(Regex("\r?\n?---PROFILEDATA---\r?\n?"))
.chunked(2) // pair up metadata (with name) and associated profile data
.filter { it.size == 2 } // ignore partial trailing item
.map {
/**
* Example:
* ```
* com.pkg/com.pkg.MyActivity/android.view.ViewRootImpl@ade24ea (visibility=8)
* Window: com.pkg/com.pkg.MyActivity
* ```
*/
val (uniqueName) = it[0]
.split("\r?\n")
.firstNotNullOf { line ->
NAME_REGEX.find(line)
}.destructured
val profileData = it[1]
FrameStatsResult(
uniqueName = uniqueName,
lastFrameNs = profileDataLatestActivityLaunchNs(
profileData
)
)
}
}
/**
* Returns latest activity launch from profile data sections between `--PROFILEDATA--`
* markers in `dumpsys gfxinfo <pkg> framestats` output
*
* For example, in the following output:
* ```
* Flags,IntendedVsync,//...
* 1,3211995467212,//...
* 0,3212079627183,//...
* 1,5077693738881,//...
* 0,6038928372818,//...
* ```
*
* Would return `6038928372818` - most recent intended vsync of frame
*
* Note that we previously checked for flag & 0x1 when looking specifically for initial
* frames or a startup, but this behavior is inconsistent, especially on emulators
*/
private fun profileDataLatestActivityLaunchNs(profileData: String): Long? {
val lines = profileData.split(Regex("\r?\n"))
val columnLabels = lines.first().split(",")
val intendedVsyncIndex = columnLabels.indexOf("IntendedVsync")
lines.forEachIndexed { index, s -> println("$index $s") }
return lines
.drop(1)
.map {
it.split(",")[intendedVsyncIndex].toLong()
}
.maxOfOrNull { it }
}
}
}