Android ProtoLog动态开启相关wm logging源码分析补充

Android ProtoLog动态开启相关wm logging源码分析补充

针对上一节已经清楚了相关的代码中怎么可以打印到logcat中,其实本质上还就是protologtool这个工具对代码中的所有ProtoLog进行了相关的替换成了具体实现,最后会条件判断输出到Slog中
本文就重点来看看相关动态控制过程
更多内容qqun:422901085 相关课程

wm logging 命令设置详细分析

这里可以看看wm logging命令的相关帮助

NX563J:/ # wm logging -h
Unknown command
Window manager logging options:
	//启动抓取proto logging
  start: Start proto logging 
  //停止抓取proto logging
  stop: Stop proto logging 
  //允许proto logging的输出针对对应group(
  enable [group...]: Enable proto logging for given groups 
  //禁止proto logging的输出针对对应group
  disable [group...]: Disable proto logging for given groups
  //允许locat的输出针对对应group
  enable-text [group...]: Enable logcat logging for given groups
  //禁止locat的输出针对对应group
  disable-text [group...]: Disable logcat logging for given groups
Not handled, please use `adb shell dumpsys activity service SystemUIService WMShell` if you are looking for ProtoLog in WMShell

这里其实主要是enable和enable-text的一个区别
enable对应是proto logging这个就是像wms课程时候winscope抓取会把日志写入到data/misc/wmtrace/路径下wm_log.winscope
enable-text对应就是logcat可以直接看到的,这个也就是我们经常用的

知道对应的命令了,这里来看看对应的代码输出
分析代码之前我们得细分这个命令
wm logging enable-text xxxx
说明这个地方肯定是调用到我们WindowManagerService的onShellCommand

    @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的onCommand中

 
    public int onCommand(String cmd) {
        if (cmd == null) {
            return handleDefaultCommands(cmd);
        }
        final PrintWriter pw = getOutPrintWriter();
        try {
            switch (cmd) {
                //省略
                case "logging":
                    String[] args = peekRemainingArgs();
                    int result = ProtoLogImpl.getSingleInstance().onShellCommand(this);
                    //省略
                    return result;
            //省略
    }

接下来调用到了ProtoLogImpl.getSingleInstance().onShellCommand

public int onShellCommand(ShellCommand shell) {
        PrintWriter pw = shell.getOutPrintWriter();
        String cmd = shell.getNextArg();
        if (cmd == null) {
            return unknownCommand(pw);
        }
        ArrayList<String> args = new ArrayList<>();
        String arg;
        while ((arg = shell.getNextArg()) != null) {
            args.add(arg);
        }
        String[] groups = args.toArray(new String[args.size()]);
        switch (cmd) {
            case "start":
                startProtoLog(pw);
                return 0;
            case "stop":
                stopProtoLog(pw, true);
                return 0;
            case "status":
                logAndPrintln(pw, getStatus());
                return 0;
            case "enable":
                return setLogging(false, true, pw, groups);
            case "enable-text":
                mViewerConfig.loadViewerConfig(pw, mViewerConfigFilename);
                return setLogging(true, true, pw, groups);
            case "disable":
                return setLogging(false, false, pw, groups);
            case "disable-text":
                return setLogging(true, false, pw, groups);
            default:
                return unknownCommand(pw);
        }
    }

针对enable和enable-text其实也都是调用了一个方法setLogging:

    protected int setLogging(boolean setTextLogging, boolean value, PrintWriter pw,
            String... groups) {
        for (int i = 0; i < groups.length; i++) {
            String group = groups[i];
            IProtoLogGroup g = LOG_GROUPS.get(group);
            //根据传递进来的group进行遍历出对应的IProtoLogGroup
            if (g != null) {
                if (setTextLogging) {//如果是logcat输出
                    g.setLogToLogcat(value);
                } else {//只是输出到proto文件
                    g.setLogToProto(value);
                }
            } else { //没有遍历到就宣告失败
                logAndPrintln(pw, "No IProtoLogGroup named " + group);
                return -1;
            }
        }
        sCacheUpdater.run();
        return 0;
    }
frameworks/base/core/java/com/android/internal/protolog/ProtoLogGroup.java
    @Override
    public void setLogToLogcat(boolean logToLogcat) {
        this.mLogToLogcat = logToLogcat;//只是有个赋值而以
    }
 @Override
    public void setLogToProto(boolean logToProto) {
        this.mLogToProto = logToProto;
    }

这里shell执行部分就已经完成了,最终就是吧对应的ProtoLogGroup的相关变量赋值了

日志输出部分源码

前一节已经知道了

ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2);

会变成如下代码

if (ProtoLogImpl.isEnabled(GROUP_NAME)) {
    int protoLogParam0 = value1;
    String protoLogParam1 = String.valueOf(value2);
    ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 123456, 0b0100, "Format string %d %s or null", protoLogParam0, protoLogParam1);
}

