blob: ba3dc607f6ccb55d70a813289830ac5030a5702b [file] [log] [blame]
Adrian Roos111aff92017-09-27 18:11:46 +02001/*
2 * Copyright (C) 2017 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
17package com.android.server.wm;
18
Vishnu Nairab250032017-11-21 07:32:43 -080019import static android.os.Build.IS_USER;
Nataniel Borges5a38bad2019-01-03 10:32:03 -080020
Yi Jin6c6e9ca2018-03-20 16:53:35 -070021import static com.android.server.wm.WindowManagerTraceFileProto.ENTRY;
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020022import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
23import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
24import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
Yi Jin6c6e9ca2018-03-20 16:53:35 -070025import static com.android.server.wm.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
26import static com.android.server.wm.WindowManagerTraceProto.WHERE;
27import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
Adrian Roos111aff92017-09-27 18:11:46 +020028
Nataniel Borges5a38bad2019-01-03 10:32:03 -080029import android.annotation.Nullable;
Adrian Roos111aff92017-09-27 18:11:46 +020030import android.os.ShellCommand;
31import android.os.SystemClock;
32import android.os.Trace;
33import android.util.Log;
34import android.util.proto.ProtoOutputStream;
Nataniel Borges48d9aa92019-02-01 14:45:55 -080035import android.view.Choreographer;
36
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020037import com.android.server.protolog.ProtoLogImpl;
Winson Chungb53b30c2019-11-18 21:03:09 -080038import com.android.internal.util.TraceBuffer;
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020039
Adrian Roos111aff92017-09-27 18:11:46 +020040import java.io.File;
Adrian Roos111aff92017-09-27 18:11:46 +020041import java.io.IOException;
Adrian Roos111aff92017-09-27 18:11:46 +020042import java.io.PrintWriter;
Adrian Roos111aff92017-09-27 18:11:46 +020043
44/**
45 * A class that allows window manager to dump its state continuously to a trace file, such that a
46 * time series of window manager state can be analyzed after the fact.
47 */
48class WindowTracing {
49
Nataniel Borges5a38bad2019-01-03 10:32:03 -080050 /**
51 * Maximum buffer size, currently defined as 512 KB
52 * Size was experimentally defined to fit between 100 to 150 elements.
53 */
Nataniel Borges7d37ce22019-02-05 10:07:02 -080054 private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024;
55 private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024;
56 private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
57 private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb";
Adrian Roos111aff92017-09-27 18:11:46 +020058 private static final String TAG = "WindowTracing";
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020059 private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
Adrian Roos111aff92017-09-27 18:11:46 +020060
Nataniel Borges48d9aa92019-02-01 14:45:55 -080061 private final WindowManagerService mService;
62 private final Choreographer mChoreographer;
63 private final WindowManagerGlobalLock mGlobalLock;
64
Nataniel Borges7d37ce22019-02-05 10:07:02 -080065 private final Object mEnabledLock = new Object();
66 private final File mTraceFile;
Winson Chungb53b30c2019-11-18 21:03:09 -080067 private final TraceBuffer mBuffer;
Nataniel Borges7d37ce22019-02-05 10:07:02 -080068 private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
69 log("onFrame" /* where */);
Nataniel Borges5a38bad2019-01-03 10:32:03 -080070
Nataniel Borges7d37ce22019-02-05 10:07:02 -080071 private @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM;
72 private boolean mLogOnFrame = false;
Adrian Roos111aff92017-09-27 18:11:46 +020073 private boolean mEnabled;
74 private volatile boolean mEnabledLockFree;
Nataniel Borges48d9aa92019-02-01 14:45:55 -080075 private boolean mScheduled;
Adrian Roos111aff92017-09-27 18:11:46 +020076
Nataniel Borges7d37ce22019-02-05 10:07:02 -080077 static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
78 Choreographer choreographer) {
79 File file = new File(TRACE_FILENAME);
80 return new WindowTracing(file, service, choreographer, BUFFER_CAPACITY_TRIM);
Nataniel Borges48d9aa92019-02-01 14:45:55 -080081 }
82
Nataniel Borges7d37ce22019-02-05 10:07:02 -080083 private WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
84 int bufferCapacity) {
85 this(file, service, choreographer, service.mGlobalLock, bufferCapacity);
86 }
Nataniel Borges48d9aa92019-02-01 14:45:55 -080087
Nataniel Borges7d37ce22019-02-05 10:07:02 -080088 WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
89 WindowManagerGlobalLock globalLock, int bufferCapacity) {
Nataniel Borges48d9aa92019-02-01 14:45:55 -080090 mChoreographer = choreographer;
91 mService = service;
92 mGlobalLock = globalLock;
Nataniel Borges7d37ce22019-02-05 10:07:02 -080093 mTraceFile = file;
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020094 mBuffer = new TraceBuffer(bufferCapacity);
Nataniel Borges7d37ce22019-02-05 10:07:02 -080095 setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */);
Adrian Roos111aff92017-09-27 18:11:46 +020096 }
97
Nataniel Borges7d37ce22019-02-05 10:07:02 -080098 void startTrace(@Nullable PrintWriter pw) {
Nataniel Borges5a38bad2019-01-03 10:32:03 -080099 if (IS_USER) {
Vishnu Nairab250032017-11-21 07:32:43 -0800100 logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
101 return;
102 }
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800103 synchronized (mEnabledLock) {
Adam Pardyl0f1b3d42019-08-19 15:24:11 +0200104 ProtoLogImpl.getSingleInstance().startProtoLog(pw);
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800105 logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
106 mBuffer.resetBuffer();
Adrian Roos111aff92017-09-27 18:11:46 +0200107 mEnabled = mEnabledLockFree = true;
108 }
Nataniel Borges8a5106c2019-03-06 11:31:34 -0800109 log("trace.enable");
Adrian Roos111aff92017-09-27 18:11:46 +0200110 }
111
Nataniel Borges9e260462019-02-27 17:02:20 -0800112 /**
113 * Stops the trace and write the current buffer to disk
114 * @param pw Print writer
115 */
Vishnu Nairc9615042018-01-09 07:23:31 -0800116 void stopTrace(@Nullable PrintWriter pw) {
Nataniel Borges9e260462019-02-27 17:02:20 -0800117 stopTrace(pw, true /* writeToFile */);
118 }
119
120 /**
121 * Stops the trace
122 * @param pw Print writer
123 * @param writeToFile If the current buffer should be written to disk or not
124 */
125 void stopTrace(@Nullable PrintWriter pw, boolean writeToFile) {
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800126 if (IS_USER) {
Vishnu Nairab250032017-11-21 07:32:43 -0800127 logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
128 return;
129 }
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800130 synchronized (mEnabledLock) {
131 logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
Adrian Roos111aff92017-09-27 18:11:46 +0200132 mEnabled = mEnabledLockFree = false;
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800133
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800134 if (mEnabled) {
135 logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
136 throw new IllegalStateException("tracing enabled while waiting for flush.");
Adrian Roos111aff92017-09-27 18:11:46 +0200137 }
Nataniel Borges9e260462019-02-27 17:02:20 -0800138 if (writeToFile) {
139 writeTraceToFileLocked();
140 logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
141 }
Adrian Roos111aff92017-09-27 18:11:46 +0200142 }
Adam Pardyl0f1b3d42019-08-19 15:24:11 +0200143 ProtoLogImpl.getSingleInstance().stopProtoLog(pw, writeToFile);
Adrian Roos111aff92017-09-27 18:11:46 +0200144 }
145
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800146 private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
147 logAndPrintln(pw, "Setting window tracing log level to " + logLevel);
148 mLogLevel = logLevel;
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800149
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800150 switch (logLevel) {
151 case WindowTraceLogLevel.ALL: {
152 setBufferCapacity(BUFFER_CAPACITY_ALL, pw);
153 break;
154 }
155 case WindowTraceLogLevel.TRIM: {
156 setBufferCapacity(BUFFER_CAPACITY_TRIM, pw);
157 break;
158 }
159 case WindowTraceLogLevel.CRITICAL: {
160 setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw);
161 break;
162 }
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800163 }
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800164 }
165
166 private void setLogFrequency(boolean onFrame, PrintWriter pw) {
167 logAndPrintln(pw, "Setting window tracing log frequency to "
168 + ((onFrame) ? "frame" : "transaction"));
169 mLogOnFrame = onFrame;
170 }
171
172 private void setBufferCapacity(int capacity, PrintWriter pw) {
173 logAndPrintln(pw, "Setting window tracing buffer capacity to " + capacity + "bytes");
174 mBuffer.setCapacity(capacity);
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800175 }
176
Adrian Roos111aff92017-09-27 18:11:46 +0200177 boolean isEnabled() {
178 return mEnabledLockFree;
179 }
180
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800181 int onShellCommand(ShellCommand shell) {
Adrian Roos111aff92017-09-27 18:11:46 +0200182 PrintWriter pw = shell.getOutPrintWriter();
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800183 String cmd = shell.getNextArgRequired();
184 switch (cmd) {
185 case "start":
186 startTrace(pw);
187 return 0;
188 case "stop":
189 stopTrace(pw);
190 return 0;
191 case "status":
192 logAndPrintln(pw, getStatus());
193 return 0;
194 case "frame":
195 setLogFrequency(true /* onFrame */, pw);
196 mBuffer.resetBuffer();
197 return 0;
198 case "transaction":
199 setLogFrequency(false /* onFrame */, pw);
200 mBuffer.resetBuffer();
201 return 0;
202 case "level":
203 String logLevelStr = shell.getNextArgRequired().toLowerCase();
204 switch (logLevelStr) {
205 case "all": {
206 setLogLevel(WindowTraceLogLevel.ALL, pw);
207 break;
208 }
209 case "trim": {
210 setLogLevel(WindowTraceLogLevel.TRIM, pw);
211 break;
212 }
213 case "critical": {
214 setLogLevel(WindowTraceLogLevel.CRITICAL, pw);
215 break;
216 }
217 default: {
218 setLogLevel(WindowTraceLogLevel.TRIM, pw);
219 break;
220 }
221 }
222 mBuffer.resetBuffer();
223 return 0;
224 case "size":
225 setBufferCapacity(Integer.parseInt(shell.getNextArgRequired()) * 1024, pw);
226 mBuffer.resetBuffer();
227 return 0;
228 default:
229 pw.println("Unknown command: " + cmd);
230 pw.println("Window manager trace options:");
231 pw.println(" start: Start logging");
232 pw.println(" stop: Stop logging");
233 pw.println(" frame: Log trace once per frame");
234 pw.println(" transaction: Log each transaction");
235 pw.println(" size: Set the maximum log size (in KB)");
Nataniel Borgesc0e18bf2019-02-25 10:45:27 -0800236 pw.println(" status: Print trace status");
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800237 pw.println(" level [lvl]: Set the log level between");
238 pw.println(" lvl may be one of:");
239 pw.println(" critical: Only visible windows with reduced information");
240 pw.println(" trim: All windows with reduced");
241 pw.println(" all: All window and information");
242 return -1;
Adrian Roos111aff92017-09-27 18:11:46 +0200243 }
244 }
245
Nataniel Borges24325ce2019-02-05 15:43:29 -0800246 String getStatus() {
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800247 return "Status: "
248 + ((isEnabled()) ? "Enabled" : "Disabled")
249 + "\n"
250 + "Log level: "
251 + mLogLevel
252 + "\n"
253 + mBuffer.getStatus();
254 }
255
Nataniel Borges48d9aa92019-02-01 14:45:55 -0800256 /**
257 * If tracing is enabled, log the current state or schedule the next frame to be logged,
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800258 * according to {@link #mLogOnFrame}.
Nataniel Borges48d9aa92019-02-01 14:45:55 -0800259 *
260 * @param where Logging point descriptor
261 */
262 void logState(String where) {
Adrian Roos111aff92017-09-27 18:11:46 +0200263 if (!isEnabled()) {
264 return;
265 }
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800266
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800267 if (mLogOnFrame) {
Nataniel Borges48d9aa92019-02-01 14:45:55 -0800268 schedule();
269 } else {
270 log(where);
271 }
272 }
Nataniel Borges023ecb52019-01-16 14:15:43 -0800273
Nataniel Borges48d9aa92019-02-01 14:45:55 -0800274 /**
275 * Schedule the log to trace the next frame
276 */
277 private void schedule() {
278 if (mScheduled) {
279 return;
280 }
281
282 mScheduled = true;
283 mChoreographer.postFrameCallback(mFrameCallback);
284 }
285
286 /**
287 * Write the current frame to the buffer
288 *
289 * @param where Logging point descriptor
290 */
291 private void log(String where) {
292 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
293 try {
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800294 ProtoOutputStream os = new ProtoOutputStream();
295 long tokenOuter = os.start(ENTRY);
296 os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
297 os.write(WHERE, where);
Nataniel Borges48d9aa92019-02-01 14:45:55 -0800298
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800299 long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
300 synchronized (mGlobalLock) {
Jeffrey Huangcb782852019-12-05 11:28:11 -0800301 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "dumpDebugLocked");
Nataniel Borges48d9aa92019-02-01 14:45:55 -0800302 try {
Jeffrey Huangcb782852019-12-05 11:28:11 -0800303 mService.dumpDebugLocked(os, mLogLevel);
Nataniel Borges48d9aa92019-02-01 14:45:55 -0800304 } finally {
305 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
306 }
Nataniel Borges023ecb52019-01-16 14:15:43 -0800307 }
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800308 os.end(tokenInner);
309 os.end(tokenOuter);
310 mBuffer.add(os);
311 mScheduled = false;
Nataniel Borges48d9aa92019-02-01 14:45:55 -0800312 } catch (Exception e) {
313 Log.wtf(TAG, "Exception while tracing state", e);
Adrian Roos111aff92017-09-27 18:11:46 +0200314 } finally {
315 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
316 }
Adrian Roos111aff92017-09-27 18:11:46 +0200317 }
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800318
319 /**
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800320 * Writes the trace buffer to new file for the bugreport.
321 *
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800322 * This method is synchronized with {@code #startTrace(PrintWriter)} and
323 * {@link #stopTrace(PrintWriter)}.
324 */
325 void writeTraceToFile() {
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800326 synchronized (mEnabledLock) {
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800327 writeTraceToFileLocked();
328 }
Adam Pardyl0f1b3d42019-08-19 15:24:11 +0200329 ProtoLogImpl.getSingleInstance().writeProtoLogToFile();
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800330 }
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800331
332 private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
333 Log.i(TAG, msg);
334 if (pw != null) {
335 pw.println(msg);
336 pw.flush();
337 }
338 }
339
340 /**
341 * Writes the trace buffer to disk. This method has no internal synchronization and should be
342 * externally synchronized
343 */
344 private void writeTraceToFileLocked() {
345 try {
346 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked");
Adam Pardyl0f1b3d42019-08-19 15:24:11 +0200347 ProtoOutputStream proto = new ProtoOutputStream();
348 proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
349 mBuffer.writeTraceToFile(mTraceFile, proto);
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800350 } catch (IOException e) {
351 Log.e(TAG, "Unable to write buffer to file", e);
352 } finally {
353 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
354 }
355 }
Adam Pardyl0f1b3d42019-08-19 15:24:11 +0200356}