背景在我们学习了wms相关课程后都知道wms和systemui很多地方调用都有Proto相关的日志有了这些日志可以大大方便调试相关窗口显示疑难问题。这些proto的日志默认都是不开放的可以动态通过相关命令打开。testtest:~$ adb shell wm logging-h Unknown command Window manager logging options:enable-text[group...]:Enable logcat loggingforgiven groups disable-text[group...]:Disable logcat loggingforgiven groups Not handled,please use adb shell dumpsys activity service SystemUIService WMShellifyou are lookingforProtoLog in WMShell但是说实话使用命令有以下几个痛点1、不一定知道具体要开放哪些Proto的TAG从而导致经常可能没有开放到对应TAG2、每次机器重启都需要重新执行wm命令打开Proto那么有没有什么工具可以解决上面痛点呢那就是今天介绍的Winscope中查看Proto日志。下面来看看google官方文档是如何介绍这块内容的https://source.android.google.cn/docs/core/graphics/winscope/analyze/protolog 英文原版上面就是google文档地址这边进行了相关简单翻译修改如下ProtoLogAndroid 日志记录系统旨在实现普遍可访问性和易用性前提是所有日志数据都可以表示为字符序列。这个前提适用于大多数用例尤其是在没有专用工具的情况下日志可读性至关重要时。不过在对日志记录性能要求很高且日志大小受限的环境中基于文本的日志记录可能不是最佳选择。WindowManager 就是这样的一个场景它需要一个强大的日志记录系统能够处理实时窗口转换日志且对系统影响最小。ProtoLog 是满足 WindowManager 和类似服务的日志记录需求的替代方案。ProtoLog 相对于 logcat 的主要优势包括用于记录日志的资源量更少。从开发者角度来看这与使用默认的 Android 日志记录框架相同。支持在运行时启用或停用日志语句。仍可根据需要将日志记录到 logcat。为了优化内存用量ProtoLog 采用了字符串驻留机制其中涉及计算和保存消息的已编译哈希值。为了提高性能ProtoLog 会在编译期间针对系统服务执行字符串驻留仅在运行时记录消息标识符和参数。此外在生成 ProtoLog 跟踪记录或获取 bug 报告时ProtoLog 会自动合并在编译时创建的消息字典从而支持从任何 build 中解码消息。使用 ProtoLog 时消息会以二进制格式 (proto) 存储在 Perfetto 跟踪记录中。消息解码发生在 Perfetto 的 trace_processor 内。该过程包括解码二进制 proto 消息、使用嵌入的消息字典将消息标识符转换为字符串以及使用动态参数设置字符串格式。ProtoLog 支持与 android.utils.Log 相同的日志级别即d、v、i、w、e、wtf。客户端 ProtoLog最初ProtoLog 仅适用于 WindowManager 的服务器端在单个进程和组件中运行。随后它被扩展到包含系统界面进程中的 WindowManager shell 代码但使用 ProtoLog 需要复杂的样板设置代码。此外Proto 日志记录仅限于系统服务器和系统界面进程因此很难集成到其他进程中并且需要为每个进程单独设置内存缓冲区。不过ProtoLog 现在已可用于客户端代码且无需额外的样板代码。与系统服务代码不同客户端代码通常会跳过编译时字符串驻留。字符串驻留则会在后台线程中动态发生。因此虽然客户端上的 ProtoLog 在内存使用方面与系统服务伤的 ProtoLog 具有相当的优势但它的性能开销略高并且不具备其服务器端对应部分的固定内存带来的内存缩减优势。注意您可以将在客户端进程中运行的系统代码例如 IME 或 WindowManager 代码配置为使用编译时优化并获得相同的优势。ProtoLog 组ProtoLog 消息会分成名为 ProtoLogGroups 的组类似于 Logcat 消息按 TAG 进行整理的方式。这些 ProtoLogGroups 用作消息集群可在运行时集体启用或停用。此外它们还控制着是否应在编译期间去除消息以及应在何处记录消息proto、logcat 或两者兼有。每个 ProtoLogGroup 都包含以下属性enabled如果设置为 false则会在编译期间排除此组中的消息并且在运行时无法使用这些消息。logToProto定义此组是否使用二进制格式记录日志。logToLogcat定义此组是否会将日志记录到 logcat。tag已记录消息的来源名称。使用 ProtoLog 的每个进程都必须配置一个 ProtoLogGroup 实例。支持的参数类型在内部ProtoLog 使用 android.text.TextUtils#formatSimple(String, Object…) 格式化字符串因此其语法相同。ProtoLog 支持以下参数类型%b - 布尔值%d、%x - 整数类型short、integer 或 long%f - 浮点类型float 或 double%s - 字符串%% - 字面量百分号系统支持宽度和精度修饰符例如 %04d 和 %10b但不支持 argument_index 和 flags。在新服务中使用 ProtoLog如需在新进程中使用 ProtoLog请执行以下操作为此服务创建 ProtoLogGroup 定义。在首次使用之前初始化定义例如在进程创建时Protolog.init(ProtologGroup.values());使用 Protolog 的方式与使用 android.util.Log 的方式相同ProtoLog.v(WM_SHELL_STARTING_WINDOW, “create taskSnapshot surface for task: %d”, taskId);启用编译时优化如需在进程中启用编译时 ProtoLog您必须更改其构建规则并调用 protologtool 二进制文件。ProtoLogTool 是一种代码转换二进制文件用于执行字符串驻留并更新 ProtoLog 调用。此二进制文件会转换每个 ProtoLog 日志记录调用如以下示例所示ProtoLog.x(ProtoLogGroup.GROUP_NAME, “Format string %d %s”, value1, value2);转换为if(ProtoLogImpl.isEnabled(GROUP_NAME)){intprotoLogParam0value1;String protoLogParam1String.valueOf(value2);ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME,1234560b0100,protoLogParam0,protoLogParam1);}在此示例中ProtoLog、ProtoLogImpl 和 ProtoLogGroup 是作为参数提供的类可以导入、静态导入或完整路径不允许通配符导入x 是日志记录方法。转换是在源代码级别完成的。系统会根据格式字符串、日志级别和日志组名称生成哈希并将其插入 ProtoLogGroup 参数后面。实际生成的代码是内嵌的并添加一些新的换行符以保持文件中的行号不变。示例genrule{name:wm_shell_protolog_src,srcs:[:protolog-impl,// protolog lib:wm_shell_protolog-groups,// protolog groups declaration:wm_shell-sources,// source code],tools:[protologtool],cmd:$(location protologtool) transform-protolog-calls --protolog-class com.android.internal.protolog.ProtoLog --loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup --loggroups-jar $(location :wm_shell_protolog-groups) --viewer-config-file-path /system_ext/etc/wmshell.protolog.pb --legacy-viewer-config-file-path /system_ext/etc/wmshell.protolog.json.gz --legacy-output-file-path /data/misc/wmtrace/shell_log.winscope --output-srcjar $(out) $(locations :wm_shell-sources),out:[wm_shell_protolog.srcjar],}命令行选项ProtoLog 的一个主要优势是您可以在运行时启用或停用它。例如您可以在 build 中启用详细日志记录默认处于停用状态并在本地开发期间启用它以便调试特定问题。例如在 WindowManager 中使用此模式其中 WM_DEBUG_WINDOW_TRANSITIONS 和 WM_DEBUG_WINDOW_TRANSITIONS_MIN 组用于启用不同类型的转换日志记录前者默认处于启用状态。在启动跟踪记录时您可以使用 Perfetto 配置 ProtoLog。您还可以使用 adb 命令行在本地配置 ProtoLog。命令 adb shell cmd protolog_configuration 支持以下参数help Printthishelp text.groups(list|status)list-lists all ProtoLog groups registered with ProtoLog service statusgroup-print the status of a ProtoLog grouplogcat(enable|disable)group enableordisable ProtoLog to logcat有效使用提示ProtoLog 会对消息和传递的任何字符串参数使用字符串驻留。这意味着为了从 ProtoLog 中获得更多优势消息应将重复值隔离到变量中。例如请参考以下语句Protolog.v(MY_GROUP,%s,The argument value is argument);在编译时优化后它会转换为以下代码ProtologImpl.v(MY_GROUP,0x123,The argument value is argument);如果在代码中使用 ProtoLog 并带有参数 A,B,CProtolog.v(MY_GROUP,%s,The argument value is A);Protolog.v(MY_GROUP,%s,The argument value is B);Protolog.v(MY_GROUP,%s,The argument value is C);Protolog.v(MY_GROUP,%s,The argument value is A);这会导致内存中出现以下消息Dict:0x123:%s0x111:The argument value is A0x222:The argument value is B0x333:The argument value is CMessage1(Hash:0x123,Arg1:0x111)Message2(Hash:0x123,Arg2:0x222)Message3(Hash:0x123,Arg3:0x333)Message4(Hash:0x123,Arg1:0x111)如果改为将 ProtoLog 语句写成如下形式Protolog.v(MY_GROUP,The argument value is %s,argument);内存缓冲区最终将变为Dict:0x123:The argument value is %s(24b)0x111:A(1b)0x222:B(1b)0x333:C(1b)Message1(Hash:0x123,Arg1:0x111)Message2(Hash:0x123,Arg2:0x222)Message3(Hash:0x123,Arg3:0x333)Message4(Hash:0x123,Arg1:0x111)此序列可使内存占用量减少 35%。Winscope 查看器抓取方式查看方式Winscope 的 ProtoLog 查看器标签页会以表格式显示 ProtoLog 跟踪记录。您可以按日志级别、标记、源文件存在 ProtoLog 语句和消息内容过滤跟踪记录。所有列均可过滤。 点击第一列中的时间戳会将时间轴移动到消息时间戳。此外点击 Go to Current Time 可将 ProtoLog 表格滚动回时间轴中所选的时间戳ProtoLog 查看器图 1. ProtoLog 查看器注意过滤条件仅支持子字符串搜索不支持正则表达式。
google官方文档:深入剖析ProtoLog原理及Winscope的查看方式
背景在我们学习了wms相关课程后都知道wms和systemui很多地方调用都有Proto相关的日志有了这些日志可以大大方便调试相关窗口显示疑难问题。这些proto的日志默认都是不开放的可以动态通过相关命令打开。testtest:~$ adb shell wm logging-h Unknown command Window manager logging options:enable-text[group...]:Enable logcat loggingforgiven groups disable-text[group...]:Disable logcat loggingforgiven groups Not handled,please use adb shell dumpsys activity service SystemUIService WMShellifyou are lookingforProtoLog in WMShell但是说实话使用命令有以下几个痛点1、不一定知道具体要开放哪些Proto的TAG从而导致经常可能没有开放到对应TAG2、每次机器重启都需要重新执行wm命令打开Proto那么有没有什么工具可以解决上面痛点呢那就是今天介绍的Winscope中查看Proto日志。下面来看看google官方文档是如何介绍这块内容的https://source.android.google.cn/docs/core/graphics/winscope/analyze/protolog 英文原版上面就是google文档地址这边进行了相关简单翻译修改如下ProtoLogAndroid 日志记录系统旨在实现普遍可访问性和易用性前提是所有日志数据都可以表示为字符序列。这个前提适用于大多数用例尤其是在没有专用工具的情况下日志可读性至关重要时。不过在对日志记录性能要求很高且日志大小受限的环境中基于文本的日志记录可能不是最佳选择。WindowManager 就是这样的一个场景它需要一个强大的日志记录系统能够处理实时窗口转换日志且对系统影响最小。ProtoLog 是满足 WindowManager 和类似服务的日志记录需求的替代方案。ProtoLog 相对于 logcat 的主要优势包括用于记录日志的资源量更少。从开发者角度来看这与使用默认的 Android 日志记录框架相同。支持在运行时启用或停用日志语句。仍可根据需要将日志记录到 logcat。为了优化内存用量ProtoLog 采用了字符串驻留机制其中涉及计算和保存消息的已编译哈希值。为了提高性能ProtoLog 会在编译期间针对系统服务执行字符串驻留仅在运行时记录消息标识符和参数。此外在生成 ProtoLog 跟踪记录或获取 bug 报告时ProtoLog 会自动合并在编译时创建的消息字典从而支持从任何 build 中解码消息。使用 ProtoLog 时消息会以二进制格式 (proto) 存储在 Perfetto 跟踪记录中。消息解码发生在 Perfetto 的 trace_processor 内。该过程包括解码二进制 proto 消息、使用嵌入的消息字典将消息标识符转换为字符串以及使用动态参数设置字符串格式。ProtoLog 支持与 android.utils.Log 相同的日志级别即d、v、i、w、e、wtf。客户端 ProtoLog最初ProtoLog 仅适用于 WindowManager 的服务器端在单个进程和组件中运行。随后它被扩展到包含系统界面进程中的 WindowManager shell 代码但使用 ProtoLog 需要复杂的样板设置代码。此外Proto 日志记录仅限于系统服务器和系统界面进程因此很难集成到其他进程中并且需要为每个进程单独设置内存缓冲区。不过ProtoLog 现在已可用于客户端代码且无需额外的样板代码。与系统服务代码不同客户端代码通常会跳过编译时字符串驻留。字符串驻留则会在后台线程中动态发生。因此虽然客户端上的 ProtoLog 在内存使用方面与系统服务伤的 ProtoLog 具有相当的优势但它的性能开销略高并且不具备其服务器端对应部分的固定内存带来的内存缩减优势。注意您可以将在客户端进程中运行的系统代码例如 IME 或 WindowManager 代码配置为使用编译时优化并获得相同的优势。ProtoLog 组ProtoLog 消息会分成名为 ProtoLogGroups 的组类似于 Logcat 消息按 TAG 进行整理的方式。这些 ProtoLogGroups 用作消息集群可在运行时集体启用或停用。此外它们还控制着是否应在编译期间去除消息以及应在何处记录消息proto、logcat 或两者兼有。每个 ProtoLogGroup 都包含以下属性enabled如果设置为 false则会在编译期间排除此组中的消息并且在运行时无法使用这些消息。logToProto定义此组是否使用二进制格式记录日志。logToLogcat定义此组是否会将日志记录到 logcat。tag已记录消息的来源名称。使用 ProtoLog 的每个进程都必须配置一个 ProtoLogGroup 实例。支持的参数类型在内部ProtoLog 使用 android.text.TextUtils#formatSimple(String, Object…) 格式化字符串因此其语法相同。ProtoLog 支持以下参数类型%b - 布尔值%d、%x - 整数类型short、integer 或 long%f - 浮点类型float 或 double%s - 字符串%% - 字面量百分号系统支持宽度和精度修饰符例如 %04d 和 %10b但不支持 argument_index 和 flags。在新服务中使用 ProtoLog如需在新进程中使用 ProtoLog请执行以下操作为此服务创建 ProtoLogGroup 定义。在首次使用之前初始化定义例如在进程创建时Protolog.init(ProtologGroup.values());使用 Protolog 的方式与使用 android.util.Log 的方式相同ProtoLog.v(WM_SHELL_STARTING_WINDOW, “create taskSnapshot surface for task: %d”, taskId);启用编译时优化如需在进程中启用编译时 ProtoLog您必须更改其构建规则并调用 protologtool 二进制文件。ProtoLogTool 是一种代码转换二进制文件用于执行字符串驻留并更新 ProtoLog 调用。此二进制文件会转换每个 ProtoLog 日志记录调用如以下示例所示ProtoLog.x(ProtoLogGroup.GROUP_NAME, “Format string %d %s”, value1, value2);转换为if(ProtoLogImpl.isEnabled(GROUP_NAME)){intprotoLogParam0value1;String protoLogParam1String.valueOf(value2);ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME,1234560b0100,protoLogParam0,protoLogParam1);}在此示例中ProtoLog、ProtoLogImpl 和 ProtoLogGroup 是作为参数提供的类可以导入、静态导入或完整路径不允许通配符导入x 是日志记录方法。转换是在源代码级别完成的。系统会根据格式字符串、日志级别和日志组名称生成哈希并将其插入 ProtoLogGroup 参数后面。实际生成的代码是内嵌的并添加一些新的换行符以保持文件中的行号不变。示例genrule{name:wm_shell_protolog_src,srcs:[:protolog-impl,// protolog lib:wm_shell_protolog-groups,// protolog groups declaration:wm_shell-sources,// source code],tools:[protologtool],cmd:$(location protologtool) transform-protolog-calls --protolog-class com.android.internal.protolog.ProtoLog --loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup --loggroups-jar $(location :wm_shell_protolog-groups) --viewer-config-file-path /system_ext/etc/wmshell.protolog.pb --legacy-viewer-config-file-path /system_ext/etc/wmshell.protolog.json.gz --legacy-output-file-path /data/misc/wmtrace/shell_log.winscope --output-srcjar $(out) $(locations :wm_shell-sources),out:[wm_shell_protolog.srcjar],}命令行选项ProtoLog 的一个主要优势是您可以在运行时启用或停用它。例如您可以在 build 中启用详细日志记录默认处于停用状态并在本地开发期间启用它以便调试特定问题。例如在 WindowManager 中使用此模式其中 WM_DEBUG_WINDOW_TRANSITIONS 和 WM_DEBUG_WINDOW_TRANSITIONS_MIN 组用于启用不同类型的转换日志记录前者默认处于启用状态。在启动跟踪记录时您可以使用 Perfetto 配置 ProtoLog。您还可以使用 adb 命令行在本地配置 ProtoLog。命令 adb shell cmd protolog_configuration 支持以下参数help Printthishelp text.groups(list|status)list-lists all ProtoLog groups registered with ProtoLog service statusgroup-print the status of a ProtoLog grouplogcat(enable|disable)group enableordisable ProtoLog to logcat有效使用提示ProtoLog 会对消息和传递的任何字符串参数使用字符串驻留。这意味着为了从 ProtoLog 中获得更多优势消息应将重复值隔离到变量中。例如请参考以下语句Protolog.v(MY_GROUP,%s,The argument value is argument);在编译时优化后它会转换为以下代码ProtologImpl.v(MY_GROUP,0x123,The argument value is argument);如果在代码中使用 ProtoLog 并带有参数 A,B,CProtolog.v(MY_GROUP,%s,The argument value is A);Protolog.v(MY_GROUP,%s,The argument value is B);Protolog.v(MY_GROUP,%s,The argument value is C);Protolog.v(MY_GROUP,%s,The argument value is A);这会导致内存中出现以下消息Dict:0x123:%s0x111:The argument value is A0x222:The argument value is B0x333:The argument value is CMessage1(Hash:0x123,Arg1:0x111)Message2(Hash:0x123,Arg2:0x222)Message3(Hash:0x123,Arg3:0x333)Message4(Hash:0x123,Arg1:0x111)如果改为将 ProtoLog 语句写成如下形式Protolog.v(MY_GROUP,The argument value is %s,argument);内存缓冲区最终将变为Dict:0x123:The argument value is %s(24b)0x111:A(1b)0x222:B(1b)0x333:C(1b)Message1(Hash:0x123,Arg1:0x111)Message2(Hash:0x123,Arg2:0x222)Message3(Hash:0x123,Arg3:0x333)Message4(Hash:0x123,Arg1:0x111)此序列可使内存占用量减少 35%。Winscope 查看器抓取方式查看方式Winscope 的 ProtoLog 查看器标签页会以表格式显示 ProtoLog 跟踪记录。您可以按日志级别、标记、源文件存在 ProtoLog 语句和消息内容过滤跟踪记录。所有列均可过滤。 点击第一列中的时间戳会将时间轴移动到消息时间戳。此外点击 Go to Current Time 可将 ProtoLog 表格滚动回时间轴中所选的时间戳ProtoLog 查看器图 1. ProtoLog 查看器注意过滤条件仅支持子字符串搜索不支持正则表达式。