这里看看ProtoLogImpl.isEnabled(GROUP_NAME)

  /** Returns true iff logging is enabled for the given {@code IProtoLogGroup}. */
    public static boolean isEnabled(IProtoLogGroup group) {
        return group.isLogToLogcat()
                || (group.isLogToProto() && getSingleInstance().isProtoEnabled());
    }

可以看出就是前面命令设置的值为true了这里就可以为true
再看对应的打印

   public static void w(IProtoLogGroup group, int messageHash, int paramsMask,
            @Nullable String messageString,
            Object... args) {
        getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args);
    }
    

调用到了log方法,它是
frameworks/base/core/java/com/android/internal/protolog/BaseProtoLogImpl.java

 @VisibleForTesting
    public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
            @Nullable String messageString, Object[] args) {
        if (group.isLogToProto()) {//判断是否属于输出到proto
            logToProto(messageHash, paramsMask, args);
        }
        if (group.isLogToLogcat()) {//判断是否属于输出到logcat
            logToLogcat(group.getTag(), level, messageHash, messageString, args);
        }
    }

下面只以logcat为例分析一下

    private void logToLogcat(String tag, LogLevel level, int messageHash,
            @Nullable String messageString, Object[] args) {
   //省略
        passToLogcat(tag, level, message);
    }
/**
     * SLog wrapper.
     */
    @VisibleForTesting
    public void passToLogcat(String tag, LogLevel level, String message) {
        switch (level) {
            case DEBUG:
                Slog.d(tag, message);//最后调用到了Slog
                break;
            case VERBOSE:
                Slog.v(tag, message);
                break;
            case INFO:
                Slog.i(tag, message);
                break;
            case WARN:
                Slog.w(tag, message);
                break;
            case ERROR:
                Slog.e(tag, message);
                break;
            case WTF:
                Slog.wtf(tag, message);
                break;
        }
    }

那相当于整个流程清晰了
同时要注意一下:proto logging和logcat其实不是互斥,而是互不影响,而且可以同时存在

proto二进制文件的解析查看方法

抓取这个proto的二进制其实又2类方法:
方法1:
命令:

NX563J:/ # test@test:~/wmtrace/nx563$ adb shell wm logging start                                           
Start logging to /data/misc/wmtrace/wm_log.winscope.
test@test:~/wmtrace/nx563$ adb shell wm logging stop 
Stop logging to /data/misc/wmtrace/wm_log.winscope. Waiting for log to flush.
Log written to /data/misc/wmtrace/wm_log.winscope.

很简单start开始和stop结束,最后日志在 /data/misc/wmtrace/wm_log.winscope文件中

方法2:
那就是我们使用的winscope方法
https://blog.csdn.net/learnframework/article/details/129432374

抓取之后查看方法:
因为wm_log.winscope属于二进制文件,直接文本打开其实乱码,那么怎么查看呢?
其实这里也有2中方法:
1、就是直接winscope.html即可以
在这里插入图片描述

这种方式最简单,但是因为只能在html网页,相对来说对想日志分析的还是不那么友好,又没有其他方法?
接下来就介绍另一种
方法2:
使用上一节提到的protologtool命令

 Command: read-log --viewer-conf <viewer.json> <wm_log.pb>

viewer.json就是/system/etc/protolog.conf.json.gz
wm_log.pb wm_log.winscope
即使用类似命令:protologtool read-log --viewer-conf protolog.conf.json wm_log.winscope
那么剩下就是protologtool命令哪里来的?
遗憾是android 13上面没有找到这个bin文件,但是它有对应可以执行的jar

java_binary_host {
    name: "protologtool",
    manifest: "manifest.txt",
    static_libs: [
        "protologtool-lib",
    ],
}

test@test:~/aosp/out$ find -name  protologtool.jar
./soong/.intermediates/frameworks/base/tools/protologtool/protologtool/linux_glibc_common/combined/protologtool.jar

要执行这个jar方法:java -jar protologtool.jar

test@test:~/aosp/out/soong/.intermediates/frameworks/base/tools/protologtool/protologtool/linux_glibc_common/combined$ java -jar protologtool.jar read-log --view
er-conf ~/wmtrace/protolog.conf.json ~/wmtrace/nx563/wmtrace/wm_log.winscope  > ~/1-wm_log.txt

日志如下:

