愿历尽千帆 归来仍少年

一个无法开机问题的分析流程

字数统计: 1,836阅读时长: 8 min
2018/05/17

问题平台
Android7.1.1 MT6739

问题现象
走完一部分开机动画后停留在recovery界面,无法进入系统

初步分析:
开机后进入recovery ,以往经验来看猜测是重要进程启动异常,可能被连续被kill

Step1 日志分析
需要的日志:eng版本uart log+main log(如果已经进入Android的话)+data/aee_exp下的db文件(如果有db生成的话)

Step1.1 抓取串口日志
首先说下串口日志的获取方式:
串口线中的白色接TX,黑色线接地,我这里是在windows下抓取,使用了一个SSCOM的工具,MTK平台上波特率需要选择921600, 这个工具使用之前需要安装usb to serial驱动,不然会出现识别不了设备的情况
这里需要说明的是需要使用eng版本获取日志,如果是user版本的话,需要做一些额外工作

Step1.2 分析串口日志
首先先看下系统走到了哪一步,关键步骤都是会有对应的日志输出,从过往的项目以及分析经验,已形成了一套既定的分析流程
这里说下分析串口日志中的一些关键字如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1"Preparing for EL3 exit to normal world, LK"
如果串口log里面没有走到这里,说明在PL就卡住了,这句表示进入LK
2"lk finished --> jump to linux kernel"
如果串口log里面没有走到这里,但是上面走到了,说明在lk卡住了,这句表示进入kernel
3"Kernel_init_done"
这句表示kernel初始化完成了,正准备启动init进程,如果这就没有走到:请搜索关键字initcall,
initcall是顺序执行例如module_init注册的init函数,看看卡在哪个call back上
4"BOOTPROF"
这是开机过程中,每走过一个重要阶段都会打印的标志,例如上面Kernel_init_done这一句就会打上这个标志
5"init: init first stage started"
这句表示init进程正常起来执行
6"fs_mgr""e2fsck"
这里标志是在检查和mount 文件系统,如果失败会有如下log:fs_mgr_mount_all returned an error fs_mgr_mount_all returned unexpected error 255
7"zygote"
Starting service 'zygote':这里表示要启动zygote,如果没有这一句表示zygote还没启动,则要检查之前的initrc执行情况,文件系统mount情况
8"SysServerInit_START"
启动system_server
9"system_server"
这里表示system_server有起来
10"BOOT_Animation:END"
表示开机动画正常结束,进入安卓

我们的这份日志中1-9均有打印,说明已经进入了Android部分,在init进程起来后,如下片段

1
[  29.560029] <1>.(1)[1:init]init: Service 'healthd' (pid 310) killed by signal 11

后面紧跟着很多关键进程均打出sig 9即被kill掉
关于linux signal信号表对应关系如下(只列出部分)

1
2
3
4
5
6
7
8
SIGHUP      1           Term    终端的挂断或进程死亡
SIGINT 2 Term 来自键盘的中断信号
SIGQUIT 3 Core 来自键盘的离开信号
SIGILL 4 Core 非法指令
SIGABRT 6 Core 来自abort的异常信号
SIGFPE 8 Core 浮点例外
SIGKILL 9 Term 杀死
SIGSEGV 11 Core 段非法错误(内存引用无效)

这里大概明白问题出在了healthd上,这是与电量相关的进程,在user space中

对于是否需要抓取logcat日志,可以看看串口日志中是否有如下片段

1
Starting service 'zygote'

这一步代表zygote起来了,这时候就必须要抓取logcat进一步分析,我们这笔问题中该日志片段有打印出来,所以需要抓取logcat进一步分析

Step1.3 分析logcat日志
找到如下片段,说明data/aee_exp下面也有db文件生成了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
05-02 04:52:48.578 I/AEE_AED (  350): Revision: '0'
05-02 04:52:48.578 I/AEE_AED ( 350): ABI: 'arm'
05-02 04:52:48.579 I/AEE_AED ( 350): pid: 310, tid: 310, name: healthd >>> /sbin/healthd <<<
05-02 04:52:48.580 I/AEE_AED ( 350): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x2
05-02 04:52:48.616 I/AEE_AED ( 350): r0 00000002 r1 00000002 r2 0000089c r3 00000006
05-02 04:52:48.616 I/AEE_AED ( 350): r4 ffffffff r5 00000001 r6 00000000 r7 00000002
05-02 04:52:48.616 I/AEE_AED ( 350): r8 becc68bc r9 0008819e sl ffffffff fp becc6680
05-02 04:52:48.616 I/AEE_AED ( 350): ip 00000002 sp becc6678 lr 0005d494 pc 0005e16c cpsr 20070030
05-02 04:52:48.723 I/AEE_AED ( 350):
05-02 04:52:48.723 I/AEE_AED ( 350): backtrace:
05-02 04:52:48.723 I/AEE_AED ( 350): #00 pc 0005e16c /sbin/healthd
05-02 04:52:48.723 I/AEE_AED ( 350): #01 pc 0005d490 /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #02 pc 00056c04 /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #03 pc 00064ef4 /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #04 pc 00027e00 /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #05 pc 0000b291 /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #06 pc 0000b10d /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #07 pc 000088c9 /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #08 pc 00008d73 /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #09 pc 00008c3f /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #10 pc 00008afb /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #11 pc 0005e055 /sbin/healthd
05-02 04:52:48.724 I/AEE_AED ( 350): #12 pc 000086c8 /sbin/healthd

这里出现了段错误,这个时候需要拿出看家小工具addr2line来定位到代码出错的位置,这里顺便说下Addr2line的作用

1
Addr2line (它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具。这种功能对于将跟踪地址转换成更有意义的内容来说简直是太棒了

我这里的addr2line使用的是代码工程中的,代码工程下有很多addr2line,这里就随便用了一个目录下的addr2line,另外分析还需要out/target/product/XXX/symbols/sbin/healthd文件,注意这里的healthd文件必须和日志中是同一套代码,否则可能分析出来的代码位置会有偏差

使用命令如下:

1
lihaizhou@ubuntu:~/Code/sw710/6739$ '/home/lihaizhou/Code/sw710/6739/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8/bin/x86_64-linux-addr2line' -e '/home/lihaizhou/Code/sw710/6739/out/target/product/lcsh6739_35_sw730/symbols/sbin/healthd' 0000b291

得到如下结果

1
/proc/self/cwd/system/core/healthd/BatteryMonitor.cpp:861

进到BatteryMonitor.cpp第861行,是一句最近添加的日志

1
KLOG_INFO(LOG_TAG, "BatteryMonitor cmd_send props.batteryStatus '%s'\n", props.batteryStatus);

Step2 问题解决
仔细查看了这行日志, 发现是类型写错了,props.batteryStatus是整型,这里使用了字串类型打印,当然会报错了
ok,将类型修改为整型,重新编译烧录img到机器中,开机正常了

小结
对系统启动流程以及addr2line的使用比较熟悉的话,这笔问题分析还是比较容易的,ok,这笔问题分析到此为止

Have a good day!


另:
关于串口日志MTK online有相关FAQ可以参考
FAQ15320 不同模式下如何保持uart log一直打开
http://online.mediatek.com/Pages/FAQ.aspx?List=SW&FAQID=FAQ15320
FAQ03891如何在User版本开启串口(Uart),抓取上层Log,开启输入控制台
http://online.mediatek.com/Pages/FAQ.aspx?List=SW&FAQID=FAQ03891

公司午休时间,电脑前,黑眼圈,脑袋一片空白,肚子有点饿,等外卖
CATALOG