Android ProtoLog dynamically opens related wm logging source code analysis supplement
For the previous section, it has been clear how the relevant code can be printed to logcat. In fact, protologtool is a tool that replaces all ProtoLogs in the code with specific implementations, and finally outputs conditional judgments to the Slog.
This article focuses on the relevant dynamic control process
More content qqun: 422901085 Related courses
Detailed analysis of wm logging command settings
Here you can see the relevant help of the wm logging command
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
In fact, here is mainly a difference between enable and enable-text.
enable corresponds to proto logging. This is like wms class. When winscope captures, it will write the log to the data/misc/wmtrace/ path. wm_log.winscope enable
-text corresponds to logcat. As you can see directly, this is what we often use
Now that we know the corresponding command, let’s take a look at the corresponding code output.
Before analyzing the code, we have to subdivide the command
wm logging enable-text xxxx
, indicating that this place must be called to our WindowManagerService’s 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);
}
Here it is executed directly into the onCommand of WindowManagerShellCommand
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;
//省略
}
Next call 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);
}
}
For enable and enable-text, a method setLogging is actually called:
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;
}
Here the shell execution part has been completed, and finally the relevant variables of the corresponding ProtoLogGroup are assigned values.
Log output part of the source code
I already knew it in the previous section
ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2);
will become the following code
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);
}
Look here 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());
}
It can be seen that the value set by the previous command is true, and here it can be true
and then see the corresponding print
public static void w(IProtoLogGroup group, int messageHash, int paramsMask,
@Nullable String messageString,
Object... args) {
getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args);
}
The log method is called, which is
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);
}
}
Let's take logcat as an example to analyze
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;
}
}
That means the whole process is clear.
At the same time, it should be noted that proto logging and logcat are not mutually exclusive, but do not affect each other, and can exist at the same time.
Analysis and viewing method of proto binary file
There are actually two types of methods to grab the binary of this proto:
Method 1:
Command:
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.
It is very simple to start and stop, and the final log is in the /data/misc/wmtrace/wm_log.winscope file
Method 2:
That is the winscope method we use
https://blog.csdn.net/learnframework/article/details/129432374
Viewing method after capture:
Because wm_log.winscope is a binary file, the direct text opening is actually garbled, so how to view it?
In fact, there are also 2 methods here:
1. Just directly winscope.html
This method is the simplest, but because it can only be used on html web pages, it is relatively unfriendly to those who want to analyze logs, and there is no other way?
Next, another
method 2 is introduced:
using the protologtool command mentioned in the previous section
Command: read-log --viewer-conf <viewer.json> <wm_log.pb>
viewer.json is /system/etc/protolog.conf.json.gz
wm_log.pb wm_log.winscope
uses a similar command: protologtool read-log --viewer-conf protolog.conf.json wm_log.winscope
then the rest is the protologtool command Where did it come from?
Unfortunately, this bin file is not found on android 13, but it has a corresponding executable 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
To execute this jar method: 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
The log is as follows:
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)