android WMS中的Proto/ProtoLog/ProtoLogTool日志原理介绍

探索Proto背景

hi,前期学习WMS/AMS专题时候,日志相关讲解最多是就是ProtoLog相关,比如我们想要观看所有的configuration相关的日志,只需要用以下命令进行开启:
更多内容qqun:422901085 相关课程

NX563J:/ # wm logging enable-text WM_DEBUG_CONFIGURATION
Loaded 724 log definitions from /system/etc/protolog.conf.json.gz

这里的WM_DEBUG_CONFIGURATION是我们再源码中看到的如下代码得出的:

            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config "
                        + "will change = %b", globalConfigWillChange);

就是这里的ProtoLog打印日志,它的TAG就是WM_DEBUG_CONFIGURATION

然后就可以通过logcat -s WindowManager看到相关的 ProtoLog.v(WM_DEBUG_CONFIGURATION所有打印

还是那句话,我们学会怎么用这个proto的日志,那么疑问来了,它是怎么个原理,这个proto怎么就可以实现动态打开呢?
要解答这个问题,那么我们最好方式那就是只能看源码了

   ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config "
                        + "will change = %b", globalConfigWillChange);

我们去看看这个实现是啥?

    public static void v(IProtoLogGroup group, String messageString, Object... args) {
        // Stub, replaced by the ProtoLogTool.
        if (REQUIRE_PROTOLOGTOOL) {
            throw new UnsupportedOperationException(
                    "ProtoLog calls MUST be processed with ProtoLogTool");
        }
    }

一看发现明显是啥也没有实现,而且还是个静态方法直接调用,把子类实现这条路都堵死了,那么到底是怎么回事?日志到底是怎么打印的,明明这个地方压根没有看到任何有日志打印痕迹

这里最后再注释中找到了一点线索:


/**
 *省略若干
 * Methods in this class are stubs, that are replaced by optimised versions by the ProtoLogTool
 * during build.
 */
public class ProtoLog {

这里注释我们看到了at are replaced by optimised versions by the ProtoLogTool,也就是ProtoLog的方法都会最后被ProtoLogTool来替代
那么这里我们来看看ProtoLogTool相关,搜索了以下发现如下路径:
frameworks/base/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
而且frameworks/base/tools/protologtool还看到了一个README,其实一般README里面信息是最多最好理解的,下面来看看这个README内容:

路径 frameworks/base/tools/protologtool/README.md
也是英文的,不过也都比较好理解,这里只对关键地方进行解释

########################README.md开始########################

ProtoLogTool

Code transformation tool and viewer for ProtoLog.//代码转化工具和对ProtoLog查看工具

What does it do?

ProtoLogTool incorporates three different modes of operation:

Code transformation //代码转化部分

//转化命令
Command: protologtool transform-protolog-calls --protolog-class <protolog class name> --protolog-impl-class <protolog implementation class name> --loggroups-class <protolog groups class name> --loggroups-jar <config jar path> --output-srcjar <output.srcjar> [<input.java>]

In this mode ProtoLogTool transforms every ProtoLog logging call in form of:

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

into:

//这个地方就是我们开始疑问的关键ProtoLog没有具体实现问题,ProtoLog.x这个代码直接被ProtoLogTool转变成了如下代码,这个代码相对就清晰多了,也可以清楚看到确实有ProtoLogImpl.x进行输出
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);
}

where ProtoLog, ProtoLogImpl and ProtoLogGroup are the classes provided as arguments
(can be imported, static imported or full path, wildcard imports are not allowed) and, x is the
logging method. The transformation is done on the source level. A hash is generated from the format
string, log level and log group name and inserted after the ProtoLogGroup argument. After the hash
we insert a bitmask specifying the types of logged parameters. The format string is replaced
by null if ProtoLogGroup.GROUP_NAME.isLogToLogcat() returns false. If ProtoLogGroup.GROUP_NAME.isEnabled()
returns false the log statement is removed entirely from the resultant code. The real generated code is inlined
and a number of new line characters is added as to preserve line numbering in file.

