1 /* 2 * Copyright (C) 2020 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.internal.protolog; 18 19 import android.os.ServiceManager; 20 import android.ravenwood.annotation.RavenwoodKeepWholeClass; 21 import android.ravenwood.annotation.RavenwoodReplace; 22 import android.tracing.perfetto.DataSourceParams; 23 import android.tracing.perfetto.InitArguments; 24 import android.tracing.perfetto.Producer; 25 26 import com.android.internal.protolog.common.IProtoLog; 27 import com.android.internal.protolog.common.IProtoLogGroup; 28 import com.android.internal.protolog.common.LogLevel; 29 30 import java.util.Arrays; 31 import java.util.HashSet; 32 33 /** 34 * ProtoLog API - exposes static logging methods. Usage of this API is similar 35 * to {@code android.utils.Log} class. Instead of plain text log messages each call consists of 36 * a messageString, which is a format string for the log message (has to be a string literal or 37 * a concatenation of string literals) and a vararg array of parameters for the formatter. 38 * 39 * The syntax for the message string depends on 40 * {@link android.text.TextUtils#formatSimple(String, Object...)}}. 41 * Supported conversions: 42 * %b - boolean 43 * %d %x - integral type (Short, Integer or Long) 44 * %f - floating point type (Float or Double) 45 * %s - string 46 * %% - a literal percent character 47 * The width and precision modifiers are supported, argument_index and flags are not. 48 * 49 * Methods in this class are stubs, that are replaced by optimised versions by the ProtoLogTool 50 * during build. 51 */ 52 @RavenwoodKeepWholeClass 53 // LINT.IfChange 54 public class ProtoLog { 55 // LINT.ThenChange(frameworks/base/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt) 56 57 // Needs to be set directly otherwise the protologtool tries to transform the method call 58 @Deprecated 59 public static boolean REQUIRE_PROTOLOGTOOL = true; 60 61 private static IProtoLog sProtoLogInstance; 62 63 private static ProtoLogDataSource sDataSource; 64 65 private static final Object sInitLock = new Object(); 66 67 /** 68 * Initialize ProtoLog in this process. 69 * <p> 70 * This method MUST be called before any protologging is performed in this process. 71 * Ensure that all groups that will be used for protologging are registered. 72 * 73 * @param groups The ProtoLog groups that will be used in the process. 74 */ init(IProtoLogGroup... groups)75 public static void init(IProtoLogGroup... groups) { 76 // These tracing instances are only used when we cannot or do not preprocess the source 77 // files to extract out the log strings. Otherwise, the trace calls are replaced with calls 78 // directly to the generated tracing implementations. 79 if (logOnlyToLogcat()) { 80 sProtoLogInstance = new LogcatOnlyProtoLogImpl(); 81 } else { 82 initializePerfettoProtoLog(groups); 83 } 84 } 85 86 @RavenwoodReplace(reason = "Always use the Log backend on ravenwood, not Perfetto") logOnlyToLogcat()87 private static boolean logOnlyToLogcat() { 88 return !android.tracing.Flags.perfettoProtologTracing(); 89 } 90 logOnlyToLogcat$ravenwood()91 private static boolean logOnlyToLogcat$ravenwood() { 92 // We don't want to initialize Perfetto data sources and have to deal with Perfetto 93 // when running tests on the host side, instead just log everything to logcat which has 94 // already been made compatible with ravenwood. 95 return true; 96 } 97 initializePerfettoProtoLog(IProtoLogGroup... groups)98 private static void initializePerfettoProtoLog(IProtoLogGroup... groups) { 99 var datasource = getSharedSingleInstanceDataSource(); 100 101 synchronized (sInitLock) { 102 final var allGroups = new HashSet<>(Arrays.stream(groups).toList()); 103 final var previousProtoLogImpl = sProtoLogInstance; 104 if (previousProtoLogImpl != null) { 105 // The ProtoLog instance has already been initialized in this process 106 final var alreadyRegisteredGroups = previousProtoLogImpl.getRegisteredGroups(); 107 allGroups.addAll(alreadyRegisteredGroups); 108 } 109 110 sProtoLogInstance = createAndEnableNewPerfettoProtoLogImpl( 111 datasource, allGroups.toArray(new IProtoLogGroup[0])); 112 if (previousProtoLogImpl instanceof PerfettoProtoLogImpl) { 113 ((PerfettoProtoLogImpl) previousProtoLogImpl).disable(); 114 } 115 } 116 } 117 createAndEnableNewPerfettoProtoLogImpl( ProtoLogDataSource datasource, IProtoLogGroup[] groups)118 private static PerfettoProtoLogImpl createAndEnableNewPerfettoProtoLogImpl( 119 ProtoLogDataSource datasource, IProtoLogGroup[] groups) { 120 try { 121 var unprocessedPerfettoProtoLogImpl = 122 new UnprocessedPerfettoProtoLogImpl(datasource, groups); 123 unprocessedPerfettoProtoLogImpl.enable(); 124 125 return unprocessedPerfettoProtoLogImpl; 126 } catch (ServiceManager.ServiceNotFoundException e) { 127 throw new RuntimeException(e); 128 } 129 } 130 131 /** 132 * DEBUG level log. 133 * 134 * @param group {@code IProtoLogGroup} controlling this log call. 135 * @param messageString constant format string for the logged message. 136 * @param args parameters to be used with the format string. 137 * 138 * NOTE: If source code is pre-processed by ProtoLogTool this is not the function call that is 139 * executed. Check generated code for actual call. 140 */ d(IProtoLogGroup group, String messageString, Object... args)141 public static void d(IProtoLogGroup group, String messageString, Object... args) { 142 logStringMessage(LogLevel.DEBUG, group, messageString, args); 143 } 144 145 /** 146 * VERBOSE level log. 147 * 148 * @param group {@code IProtoLogGroup} controlling this log call. 149 * @param messageString constant format string for the logged message. 150 * @param args parameters to be used with the format string. 151 * 152 * NOTE: If source code is pre-processed by ProtoLogTool this is not the function call that is 153 * executed. Check generated code for actual call. 154 */ v(IProtoLogGroup group, String messageString, Object... args)155 public static void v(IProtoLogGroup group, String messageString, Object... args) { 156 logStringMessage(LogLevel.VERBOSE, group, messageString, args); 157 } 158 159 /** 160 * INFO level log. 161 * 162 * @param group {@code IProtoLogGroup} controlling this log call. 163 * @param messageString constant format string for the logged message. 164 * @param args parameters to be used with the format string. 165 * 166 * NOTE: If source code is pre-processed by ProtoLogTool this is not the function call that is 167 * executed. Check generated code for actual call. 168 */ i(IProtoLogGroup group, String messageString, Object... args)169 public static void i(IProtoLogGroup group, String messageString, Object... args) { 170 logStringMessage(LogLevel.INFO, group, messageString, args); 171 } 172 173 /** 174 * WARNING level log. 175 * 176 * @param group {@code IProtoLogGroup} controlling this log call. 177 * @param messageString constant format string for the logged message. 178 * @param args parameters to be used with the format string. 179 * 180 * NOTE: If source code is pre-processed by ProtoLogTool this is not the function call that is 181 * executed. Check generated code for actual call. 182 */ w(IProtoLogGroup group, String messageString, Object... args)183 public static void w(IProtoLogGroup group, String messageString, Object... args) { 184 logStringMessage(LogLevel.WARN, group, messageString, args); 185 } 186 187 /** 188 * ERROR level log. 189 * 190 * @param group {@code IProtoLogGroup} controlling this log call. 191 * @param messageString constant format string for the logged message. 192 * @param args parameters to be used with the format string. 193 * 194 * NOTE: If source code is pre-processed by ProtoLogTool this is not the function call that is 195 * executed. Check generated code for actual call. 196 */ e(IProtoLogGroup group, String messageString, Object... args)197 public static void e(IProtoLogGroup group, String messageString, Object... args) { 198 logStringMessage(LogLevel.ERROR, group, messageString, args); 199 } 200 201 /** 202 * WHAT A TERRIBLE FAILURE level log. 203 * 204 * @param group {@code IProtoLogGroup} controlling this log call. 205 * @param messageString constant format string for the logged message. 206 * @param args parameters to be used with the format string. 207 * 208 * NOTE: If source code is pre-processed by ProtoLogTool this is not the function call that is 209 * executed. Check generated code for actual call. 210 */ wtf(IProtoLogGroup group, String messageString, Object... args)211 public static void wtf(IProtoLogGroup group, String messageString, Object... args) { 212 logStringMessage(LogLevel.WTF, group, messageString, args); 213 } 214 215 /** 216 * Check if ProtoLog isEnabled for a target group. 217 * @param group Group to check enable status of. 218 * @return true iff this is being logged. 219 */ isEnabled(IProtoLogGroup group, LogLevel level)220 public static boolean isEnabled(IProtoLogGroup group, LogLevel level) { 221 return sProtoLogInstance.isEnabled(group, level); 222 } 223 224 /** 225 * Get the single ProtoLog instance. 226 * @return A singleton instance of ProtoLog. 227 */ getSingleInstance()228 public static IProtoLog getSingleInstance() { 229 return sProtoLogInstance; 230 } 231 232 /** 233 * Gets or creates if it doesn't exist yet the protolog datasource to use in this process. 234 * We should re-use the same datasource to avoid registering the datasource multiple times in 235 * the same process, since there is no way to unregister the datasource after registration. 236 * 237 * @return The single ProtoLog datasource instance to be shared across all ProtoLog tracing 238 * objects. 239 */ getSharedSingleInstanceDataSource()240 public static synchronized ProtoLogDataSource getSharedSingleInstanceDataSource() { 241 if (sDataSource == null) { 242 Producer.init(InitArguments.DEFAULTS); 243 sDataSource = new ProtoLogDataSource(); 244 DataSourceParams params = 245 new DataSourceParams.Builder() 246 .setBufferExhaustedPolicy( 247 DataSourceParams 248 .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP) 249 .build(); 250 // NOTE: Registering that datasource is an async operation, so there may be no data 251 // traced for some messages logged right after the construction of this class. 252 sDataSource.register(params); 253 } 254 255 return sDataSource; 256 } 257 logStringMessage(LogLevel logLevel, IProtoLogGroup group, String stringMessage, Object... args)258 private static void logStringMessage(LogLevel logLevel, IProtoLogGroup group, 259 String stringMessage, Object... args) { 260 if (sProtoLogInstance == null) { 261 throw new IllegalStateException( 262 "Trying to use ProtoLog before it is initialized in this process."); 263 } 264 265 if (sProtoLogInstance.isEnabled(group, logLevel)) { 266 sProtoLogInstance.log(logLevel, group, stringMessage, args); 267 } 268 } 269 } 270