Android系统启动篇
4,《Android SystemServer进程启动流程》
Android系统开发准备篇
3,《Android Framework代码IDE加载和调试》
Android系统开发实践篇
4,《android单独编译framework模块并push》
Android系统开发核心知识储备篇
1,《Android编译系统-envsetup和lunch代码篇》
6,《Android中Activity、View和Window关系详解》
11,《android中AMS进程通知Zygote进程fork新进程的通信方式》
Android核心功能详解篇
2,《Android 手势导航(从下往上滑动进入多任务页面)》
3,《android手势分析(应用界面左往右边滑动退出应用)》
———————————————————————————————————————————
目录
一,背景介绍
在framework开发过程中,日志是必不可少的一个调试手段。Android通过LOGD、SLOGD和EventLog等方式进行日志的打点。但是日志是如何写入和读取的?开启android日志系统之旅。
二,基础概念
2.1 日志等级
Android日志分为如下7个等级:
V:详细(最低优先级)--(Verbose:2)
D:调试 --(Debug:3)
I :信息 -- (Info:4)
W:警告 --(Warning:5)
E:错误 --(Error:6)
F:严重错误 --(Fatal:7)
S:静默(最高优先级,绝不会输出任何内容)--(Silent)
下面这是一条正常输出的Android11日志:
日志内容:2023-05-08 10:48:30.111 21802-21838 AutofillManagerService system_process D onBackKeyPressed()
日志格式:<日期> <时间> <PID>-<TID> <日志标签TAG> <进程名称> <日志等级> <日志内容>
2.2 日志缓冲区
Android 日志系统为日志消息保留了多个缓冲区,可以通过 -b 参数指定特定的缓冲区日志,常用缓存区如下:
radio:查看包含无线装置/电话相关消息的缓冲区。
events:查看已经过解译的二进制系统事件缓冲区消息。
main:查看主日志缓冲区(默认),不包含系统和崩溃日志消息。
system:查看系统日志缓冲区(默认)。
crash:查看崩溃日志缓冲区(默认)。
all:查看所有缓冲区。
default:报告 main、system 和 crash 缓冲区。
$ adb logcat -b events
05-08 10:48:30.199 29284 29284 I wm_on_start_called: [193592054,com.android.launcher3.uioverrides.QuickstepLauncher,handleStartActivity]
05-08 10:48:30.201 29284 29284 I wm_on_resume_called: [193592054,com.android.launcher3.uioverrides.QuickstepLauncher,RESUME_ACTIVITY]
05-08 10:48:30.201 29284 29284 I wm_on_top_resumed_gained_called: [193592054,com.android.launcher3.uioverrides.QuickstepLauncher,topWhenResuming]
05-08 10:48:30.886 21802 21840 I wm_destroy_activity: [0,185499341,49,com.android.settings/.Settings,finish-imm:idle]
05-08 10:48:30.918 6828 6828 I wm_on_stop_called: [185499341,com.android.settings.Settings,LIFECYCLER_STOP_ACTIVITY]
05-08 10:48:30.923 6828 6828 I wm_on_destroy_called: [185499341,com.android.settings.Settings,performDestroy]
05-08 10:48:30.937 21802 10690 I wm_task_removed: [49,removeChild: last r=ActivityRecord{b0e7ecd u0 com.android.settings/.Settings t-1 f}} in t=Task{ccf6a93 #49 visible=true type=standard mode=fullscreen translucent=false A=1000:com.android.settings.root U=0 StackId=49 sz=0}]
05-08 10:48:30.937 21802 10690 I wm_task_removed: [49,removeTask]
05-08 10:48:30.938 21802 10690 I wm_task_removed: [49,removeTask]
05-08 10:48:30.938 21802 10690 I wm_stack_removed: 49
05-08 10:49:23.205 21802 21948 I battery_level: [90,4346,256]
05-08 10:49:30.187 21802 21850 I am_uid_idle: 10136
05-08 10:49:30.189 21802 21850 I am_uid_idle: 10107
05-08 10:51:45.883 22174 22174 I service_manager_stats: [100,223,3074203]
05-08 10:51:49.401 21802 23866 I am_kill : [0,9667,android.process.acore,995,empty for 114809s]
05-08 10:51:49.444 21802 25008 I am_proc_died: [0,9667,android.process.acore,995,19]
05-08 10:51:49.452 21802 25008 I am_uid_stopped: 10072
05-08 10:51:49.455 21802 21948 I battery_level: [91,4345,259]
2.3 调整Android log buffer 大小
日常调试时,会遇到日志丢失或者不全的情况,主要原因是日志量很大,但是日志缓冲区很小,此时只要把日志的缓冲区调大即可。
原因就是Log Buffer太小导致部分Log被冲掉。有时候是客户打印了太多的Log导致。目前默认大小是256K.
查询Android Log buffer的方法:
备注: 目前默认大小是256K,一般建议设置不超过16M。
方案1.
命令: logcat -G Size
Example: logcat -G 4M
方案2.
"开发者选项"打开 -> "Developer options" -> "Logger buffer sizes" -> 按需调整
三,实现原理
3.1 日志架构
图片来自于CSDN-私房菜:
3.2 调用流程
3.2.1 写流程
在应用层可以通过android.util.Log,android.util.SLog,android.util.EventLog接口,把日志写入到main,system,event的不同缓冲区中去。
应用层写日志方法如下:
在Native C/C++中,进程通过加载liblog.so,调用ALOGD()、ALOGI()来进行日志的写入,最终也是通过logd写入到logdw的socket中。
Native 层写日志方法如下:
3.2.2 读流程
Android中主要通过logcat进程来读取日志,logcat属于native-C的进程,通过加载liblog,从而调用logd的read接口读取 logdr socket的日志内容。
四,源码分析
Android系统日志主要有三个部分需要关注:
logd守护进程:日志系统的大管家,管理三个日志的socket:logd、logdr、logdw。
logcat进程:日志读取工具。
liblog:提供日志读写、过滤等接口,供logcat、JAVA、Native等程序使用
4.1 logd守护进程启动
见文件/system/core/logd/logd.rc
service logd /system/bin/logd
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram+passcred 0222 logd logd
file /proc/kmsg r
file /dev/kmsg w
user logd
group logd system package_info readproc
capabilities SYSLOG AUDIT_CONTROL
priority 10
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
oneshot
disabled
user logd
group logd
writepid /dev/cpuset/system-background/tasks
# Limit SELinux denial generation to 5/second
service logd-auditctl /system/bin/auditctl -r 5
oneshot
disabled
user logd
group logd
capabilities AUDIT_CONTROL
on fs
write /dev/event-log-tags "# content owned by logd
"
chown logd logd /dev/event-log-tags
chmod 0644 /dev/event-log-tags
on property:sys.boot_completed=1
start logd-auditctl
从上面的service可以看出,启动了一个守护进程为logd,存放在手机的/system/bin中,同时创建并启动三个socket:
logd 接收logcat 传递的指令然后处理 ,比如logcat -g, logcat -wrap等
logdr logcat从此buffer中读取buffer
logdw 日志写入的buffer
logd初始化调用栈如下:
logd的初始化流程:
打开/dev/kmsg 来读取内核日志,通过LogKlog来进行存储
如果属性"ro.logd.kernel" 配置了,打开/proc/kmsg来读取内核日志
设置运行时优先级、权限
启动 Reinit线程,当logd-reinit传入参数reinit时,进行调用,reinit开机只启动一次
启动各个 log 监听器:LogBuffer、LogReader、LogListener、CommandListener、LogAudit和LogKlog
4.2 启动 logd-reinit
启动logd-reinit的服务,主要工作是重新初始化logd的LogBuffer,在上面的启动脚本中,配置为oneshot,即开机只执行一次。
通过上面logd的初始化,可以看到,logd启动后,创建了一个线程reinit_thread_start(),当logd-reinit 传入参数 reinit后,进行功能执行。
logd-reinit两个步骤:
如果reinit启动后,并且/deg/kmsg打开成功,把 logd.daemon: renit写入kmsg
重新初始化各个log buffer的大小,以及其他参数的初始化,但不会重新生成LogBuffer对象
4.3 启动 logd-auditctl
logd-auditctl 的主体是 /system/bin/auditctl,在logd的android.bp中,通过编译 auditctl.cpp得来,并加载了liblogd的 库。
logd-auditctl是Android 10.0中引入的新功能,目的是让selinux denia的日志打印限制为5秒一次。
logd-auditctl 初始化调用栈如下:
logd-auditctl的主要作用是 让selinux denia的日志打印限制为5秒一次
说明:在logd.rc中配置了logd-auditctl,传入参数为-r5,即限制selinux日志写入频率更新为5秒
4.4 logcat启动
logcat编译时,会编译两个进程/system/bin/logcat 和/system/bin/logcatd。
和logd一样,logcat进程启动,是init进程解析了logcatd.rc来进行加载。
————————————————
参考:
1,博主「IngresGe」,原文链接:https://blog.csdn.net/yiranfeng/article/details/104244900