安卓Winscope原理

简介

谷歌介绍文档:https://source.android.com/docs/core/graphics/tracing-win-transitions

安卓手机内置抓取Winscope流程:

  1. 打开 开发者选项-快捷设置开发者图块-Winscope跟踪;
  2. 下拉控制中心即会出现Winscope跟踪按钮;
  3. 点击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默认保存的位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SSSxCCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值