• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Faultlogger开发指导
2
3
4## 概述
5
6
7### 功能简介
8
9Faultlogger是OpenHarmony为开发者提供的一个维测日志框架,能够为应用、元能力、系统服务进程崩溃故障提供统一检测、日志采集、日志存储、日志上报功能,为应用崩溃故障提供详细的维测日志用以辅助故障定位。本章节内容适用于标准系统以及Linux内核的小型系统。
10
11FaultLogger承载OpenHarmony系统上的故障记录功能,按照服务对象不同分别运行在两个部件中:
12
13- Hiview部件中的服务:服务于应用层和native层的功能模块,功能是分类管理系统中发生的各类故障信息,并为模块提供查询故障的API。
14
15- Faultloggerd部件中的服务:服务于崩溃进程,功能是收集C/C++运行时异常的守护进程和获取进程调用栈。
16
17基于Faultlogger服务,进程崩溃的处理流程如下图所示:
18
19  **图1** 进程崩溃处理流程图
20
21![zh-cn_image_0000001261812333](figures/zh-cn_image_0000001261812334.png)
22
231. 进程安装信号处理器后,通过DFX_SignalHandler函数检测并响应由Kernel抛出的进程崩溃异常信号;
24
252. SignalHandler检测到异常信号后Fork出子进程,并运行ProcessDump程序开始dump崩溃进程和线程的堆栈信息;
26
273. ProcessDump程序向Faultloggerd服务申请用于存储故障日志的文件句柄,在读取到异常堆栈信息后写入到该文件中,进而生成完整的崩溃日志;
28
294. ProcessDump完成崩溃日志收集后,根据需要将故障通过Hiview提供的AddFaultLog()接口进行上报,hiview将处理生成简化的崩溃日志,并上报Hisysevent事件。
30
31基于这样的设计,在资源有限的小型系统上可只部署Faultloggerd,也依然可以获取用于定位崩溃问题的日志。
32
33
34### 使用场景
35
36Faultloggerd意在为开发者在开发测试过程中遇到的崩溃或卡死问题提供一种简单轻量的定位手段。
37
38主要包含以下应用场景:
39
40  **表1** Faultloggerd模块应用场景
41
42| 场景描述 | 使用工具 | 使用方式 |
43| -------- | -------- | -------- |
44| 了解函数的调用顺序 | DumpCatcher API | 参见:[使用DumpCatcher接口获取调用栈](#使用dumpcatcher接口获取调用栈) |
45| 应用卡死/CPU占用高 | DumpCatcher Command Tool | 参见:[使用DumpCatcher命令获取调用栈](#使用dumpcatcher命令获取调用栈) |
46| 崩溃问题定位 | 崩溃日志和addr2line工具 | 参见:[基于崩溃日志定位问题](#基于崩溃日志定位问题) |
47
48
49## 使用DumpCatcher接口获取调用栈
50
51
52### 接口说明
53
54DumpCatcher可以抓取OpenHarmony指定进程(线程)的调用栈。
55
56  **表2** DumpCatcher接口说明
57
58| 类 | 方法 | 描述 |
59| -------- | -------- | -------- |
60| DfxDumpCatcher | bool DumpCatch(const int pid, const int tid, std::string&amp; msg) |   接口返回值:<br/>- true:回栈成功,回栈信息存储在msg字符串对象中;<br/>- false:回栈失败。<br/>  输入参数:<br/>- pid:目标进程号;<br/>- tid:目标线程号,如果需要回栈进程中的所有线程,则tid设定为0;<br/>  输出参数:<br/>- msg:如果回栈成功,则通过msg返回调用栈信息。 |
61| DfxDumpCatcher | bool DumpCatchMix(const int pid, const int tid, std::string&amp; msg) |   接口返回值:<br/>- true:回栈成功,回栈信息存储在msg字符串对象中;<br/>- false:回栈失败。<br/>  输入参数:<br/>- pid:目标进程号;<br/>- tid:目标线程号,如果需要回栈进程中的所有线程,则tid设定为0;<br/>  输出参数:<br/>- msg:如果回栈成功,则通过msg返回混合栈信息。 |
62| DfxDumpCatcher | bool DumpCatchFd(const int pid, const int tid, std::string&amp; msg, int fd) |   接口返回值:<br/>- true:回栈成功,回栈信息存储在msg字符串对象中;<br/>- false:回栈失败。<br/>  输入参数:<br/>- pid:目标进程号;<br/>- tid:目标线程号,如果需要回栈进程中的所有线程,则tid设定为0;<br/>- fd:指定写入的文件句柄号;<br/>  输出参数:<br/>- msg:如果回栈成功,则通过msg返回调用栈信息。 |
63| DfxDumpCatcher | bool DumpCatchMultiPid(const std::vector\<int> pidV, std::string&amp; msg) |   接口返回值:<br/>- true:回栈成功,回栈信息存储在msg字符串对象中;<br/>- false:回栈失败。<br/>  输入参数:<br/>- pidV:目标进程号列表;<br/>  输出参数:<br/>- msg:如果回栈成功,则通过msg返回调用栈信息。 |
64
65> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
66> 当调用此接口的进程id与目标pid不一致时需要调用者是管理员(system,root)用户。
67
68
69### 开发实例
70
71
72系统应用开发者可以用DumpCatcher在自己的应用中获取指定进程(线程)的调用栈。下文以dumpcatcherdemo模块使用DumpCatcher基础接口获取调用栈作为实例进行讲解。
73
74
751. 编译构建文件添加dumpcatcher依赖:以/base/hiviewdfx/faultloggerd/example/BUILD.gn为例,在include_dirs中添加DfxDumpCatcher头文件路径,并在deps中添加“//base/hiviewdfx/faultloggerd/interfaces/innerkits/dump_catcher:lib_dfx_dump_catcher”模块依赖。
76
77   ```
78   import("//base/hiviewdfx/faultloggerd/faultloggerd.gni")
79   import("//build/ohos.gni")
80
81   config("dumpcatcherdemo_config") {
82     visibility = [ ":*" ]
83
84     include_dirs = [
85       ".",
86       "//utils/native/base/include",
87       "//base/hiviewdfx/faultloggerd/interfaces/innerkits/dump_catcher/include/",  # 添加dumpcatcher头文件路径
88     ]
89   }
90
91   ohos_executable("dumpcatcherdemo") {
92    sources = [ "dump_catcher_demo.cpp" ]
93    configs = [ ":dumpcatcherdemo_config" ]
94    deps = [
95      "//base/hiviewdfx/faultloggerd/interfaces/innerkits/dump_catcher:lib_dfx_dump_catcher", # 添加dumpcathcer模块依赖
96      "//utils/native/base:utils",
97    ]
98    external_deps = [ "hilog_native:libhilog" ]
99    install_enable = true
100    part_name = "faultloggerd"
101    subsystem_name = "hiviewdfx"
102   }
103   ```
104
1052. 头文件定义用到的函数:以/base/hiviewdfx/faultloggerd/example/dump_catcher_demo.h为例,本样例代码中,通过调用栈深度测试的测试函数来构造一个指定深度的调用栈。
106
107   ```
108   #ifndef DUMP_CATCHER_DEMO_H
109   #define DUMP_CATCHER_DEMO_H
110
111   #include <inttypes.h>
112
113   #define NOINLINE __attribute__((noinline))
114
115   // 定义该宏函数用于自动生成函数调用链
116   #define GEN_TEST_FUNCTION(FuncNumA, FuncNumB)          \
117       __attribute__((noinline)) int TestFunc##FuncNumA() \
118       {                                                  \
119           return TestFunc##FuncNumB();                   \
120       }
121
122   // 调用栈深度测试的测试函数
123   int TestFunc0(void);
124   int TestFunc1(void);
125   int TestFunc2(void);
126   int TestFunc3(void);
127   int TestFunc4(void);
128   int TestFunc5(void);
129   int TestFunc6(void);
130   int TestFunc7(void);
131   int TestFunc8(void);
132   int TestFunc9(void);
133   int TestFunc10(void);
134
135   #endif // DUMP_CATCHER_DEMO_H
136   ```
137
1383. 在源文件中调用DumpCatch接口:以/base/hiviewdfx/faultloggerd/example/dump_catcher_demo.cpp为例,引用dfx_dump_catcher.h头文件,声明DfxDumpCatcher对象,通过宏函数构造函数调用链,并最后调用DumpCatch接口方法,传入需要抓取调用栈的进程号、线程号。
139
140   ```
141   #include "dump_catcher_demo.h"
142
143   #include <iostream>
144   #include <string>
145   #include <unistd.h>
146   // dfx_dump_catcher.h头文件引入
147   #include "dfx_dump_catcher.h"
148   using namespace std;
149
150   NOINLINE int TestFunc10(void)
151   {
152       OHOS::HiviewDFX::DfxDumpCatcher dumplog;
153       string msg = "";
154       bool ret = dumplog.DumpCatch(getpid(), gettid(), msg);  // 调用DumpCatch接口获取调用栈
155       if (ret) {
156           cout << msg << endl;
157       }
158       return 0;
159   }
160
161   // 通过宏函数自动生成函数调用链
162   GEN_TEST_FUNCTION(0, 1)
163   GEN_TEST_FUNCTION(1, 2)
164   GEN_TEST_FUNCTION(2, 3)
165   GEN_TEST_FUNCTION(3, 4)
166   GEN_TEST_FUNCTION(4, 5)
167   GEN_TEST_FUNCTION(5, 6)
168   GEN_TEST_FUNCTION(6, 7)
169   GEN_TEST_FUNCTION(7, 8)
170   GEN_TEST_FUNCTION(8, 9)
171   GEN_TEST_FUNCTION(9, 10)
172
173   int main(int argc, char *argv[])
174   {
175       TestFunc0();
176       return 0;
177   }
178   ```
179
180
181## 使用DumpCatcher命令获取调用栈
182
183
184### 工具说明
185
186DumpCatcher Command Tool是一个抓取调用栈的命令行工具,在OpenHarmony系统中可直接使用,该工具通过-p、-t参数指定进程和线程,命令执行后在命令行窗口打印指定进程的线程栈信息。还可通过添加-m参数来抓取应用进程的JS Native混合栈。
187
188  **表3** DumpCatcher Command Tool使用说明
189
190| 工具名称 | 命令行工具路径 | 执行命令 | 描述 |
191| -------- | -------- | -------- | -------- |
192| dumpcatcher | /system/bin | - dumpcatcher -p [pid]<br/>- dumpcatcher -p [pid] -t [tid]<br/>- dumpcatcher -m -p [pid]<br/>- dumpcatcher -m -p [pid] -t [tid]<br/> | **参数说明:**<br/>- -p [pid]:打印指定进程下面的所有线程栈信息。<br/>- -p [pid] -t [tid]:打印指定进程下面的指定线程信息。<br/>- -m -p [pid]:打印指定进程下面的所有线程混合栈信息。<br/>- -m -p [pid] -t [tid]:打印指定进程下面的指定线程混合栈信息。<br/>**返回值说明:**<br/>如果栈信息解析成功,则将信息显示到标准输出;失败则打印错误信息。 |
193
194
195### 使用实例
196
197通过dumpcatcher命令打印hiview进程的调用栈。
198
199
200```
201# ps -ef |grep hiview
202hiview         240     1 0 17:01:49 ?     00:00:14 hiview
203root          1822  1560 7 20:56:36 pts/0 00:00:00 grep hiview
204# dumpcatcher -p 240 -t 240
205Result: 0 ( no error )
206Timestamp:2017-08-05 20:56:43.000
207Pid:240
208Uid:1201
209Process name:/system/bin/hiview
210Tid:240, Name:hiview
211#00 pc 00098f8c /system/lib/ld-musl-arm.so.1(ioctl+68)
212#01 pc 0000e2a1 /system/lib/chipset-pub-sdk/libipc_single.z.so
213#02 pc 0000ed59 /system/lib/chipset-pub-sdk/libipc_single.z.so
214#03 pc 0000ee1f /system/lib/chipset-pub-sdk/libipc_single.z.so
215#04 pc 0000f745 /system/lib/chipset-pub-sdk/libipc_single.z.so
216#05 pc 00037577 /system/bin/hiview
217#06 pc 00025973 /system/bin/hiview
218#07 pc 000db210 /system/lib/ld-musl-arm.so.1
219#08 pc 000258d8 /system/bin/hiview
220#09 pc 0002587c /system/bin/hiview
221```
222
223
224## 基于崩溃日志定位问题
225
226开发者可以通过faultloggerd生成的崩溃堆栈日志进行问题定位。本章节将主要介绍如何利用addr2line工具进行崩溃问题定位。
227
2281. 程序自崩溃或构造崩溃。
229   例如将如下代码植入自己的代码中,调用触发一个无效内存访问故障(SIGSEGV)。
230
231
232   ```
233   NOINLINE int TriggerSegmentFaultException()
234   {
235       printf("test TriggerSegmentFaultException \n");
236       // 为构造崩溃,强制进行类型转换
237       int *a = (int *)(&RaiseAbort);
238       *a = SIGSEGV;
239       return 0;
240   }
241   ```
242
2432. 获取崩溃函数调用栈日志。
244   因为存在未处理的异常,进程会在/data/log/faultlog/temp路径下生成临时的日志文件,其命名规则为:
245
246
247   ```
248   cppcrash-pid-time
249   ```
250
251   获取其生成的调用栈如下:
252
253
254   ```
255   Timestamp:2017-08-05 17:35:03.000
256   Pid:816
257   Uid:0
258   Process name:./crasher_c
259   Reason:Signal:SIGSEGV(SEGV_ACCERR)@0x0042d33d
260   Fault thread Info:
261   Tid:816, Name:crasher
262   #00 pc 0000332c /data/crasher(TriggerSegmentFaultException+15)(8bc37ceb8d6169e919d178fdc7f5449e)
263   #01 pc 000035c7 /data/crasher(ParseAndDoCrash+277)(8bc37ceb8d6169e919d178fdc7f5449e)
264   #02 pc 00003689 /data/crasher(main+39)(8bc37ceb8d6169e919d178fdc7f5449e)
265   #03 pc 000c3b08 /system/lib/ld-musl-arm.so.1(__libc_start_main+116)
266   #04 pc 000032f8 /data/crasher(_start_c+112)(8bc37ceb8d6169e919d178fdc7f5449e)
267   #05 pc 00003284 /data/crasher(_start+32)(8bc37ceb8d6169e919d178fdc7f5449e)
268   Registers:
269   r0:0042d33d r1:0000000b r2:1725d4c4 r3:b6f9fa84
270   r4:bec97e69 r5:b6fc0268 r6:0042d661 r7:bec97d60
271   r8:00000000 r9:00000000 r10:00000000
272   fp:bec97d20 ip:00000020 sp:bec97cd0 lr:b6f9fae4 pc:0042d32c
273   ```
274
2753. 利用addr2line工具进行调用栈分析。
276   使用addr2line工具根据偏移地址解析行号:
277
278
279   ```
280   root:~/OpenHarmony/out/hi3516dv300/exe.unstripped/hiviewdfx/faultloggerd$ addr2line -e crasher 0000332c
281   base/hiviewdfx/faultloggerd/tools/crasher/dfx_crasher.c:57
282   ```
283
284   这个崩溃是由赋值给一块不可写的区域导致的,代码行为dfx_crasher.c文件的57行,修改后可以避免发生此崩溃。
285