03-04 09:06:45.953 INFO WindowManager: >>> OPEN TRANSACTION animate
03-04 09:06:45.958 INFO WindowManager: <<< CLOSE TRANSACTION animate
03-04 09:06:47.190 INFO WindowManager: Relayout Window{ce1f2d7 u0 StatusBar}: oldVis=0 newVis=0. java.lang.RuntimeException
03-04 09:06:47.191 INFO WindowManager: OUT SURFACE Surface(name=StatusBar)/@0x46c3092: copied
03-04 09:06:47.193 DEBUG WindowManager: InsetsSource updateVisibility for ITYPE_IME, serverVisible: false clientVisible: false
03-04 09:06:47.193 DEBUG WindowManager: InsetsSource updateVisibility for ITYPE_TOP_TAPPABLE_ELEMENT, serverVisible: true clientVisible: true
03-04 09:06:47.193 DEBUG WindowManager: InsetsSource updateVisibility for ITYPE_TOP_MANDATORY_GESTURES, serverVisible: true clientVisible: true
03-04 09:06:47.193 DEBUG WindowManager: InsetsSource updateVisibility for ITYPE_STATUS_BAR, serverVisible: true clientVisible: true
03-04 09:06:47.193 DEBUG WindowManager: handleNotObscuredLocked w: Window{41477bf u0 ScreenDecorOverlayBottom}, w.mHasSurface: false, w.isOnScreen(): false, w.isDisplayedLw(): false, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.194 DEBUG WindowManager: handleNotObscuredLocked w: Window{b45a65c u0 ScreenDecorOverlay}, w.mHasSurface: false, w.isOnScreen(): false, w.isDisplayedLw(): false, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.194 DEBUG WindowManager: handleNotObscuredLocked w: Window{66460ee u0 pip-dismiss-overlay}, w.mHasSurface: false, w.isOnScreen(): false, w.isDisplayedLw(): false, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.194 DEBUG WindowManager: handleNotObscuredLocked w: Window{e44117e u0 NotificationShade}, w.mHasSurface: true, w.isOnScreen(): true, w.isDisplayedLw(): true, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.194 DEBUG WindowManager: handleNotObscuredLocked w: Window{ce1f2d7 u0 StatusBar}, w.mHasSurface: true, w.isOnScreen(): true, w.isDisplayedLw(): true, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.194 DEBUG WindowManager: handleNotObscuredLocked w: Window{13ab3d9 u0 ShellDropTarget}, w.mHasSurface: false, w.isOnScreen(): false, w.isDisplayedLw(): false, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.194 DEBUG WindowManager: handleNotObscuredLocked w: Window{d0a84ad u0 InputMethod}, w.mHasSurface: false, w.isOnScreen(): false, w.isDisplayedLw(): false, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.194 DEBUG WindowManager: handleNotObscuredLocked w: Window{cd7897f u0 com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher}, w.mHasSurface: true, w.isOnScreen(): true, w.isDisplayedLw(): true, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.195 DEBUG WindowManager: handleNotObscuredLocked w: Window{1b1cf58 u0 com.android.settings/com.android.settings.SubSettings}, w.mHasSurface: false, w.isOnScreen(): false, w.isDisplayedLw(): false, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.195 DEBUG WindowManager: handleNotObscuredLocked w: Window{b350833 u0 com.android.settings/com.android.settings.SubSettings}, w.mHasSurface: false, w.isOnScreen(): false, w.isDisplayedLw(): false, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.195 DEBUG WindowManager: handleNotObscuredLocked w: Window{88c7bc6 u0 com.android.settings/com.android.settings.SubSettings}, w.mHasSurface: false, w.isOnScreen(): false, w.isDisplayedLw(): false, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.195 DEBUG WindowManager: handleNotObscuredLocked w: Window{e7b31e4 u0 com.android.settings/com.android.settings.Settings}, w.mHasSurface: false, w.isOnScreen(): false, w.isDisplayedLw(): false, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.195 DEBUG WindowManager: handleNotObscuredLocked w: Window{6ea66a3 u0 com.android.systemui.wallpapers.ImageWallpaper}, w.mHasSurface: true, w.isOnScreen(): true, w.isDisplayedLw(): true, w.mAttrs.userActivityTimeout: -1
03-04 09:06:47.208 INFO WindowManager: enableScreenIfNeededLocked: mDisplayEnabled=true mForceDisplayEnabled=false mShowingBootMessages=false mSystemBooted=true. java.lang.RuntimeException: here
03-04 09:06:47.209 VERBOSE WindowManager: ActivityRecord{cfb41da u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t148} is requesting orientation 5 (SCREEN_ORIENTATION_NOSENSOR)
03-04 09:06:47.209 VERBOSE WindowManager: Task{f56c100 #148 type=home I=com.android.launcher3/.uioverrides.QuickstepLauncher} is requesting orientation 5 (SCREEN_ORIENTATION_NOSENSOR)
03-04 09:06:47.209 VERBOSE WindowManager: Task{c7e1a66 #1 type=home} is requesting orientation 5 (SCREEN_ORIENTATION_NOSENSOR)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值