1 /*
<lambda>null2  * 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.FileLinkingRule
20 import androidx.benchmark.macro.Packages
21 import androidx.benchmark.macro.runSingleSessionServer
22 import androidx.benchmark.perfetto.PerfettoCapture
23 import androidx.benchmark.perfetto.PerfettoConfig
24 import androidx.benchmark.perfetto.PerfettoHelper
25 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
26 import androidx.benchmark.traceprocessor.TraceProcessor
27 import androidx.benchmark.traceprocessor.toSlices
28 import androidx.test.ext.junit.runners.AndroidJUnit4
29 import androidx.test.filters.LargeTest
30 import androidx.test.filters.SdkSuppress
31 import androidx.tracing.Trace
32 import androidx.tracing.trace
33 import kotlin.test.assertEquals
34 import kotlin.test.assertTrue
35 import org.junit.After
36 import org.junit.Assert.assertTrue
37 import org.junit.Assume.assumeTrue
38 import org.junit.Before
39 import org.junit.Rule
40 import org.junit.Test
41 import org.junit.runner.RunWith
42 
43 /**
44  * Tests for androidx.tracing.Trace, which validate actual trace content
45  *
46  * These can't be defined in the androidx.tracing library, as Trace capture / validation APIs are
47  * only available to the benchmark group.
48  */
49 @SdkSuppress(minSdkVersion = 23)
50 @RunWith(AndroidJUnit4::class)
51 class AndroidxTracingTraceTest {
52     @get:Rule val linkRule = FileLinkingRule()
53 
54     @Before
55     @After
56     fun cleanup() {
57         PerfettoHelper.cleanupPerfettoState()
58     }
59 
60     @LargeTest
61     @Test
62     fun captureAndValidateTrace() {
63         assumeTrue(isAbiSupported())
64 
65         val traceFilePath = linkRule.createReportedTracePath(Packages.TEST)
66         val perfettoCapture = PerfettoCapture()
67 
68         perfettoCapture.start(
69             PerfettoConfig.Benchmark(
70                 appTagPackages = listOf(Packages.TEST),
71                 useStackSamplingConfig = false
72             )
73         )
74 
75         assertTrue(
76             Trace.isEnabled(),
77             "In-process tracing should be enabled immediately after trace capture is started"
78         )
79 
80         repeat(20) {
81             "$PREFIX$it"
82                 .also { label ->
83                     // actual test content. This is done in the middle of the other sections
84                     // to isolate it from trace truncation issues
85                     if (it == 10) {
86                         Trace.setCounter("${PREFIX}counter", 1)
87                         Trace.beginSection("${PREFIX}beginSection")
88                         Trace.beginAsyncSection("${PREFIX}beginAsyncSection", 9827)
89                         Thread.sleep(50)
90                         Trace.setCounter("${PREFIX}counter", 0)
91                         Trace.endSection()
92                         Trace.endAsyncSection("${PREFIX}beginAsyncSection", 9827)
93                     }
94 
95                     // trace sections before and after actual test content, to look for problems in
96                     // front/back trace truncation. If these sections are missing, it's most likely
97                     // issues in trace capture
98                     trace(label) { Thread.sleep(50) }
99                 }
100         }
101 
102         perfettoCapture.stop(traceFilePath)
103 
104         val queryResult =
105             TraceProcessor.runSingleSessionServer(traceFilePath) { query(query = QUERY) }
106 
107         val matchingSlices = queryResult.toSlices()
108         assertEquals(
109             List(10) { "$PREFIX$it" } +
110                 listOf(
111                     "${PREFIX}counter1.0",
112                     "${PREFIX}beginSection",
113                     "${PREFIX}beginAsyncSection",
114                     "${PREFIX}counter0.0",
115                 ) +
116                 List(10) { "$PREFIX${it + 10}" },
117             matchingSlices.map { it.name }
118         )
119         matchingSlices.forEach {
120             if (it.name.startsWith("${PREFIX}counter")) {
121                 assertEquals(0L, it.dur) // counter has no length
122             } else {
123                 assertTrue(it.dur > 30_000_000) // should be at least 30ms
124             }
125         }
126     }
127 
128     companion object {
129         const val PREFIX = "AndroidxTracingTraceTest_"
130 
131         const val QUERY =
132             """
133             ------ select all relevant standard slices
134             SELECT
135                 slice.name as name,
136                 slice.ts as ts,
137                 slice.dur as dur
138             FROM slice
139                 INNER JOIN thread_track on slice.track_id = thread_track.id
140                 INNER JOIN thread USING(utid)
141                 INNER JOIN process USING(upid)
142             WHERE
143                 slice.name LIKE "$PREFIX%" AND
144                 process.name LIKE "androidx.benchmark.macro.test"
145             UNION
146             ------ add in async slices
147             SELECT
148                 slice.name as name,
149                 slice.ts as ts,
150                 slice.dur as dur
151             FROM slice
152                 INNER JOIN process_track on slice.track_id = process_track.id
153                 INNER JOIN process USING(upid)
154             WHERE
155                 slice.name LIKE "$PREFIX%" AND
156                 process.name LIKE "androidx.benchmark.macro.test"
157             UNION
158             ------ add in the counter values, with value prepended to name
159             SELECT
160                 counter_track.name || counter.value as name,
161                 counter.ts as ts,
162                 0 as dur
163             FROM counter
164                 INNER JOIN counter_track on counter.track_id = counter_track.id
165             WHERE
166                 counter_track.name LIKE "${PREFIX}counter"
167             ------ order the whole thing by timestamp
168             ORDER BY
169                 ts
170         """
171     }
172 }
173