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