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