1 /*
2  * 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.events
18 
19 import android.tools.Timestamp
20 import android.tools.Timestamps
21 import android.tools.Trace
22 
23 class CujTrace(override val entries: Collection<Cuj>) : Trace<Cuj> {
24 
slicenull25     override fun slice(startTimestamp: Timestamp, endTimestamp: Timestamp): CujTrace {
26         return CujTrace(
27             entries
28                 .dropWhile { it.endTimestamp < startTimestamp }
29                 .dropLastWhile { it.startTimestamp > endTimestamp }
30         )
31     }
32 
33     companion object {
fromnull34         fun from(cujEvents: Collection<CujEvent>): CujTrace {
35             val cujs = mutableListOf<Cuj>()
36 
37             val sortedCujEvents = cujEvents.sortedBy { it.timestamp.unixNanos }
38             val startEvents = sortedCujEvents.filter { it.type == CujEvent.Companion.Type.START }
39             val endEvents = sortedCujEvents.filter { it.type == CujEvent.Companion.Type.END }
40             val canceledEvents =
41                 sortedCujEvents.filter { it.type == CujEvent.Companion.Type.CANCEL }
42 
43             for (startEvent in startEvents) {
44                 val matchingEndEvent =
45                     endEvents.firstOrNull {
46                         it.cuj == startEvent.cuj && it.timestamp >= startEvent.timestamp
47                     }
48                 val matchingCancelEvent =
49                     canceledEvents.firstOrNull {
50                         it.cuj == startEvent.cuj && it.timestamp >= startEvent.timestamp
51                     }
52 
53                 if (matchingCancelEvent == null && matchingEndEvent == null) {
54                     // CUJ started but not ended within the trace
55                     continue
56                 }
57 
58                 val closingEvent =
59                     listOf(matchingCancelEvent, matchingEndEvent).minBy {
60                         it?.timestamp ?: Timestamps.max()
61                     } ?: error("Should have found one matching closing event")
62                 val canceled = closingEvent.type == CujEvent.Companion.Type.CANCEL
63 
64                 cujs.add(
65                     Cuj(
66                         startEvent.cuj,
67                         startEvent.timestamp,
68                         closingEvent.timestamp,
69                         canceled,
70                         startEvent.cujTag?.ifBlank { null },
71                     )
72                 )
73             }
74 
75             return CujTrace(cujs)
76         }
77     }
78 }
79