Winscope中抓取window相关数据的原理剖析

背景:

前面文章有对Winscope的Transaction的数据手机等进行了源码分析,今天来继续对Winscope中抓取Window相关的信息进行深入剖析
在这里插入图片描述首先切入点可以从触发window相关Winscope的命令进行入手:

adb shell cmd window tracing start  --开启抓取

adb shell cmd window tracing stop  --结束抓取

使用命令抓取后成果:

在这里插入图片描述

源码分析:

触发window相关Winscope的命令:

adb shell cmd window tracing start

这里会调用到WindowManagerService的onShellCommand方法:
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
        String[] args, ShellCallback callback, ResultReceiver result) {
    new WindowManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
}

然后主要看看WindowManagerShellCommand的相关的方法
frameworks/base/services/core/java/com/android/server/wm/WindowManagerShellCommand.java

@Override
    public int onCommand(String cmd) {
        if (cmd == null) {
            return handleDefaultCommands(cmd);
        }
        final PrintWriter pw = getOutPrintWriter();
        try {
            switch (cmd) {
                case "size":
                    return runDisplaySize(pw);
                case "density":
                    return runDisplayDensity(pw);
                case "folded-area":
                    return runDisplayFoldedArea(pw);
                case "scaling":
                    return runDisplayScaling(pw);
                case "dismiss-keyguard":
                    return runDismissKeyguard(pw);
                case "tracing":
                    // XXX this should probably be changed to use openFileForSystem() to create
                    // the output trace file, so the shell gets the correct semantics for where
                    // trace files can be written.
                    return mInternal.mWindowTracing.onShellCommand(this);
                    //省略
                    }
}

这里调用到了WindowManagerShellCommand的onCommand,走到了"tracing"分支,即最后调用到了mWindowTracing.onShellCommand方法
frameworks/base/services/core/java/com/android/server/wm/WindowTracing.java


    int onShellCommand(ShellCommand shell) {
        PrintWriter pw = shell.getOutPrintWriter();
        String cmd = shell.getNextArgRequired();
        switch (cmd) {
            case "start":
                startTrace(pw);
                return 0;
            case "stop":
                stopTrace(pw);
                return 0;
                //省略
                }
}

下面看看 startTrace(pw)方法:

void startTrace(@Nullable PrintWriter pw) {
    synchronized (mEnabledLock) {
        ProtoLogImpl.getSingleInstance().startProtoLog(pw);
        logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
        mBuffer.resetBuffer();//把mBuffer清空
        mEnabled = mEnabledLockFree = true;
    }
    log("trace.enable");//这里是主要的核心方法会往mBuffer填入数据
}

可以看到startTrace主要干的事:
1、清空mBuffer,及一些标志位mEnabled为true
2、使用log往mBuffer填入数据
下面重点看看log方法

/**
     * 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);//这里是核心,把WindowManagerService相关的数据填入到了os
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                }
            }
            os.end(tokenInner);
            os.end(tokenOuter);
            mBuffer.add(os);//os数据丢入mBuffer
            mScheduled = false;
        } catch (Exception e) {
            Log.wtf(TAG, "Exception while tracing state", e);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
        }
    }

可以看出log方法主要就是WindowManagerService会dumpDebugLocked进入os,os数据放入mBuffer,那么重点就是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);//这里把所有WindowContainer数据都会进行dump
        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);

        // Write the BackNavigationController's state into the protocol buffer
        mAtmService.mBackNavigationController.dumpDebug(proto, BACK_NAVIGATION);
    }

可以看出来这里会有一个很重要的 mRoot.dumpDebug,mRoot就是RootWindowContainer,它会迭代dump出来整个wms层级结构的所有数据的详细信息,这样也是浏览器上可以绘制层级结构详细信息的关键。

更新数据时机:

因为Winscope是一个动态的过程,属于数据是不断变化的而上面只是开始抓取时候有往mBuffer中添加,那么是什么地方可以触发这个WindowTracing源源不断的打入log,即添加到mBuffer中?这里就需要加入相关堆栈追踪
在这里插入图片描述

每次在WindowManagerService.closeSurfaceTransaction时候就触发输入新的数据,相关堆栈如下:

11-30 11:32:12.809   489   511 I WindowTracing: log 24135
11-30 11:32:12.809   489   511 I WindowTracing: java.lang.Exception
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.wm.WindowTracing.log(WindowTracing.java:332)
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.wm.WindowTracing.logState(WindowTracing.java:291)
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.wm.WindowManagerService.closeSurfaceTransaction(WindowManagerService.java:1075)
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:799)
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.wm.RootWindowContainer.performSurfacePlacement(RootWindowContainer.java:756)
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:177)
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:126)
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:115)
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.wm.WindowSurfacePlacer$Traverser.run(WindowSurfacePlacer.java:57)
11-30 11:32:12.809   489   511 I WindowTracing: 	at android.os.Handler.handleCallback(Handler.java:958)
11-30 11:32:12.809   489   511 I WindowTracing: 	at android.os.Handler.dispatchMessage(Handler.java:99)
11-30 11:32:12.809   489   511 I WindowTracing: 	at android.os.Looper.loopOnce(Looper.java:205)
11-30 11:32:12.809   489   511 I WindowTracing: 	at android.os.Looper.loop(Looper.java:294)
11-30 11:32:12.809   489   511 I WindowTracing: 	at android.os.HandlerThread.run(HandlerThread.java:67)
11-30 11:32:12.809   489   511 I WindowTracing: 	at com.android.server.ServiceThread.run(ServiceThread.java:46)
11-30 11:32:12.819   489   511 I WindowTracing: log 24120
11-30 11:32:12.819   489   511 I WindowTracing: java.lang.Exception
11-30 11:32:12.819   489   511 I WindowTracing: 	at com.android.server.wm.WindowTracing.log(WindowTracing.java:332)
11-30 11:32:12.819   489   511 I WindowTracing: 	at com.android.server.wm.WindowTracing.logState(WindowTracing.java:291)
11-30 11:32:12.819   489   511 I WindowTracing: 	at com.android.server.wm.WindowManagerService.closeSurfaceTransaction(WindowManagerService.java:1075)
11-30 11:32:12.819   489   511 I WindowTracing: 	at com.android.server.wm.WindowAnimator.animate(WindowAnimator.java:198)
11-30 11:32:12.819   489   511 I WindowTracing: 	at com.android.server.wm.WindowAnimator.lambda$new$1(WindowAnimator.java:99)
11-30 11:32:12.819   489   511 I WindowTracing: 	at com.android.server.wm.WindowAnimator.$r8$lambda$aHNu1uhcqxihX5NZc4McDDQPAyw(WindowAnimator.java:0)
11-30 11:32:12.819   489   511 I WindowTracing: 	at com.android.server.wm.WindowAnimator$$ExternalSyntheticLambda1.doFrame(R8$$SyntheticClass:0)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1337)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.view.Choreographer.doCallbacks(Choreographer.java:952)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.view.Choreographer.doFrame(Choreographer.java:878)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.os.Handler.handleCallback(Handler.java:958)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.os.Handler.dispatchMessage(Handler.java:99)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.os.Looper.loopOnce(Looper.java:205)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.os.Looper.loop(Looper.java:294)
11-30 11:32:12.819   489   511 I WindowTracing: 	at android.os.HandlerThread.run(HandlerThread.java:67)
11-30 11:32:12.819   489   511 I WindowTracing: 	at com.android.server.ServiceThread.run(ServiceThread.java:46)

结束部分分析:

结束命令:

adb shell cmd window tracing stop --结束抓取

也会执行到WindowTracing的onShellCommand方法中在这里插入图片描述
执行到如下代码:

 /**
     * 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方法:

  /**
     * 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);
            long timeOffsetNs =
                    TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis())
                    - SystemClock.elapsedRealtimeNanos();
            proto.write(REAL_TO_ELAPSED_TIME_OFFSET_NANOS, timeOffsetNs);
            mBuffer.writeTraceToFile(mTraceFile, proto);//这里把mBuffer写入文件
        } catch (IOException e) {
            Log.e(TAG, "Unable to write buffer to file", e);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
        }
    }

mBuffer.writeTraceToFile写入的文件就是/data/misc/wmtrace/wm_trace.winscope.这个

https://mp.weixin.qq.com/s/QJCORrJQZS4daxDDlhe83A
更多framework技术干货,请关注下面“千里马学框架”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

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

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

打赏作者

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

抵扣说明:

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

余额充值