最近在Android 8.0上做启动时间优化,对部分service优化后Android无法正常启动了,此时shell和adb都不可用,service的调试输出信息是通过logcat输出的,串口无法看到。凭经验猜测原因,调试起来比较困难。于是想到在Android启动中将logcat消息发送到kmsg,通过串口输出。
将logcat消息发送到串口还是有意义的,主要有以下两种需求:
- shell和adb不可用时,需要检查logcat消息 (也就是我前面提到的需求)
将kmesg和logcat消息合并到一起,用于启动时间优化时进行时间点检查
这样做有两点局限
- logcat其自身消息机制的原因,得到的消息并不是实时的(有待进一步检验)
- 将很多logcat消息发往串口低速设备,大量I/O会影响启动时间
网上很多地方也提到了通过logcat的"-f"
选项将输出重定向到"/dev/kmsg"
service logcat /system/bin/logcat -f /dev/kmsg
oneshot
这里没有为service指定class,其默认为”default”,也可以将其指定为其它名称,这样就能以"class_start xxx"
的方式启动。
在Android 8.0上默认情况下,由于selinux的原因,只添加上面的service后logcat是无法使用的,启动中会出现下面的警告:
...
type=1400 audit(1420070407.770:3): avc: denied { read } for pid=3408 comm="logcat" name="/" dev="tmpfs" ino=7303 scontext=u:r:logpersist:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=1
type=1400 audit(0.0:3): avc: denied { read } for name="/" dev="tmpfs" ino=7303 scontext=u:r:logpersist:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=1
...
type=1400 audit(1420070407.770:4): avc: denied { open } for pid=3408 comm="logcat" path="/dev" dev="tmpfs" ino=7303 scontext=u:r:logpersist:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=1
...
type=1400 audit(1420070407.790:5): avc: denied { append } for pid=3408 comm="logcat" name="kmsg" dev="tmpfs" ino=7306 scontext=u:r:logpersist:s0 tcontext=u:object_r:kmsg_device:s0 tclass=chr_file permissive=1
...
type=1400 audit(1420070407.790:6): avc: denied { open } for pid=3408 comm="logcat" path="/dev/kmsg" dev="tmpfs" ino=7306 scontext=u:r:logpersist:s0 tcontext=u:object_r:kmsg_device:s0 tclass=chr_file permissive=1
type=1400 audit(1420070407.790:7): avc: denied { getattr } for pid=3408 comm="logcat" path="/dev/kmsg" dev="tmpfs" ino=7306 scontext=u:r:logpersist:s0 tcontext=u:object_r:kmsg_device:s0 tclass=chr_file permissive=1
...
有两种方式设置selinux:
启动Android的命令行中指定
"androidboot.selinux=permissive"
抑制selinux操作这里的
permissive
选项表示selinux会执行权限检查,对于不符合规则的审查会显示警告信息,但会授予权限。适合开发时使用。添加selinux规则,授予logcat操作的权限
根据规则,在Android的sepolicy指定目录下新建
logpersist.te
文件,包含以下规则:allow logpersist device:dir { open read }; allow logpersist kmsg_device:chr_file { open append getattr };
当然,logcat输出的消息可以通过"-v"
选项和过滤器进行设置,例如:
"-v time"
生成带时间戳的信息service logcat /system/bin/logcat -v time -f /dev/kmsg oneshot
输出的格式如下:
01-01 00:00:06.692 D/Zygote ( 2429): begin preload 01-01 00:00:06.699 I/Zygote ( 2429): Preloading classes... 01-01 00:00:08.136 I/Zygote ( 2429): ...preloaded 3005 classes in 1437ms. 01-01 00:00:08.342 I/Zygote ( 2429): Preloading resources... 01-01 00:00:09.591 I/Zygote ( 2429): ...preloaded 343 resources in 1249ms. 01-01 00:00:09.609 I/Zygote ( 2429): ...preloaded 41 resources in 18ms. 01-01 00:00:09.610 I/Zygote ( 2429): Preloading shared libraries... 01-01 00:00:09.630 D/Zygote ( 2429): end preload
使用过滤器筛选信息
这里只显示
Zygote
的D
级别以上信息:service logcat /system/bin/logcat -v time -f /dev/kmsg Zygote:D *:S oneshot
输出的信息如下:
--------- beginning of main --------- beginning of system 01-01 00:00:06.692 D/Zygote ( 2429): begin preload 01-01 00:00:06.699 I/Zygote ( 2429): Preloading classes... 01-01 00:00:08.136 I/Zygote ( 2429): ...preloaded 3005 classes in 1437ms. 01-01 00:00:08.342 I/Zygote ( 2429): Preloading resources... 01-01 00:00:09.591 I/Zygote ( 2429): ...preloaded 343 resources in 1249ms. 01-01 00:00:09.609 I/Zygote ( 2429): ...preloaded 41 resources in 18ms. 01-01 00:00:09.610 I/Zygote ( 2429): Preloading shared libraries... 01-01 00:00:09.630 D/Zygote ( 2429): end preload 01-01 00:00:09.738 I/Zygote ( 2429): System server process 2783 has been created 01-01 00:00:09.740 I/Zygote ( 2429): Accepting command socket connections 01-01 00:00:14.051 I/Zygote ( 2783): Process: zygote socket opened, supported ABIS: armeabi-v7a,armeabi