Input is provided as a list of java source file names. Transformed source is saved to a single
source jar file. The ProtoLogGroup class with all dependencies should be provided as a compiled
jar file (config.jar).
这里对Code transformation进行总结一下:
1、功能主要是把代码中的ProtoLog的打印代码要转化成真正实现的ProtoLogImpl代码
2、这里转化后其实Format String已经被设置为null,只保留了对应String的hash值
3、转化后代码到了一个source.jar中,ProtoLogGroup相关的类应该被作为config.jar提供

其实也是比较好理解,也就是这里借助的protologtool工具来把我们代码中proto相关打印进行新的转化而以和一些字符优化,没有其他了。

Viewer config generation

Command: generate-viewer-config --protolog-class <protolog class name> --loggroups-class <protolog groups class name> --loggroups-jar <config jar path> --viewer-conf <viewer.json> [<input.java>]

This command is similar in it’s syntax to the previous one, only instead of creating a processed source jar
it writes a viewer configuration file with following schema:

{
  "version": "1.0.0",
  "messages": {
    "123456": {
      "message": "Format string %d %s",
      "level": "ERROR",
      "group": "GROUP_NAME",
      "at": "com\/android\/server\/example\/Class.java"
    }
  },
  "groups": {
    "GROUP_NAME": {
      "tag": "TestLog"
    }
  }
}

这里也总结一下:
1、主就是对相关的有Proto打印的类进行了相关的识别记录转化,成了一个个的json条目,即可以根据这个json文件就知道系统中所有的Proto打印情况

Binary log viewing

//这里是对应的读取二进制Proto log文件的命令
Command: read-log --viewer-conf <viewer.json> <wm_log.pb>

Reads the binary ProtoLog log file and outputs a human-readable LogCat-like text log.

What is ProtoLog?

//解释什么是ProtoLog
ProtoLog is a generic logging system created for the WindowManager project. It allows both binary and text logging
and is tunable in runtime. It consists of 3 different submodules:

  • logging system built-in the Android app,
  • log viewer for reading binary logs,
  • a code processing tool.

ProtoLog is designed to reduce both application size (and by that memory usage) and amount of resources needed
for logging. This is achieved by replacing log message strings with their hashes and only loading to memory/writing
full log messages when necessary.

Text logging

For text-based logs Android LogCat is used as a backend. Message strings are loaded from a viewer config
located on the device when needed.

Binary logging

Binary logs are saved as Protocol Buffers file. They can be read using the ProtoLog tool or specialised
viewer like Winscope.

How to use ProtoLog?

Adding a new logging group or log statement

To add a new ProtoLogGroup simple create a new enum ProtoLogGroup member with desired parameters.

To add a new logging statement just add a new call to ProtoLog.x where x is a log level.

After doing any changes to logging groups or statements you should build the project and follow instructions printed by the tool.

How to change settings on device in runtime?

Use the adb shell su root cmd window logging command. To get help just type
adb shell su root cmd window logging help.

########################README.md结束########################

上面就是对ProtoLogTool的介绍,其实ProtoLogTool才是核心,它是在编译期间运行的,把对应的java文件进行处理的,具体可以看对应的bp
frameworks/base/services/core/Android.bp


genrule {
    name: "services.core.protologsrc",
    srcs: [
        ":protolog-groups",
        ":services.core-sources-am-wm",
    ],
    tools: ["protologtool"],
    cmd: "$(location protologtool) transform-protolog-calls " +
        "--protolog-class com.android.internal.protolog.common.ProtoLog " +
        "--protolog-impl-class com.android.internal.protolog.ProtoLogImpl " +
        "--protolog-cache-class 'com.android.server.wm.ProtoLogCache' " +
        "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
        "--loggroups-jar $(location :protolog-groups) " +
        "--output-srcjar $(out) " +
        "$(locations :services.core-sources-am-wm)",
    out: ["services.core.protolog.srcjar"],
}

genrule {
    name: "generate-protolog.json",
    srcs: [
        ":protolog-groups",
        ":services.core-sources-am-wm",
    ],
    tools: ["protologtool"],
    cmd: "$(location protologtool) generate-viewer-config " +
        "--protolog-class com.android.internal.protolog.common.ProtoLog " +
        "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
        "--loggroups-jar $(location :protolog-groups) " +
        "--viewer-conf $(out) " +
        "$(locations :services.core-sources-am-wm)",
    out: ["services.core.protolog.json"],
}


Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