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 22 /** 23 * Represents a CUJ Event from the [EventLog] 24 * 25 * {@inheritDoc} 26 */ 27 class CujEvent( 28 timestamp: Timestamp, 29 val cuj: CujType, 30 processId: Int, 31 uid: String, 32 threadId: Int, 33 eventTag: String, 34 val cujTag: String?, 35 ) : Event(timestamp, processId, uid, threadId, eventTag) { 36 37 val type: Type = eventTag.asCujType() 38 toStringnull39 override fun toString(): String { 40 return "CujEvent(" + 41 "timestamp=$timestamp, " + 42 "cuj=$cuj, " + 43 "processId=$processId, " + 44 "uid=$uid, " + 45 "threadId=$threadId, " + 46 "eventTag=$eventTag" + 47 ")" 48 } 49 50 companion object { fromDatanull51 fun fromData( 52 processId: Int, 53 uid: String, 54 threadId: Int, 55 eventTag: String, 56 data: String 57 ): CujEvent { 58 return CujEvent( 59 Timestamps.from( 60 elapsedNanos = getElapsedTimestampFromData(data, eventTag.asCujType()), 61 systemUptimeNanos = getSystemUptimeNanosFromData(data, eventTag.asCujType()), 62 unixNanos = getUnixTimestampFromData(data, eventTag.asCujType()) 63 ), 64 getCujMarkerFromData(data, eventTag.asCujType()), 65 processId, 66 uid, 67 threadId, 68 eventTag, 69 getCujTagFromData(data, eventTag.asCujType()) 70 ) 71 } 72 getCujMarkerFromDatanull73 private fun getCujMarkerFromData(data: String, cujType: Type): CujType { 74 val dataEntries = getDataEntries(data, cujType) 75 val eventId = dataEntries.first().toInt() 76 return CujType.from(eventId) 77 } 78 getUnixTimestampFromDatanull79 private fun getUnixTimestampFromData(data: String, cujType: Type): Long { 80 val dataEntries = getDataEntries(data, cujType) 81 return dataEntries[1].toLong() 82 } 83 getElapsedTimestampFromDatanull84 private fun getElapsedTimestampFromData(data: String, cujType: Type): Long { 85 val dataEntries = getDataEntries(data, cujType) 86 return dataEntries[2].toLong() 87 } 88 getSystemUptimeNanosFromDatanull89 private fun getSystemUptimeNanosFromData(data: String, cujType: Type): Long { 90 val dataEntries = getDataEntries(data, cujType) 91 return dataEntries[3].toLong() 92 } 93 getCujTagFromDatanull94 private fun getCujTagFromData(data: String, cujType: Type): String? { 95 val dataEntries = getDataEntries(data, cujType) 96 return when (cujType) { 97 Type.START -> dataEntries[4] 98 else -> null 99 } 100 } 101 isNumericnull102 private fun isNumeric(toCheck: String): Boolean { 103 return try { 104 toCheck.toLong().toString() == toCheck 105 } catch (e: NumberFormatException) { 106 false 107 } 108 } 109 getDataEntriesnull110 private fun getDataEntries(data: String, cujType: Type): List<String> { 111 when (cujType) { 112 Type.START -> { 113 // (CUJ Type|1|5),(Unix Time Ns|2|3),(Elapsed Time Ns|2|3),(Uptime Time Ns|2|3) 114 val (cujType, unixNs, elapsedNs, uptimeNs, tag) = 115 data.replace("[", "").replace("]", "").split(",") 116 // Not using a Regex because it's not supported by Kotlin/Closure 117 require( 118 isNumeric(cujType) && 119 isNumeric(unixNs) && 120 isNumeric(elapsedNs) && 121 isNumeric(uptimeNs) 122 ) { 123 "Data \"$data\" didn't match expected format" 124 } 125 } 126 else -> { 127 // Not using a Regex because it's not supported by Kotlin/Closure 128 val (cujType, unixNs, elapsedNs, uptimeNs) = 129 data.replace("[", "").replace("]", "").split(",") 130 require( 131 isNumeric(cujType) && 132 isNumeric(unixNs) && 133 isNumeric(elapsedNs) && 134 isNumeric(uptimeNs) 135 ) { 136 "Data \"$data\" didn't match expected format" 137 } 138 } 139 } 140 141 return data.slice(1..data.length - 2).split(",") 142 } 143 144 enum class Type { 145 START, 146 END, 147 CANCEL 148 } 149 150 const val JANK_CUJ_BEGIN_TAG = "jank_cuj_events_begin_request" 151 const val JANK_CUJ_END_TAG = "jank_cuj_events_end_request" 152 const val JANK_CUJ_CANCEL_TAG = "jank_cuj_events_cancel_request" 153 Stringnull154 fun String.asCujType(): Type { 155 return when (this) { 156 JANK_CUJ_BEGIN_TAG -> Type.START 157 JANK_CUJ_END_TAG -> Type.END 158 JANK_CUJ_CANCEL_TAG -> Type.CANCEL 159 else -> error("Unhandled tag type") 160 } 161 } 162 } 163 } 164