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)