• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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 android.tools.traces.parsers.perfetto
18 
19 import android.tools.CrossPlatform
20 import android.tools.Timestamp
21 import android.tools.parsers.AbstractTraceParser
22 import android.tools.traces.surfaceflinger.Transaction
23 import android.tools.traces.surfaceflinger.TransactionsTrace
24 import android.tools.traces.surfaceflinger.TransactionsTraceEntry
25 import kotlin.math.min
26 
27 /** Parser for [TransactionsTrace] */
28 class TransactionsTraceParser :
29     AbstractTraceParser<
30         TraceProcessorSession,
31         TransactionsTraceEntry,
32         TransactionsTraceEntry,
33         TransactionsTrace,
34     >() {
35 
36     override val traceName = "Layers trace (SF)"
37 
38     override fun createTrace(entries: Collection<TransactionsTraceEntry>): TransactionsTrace {
39         return TransactionsTrace(entries)
40     }
41 
42     override fun doDecodeByteArray(bytes: ByteArray): TraceProcessorSession {
43         error("This parser can only read from perfetto trace processor")
44     }
45 
46     override fun shouldParseEntry(entry: TransactionsTraceEntry) = true
47 
48     override fun getEntries(input: TraceProcessorSession): List<TransactionsTraceEntry> {
49         val traceEntries = ArrayList<TransactionsTraceEntry>()
50 
51         val realToMonotonicTimeOffsetNs =
52             queryRealToMonotonicTimeOffsetNs(input, "surfaceflinger_transactions")
53         val entriesCount = queryTraceEntriesCount(input)
54 
55         for (startEntryId in 0L until entriesCount step BATCH_SIZE) {
56             val endEntryId = min(startEntryId + BATCH_SIZE, entriesCount)
57 
58             val batchRows = input.query(getSqlQueryTransactions(startEntryId, endEntryId)) { it }
59             val entryGroups = batchRows.groupBy { it.get("trace_entry_id") }
60 
61             for (entryId in startEntryId until endEntryId) {
62                 val rows = entryGroups[entryId]!!
63                 val entry = buildTraceEntry(rows, realToMonotonicTimeOffsetNs)
64                 traceEntries.add(entry)
65             }
66         }
67 
68         return traceEntries
69     }
70 
71     override fun getTimestamp(entry: TransactionsTraceEntry): Timestamp = entry.timestamp
72 
73     override fun onBeforeParse(input: TraceProcessorSession) {}
74 
75     override fun doParseEntry(entry: TransactionsTraceEntry) = entry
76 
77     companion object {
78         private const val BATCH_SIZE = 200L
79 
80         private fun queryTraceEntriesCount(session: TraceProcessorSession): Long {
81             val sql =
82                 """
83                 SELECT count(*) FROM surfaceflinger_transactions;
84             """
85                     .trimIndent()
86             return session.query(sql) { rows ->
87                 require(rows.size == 1) { "Expected one row with count of trace entries." }
88                 rows.get(0).get("count(*)") as Long
89             }
90         }
91 
92         private fun getSqlQueryTransactions(startEntryId: Long, endEntryId: Long): String {
93             return """
94                SELECT sft.id AS trace_entry_id, args.key, args.display_value AS value, args.value_type
95                FROM surfaceflinger_transactions AS sft
96                INNER JOIN args ON sft.arg_set_id = args.arg_set_id
97                WHERE trace_entry_id BETWEEN $startEntryId AND ${endEntryId - 1};
98             """
99                 .trimIndent()
100         }
101 
102         private fun buildTraceEntry(
103             rows: List<Row>,
104             realToMonotonicTimeOffsetNs: Long,
105         ): TransactionsTraceEntry {
106             val args = Args.build(rows)
107             val transactions: Collection<Transaction> =
108                 args.getChildren("transactions")?.map { transaction ->
109                     Transaction(
110                         transaction.getChild("pid")?.getInt() ?: -1,
111                         transaction.getChild("uid")?.getInt() ?: -1,
112                         transaction.getChild("vsync_id")?.getLong() ?: -1,
113                         transaction.getChild("post_time")?.getLong() ?: -1,
114                         transaction.getChild("transaction_id")?.getLong() ?: -1,
115                         transaction.getChildren("merged_transaction_ids")?.map { it.getLong() }
116                             ?: emptyList(),
117                     )
118                 } ?: emptyList()
119 
120             val traceEntry =
121                 TransactionsTraceEntry(
122                     CrossPlatform.timestamp.from(
123                         elapsedNanos = args.getChild("elapsed_realtime_nanos")?.getLong() ?: 0,
124                         elapsedOffsetNanos = realToMonotonicTimeOffsetNs,
125                     ),
126                     args.getChild("vsync_id")?.getLong() ?: -1,
127                     transactions,
128                 )
129 
130             transactions.forEach { it.appliedInEntry = traceEntry }
131 
132             return traceEntry
133         }
134     }
135 }
136