1 /*
<lambda>null2  * Copyright 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.tracing.driver
18 
19 import androidx.collection.mutableScatterMapOf
20 
21 /** Represents a track for a process in a perfetto trace. */
22 public open class ProcessTrack(
23     /** The tracing context. */
24     context: TraceContext,
25     /** The process id */
26     internal val id: Int,
27     /** The name of the process. */
28     internal val name: String,
29 ) : SliceTrack(context = context, uuid = monotonicId()) {
30     internal val packetLock = Any()
31     internal val threads = mutableScatterMapOf<String, ThreadTrack>()
32     internal val counters = mutableScatterMapOf<String, CounterTrack>()
33 
34     init {
35         synchronized(packetLock) {
36             emitTraceEvent(immediateDispatch = true) { event ->
37                 event.setPreamble(
38                     TrackDescriptor(
39                         name,
40                         uuid,
41                         parentUuid = INVALID_LONG,
42                         type = TRACK_DESCRIPTOR_TYPE_PROCESS,
43                         pid = id,
44                         tid = INVALID_INT,
45                     )
46                 )
47             }
48         }
49     }
50 
51     public override fun beginSection(name: String, flowIds: List<Long>) {
52         if (context.isEnabled) {
53             synchronized(packetLock) {
54                 emitTraceEvent { event -> event.setBeginSectionWithFlows(uuid, name, flowIds) }
55             }
56         }
57     }
58 
59     public override fun beginSection(name: String) {
60         if (context.isEnabled) {
61             synchronized(packetLock) {
62                 emitTraceEvent { event -> event.setBeginSection(uuid, name) }
63             }
64         }
65     }
66 
67     public override fun endSection() {
68         if (context.isEnabled) {
69             synchronized(packetLock) { emitTraceEvent { event -> event.setEndSection(uuid) } }
70         }
71     }
72 
73     public override fun instant(name: String) {
74         if (context.isEnabled) {
75             synchronized(packetLock) { emitTraceEvent { event -> event.setInstant(uuid, name) } }
76         }
77     }
78 
79     /**
80      * @return A [ThreadTrack] for a given [ProcessTrack] using the unique thread [id] and a thread
81      *   [name].
82      */
83     public open fun getOrCreateThreadTrack(id: Int, name: String): ThreadTrack {
84         // Thread ids are only unique for lifetime of the thread and can be potentially reused.
85         // Therefore we end up combining the `name` of the thread and its `id` as a key.
86         val key = "$id/$name"
87         return threads[key]
88             ?: synchronized(threads) {
89                 val track =
90                     threads.getOrPut(key) { ThreadTrack(id = id, name = name, process = this) }
91                 check(track.name == name)
92                 track
93             }
94     }
95 
96     /** @return A [CounterTrack] for a given [ProcessTrack] and the provided counter [name]. */
97     public open fun getOrCreateCounterTrack(name: String): CounterTrack {
98         return counters[name]
99             ?: synchronized(counters) {
100                 counters.getOrPut(name) { CounterTrack(name = name, parent = this) }
101             }
102     }
103 }
104 
105 // An empty process track when tracing is disabled.
106 
107 private const val EMPTY_PROCESS_ID = -1
108 private const val EMPTY_PROCESS_NAME = "Empty Process"
109 
110 internal class EmptyProcessTrack(context: EmptyTraceContext) :
111     ProcessTrack(
112         context = context,
113         id = EMPTY_PROCESS_ID,
114         name = EMPTY_PROCESS_NAME,
115     ) {
116 
117     private val emptyContext: EmptyTraceContext = context
118 
getOrCreateThreadTracknull119     override fun getOrCreateThreadTrack(id: Int, name: String): ThreadTrack = emptyContext.thread
120 
121     override fun getOrCreateCounterTrack(name: String): CounterTrack = emptyContext.counter
122 }
123