1 /*
<lambda>null2 * Copyright (C) 2024 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
18
19 import androidx.annotation.RestrictTo
20 import androidx.benchmark.traceprocessor.Insight
21 import androidx.benchmark.traceprocessor.PerfettoTrace
22
23 /**
24 * Create a benchmark markdown trace deeplink
25 *
26 * LinkFormat isn't a TraceProcessor concept, which is why this functionality is here
27 */
28 internal fun PerfettoTrace.Link.benchmarkMarkdownLink(linkFormat: LinkFormat): String =
29 when (linkFormat) {
30 LinkFormat.V2 -> Markdown.createFileLink(label = title, path = path)
31 LinkFormat.V3 -> markdownUriLink
32 }
33
34 /**
35 * Create a insight category title markdown link
36 *
37 * LinkFormat isn't a TraceProcessor concept, which is why this functionality is here
38 */
Insightnull39 private fun Insight.Category.header(linkFormat: LinkFormat): String =
40 if (linkFormat == LinkFormat.V3 && titleUrl != null) {
41 Markdown.createLink(title, titleUrl!!)
42 } else {
43 title
44 } + postTitleLabel
45
toObservednull46 private fun List<Insight>.toObserved(linkFormat: LinkFormat): String {
47 return this.joinToString(separator = " ", prefix = "seen in iterations: ") { insight ->
48 insight.traceLink.benchmarkMarkdownLink(linkFormat) + "(${insight.observedLabel})"
49 }
50 }
51
52 /**
53 * Create a list of [InsightSummary]s, each summarizing multiple [Insight]s with the same
54 * [Insight.Category]
55 */
56 @RestrictTo(RestrictTo.Scope.LIBRARY)
Listnull57 fun List<Insight>.createInsightSummaries(): List<InsightSummary> {
58 return this.groupBy { it.category }
59 .map { (category, insights) ->
60 InsightSummary(
61 category,
62 insights.sortedBy {
63 // sort by iteration (embedded in title)
64 it.traceLink.title.toInt()
65 }
66 )
67 }
68 }
69
70 /**
71 * Represents an insight into performance issues detected during a benchmark.
72 *
73 * Provides details about the specific criterion that was violated, along with information about
74 * where and how the violation was observed.
75 *
76 * @param category A description of the performance issue, including the expected behavior and any
77 * relevant thresholds.
78 * @param observedV2 Specific details about when and how the violation occurred, such as the
79 * iterations where it was observed and any associated values. Uses [LinkFormat.V2].
80 * @param observedV3 Specific details about when and how the violation occurred, such as the
81 * iterations where it was observed and any associated values. Uses [LinkFormat.V3] to link more
82 * precisely into traces.
83 *
84 * TODO(364598145): generalize
85 */
86 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
87 data class InsightSummary(
88 val category: String,
89 val observedV2: String,
90 val observedV3: String,
91 ) {
92 constructor(
93 category: Insight.Category,
94 insights: List<Insight>
95 ) : this(
96 category = category.header(LinkFormat.V3), // TODO: avoid this format hard coding!
97 observedV2 = insights.toObserved(LinkFormat.V2),
98 observedV3 = insights.toObserved(LinkFormat.V3)
99 ) {
100 require(insights.isNotEmpty())
<lambda>null101 require(insights.all { it.category == category })
102 }
103 }
104