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 package com.android.internal.protolog; 17 18 import android.app.Activity; 19 import android.os.Bundle; 20 import android.os.ServiceManager.ServiceNotFoundException; 21 import android.perftests.utils.Stats; 22 import android.tracing.perfetto.DataSourceParams; 23 import android.tracing.perfetto.InitArguments; 24 import android.tracing.perfetto.Producer; 25 26 import androidx.test.InstrumentationRegistry; 27 28 import com.android.internal.protolog.common.IProtoLog; 29 import com.android.internal.protolog.common.IProtoLogGroup; 30 import com.android.internal.protolog.common.LogLevel; 31 32 import org.junit.Before; 33 import org.junit.BeforeClass; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 import org.junit.runners.Parameterized; 37 import org.junit.runners.Parameterized.Parameters; 38 39 import perfetto.protos.ProtologCommon; 40 41 import java.util.ArrayList; 42 import java.util.Collection; 43 44 @RunWith(Parameterized.class) 45 public class ProtoLogPerfTest { 46 private final boolean mLogToProto; 47 private final boolean mLogToLogcat; 48 49 /** 50 * Generates the parameters used for this test class 51 */ 52 @Parameters(name = "LOG_TO_{0}") params()53 public static Collection<Object[]> params() { 54 var params = new ArrayList<Object[]>(); 55 56 for (LogTo logTo : LogTo.values()) { 57 params.add(new Object[] { logTo }); 58 } 59 60 return params; 61 } 62 ProtoLogPerfTest(LogTo logTo)63 public ProtoLogPerfTest(LogTo logTo) { 64 mLogToProto = switch (logTo) { 65 case ALL, PROTO_ONLY -> true; 66 case LOGCAT_ONLY, NONE -> false; 67 }; 68 69 mLogToLogcat = switch (logTo) { 70 case ALL, LOGCAT_ONLY -> true; 71 case PROTO_ONLY, NONE -> false; 72 }; 73 } 74 75 private IProtoLog mProcessedProtoLogger; 76 private static ProtoLogDataSource sTestDataSource; 77 private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog"; 78 private static final String MOCK_TEST_FILE_PATH = "mock/file/path"; 79 private static final perfetto.protos.Protolog.ProtoLogViewerConfig VIEWER_CONFIG = 80 perfetto.protos.Protolog.ProtoLogViewerConfig.newBuilder() 81 .addGroups( 82 perfetto.protos.Protolog.ProtoLogViewerConfig.Group.newBuilder() 83 .setId(1) 84 .setName(TestProtoLogGroup.TEST_GROUP.toString()) 85 .setTag(TestProtoLogGroup.TEST_GROUP.getTag()) 86 ).addMessages( 87 perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() 88 .setMessageId(123) 89 .setMessage("My Test Debug Log Message %b") 90 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) 91 .setGroupId(1) 92 .setLocation("com/test/MyTestClass.java:123") 93 ).build(); 94 95 @BeforeClass init()96 public static void init() { 97 Producer.init(InitArguments.DEFAULTS); 98 99 sTestDataSource = new ProtoLogDataSource(TEST_PROTOLOG_DATASOURCE_NAME); 100 DataSourceParams params = 101 new DataSourceParams.Builder() 102 .setBufferExhaustedPolicy( 103 DataSourceParams 104 .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP) 105 .build(); 106 sTestDataSource.register(params); 107 108 ProtoLog.init(TestProtoLogGroup.values()); 109 } 110 111 @Before setUp()112 public void setUp() throws ServiceNotFoundException { 113 TestProtoLogGroup.TEST_GROUP.setLogToProto(mLogToProto); 114 TestProtoLogGroup.TEST_GROUP.setLogToLogcat(mLogToLogcat); 115 116 mProcessedProtoLogger = new ProcessedPerfettoProtoLogImpl( 117 sTestDataSource, 118 MOCK_TEST_FILE_PATH, 119 () -> new AutoClosableProtoInputStream(VIEWER_CONFIG.toByteArray()), 120 (instance) -> {}, 121 TestProtoLogGroup.values() 122 ); 123 } 124 125 @Test log_Processed_NoArgs()126 public void log_Processed_NoArgs() { 127 final var perfMonitor = new PerfMonitor(); 128 129 while (perfMonitor.keepRunning()) { 130 mProcessedProtoLogger.log( 131 LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123, 132 0, (Object[]) null); 133 } 134 } 135 136 @Test log_Processed_WithArgs()137 public void log_Processed_WithArgs() { 138 final var perfMonitor = new PerfMonitor(); 139 140 while (perfMonitor.keepRunning()) { 141 mProcessedProtoLogger.log( 142 LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123, 143 0b1110101001010100, 144 new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true}); 145 } 146 } 147 148 @Test log_Unprocessed_NoArgs()149 public void log_Unprocessed_NoArgs() { 150 final var perfMonitor = new PerfMonitor(); 151 152 while (perfMonitor.keepRunning()) { 153 ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test message"); 154 } 155 } 156 157 @Test log_Unprocessed_WithArgs()158 public void log_Unprocessed_WithArgs() { 159 final var perfMonitor = new PerfMonitor(); 160 161 while (perfMonitor.keepRunning()) { 162 ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test message %s, %d, %b", "arg1", 2, true); 163 } 164 } 165 166 private class PerfMonitor { 167 private int mIteration = 0; 168 169 private static final int WARM_UP_ITERATIONS = 10; 170 private static final int ITERATIONS = 1000; 171 172 private final ArrayList<Long> mResults = new ArrayList<>(); 173 174 private long mStartTimeNs; 175 keepRunning()176 public boolean keepRunning() { 177 final long currentTime = System.nanoTime(); 178 179 mIteration++; 180 181 if (mIteration > ITERATIONS) { 182 reportResults(); 183 return false; 184 } 185 186 if (mIteration > WARM_UP_ITERATIONS) { 187 mResults.add(currentTime - mStartTimeNs); 188 } 189 190 mStartTimeNs = System.nanoTime(); 191 return true; 192 } 193 reportResults()194 public void reportResults() { 195 final var stats = new Stats(mResults); 196 197 Bundle status = new Bundle(); 198 status.putLong("protologging_time_mean_ns", (long) stats.getMean()); 199 status.putLong("protologging_time_median_ns", (long) stats.getMedian()); 200 InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status); 201 } 202 } 203 204 private enum TestProtoLogGroup implements IProtoLogGroup { 205 TEST_GROUP(true, true, false, "WindowManagetProtoLogTest"); 206 207 private final boolean mEnabled; 208 private volatile boolean mLogToProto; 209 private volatile boolean mLogToLogcat; 210 private final String mTag; 211 212 /** 213 * @param enabled set to false to exclude all log statements for this group from 214 * compilation, they will not be available in runtime. 215 * @param logToProto enable binary logging for the group 216 * @param logToLogcat enable text logging for the group 217 * @param tag name of the source of the logged message 218 */ TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag)219 TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) { 220 this.mEnabled = enabled; 221 this.mLogToProto = logToProto; 222 this.mLogToLogcat = logToLogcat; 223 this.mTag = tag; 224 } 225 226 @Override isEnabled()227 public boolean isEnabled() { 228 return mEnabled; 229 } 230 231 @Override isLogToProto()232 public boolean isLogToProto() { 233 return mLogToProto; 234 } 235 236 @Override isLogToLogcat()237 public boolean isLogToLogcat() { 238 return mLogToLogcat; 239 } 240 241 @Override isLogToAny()242 public boolean isLogToAny() { 243 return mLogToLogcat || mLogToProto; 244 } 245 246 @Override getTag()247 public String getTag() { 248 return mTag; 249 } 250 251 @Override setLogToProto(boolean logToProto)252 public void setLogToProto(boolean logToProto) { 253 this.mLogToProto = logToProto; 254 } 255 256 @Override setLogToLogcat(boolean logToLogcat)257 public void setLogToLogcat(boolean logToLogcat) { 258 this.mLogToLogcat = logToLogcat; 259 } 260 261 @Override getId()262 public int getId() { 263 return ordinal(); 264 } 265 } 266 267 private enum LogTo { 268 PROTO_ONLY, 269 LOGCAT_ONLY, 270 ALL, 271 NONE, 272 } 273 } 274