1 /* 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 17 package com.android.server.wm; 18 19 import static android.os.Build.IS_USER; 20 21 import static com.android.server.wm.WindowManagerTraceFileProto.ENTRY; 22 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER; 23 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H; 24 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L; 25 import static com.android.server.wm.WindowManagerTraceFileProto.REAL_TO_ELAPSED_TIME_OFFSET_NANOS; 26 import static com.android.server.wm.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS; 27 import static com.android.server.wm.WindowManagerTraceProto.WHERE; 28 import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE; 29 30 import android.annotation.Nullable; 31 import android.os.ShellCommand; 32 import android.os.SystemClock; 33 import android.os.Trace; 34 import android.util.Log; 35 import android.util.proto.ProtoOutputStream; 36 import android.view.Choreographer; 37 38 import com.android.internal.protolog.ProtoLogImpl; 39 import com.android.internal.util.TraceBuffer; 40 41 import java.io.File; 42 import java.io.IOException; 43 import java.io.PrintWriter; 44 import java.util.concurrent.TimeUnit; 45 46 /** 47 * A class that allows window manager to dump its state continuously to a trace file, such that a 48 * time series of window manager state can be analyzed after the fact. 49 */ 50 class WindowTracing { 51 52 /** 53 * Maximum buffer size, currently defined as 5 MB 54 * Size was experimentally defined to fit between 100 to 150 elements. 55 */ 56 private static final int BUFFER_CAPACITY_CRITICAL = 5120 * 1024; // 5 MB 57 private static final int BUFFER_CAPACITY_TRIM = 10240 * 1024; // 10 MB 58 private static final int BUFFER_CAPACITY_ALL = 20480 * 1024; // 20 MB 59 static final String WINSCOPE_EXT = ".winscope"; 60 private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace" + WINSCOPE_EXT; 61 private static final String TAG = "WindowTracing"; 62 private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; 63 64 private final WindowManagerService mService; 65 private final Choreographer mChoreographer; 66 private final WindowManagerGlobalLock mGlobalLock; 67 68 private final Object mEnabledLock = new Object(); 69 private final File mTraceFile; 70 private final TraceBuffer mBuffer; 71 private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) -> 72 log("onFrame" /* where */); 73 74 private @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM; 75 private boolean mLogOnFrame = false; 76 private boolean mEnabled; 77 private volatile boolean mEnabledLockFree; 78 private boolean mScheduled; 79 createDefaultAndStartLooper(WindowManagerService service, Choreographer choreographer)80 static WindowTracing createDefaultAndStartLooper(WindowManagerService service, 81 Choreographer choreographer) { 82 File file = new File(TRACE_FILENAME); 83 return new WindowTracing(file, service, choreographer, BUFFER_CAPACITY_TRIM); 84 } 85 WindowTracing(File file, WindowManagerService service, Choreographer choreographer, int bufferCapacity)86 private WindowTracing(File file, WindowManagerService service, Choreographer choreographer, 87 int bufferCapacity) { 88 this(file, service, choreographer, service.mGlobalLock, bufferCapacity); 89 } 90 WindowTracing(File file, WindowManagerService service, Choreographer choreographer, WindowManagerGlobalLock globalLock, int bufferCapacity)91 WindowTracing(File file, WindowManagerService service, Choreographer choreographer, 92 WindowManagerGlobalLock globalLock, int bufferCapacity) { 93 mChoreographer = choreographer; 94 mService = service; 95 mGlobalLock = globalLock; 96 mTraceFile = file; 97 mBuffer = new TraceBuffer(bufferCapacity); 98 setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */); 99 } 100 startTrace(@ullable PrintWriter pw)101 void startTrace(@Nullable PrintWriter pw) { 102 if (IS_USER) { 103 logAndPrintln(pw, "Error: Tracing is not supported on user builds."); 104 return; 105 } 106 synchronized (mEnabledLock) { 107 ProtoLogImpl.getSingleInstance().startProtoLog(pw); 108 logAndPrintln(pw, "Start tracing to " + mTraceFile + "."); 109 mBuffer.resetBuffer(); 110 mEnabled = mEnabledLockFree = true; 111 } 112 log("trace.enable"); 113 } 114 115 /** 116 * Stops the trace and write the current buffer to disk 117 * @param pw Print writer 118 */ stopTrace(@ullable PrintWriter pw)119 void stopTrace(@Nullable PrintWriter pw) { 120 if (IS_USER) { 121 logAndPrintln(pw, "Error: Tracing is not supported on user builds."); 122 return; 123 } 124 synchronized (mEnabledLock) { 125 logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush."); 126 mEnabled = mEnabledLockFree = false; 127 128 if (mEnabled) { 129 logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush."); 130 throw new IllegalStateException("tracing enabled while waiting for flush."); 131 } 132 writeTraceToFileLocked(); 133 logAndPrintln(pw, "Trace written to " + mTraceFile + "."); 134 } 135 ProtoLogImpl.getSingleInstance().stopProtoLog(pw, true); 136 } 137 138 /** 139 * Stops the trace and write the current buffer to disk then restart, if it's already running. 140 * @param pw Print writer 141 */ saveForBugreport(@ullable PrintWriter pw)142 void saveForBugreport(@Nullable PrintWriter pw) { 143 if (IS_USER) { 144 logAndPrintln(pw, "Error: Tracing is not supported on user builds."); 145 return; 146 } 147 synchronized (mEnabledLock) { 148 if (!mEnabled) { 149 return; 150 } 151 mEnabled = mEnabledLockFree = false; 152 logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush."); 153 writeTraceToFileLocked(); 154 logAndPrintln(pw, "Trace written to " + mTraceFile + "."); 155 ProtoLogImpl.getSingleInstance().stopProtoLog(pw, true); 156 logAndPrintln(pw, "Start tracing to " + mTraceFile + "."); 157 mBuffer.resetBuffer(); 158 mEnabled = mEnabledLockFree = true; 159 ProtoLogImpl.getSingleInstance().startProtoLog(pw); 160 } 161 } 162 setLogLevel(@indowTraceLogLevel int logLevel, PrintWriter pw)163 private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) { 164 logAndPrintln(pw, "Setting window tracing log level to " + logLevel); 165 mLogLevel = logLevel; 166 167 switch (logLevel) { 168 case WindowTraceLogLevel.ALL: { 169 setBufferCapacity(BUFFER_CAPACITY_ALL, pw); 170 break; 171 } 172 case WindowTraceLogLevel.TRIM: { 173 setBufferCapacity(BUFFER_CAPACITY_TRIM, pw); 174 break; 175 } 176 case WindowTraceLogLevel.CRITICAL: { 177 setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw); 178 break; 179 } 180 } 181 } 182 setLogFrequency(boolean onFrame, PrintWriter pw)183 private void setLogFrequency(boolean onFrame, PrintWriter pw) { 184 logAndPrintln(pw, "Setting window tracing log frequency to " 185 + ((onFrame) ? "frame" : "transaction")); 186 mLogOnFrame = onFrame; 187 } 188 setBufferCapacity(int capacity, PrintWriter pw)189 private void setBufferCapacity(int capacity, PrintWriter pw) { 190 logAndPrintln(pw, "Setting window tracing buffer capacity to " + capacity + "bytes"); 191 mBuffer.setCapacity(capacity); 192 } 193 isEnabled()194 boolean isEnabled() { 195 return mEnabledLockFree; 196 } 197 onShellCommand(ShellCommand shell)198 int onShellCommand(ShellCommand shell) { 199 PrintWriter pw = shell.getOutPrintWriter(); 200 String cmd = shell.getNextArgRequired(); 201 switch (cmd) { 202 case "start": 203 startTrace(pw); 204 return 0; 205 case "stop": 206 stopTrace(pw); 207 return 0; 208 case "save-for-bugreport": 209 saveForBugreport(pw); 210 return 0; 211 case "status": 212 logAndPrintln(pw, getStatus()); 213 return 0; 214 case "frame": 215 setLogFrequency(true /* onFrame */, pw); 216 mBuffer.resetBuffer(); 217 return 0; 218 case "transaction": 219 setLogFrequency(false /* onFrame */, pw); 220 mBuffer.resetBuffer(); 221 return 0; 222 case "level": 223 String logLevelStr = shell.getNextArgRequired().toLowerCase(); 224 switch (logLevelStr) { 225 case "all": { 226 setLogLevel(WindowTraceLogLevel.ALL, pw); 227 break; 228 } 229 case "trim": { 230 setLogLevel(WindowTraceLogLevel.TRIM, pw); 231 break; 232 } 233 case "critical": { 234 setLogLevel(WindowTraceLogLevel.CRITICAL, pw); 235 break; 236 } 237 default: { 238 setLogLevel(WindowTraceLogLevel.TRIM, pw); 239 break; 240 } 241 } 242 mBuffer.resetBuffer(); 243 return 0; 244 case "size": 245 setBufferCapacity(Integer.parseInt(shell.getNextArgRequired()) * 1024, pw); 246 mBuffer.resetBuffer(); 247 return 0; 248 default: 249 pw.println("Unknown command: " + cmd); 250 pw.println("Window manager trace options:"); 251 pw.println(" start: Start logging"); 252 pw.println(" stop: Stop logging"); 253 pw.println(" save-for-bugreport: Save logging data to file if it's running."); 254 pw.println(" frame: Log trace once per frame"); 255 pw.println(" transaction: Log each transaction"); 256 pw.println(" size: Set the maximum log size (in KB)"); 257 pw.println(" status: Print trace status"); 258 pw.println(" level [lvl]: Set the log level between"); 259 pw.println(" lvl may be one of:"); 260 pw.println(" critical: Only visible windows with reduced information"); 261 pw.println(" trim: All windows with reduced"); 262 pw.println(" all: All window and information"); 263 return -1; 264 } 265 } 266 getStatus()267 String getStatus() { 268 return "Status: " 269 + ((isEnabled()) ? "Enabled" : "Disabled") 270 + "\n" 271 + "Log level: " 272 + mLogLevel 273 + "\n" 274 + mBuffer.getStatus(); 275 } 276 277 /** 278 * If tracing is enabled, log the current state or schedule the next frame to be logged, 279 * according to {@link #mLogOnFrame}. 280 * 281 * @param where Logging point descriptor 282 */ logState(String where)283 void logState(String where) { 284 if (!isEnabled()) { 285 return; 286 } 287 288 if (mLogOnFrame) { 289 schedule(); 290 } else { 291 log(where); 292 } 293 } 294 295 /** 296 * Schedule the log to trace the next frame 297 */ schedule()298 private void schedule() { 299 if (mScheduled) { 300 return; 301 } 302 303 mScheduled = true; 304 mChoreographer.postFrameCallback(mFrameCallback); 305 } 306 307 /** 308 * Write the current frame to the buffer 309 * 310 * @param where Logging point descriptor 311 */ log(String where)312 private void log(String where) { 313 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked"); 314 try { 315 ProtoOutputStream os = new ProtoOutputStream(); 316 long tokenOuter = os.start(ENTRY); 317 os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); 318 os.write(WHERE, where); 319 320 long tokenInner = os.start(WINDOW_MANAGER_SERVICE); 321 synchronized (mGlobalLock) { 322 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "dumpDebugLocked"); 323 try { 324 mService.dumpDebugLocked(os, mLogLevel); 325 } finally { 326 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 327 } 328 } 329 os.end(tokenInner); 330 os.end(tokenOuter); 331 mBuffer.add(os); 332 mScheduled = false; 333 } catch (Exception e) { 334 Log.wtf(TAG, "Exception while tracing state", e); 335 } finally { 336 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 337 } 338 } 339 logAndPrintln(@ullable PrintWriter pw, String msg)340 private void logAndPrintln(@Nullable PrintWriter pw, String msg) { 341 Log.i(TAG, msg); 342 if (pw != null) { 343 pw.println(msg); 344 pw.flush(); 345 } 346 } 347 348 /** 349 * Writes the trace buffer to disk. This method has no internal synchronization and should be 350 * externally synchronized 351 */ writeTraceToFileLocked()352 private void writeTraceToFileLocked() { 353 try { 354 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked"); 355 ProtoOutputStream proto = new ProtoOutputStream(); 356 proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); 357 long timeOffsetNs = 358 TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()) 359 - SystemClock.elapsedRealtimeNanos(); 360 proto.write(REAL_TO_ELAPSED_TIME_OFFSET_NANOS, timeOffsetNs); 361 mBuffer.writeTraceToFile(mTraceFile, proto); 362 } catch (IOException e) { 363 Log.e(TAG, "Unable to write buffer to file", e); 364 } finally { 365 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 366 } 367 } 368 } 369