1 /*
2  * 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.app.tracing
18 
19 import org.junit.Assert.assertFalse
20 
21 const val DEBUG = false
22 
23 /** Log a message with a tag indicating the current thread ID */
debugnull24 private fun debug(message: String) {
25     if (DEBUG) println("Thread #${Thread.currentThread().id}: $message")
26 }
27 
28 @PublishedApi
isEnablednull29 internal actual fun isEnabled(): Boolean {
30     return true
31 }
32 
33 val traceCounters = mutableMapOf<String, Int>()
34 
traceCounternull35 internal actual fun traceCounter(counterName: String, counterValue: Int) {
36     traceCounters[counterName] = counterValue
37 }
38 
39 object FakeTraceState {
40 
41     private val allThreadStates = hashMapOf<Long, MutableList<String>>()
42 
beginnull43     fun begin(sectionName: String) {
44         val threadId = Thread.currentThread().id
45         synchronized(allThreadStates) {
46             if (allThreadStates.containsKey(threadId)) {
47                 allThreadStates[threadId]!!.add(sectionName)
48             } else {
49                 allThreadStates[threadId] = mutableListOf(sectionName)
50             }
51         }
52     }
53 
endnull54     fun end() {
55         val threadId = Thread.currentThread().id
56         synchronized(allThreadStates) {
57             assertFalse(
58                 "Attempting to close trace section on thread=$threadId, " +
59                     "but there are no open sections",
60                 allThreadStates[threadId].isNullOrEmpty()
61             )
62             // TODO: Replace with .removeLast() once available
63             allThreadStates[threadId]!!.removeAt(allThreadStates[threadId]!!.lastIndex)
64         }
65     }
66 
getOpenTraceSectionsOnCurrentThreadnull67     fun getOpenTraceSectionsOnCurrentThread(): Array<String> {
68         val threadId = Thread.currentThread().id
69         synchronized(allThreadStates) {
70             return allThreadStates[threadId]?.toTypedArray() ?: emptyArray()
71         }
72     }
73 
74     /**
75      * Helper function for debugging; use as follows:
76      * ```
77      * println(FakeThreadStateLocal)
78      * ```
79      */
toStringnull80     override fun toString(): String {
81         val sb = StringBuilder()
82         synchronized(allThreadStates) {
83             allThreadStates.entries.forEach { sb.appendLine("${it.key} -> ${it.value}") }
84         }
85         return sb.toString()
86     }
87 }
88 
traceBeginnull89 internal actual fun traceBegin(methodName: String) {
90     debug("traceBegin: name=$methodName")
91     FakeTraceState.begin(methodName)
92 }
93 
traceEndnull94 internal actual fun traceEnd() {
95     debug("traceEnd")
96     FakeTraceState.end()
97 }
98 
asyncTraceBeginnull99 internal actual fun asyncTraceBegin(methodName: String, cookie: Int) {
100     debug("asyncTraceBegin: name=$methodName cookie=${cookie.toHexString()}")
101 }
102 
asyncTraceEndnull103 internal actual fun asyncTraceEnd(methodName: String, cookie: Int) {
104     debug("asyncTraceEnd: name=$methodName cookie=${cookie.toHexString()}")
105 }
106 
107 @PublishedApi
asyncTraceForTrackBeginnull108 internal actual fun asyncTraceForTrackBegin(trackName: String, methodName: String, cookie: Int) {
109     debug(
110         "asyncTraceForTrackBegin: track=$trackName name=$methodName cookie=${cookie.toHexString()}"
111     )
112 }
113 
114 @PublishedApi
asyncTraceForTrackEndnull115 internal actual fun asyncTraceForTrackEnd(trackName: String, methodName: String, cookie: Int) {
116     debug("asyncTraceForTrackEnd: track=$trackName name=$methodName cookie=${cookie.toHexString()}")
117 }
118 
instantnull119 internal actual fun instant(eventName: String) {
120     debug("instant: name=$eventName")
121 }
122 
instantForTracknull123 internal actual fun instantForTrack(trackName: String, eventName: String) {
124     debug("instantForTrack: track=$trackName name=$eventName")
125 }
126