简介
谷歌介绍文档:https://source.android.com/docs/core/graphics/tracing-win-transitions
安卓手机内置抓取Winscope流程:
- 打开 开发者选项-快捷设置开发者图块-Winscope跟踪;
- 下拉控制中心即会出现Winscope跟踪按钮;
- 点击Winscope跟踪按钮开始录制,再次点击停止录制,录制文件保存在手机/data/misc/wmtrace目录下;
谷歌文档说“跟踪记录会被写入/data/misc/wmtrace/wm_trace.winscope和/data/misc/wmtrace/layers_trace.winscope,同时还会包含在错误报告中。”。
本文分析安卓抓取Winscope代码流程。
Winscope发起录制
在设置app中有一个服务DevelopmentTiles$WinscopeTrace用来完成winscope的抓取。其定义在packages/apps/Settings/src/com/android/settings/development/qstile/DevelopmentTiles.java:
/**
* Tile to toggle Winscope trace which consists of Window and Layer traces.
*/
public static class WinscopeTrace extends DevelopmentTiles {
@VisibleForTesting
static final int SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE = 1025;
@VisibleForTesting
static final int SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE = 1026;
private IBinder mSurfaceFlinger;
private IWindowManager mWindowManager;
private ImeTracing mImeTracing;
private Toast mToast;
@Override
public void onCreate() {
super.onCreate();
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger");
mImeTracing = ImeTracing.getInstance();
Context context = getApplicationContext();
CharSequence text = "Trace files written to /data/misc/wmtrace";
mToast = Toast.makeText(context, text, Toast.LENGTH_LONG);
}
......
}
这个服务在onCreate中获取了要抓取Winscope的相关系统服务的binder,其中包括SurfaceFlinger,WindowManager等,还准备了录制完成时弹出的Toast。
这个服务主要是通过点击时调用setIsEnabled方法开开始/结束录制:
@Override
protected void setIsEnabled(boolean isEnabled) {
setWindowTraceEnabled(isEnabled);
setLayerTraceEnabled(isEnabled);
setSystemUiTracing(isEnabled);
setImeTraceEnabled(isEnabled);
if (!isEnabled) {
mToast.show();
}
}
其中setWindowTraceEnabled方法:
private void setWindowTraceEnabled(boolean isEnabled) {
try {
if (isEnabled) {
mWindowManager.startWindowTrace();
} else {
mWindowManager.stopWindowTrace();
}
} catch (RemoteException e) {
Log.e(TAG, "Could not set window trace status." + e.toString());
}
}
这等同于执行命令adb shell cmd window tracing start和adb shell cmd window tracing stop。
其中setLayerTraceEnabled方法:
private void setLayerTraceEnabled(boolean isEnabled) {
Parcel data = null;
try {
if (mSurfaceFlinger != null) {
data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
data.writeInt(isEnabled ? 1 : 0);
mSurfaceFlinger.transact(SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE,
data, null, 0 /* flags */);
}
} catch (RemoteException e) {
Log.e(TAG, "Could not set layer tracing." + e.toString());
} finally {
if (data != null) {
data.recycle();
}
}
}
这等同于执行命令adb shell service call SurfaceFlinger 1025 i32 1和adb shell service call SurfaceFlinger 1025 i32 0
WindowManager
开始录制时会调用WindowTracing的startTrace方法:(frameworks/base/services/core/java/com/android/server/wm/WindowTracing.java)
void startTrace(@Nullable PrintWriter pw) {
if (IS_USER) { // user版本的镜像不支持抓取Winscope
logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
return;
}
synchronized (mEnabledLock) {
ProtoLogImpl.getSingleInstance().startProtoLog(pw);
logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
mBuffer.resetBuffer(); // 存录制数据的buffer
mEnabled = mEnabledLockFree = true;
}
log("trace.enable"); // 录制一帧
}
首先判断当前系统是否为user版本,如果是user版本则直接return不支持录制。
接下来调用mBuffer的resetBuffer清空这个buffer,后续的录制数据都存储在这个mBuffer中。然后调用log方法录制一帧:(frameworks/base/services/core/java/com/android/server/wm/WindowTracing.java)
/**
* Write the current frame to the buffer
*
* @param where Logging point descriptor
*/
private void log(String where) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
try {
ProtoOutputStream os = new ProtoOutputStream(); // 准备输出流
long tokenOuter = os.start(ENTRY);
os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
os.write(WHERE, where);
long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
synchronized (mGlobalLock) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "dumpDebugLocked");
try {
mService.dumpDebugLocked(os, mLogLevel); // 录制一帧
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
os.end(tokenInner);
os.end(tokenOuter);
mBuffer.add(os); // 录制的这一帧数据填入mBuffer
mScheduled = false;
} catch (Exception e) {
Log.wtf(TAG, "Exception while tracing state", e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
其中是通过WindowManagerService的dumpDebugLocked方法录制的:(frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java)
/**
* Write to a protocol buffer output stream. Protocol buffer message definition is at
* {@link com.android.server.wm.WindowManagerServiceDumpProto}.
*
* @param proto Stream to write the WindowContainer object to.
* @param logLevel Determines the amount of data to be written to the Protobuf.
*/
void dumpDebugLocked(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
mPolicy.dumpDebug(proto, POLICY);
mRoot.dumpDebug(proto, ROOT_WINDOW_CONTAINER, logLevel);
final DisplayContent topFocusedDisplayContent = mRoot.getTopFocusedDisplayContent();
if (topFocusedDisplayContent.mCurrentFocus != null) {
topFocusedDisplayContent.mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
}
if (topFocusedDisplayContent.mFocusedApp != null) {
topFocusedDisplayContent.mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
if (imeWindow != null) {
imeWindow.writeIdentifierToProto(proto, INPUT_METHOD_WINDOW);
}
proto.write(DISPLAY_FROZEN, mDisplayFrozen);
proto.write(FOCUSED_DISPLAY_ID, topFocusedDisplayContent.getDisplayId());
proto.write(HARD_KEYBOARD_AVAILABLE, mHardKeyboardAvailable);
// This is always true for now since we still update the window frames at the server side.
// Once we move the window layout to the client side, this can be false when we are waiting
// for the frames.
proto.write(WINDOW_FRAMES_VALID, true);
}
这样就完成了一帧的录制。
这个录制方法后续主要从WindowTracing的logState方法调用:(frameworks/base/services/core/java/com/android/server/wm/WindowTracing.java)
private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
log("onFrame" /* where */);
......
/**
* If tracing is enabled, log the current state or schedule the next frame to be logged,
* according to {@link #mLogOnFrame}.
*
* @param where Logging point descriptor
*/
void logState(String where) {
if (!isEnabled()) {
return;
}
if (mLogOnFrame) {
schedule();
} else {
log(where);
}
}
/**
* Schedule the log to trace the next frame
*/
private void schedule() {
if (mScheduled) {
return;
}
mScheduled = true;
mChoreographer.postFrameCallback(mFrameCallback);
}
logState方法只要isEnabled为true最终会调用log方法录制一帧。然后还根据mLogOnFrame是否为true,来决定是等下次vsync的时候调用,还是直接调用。
logState方法调用的位置是WindowManagerService的closeSurfaceTransaction:(frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java)
/**
* Closes a surface transaction.
* @param where debug string indicating where the transaction originated
*/
void closeSurfaceTransaction(String where) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
SurfaceControl.closeTransaction();
mWindowTracing.logState(where);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
也就是说WindowManagerService这边每次提交layer相关改动给SurfaceFlinger的同时会触发Winscope抓取一帧信息。
最终结束录制时调用WindowTracing的stopTrace方法:(frameworks/base/services/core/java/com/android/server/wm/WindowTracing.java)
/**
* Stops the trace and write the current buffer to disk
* @param pw Print writer
*/
void stopTrace(@Nullable PrintWriter pw) {
if (IS_USER) {
logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
return;
}
synchronized (mEnabledLock) {
logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
mEnabled = mEnabledLockFree = false;
if (mEnabled) {
logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
throw new IllegalStateException("tracing enabled while waiting for flush.");
}
writeTraceToFileLocked();
logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
}
ProtoLogImpl.getSingleInstance().stopProtoLog(pw, true);
}
这里调用了writeTraceToFileLocked方法来保存录制文件:(frameworks/base/services/core/java/com/android/server/wm/WindowTracing.java)
static final String WINSCOPE_EXT = ".winscope";
private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace" + WINSCOPE_EXT;
/**
* Writes the trace buffer to disk. This method has no internal synchronization and should be
* externally synchronized
*/
private void writeTraceToFileLocked() {
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked");
ProtoOutputStream proto = new ProtoOutputStream();
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
mBuffer.writeTraceToFile(mTraceFile, proto);
} catch (IOException e) {
Log.e(TAG, "Unable to write buffer to file", e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
这里mTraceFile的路径是/data/misc/wmtrace/wm_trace.winscope,这也是WindowManager的Winscope保存的位置。
最后调用了TraceBuffer的writeTraceToFile方法保存文件。
SurfaceFlinger
给SurfaceFlinger通信使用code为1025,即可开始或结束抓取Winscope:(frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp)
status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) {
......
if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
......
switch (code) {
......
case 1025: { // Set layer tracing
n = data.readInt32();
bool tracingEnabledChanged;
if (n == 1) {
int64_t fixedStartingTime = data.readInt64();
ALOGD("LayerTracing enabled");
tracingEnabledChanged = mLayerTracing.enable();
if (tracingEnabledChanged) {
int64_t startingTime =
(fixedStartingTime) ? fixedStartingTime : systemTime();
mScheduler
->schedule([&]() FTL_FAKE_GUARD(mStateLock) {
mLayerTracing.notify("start", startingTime);
})
.wait();
} else if (n == 2) {
std::string filename = std::string(data.readCString());
ALOGD("LayerTracing disabled. Trace wrote to %s", filename.c_str());
tracingEnabledChanged = mLayerTracing.disable(filename.c_str());
} else {
ALOGD("LayerTracing disabled");
tracingEnabledChanged = mLayerTracing.disable();
}
mTracingEnabledChanged = tracingEnabledChanged;
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
......
其中需要传一个整数作为参数,1代表开始,2代表结束并提供一个保存文件的路径,其它值代表结束并保存到默认位置。
开始录制时首先调用了LayerTracing的enable方法(frameworks/native/services/surfaceflinger/Tracing/LayerTracing.cpp):
bool LayerTracing::enable() {
std::scoped_lock lock(mTraceLock);
if (mEnabled) {
return false;
}
mBuffer->setSize(mBufferSizeInBytes);
mEnabled = true;
return true;
}
这里只是把mEnabled设为了true。
然后调用了LayerTracing的notify方法:(frameworks/native/services/surfaceflinger/Tracing/LayerTracing.cpp)
void LayerTracing::notify(bool visibleRegionDirty, int64_t time) {
std::scoped_lock lock(mTraceLock);
if (!mEnabled) {
return;
}
if (!visibleRegionDirty && !flagIsSet(LayerTracing::TRACE_BUFFERS)) {
return;
}
ATRACE_CALL();
LayersTraceProto entry;
entry.set_elapsed_realtime_nanos(time);
const char* where = visibleRegionDirty ? "visibleRegionsDirty" : "bufferLatched";
entry.set_where(where);
LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags));
if (flagIsSet(LayerTracing::TRACE_EXTRA)) {
mFlinger.dumpOffscreenLayersProto(layers);
}
entry.mutable_layers()->Swap(&layers);
if (flagIsSet(LayerTracing::TRACE_HWC)) {
std::string hwcDump;
mFlinger.dumpHwc(hwcDump);
entry.set_hwc_blob(hwcDump);
}
if (!flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
entry.set_excludes_composition_state(true);
}
mFlinger.dumpDisplayProto(entry);
mBuffer->emplace(std::move(entry));
}
注意第一个参数是bool类型的,但是传入的参数却是"start",编译器认为这是一个不为null的char指针,会被隐式转换成true。(这应该是aosp的一个小bug)
这里依次调用SurfaceFlinger的dumpDrawingStateProto,dumpOffscreenLayersProto,dumpHwc,dumpDisplayProto方法,录制一帧的信息,最终存入mBuffer。
这个录制一帧的notify方法后续会在SurfaceFlinger的commit和composite方法中调用,也就是说当SurfaceFlinger中的layer状态发生更改时或合成一帧时会录制一帧Winscope。
最后录制结束时会调用LayerTracing的disable方法:(frameworks/native/services/surfaceflinger/Tracing/LayerTracing.cpp)
bool LayerTracing::disable(std::string filename) {
std::scoped_lock lock(mTraceLock);
if (!mEnabled) {
return false;
}
mEnabled = false;
LayersTraceFileProto fileProto = createTraceFileProto();
mBuffer->writeToFile(fileProto, filename);
mBuffer->reset();
return true;
}
根据参数filename将录制结果写入文件,继续去看这个方法的定义:(frameworks/native/services/surfaceflinger/Tracing/LayerTracing.h)
class LayerTracing {
public:
......
bool disable(std::string filename = FILE_NAME);
......
private:
static constexpr auto FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
......
};
这里FILE_NAME的路径是/data/misc/wmtrace/layers_trace.winscope,这就是SurfaceFlinger的Winscope默认保存的位置。