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