• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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