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 com.android.internal.protolog; 18 19 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogConfig.DEFAULT; 20 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogConfig.ENABLE_ALL; 21 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogConfig.GROUP_OVERRIDES; 22 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogConfig.TRACING_MODE; 23 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.COLLECT_STACKTRACE; 24 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.GROUP_NAME; 25 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.LOG_FROM; 26 27 import android.internal.perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig; 28 import android.internal.perfetto.protos.ProtologCommon; 29 import android.tracing.perfetto.CreateIncrementalStateArgs; 30 import android.tracing.perfetto.CreateTlsStateArgs; 31 import android.tracing.perfetto.DataSource; 32 import android.tracing.perfetto.DataSourceInstance; 33 import android.tracing.perfetto.FlushCallbackArguments; 34 import android.tracing.perfetto.StartCallbackArguments; 35 import android.tracing.perfetto.StopCallbackArguments; 36 import android.util.proto.ProtoInputStream; 37 import android.util.proto.WireTypeMismatchException; 38 39 import com.android.internal.protolog.common.LogLevel; 40 41 import java.io.IOException; 42 import java.util.HashMap; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.function.Consumer; 46 47 public class ProtoLogDataSource extends DataSource<ProtoLogDataSource.Instance, 48 ProtoLogDataSource.TlsState, 49 ProtoLogDataSource.IncrementalState> { 50 51 private final Consumer<ProtoLogConfig> mOnStart; 52 private final Runnable mOnFlush; 53 private final Consumer<ProtoLogConfig> mOnStop; 54 ProtoLogDataSource(Consumer<ProtoLogConfig> onStart, Runnable onFlush, Consumer<ProtoLogConfig> onStop)55 public ProtoLogDataSource(Consumer<ProtoLogConfig> onStart, Runnable onFlush, 56 Consumer<ProtoLogConfig> onStop) { 57 super("android.protolog"); 58 this.mOnStart = onStart; 59 this.mOnFlush = onFlush; 60 this.mOnStop = onStop; 61 } 62 63 @Override createInstance(ProtoInputStream configStream, int instanceIndex)64 public Instance createInstance(ProtoInputStream configStream, int instanceIndex) { 65 ProtoLogConfig config = null; 66 67 try { 68 while (configStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { 69 try { 70 if (configStream.getFieldNumber() == (int) DataSourceConfig.PROTOLOG_CONFIG) { 71 if (config != null) { 72 throw new RuntimeException("ProtoLog config already set in loop"); 73 } 74 config = readProtoLogConfig(configStream); 75 } 76 } catch (WireTypeMismatchException e) { 77 throw new RuntimeException("Failed to parse ProtoLog DataSource config", e); 78 } 79 } 80 } catch (IOException e) { 81 throw new RuntimeException("Failed to read ProtoLog DataSource config", e); 82 } 83 84 if (config == null) { 85 // No config found 86 config = ProtoLogConfig.DEFAULT; 87 } 88 89 return new Instance( 90 this, instanceIndex, config, mOnStart, mOnFlush, mOnStop); 91 } 92 93 @Override createTlsState(CreateTlsStateArgs<Instance> args)94 public TlsState createTlsState(CreateTlsStateArgs<Instance> args) { 95 try (Instance dsInstance = args.getDataSourceInstanceLocked()) { 96 if (dsInstance == null) { 97 // Datasource instance has been removed 98 return new TlsState(ProtoLogConfig.DEFAULT); 99 } 100 return new TlsState(dsInstance.mConfig); 101 } 102 } 103 104 @Override createIncrementalState(CreateIncrementalStateArgs<Instance> args)105 public IncrementalState createIncrementalState(CreateIncrementalStateArgs<Instance> args) { 106 return new IncrementalState(); 107 } 108 109 public static class TlsState { 110 private final ProtoLogConfig mConfig; 111 TlsState(ProtoLogConfig config)112 private TlsState(ProtoLogConfig config) { 113 this.mConfig = config; 114 } 115 116 /** 117 * Get the log from level for a group. 118 * @param groupTag The tag of the group to get the log from level. 119 * @return The lowest LogLevel (inclusive) to log message from. 120 */ getLogFromLevel(String groupTag)121 public LogLevel getLogFromLevel(String groupTag) { 122 return getConfigFor(groupTag).logFrom; 123 } 124 125 /** 126 * Get if the stacktrace for the log message should be collected for this group. 127 * @param groupTag The tag of the group to get whether or not a stacktrace was requested. 128 * @return True iff a stacktrace was requested to be collected from this group in the 129 * tracing config. 130 */ getShouldCollectStacktrace(String groupTag)131 public boolean getShouldCollectStacktrace(String groupTag) { 132 return getConfigFor(groupTag).collectStackTrace; 133 } 134 getConfigFor(String groupTag)135 private GroupConfig getConfigFor(String groupTag) { 136 return mConfig.getConfigFor(groupTag); 137 } 138 } 139 140 public static class IncrementalState { 141 public final Map<String, Integer> argumentInterningMap = new HashMap<>(); 142 public final Map<String, Integer> stacktraceInterningMap = new HashMap<>(); 143 public boolean clearReported = false; 144 } 145 146 public static class ProtoLogConfig { 147 private final LogLevel mDefaultLogFromLevel; 148 private final Map<String, GroupConfig> mGroupConfigs; 149 150 private static final ProtoLogConfig DEFAULT = 151 new ProtoLogConfig(LogLevel.WTF, new HashMap<>()); 152 ProtoLogConfig( LogLevel defaultLogFromLevel, Map<String, GroupConfig> groupConfigs)153 private ProtoLogConfig( 154 LogLevel defaultLogFromLevel, Map<String, GroupConfig> groupConfigs) { 155 this.mDefaultLogFromLevel = defaultLogFromLevel; 156 this.mGroupConfigs = groupConfigs; 157 } 158 getConfigFor(String groupTag)159 public GroupConfig getConfigFor(String groupTag) { 160 return mGroupConfigs.getOrDefault(groupTag, getDefaultGroupConfig()); 161 } 162 getDefaultGroupConfig()163 public GroupConfig getDefaultGroupConfig() { 164 return new GroupConfig(mDefaultLogFromLevel, false); 165 } 166 getGroupTagsWithOverriddenConfigs()167 public Set<String> getGroupTagsWithOverriddenConfigs() { 168 return mGroupConfigs.keySet(); 169 } 170 } 171 172 public static class GroupConfig { 173 public final LogLevel logFrom; 174 public final boolean collectStackTrace; 175 GroupConfig(LogLevel logFromLevel, boolean collectStackTrace)176 public GroupConfig(LogLevel logFromLevel, boolean collectStackTrace) { 177 this.logFrom = logFromLevel; 178 this.collectStackTrace = collectStackTrace; 179 } 180 } 181 readProtoLogConfig(ProtoInputStream configStream)182 private ProtoLogConfig readProtoLogConfig(ProtoInputStream configStream) 183 throws IOException { 184 final long config_token = configStream.start(DataSourceConfig.PROTOLOG_CONFIG); 185 186 LogLevel defaultLogFromLevel = LogLevel.WTF; 187 final Map<String, GroupConfig> groupConfigs = new HashMap<>(); 188 189 while (configStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { 190 if (configStream.getFieldNumber() == (int) TRACING_MODE) { 191 int tracingMode = configStream.readInt(TRACING_MODE); 192 switch (tracingMode) { 193 case DEFAULT: 194 break; 195 case ENABLE_ALL: 196 defaultLogFromLevel = LogLevel.DEBUG; 197 break; 198 default: 199 throw new RuntimeException("Unhandled ProtoLog tracing mode type"); 200 } 201 } 202 if (configStream.getFieldNumber() == (int) GROUP_OVERRIDES) { 203 final long group_overrides_token = configStream.start(GROUP_OVERRIDES); 204 205 String tag = null; 206 LogLevel logFromLevel = defaultLogFromLevel; 207 boolean collectStackTrace = false; 208 while (configStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { 209 if (configStream.getFieldNumber() == (int) GROUP_NAME) { 210 tag = configStream.readString(GROUP_NAME); 211 } 212 if (configStream.getFieldNumber() == (int) LOG_FROM) { 213 final int logFromInt = configStream.readInt(LOG_FROM); 214 switch (logFromInt) { 215 case (ProtologCommon.PROTOLOG_LEVEL_DEBUG): { 216 logFromLevel = LogLevel.DEBUG; 217 break; 218 } 219 case (ProtologCommon.PROTOLOG_LEVEL_VERBOSE): { 220 logFromLevel = LogLevel.VERBOSE; 221 break; 222 } 223 case (ProtologCommon.PROTOLOG_LEVEL_INFO): { 224 logFromLevel = LogLevel.INFO; 225 break; 226 } 227 case (ProtologCommon.PROTOLOG_LEVEL_WARN): { 228 logFromLevel = LogLevel.WARN; 229 break; 230 } 231 case (ProtologCommon.PROTOLOG_LEVEL_ERROR): { 232 logFromLevel = LogLevel.ERROR; 233 break; 234 } 235 case (ProtologCommon.PROTOLOG_LEVEL_WTF): { 236 logFromLevel = LogLevel.WTF; 237 break; 238 } 239 default: { 240 throw new RuntimeException("Unhandled log level"); 241 } 242 } 243 } 244 if (configStream.getFieldNumber() == (int) COLLECT_STACKTRACE) { 245 collectStackTrace = configStream.readBoolean(COLLECT_STACKTRACE); 246 } 247 } 248 249 if (tag == null) { 250 throw new RuntimeException("Failed to decode proto config. " 251 + "Got a group override without a group tag."); 252 } 253 254 groupConfigs.put(tag, new GroupConfig(logFromLevel, collectStackTrace)); 255 256 configStream.end(group_overrides_token); 257 } 258 } 259 260 configStream.end(config_token); 261 262 return new ProtoLogConfig(defaultLogFromLevel, groupConfigs); 263 } 264 265 public static class Instance extends DataSourceInstance { 266 267 private final Consumer<ProtoLogConfig> mOnStart; 268 private final Runnable mOnFlush; 269 private final Consumer<ProtoLogConfig> mOnStop; 270 private final ProtoLogConfig mConfig; 271 Instance( DataSource<Instance, TlsState, IncrementalState> dataSource, int instanceIdx, ProtoLogConfig config, Consumer<ProtoLogConfig> onStart, Runnable onFlush, Consumer<ProtoLogConfig> onStop )272 public Instance( 273 DataSource<Instance, TlsState, IncrementalState> dataSource, 274 int instanceIdx, 275 ProtoLogConfig config, 276 Consumer<ProtoLogConfig> onStart, 277 Runnable onFlush, 278 Consumer<ProtoLogConfig> onStop 279 ) { 280 super(dataSource, instanceIdx); 281 this.mOnStart = onStart; 282 this.mOnFlush = onFlush; 283 this.mOnStop = onStop; 284 this.mConfig = config; 285 } 286 287 @Override onStart(StartCallbackArguments args)288 public void onStart(StartCallbackArguments args) { 289 this.mOnStart.accept(this.mConfig); 290 } 291 292 @Override onFlush(FlushCallbackArguments args)293 public void onFlush(FlushCallbackArguments args) { 294 this.mOnFlush.run(); 295 } 296 297 @Override onStop(StopCallbackArguments args)298 public void onStop(StopCallbackArguments args) { 299 this.mOnStop.accept(this.mConfig); 300 } 301 } 302 } 303