Skip to content

Commit 46ff029

Browse files
Treehugger RobotGerrit Code Review
authored andcommitted
Merge "Add fully drawn to StartupTimingMetric output" into androidx-main
2 parents d8a816b + 2f8820c commit 46ff029

File tree

6 files changed

+265
-39
lines changed

6 files changed

+265
-39
lines changed

benchmark/benchmark-macro/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ dependencies {
8080
implementation(libs.wireRuntime)
8181

8282
androidTestImplementation(project(":internal-testutils-ktx"))
83+
androidTestImplementation(project(":activity:activity-ktx"))
8384
androidTestImplementation(libs.testExtJunit)
8485
androidTestImplementation(libs.testRules)
8586
androidTestImplementation(libs.testRunner)

benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/ConfigurableActivity.kt

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,66 @@
1616

1717
package androidx.benchmark.macro
1818

19-
import android.app.Activity
2019
import android.content.Intent
2120
import android.os.Bundle
2221
import android.util.Log
2322
import android.widget.TextView
23+
import androidx.activity.ComponentActivity
2424

2525
/**
2626
* Activity with configurable text and launch time, for testing.
2727
*/
28-
public class ConfigurableActivity : Activity() {
28+
class ConfigurableActivity : ComponentActivity() {
2929
override fun onCreate(savedInstanceState: Bundle?) {
3030
super.onCreate(savedInstanceState)
3131

32-
setContentView(
33-
TextView(this).apply {
34-
text = intent.getStringExtra(EXTRA_TEXT)
35-
}
36-
)
32+
val view = TextView(this).apply {
33+
text = intent.getStringExtra(EXTRA_TEXT)
34+
}
35+
setContentView(view)
3736

3837
val sleepDurMs = intent.getLongExtra(EXTRA_SLEEP_DUR_MS, 0)
3938
if (sleepDurMs > 0) {
4039
Log.d(TAG, "sleeping $sleepDurMs ms")
4140
Thread.sleep(sleepDurMs)
4241
Log.d(TAG, "sleep complete")
4342
}
43+
44+
val reportFullyDrawnDelayMs = intent.getLongExtra(
45+
EXTRA_REPORT_FULLY_DRAWN_DELAY_MS, /* default */ -1
46+
)
47+
when (reportFullyDrawnDelayMs) {
48+
-1L -> {} // ignore
49+
0L -> reportFullyDrawn() // report immediately
50+
else -> {
51+
// report delayed, modify text
52+
val runnable = {
53+
view.text = FULLY_DRAWN_TEXT
54+
reportFullyDrawn()
55+
}
56+
view.postDelayed(runnable, reportFullyDrawnDelayMs)
57+
}
58+
}
4459
}
4560

46-
public companion object {
61+
companion object {
4762
private const val TAG = "ConfigurableActivity"
48-
public const val ACTION: String = "androidx.benchmark.macro.CONFIGURABLE_ACTIVITY"
49-
public const val EXTRA_TEXT: String = "TEXT"
50-
public const val EXTRA_SLEEP_DUR_MS: String = "SLEEP_DUR_MS"
63+
private const val ACTION: String = "androidx.benchmark.macro.CONFIGURABLE_ACTIVITY"
64+
private const val EXTRA_TEXT: String = "TEXT"
65+
private const val EXTRA_SLEEP_DUR_MS: String = "SLEEP_DUR_MS"
66+
private const val EXTRA_REPORT_FULLY_DRAWN_DELAY_MS = "REPORT_FULLY_DRAWN_DELAY_MS"
67+
const val FULLY_DRAWN_TEXT = "FULLY DRAWN"
5168

52-
public fun createIntent(text: String, sleepDurMs: Long = 0): Intent {
69+
fun createIntent(
70+
text: String,
71+
sleepDurMs: Long = 0,
72+
reportFullyDrawnWithDelay: Long? = null
73+
): Intent {
5374
return Intent().apply {
5475
action = ACTION
5576
putExtra(EXTRA_TEXT, text)
5677
putExtra(EXTRA_SLEEP_DUR_MS, sleepDurMs)
78+
putExtra(EXTRA_REPORT_FULLY_DRAWN_DELAY_MS, reportFullyDrawnWithDelay)
5779
}
5880
}
5981
}

benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,36 @@ import androidx.benchmark.macro.perfetto.PerfettoHelper.Companion.isAbiSupported
2323
import androidx.benchmark.macro.perfetto.createTempFileFromAsset
2424
import androidx.test.ext.junit.runners.AndroidJUnit4
2525
import androidx.test.filters.LargeTest
26+
import androidx.test.filters.MediumTest
2627
import androidx.test.filters.SdkSuppress
27-
import org.junit.Assert.assertEquals
28-
import org.junit.Assert.assertNotNull
28+
import androidx.test.platform.app.InstrumentationRegistry
29+
import androidx.test.uiautomator.By
30+
import androidx.test.uiautomator.UiDevice
31+
import androidx.test.uiautomator.Until
2932
import org.junit.Assume.assumeTrue
3033
import org.junit.Test
3134
import org.junit.runner.RunWith
35+
import kotlin.test.assertEquals
36+
import kotlin.test.assertNotNull
37+
import kotlin.test.assertTrue
3238

3339
@SdkSuppress(minSdkVersion = 29)
3440
@RunWith(AndroidJUnit4::class)
35-
public class StartupTimingMetricTest {
36-
@LargeTest
41+
class StartupTimingMetricTest {
42+
@MediumTest
3743
@Test
38-
public fun noResults() {
44+
fun noResults() {
3945
assumeTrue(isAbiSupported())
4046
val packageName = "fake.package.fiction.nostartups"
4147
val metrics = measureStartup(packageName) {
4248
// Do nothing
4349
}
44-
assertEquals(metrics.metrics.isEmpty(), true)
50+
assertEquals(true, metrics.metrics.isEmpty())
4551
}
4652

4753
@LargeTest
4854
@Test
49-
public fun validateStartup() {
55+
fun validateStartup() {
5056
assumeTrue(isAbiSupported())
5157
val packageName = "androidx.benchmark.integration.macrobenchmark.target"
5258
val scope = MacrobenchmarkScope(packageName = packageName, launchWithClearTask = true)
@@ -67,9 +73,57 @@ public class StartupTimingMetricTest {
6773
assertNotNull(metrics.timelineEnd)
6874
}
6975

76+
private fun validateStartup_fullyDrawn(delay: Long) {
77+
assumeTrue(isAbiSupported())
78+
val packageName = "androidx.benchmark.macro.test"
79+
val scope = MacrobenchmarkScope(packageName = packageName, launchWithClearTask = true)
80+
val metrics = measureStartup(packageName) {
81+
// Simulate a warm start, since it's our own process
82+
scope.pressHome()
83+
scope.startActivityAndWait(
84+
ConfigurableActivity.createIntent(
85+
text = "ORIGINAL TEXT",
86+
reportFullyDrawnWithDelay = delay
87+
)
88+
)
89+
90+
if (delay > 0) {
91+
UiDevice
92+
.getInstance(InstrumentationRegistry.getInstrumentation())
93+
.wait(Until.findObject(By.text(ConfigurableActivity.FULLY_DRAWN_TEXT)), 3000)
94+
}
95+
}
96+
assertTrue("startupMs" in metrics.metrics)
97+
assertTrue("fullyDrawnMs" in metrics.metrics)
98+
99+
val startupMs = metrics.metrics["startupMs"]!!
100+
val fullyDrawnMs = metrics.metrics["fullyDrawnMs"]!!
101+
102+
val startupShouldBeFaster = delay > 0
103+
assertEquals(
104+
startupShouldBeFaster,
105+
startupMs < fullyDrawnMs,
106+
"startup $startupMs, fully drawn $fullyDrawnMs"
107+
)
108+
assertNotNull(metrics.timelineStart)
109+
assertNotNull(metrics.timelineEnd)
110+
}
111+
112+
@LargeTest
113+
@Test
114+
fun validateStartup_fullyDrawn_immediate() {
115+
validateStartup_fullyDrawn(0)
116+
}
117+
70118
@LargeTest
71119
@Test
72-
public fun fixedStartupTraceMetrics() {
120+
fun validateStartup_fullyDrawn_delayed() {
121+
validateStartup_fullyDrawn(100)
122+
}
123+
124+
@MediumTest
125+
@Test
126+
fun fixedStartupTraceMetrics() {
73127
assumeTrue(isAbiSupported())
74128
val traceFile = createTempFileFromAsset("WarmStartup", ".trace")
75129
val metric = StartupTimingMetric()

benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TrivialStartupActivity.kt

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,6 @@
1616

1717
package androidx.benchmark.macro
1818

19-
import android.app.Activity
20-
import android.os.Build
21-
import android.os.Bundle
19+
import androidx.activity.ComponentActivity
2220

23-
class TrivialStartupActivity : Activity() {
24-
override fun onCreate(savedInstanceState: Bundle?) {
25-
super.onCreate(savedInstanceState)
26-
27-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
28-
if (intent.getBooleanExtra(EXTRA_REPORT_FULLY_DRAWN, true)) {
29-
reportFullyDrawn()
30-
}
31-
}
32-
}
33-
34-
companion object {
35-
const val EXTRA_REPORT_FULLY_DRAWN = "REPORT_FULLY_DRAWN"
36-
}
37-
}
21+
class TrivialStartupActivity : ComponentActivity()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* Copyright 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.benchmark.macro.perfetto
18+
19+
import androidx.benchmark.macro.MetricsWithUiState
20+
import androidx.test.ext.junit.runners.AndroidJUnit4
21+
import androidx.test.filters.SmallTest
22+
import org.junit.Test
23+
import org.junit.runner.RunWith
24+
import kotlin.test.assertEquals
25+
26+
@SmallTest
27+
@RunWith(AndroidJUnit4::class)
28+
class PerfettoResultsParserTest {
29+
private fun startupJson(fullyDrawn: Boolean): String {
30+
val fullyDrawnString = if (fullyDrawn) {
31+
"""
32+
"report_fully_drawn": {
33+
"dur_ns": 204445333,
34+
"dur_ms": 204.445333
35+
},
36+
"""
37+
} else {
38+
""
39+
}
40+
return """
41+
{
42+
"android_startup": {
43+
"startup": [
44+
{
45+
"startup_id": 2,
46+
"package_name": "androidx.benchmark.macro.test",
47+
"process_name": "androidx.benchmark.macro.test",
48+
"zygote_new_process": 0,
49+
"to_first_frame": {
50+
"dur_ns": 149438504,
51+
"main_thread_by_task_state": {
52+
"running_dur_ns": 66840634,
53+
"runnable_dur_ns": 13585470,
54+
"uninterruptible_sleep_dur_ns": 2215416,
55+
"interruptible_sleep_dur_ns": 45290784
56+
},
57+
"other_processes_spawned_count": 0,
58+
"time_activity_manager": {
59+
"dur_ns": 12352501,
60+
"dur_ms": 12.352501
61+
},
62+
"time_activity_start": {
63+
"dur_ns": 53247818,
64+
"dur_ms": 53.247818
65+
},
66+
"time_activity_resume": {
67+
"dur_ns": 11945314,
68+
"dur_ms": 11.945314
69+
},
70+
"time_choreographer": {
71+
"dur_ns": 45386619,
72+
"dur_ms": 45.386619
73+
},
74+
"dur_ms": 149.438504,
75+
"time_inflate": {
76+
"dur_ns": 8330678,
77+
"dur_ms": 8.330678
78+
},
79+
"time_get_resources": {
80+
"dur_ns": 1426719,
81+
"dur_ms": 1.426719
82+
},
83+
"time_verify_class": {
84+
"dur_ns": 7012711,
85+
"dur_ms": 7.012711
86+
},
87+
"mcycles_by_core_type": {
88+
"little": 415,
89+
"big": 446,
90+
"bigger": 152
91+
},
92+
"jit_compiled_methods": 19,
93+
"time_jit_thread_pool_on_cpu": {
94+
"dur_ns": 6647968,
95+
"dur_ms": 6.647968
96+
}
97+
},
98+
"activity_hosting_process_count": 1,
99+
"process": {
100+
"name": "androidx.benchmark.macro.test",
101+
"uid": 10327
102+
},
103+
$fullyDrawnString
104+
"activities": [
105+
{
106+
"name": "androidx.benchmark.macro.ConfigurableActivity",
107+
"method": "performCreate",
108+
"ts_method_start": 345883126877037
109+
},
110+
{
111+
"name": "androidx.benchmark.macro.ConfigurableActivity",
112+
"method": "performResume",
113+
"ts_method_start": 345883158971676
114+
}
115+
],
116+
"event_timestamps": {
117+
"intent_received": 345883080735887,
118+
"first_frame": 345883230174391
119+
}
120+
}
121+
]
122+
}
123+
}
124+
"""
125+
}
126+
127+
@Test
128+
fun parseStartupResult_notFullyDrawn() {
129+
assertEquals(
130+
PerfettoResultsParser.parseStartupResult(
131+
startupJson(fullyDrawn = false),
132+
"androidx.benchmark.macro.test"
133+
),
134+
MetricsWithUiState(
135+
metrics = mapOf("startupMs" to 149),
136+
timelineStart = 345883080735887,
137+
timelineEnd = 345883230174391
138+
)
139+
)
140+
}
141+
142+
@Test
143+
fun parseStartupResult_fullyDrawn() {
144+
assertEquals(
145+
PerfettoResultsParser.parseStartupResult(
146+
startupJson(fullyDrawn = true),
147+
"androidx.benchmark.macro.test"
148+
),
149+
MetricsWithUiState(
150+
metrics = mapOf(
151+
"startupMs" to 149,
152+
"fullyDrawnMs" to 204
153+
),
154+
timelineStart = 345883080735887,
155+
timelineEnd = 345883230174391
156+
)
157+
)
158+
}
159+
}

benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/PerfettoResultsParser.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,18 @@ internal object PerfettoResultsParser {
3939

4040
private fun JSONObject.parseStartupMetricsWithUiState(): MetricsWithUiState {
4141
val durMs = getJSONObject("to_first_frame").getDouble("dur_ms")
42+
val fullyDrawnMs = optJSONObject("report_fully_drawn")?.getDouble("dur_ms")
43+
44+
val metricMap = mutableMapOf("startupMs" to durMs.toLong())
45+
if (fullyDrawnMs != null) {
46+
metricMap["fullyDrawnMs"] = fullyDrawnMs.toLong()
47+
}
4248

4349
val eventTimestamps = optJSONObject("event_timestamps")
4450
val timelineStart = eventTimestamps?.optLong("intent_received")
4551
val timelineEnd = eventTimestamps?.optLong("first_frame")
4652
return MetricsWithUiState(
47-
metrics = mapOf("startupMs" to durMs.toLong()),
53+
metrics = metricMap,
4854
timelineStart = timelineStart,
4955
timelineEnd = timelineEnd
5056
)

0 commit comments

Comments
 (0)