xref: /aosp_15_r20/frameworks/base/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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 com.android.systemui.kairos.debug
18 
19 import com.android.systemui.kairos.MutableTState
20 import com.android.systemui.kairos.TState
21 import com.android.systemui.kairos.TStateInit
22 import com.android.systemui.kairos.TStateLoop
23 import com.android.systemui.kairos.internal.DerivedFlatten
24 import com.android.systemui.kairos.internal.DerivedMap
25 import com.android.systemui.kairos.internal.DerivedMapCheap
26 import com.android.systemui.kairos.internal.DerivedZipped
27 import com.android.systemui.kairos.internal.Init
28 import com.android.systemui.kairos.internal.TStateDerived
29 import com.android.systemui.kairos.internal.TStateImpl
30 import com.android.systemui.kairos.internal.TStateSource
31 import com.android.systemui.kairos.util.Just
32 import com.android.systemui.kairos.util.Maybe
33 import com.android.systemui.kairos.util.None
34 import com.android.systemui.kairos.util.flatMap
35 import com.android.systemui.kairos.util.map
36 import com.android.systemui.kairos.util.none
37 import com.android.systemui.kairos.util.orElseGet
38 
39 // object IdGen {
40 //    private val counter = AtomicLong()
41 //    fun getId() = counter.getAndIncrement()
42 // }
43 
44 typealias StateGraph = Graph<ActivationInfo>
45 
46 sealed class StateInfo(
47     val name: String,
48     val value: Maybe<Any?>,
49     val operator: String,
50     val epoch: Long?,
51 )
52 
53 class Source(name: String, value: Maybe<Any?>, operator: String, epoch: Long) :
54     StateInfo(name, value, operator, epoch)
55 
56 class Derived(
57     name: String,
58     val type: DerivedStateType,
59     value: Maybe<Any?>,
60     operator: String,
61     epoch: Long?,
62 ) : StateInfo(name, value, operator, epoch)
63 
64 sealed interface DerivedStateType
65 
66 data object Flatten : DerivedStateType
67 
68 data class Mapped(val cheap: Boolean) : DerivedStateType
69 
70 data object Combine : DerivedStateType
71 
72 sealed class InitInfo(val name: String)
73 
74 class Uninitialized(name: String) : InitInfo(name)
75 
76 class Initialized(val state: StateInfo) : InitInfo(state.name)
77 
78 sealed interface ActivationInfo
79 
80 class Inactive(val name: String) : ActivationInfo
81 
82 class Active(val nodeInfo: StateInfo) : ActivationInfo
83 
84 class Dead(val name: String) : ActivationInfo
85 
86 data class Edge(val upstream: Any, val downstream: Any, val tag: Any? = null)
87 
88 data class Graph<T>(val nodes: Map<Any, T>, val edges: List<Edge>)
89 
90 internal fun TState<*>.dump(infoMap: MutableMap<Any, InitInfo>, edges: MutableList<Edge>) {
91     val init: Init<TStateImpl<Any?>> =
92         when (this) {
93             is TStateInit -> init
94             is TStateLoop -> init
95             is MutableTState -> tState.init
96         }
97     when (val stateMaybe = init.getUnsafe()) {
98         None -> {
99             infoMap[this] = Uninitialized(init.name ?: init.toString())
100         }
101         is Just -> {
102             stateMaybe.value.dump(infoMap, edges)
103         }
104     }
105 }
106 
dumpnull107 internal fun TStateImpl<*>.dump(infoById: MutableMap<Any, InitInfo>, edges: MutableList<Edge>) {
108     val state = this
109     if (state in infoById) return
110     val stateInfo =
111         when (state) {
112             is TStateDerived -> {
113                 val type =
114                     when (state) {
115                         is DerivedFlatten -> {
116                             state.upstream.dump(infoById, edges)
117                             edges.add(
118                                 Edge(upstream = state.upstream, downstream = state, tag = "outer")
119                             )
120                             state.upstream
121                                 .getUnsafe()
122                                 .orElseGet { null }
123                                 ?.let {
124                                     edges.add(
125                                         Edge(upstream = it, downstream = state, tag = "inner")
126                                     )
127                                     it.dump(infoById, edges)
128                                 }
129                             Flatten
130                         }
131                         is DerivedMap<*, *> -> {
132                             state.upstream.dump(infoById, edges)
133                             edges.add(Edge(upstream = state.upstream, downstream = state))
134                             Mapped(cheap = false)
135                         }
136                         is DerivedZipped<*, *> -> {
137                             state.upstream.forEach { (key, upstream) ->
138                                 edges.add(
139                                     Edge(upstream = upstream, downstream = state, tag = "key=$key")
140                                 )
141                                 upstream.dump(infoById, edges)
142                             }
143                             Combine
144                         }
145                     }
146                 Derived(
147                     state.name ?: state.operatorName,
148                     type,
149                     state.getCachedUnsafe(),
150                     state.operatorName,
151                     state.invalidatedEpoch,
152                 )
153             }
154             is TStateSource ->
155                 Source(
156                     state.name ?: state.operatorName,
157                     state.getStorageUnsafe(),
158                     state.operatorName,
159                     state.writeEpoch,
160                 )
161             is DerivedMapCheap<*, *> -> {
162                 state.upstream.dump(infoById, edges)
163                 edges.add(Edge(upstream = state.upstream, downstream = state))
164                 val type = Mapped(cheap = true)
165                 Derived(
166                     state.name ?: state.operatorName,
167                     type,
168                     state.getUnsafe(),
169                     state.operatorName,
170                     null,
171                 )
172             }
173         }
174     infoById[state] = Initialized(stateInfo)
175 }
176 
getUnsafenull177 private fun <A> TStateImpl<A>.getUnsafe(): Maybe<A> =
178     when (this) {
179         is TStateDerived -> getCachedUnsafe()
180         is TStateSource -> getStorageUnsafe()
181         is DerivedMapCheap<*, *> -> none
182     }
183 
getUnsafeWithEpochnull184 private fun <A> TStateImpl<A>.getUnsafeWithEpoch(): Maybe<Pair<A, Long>> =
185     when (this) {
186         is TStateDerived -> getCachedUnsafe().map { it to invalidatedEpoch }
187         is TStateSource -> getStorageUnsafe().map { it to writeEpoch }
188         is DerivedMapCheap<*, *> -> none
189     }
190 
191 /**
192  * Returns the current value held in this [TState], or [none] if the [TState] has not been
193  * initialized.
194  *
195  * The returned [Long] is the *epoch* at which the internal cache was last updated. This can be used
196  * to identify values which are out-of-date.
197  */
sampleUnsafenull198 fun <A> TState<A>.sampleUnsafe(): Maybe<Pair<A, Long>> =
199     when (this) {
200         is MutableTState -> tState.init.getUnsafe().flatMap { it.getUnsafeWithEpoch() }
201         is TStateInit -> init.getUnsafe().flatMap { it.getUnsafeWithEpoch() }
202         is TStateLoop -> this.init.getUnsafe().flatMap { it.getUnsafeWithEpoch() }
203     }
204