OpenHarmony HiviewDFX Faultloggerd 模块解析

本文档已过时,计划删除

FaultloggerdClient 客户端

接口

1
2
3
4
5
6
int32_t RequestFileDescriptor(int32_t type); // 通过socket请求文件句柄
int32_t RequestLogFileDescriptor(struct FaultLoggerdRequest *request); // 通过socket请求日志文件句柄
int RequestFileDescriptorEx(const struct FaultLoggerdRequest *request); // 实际执行请求文件句柄的方法
bool RequestCheckPermission(int32_t pid); // 鉴权接口
void RequestPrintTHilog(const char *msg, int length); // 申请打印log
bool RequestSdkDump(int32_t pid, int32_t tid); // 申请打印堆栈

请求结构体(FaultloggerdRequest

int32_t type 请求类型
int32_t clientType 客户端类型
int32_t pid 进程ID
int32_t tid 线程ID
int32_t uid 用户ID
int32_t callerPid 调用方进程ID
int32_t callerTid 调用方线程ID
char module[128] /proc/self/cmdline
uint64_t time 时间戳

枚举类型定义

  • FaultLoggerType:包含JAVA_CRASH、CPP_CRASH、JS_CRASH、APP_FREEZE、JAVA_STACKTRACE、CPP_STACKTRACE;
  • FaultLoggerClientType:包含DEFAULT_CLIENT、LOG_FILE_DES_CLIENT、PRINT_T_HILOG_CLIENT、PERMISSION_CLIENT、SDK_DUMP_CLIENT;
  • FaultLoggerCheckPermissionResp:为权限校验请求返回状态值;
  • FaultLoggerSdkDumpResp:为dump请求返回状态值;

faultloggerd服务层

FaultloggerDaemon

服务启动(StartServer)

  • 创建sokect:/dev/unix/socket/faultloggerd.server,监听并处理
  • 创建faultloggerd对象,循环监听socket端口,如果有请求,则通过fork创建faultloggerd子进程进行处理。

请求响应处理函数

1
2
3
4
5
void HandleDefaultClientReqeust(int32_t connectionFd, const FaultLoggerdRequest* request);
void HandleLogFileDesClientReqeust(int32_t connectionFd, const FaultLoggerdRequest* request);
void HandlePrintTHilogClientReqeust(int32_t const connectionFd, FaultLoggerdRequest* request);
void HandlePermissionReqeust(int32_t connectionFd, FaultLoggerdRequest* request);
void HandleSdkDumpReqeust(int32_t connectionFd, FaultLoggerdRequest* request);

异常处理

1
2
3
4
5
6
7
8
9
10
void FaultLoggerDaemon::GcZStatProcess(void)
{
int status = -1;

while (true) {
// try to wait and clear the finished child process(Z status).
waitpid(-1, &status, WNOHANG);
usleep(GC_TIME_US);
}
}

faultloggerd起一个线程专门用于回收fork出来的请求处理子线程,一秒处理一个,避免环境中存在Z状态的faultloggerd进程。

FaultloggerConfig

  • crash日志管理,包含路径、数量、老化
  • debug crash日志管理,包含路径

FaultloggerSecure

  • 校验调用进程是否有dump权限

DumpCatcher(SDK API)

接口

1
2
3
4
bool DumpCatch(int pid, int tid, std::string& msg);
bool DumpCatchMultiPid(const std::vector<int> pidV, std::string& msg);
bool DumpCatchFrame(int pid, int tid, std::string& msg,
std::vector<std::shared_ptr<DfxDumpCatcherFrame>>& frameV);

根据pidtid来区分使用dump的方式,如果是dump当前进程的当前线程堆栈,则走基于libunwind的本地回栈;而其他则一律通过向faultloggerd_client发送请求走remote回栈。


SignalHandler(信号处理器)

接口

1
void DFX_InstallSignalHandler();

用于给当前进程注册信号处理器,可用于监听自己的崩溃信号。

目前支持处理的信号有:

信号编码 信号值 备注
SIGABRT 6 程序异常终止信号(abort)
SIGBUS 7 程序访问不存在的内存区域
SIGDUMP 35 自定义信号,dump堆栈
SIGFPE 8 算术异常,例如除以0
SIGILL 4 非法指令信号
SIGSEGV 11 无效内存引用,访问没有权限的内存区域、非可读内存区域,数组越界等
SIGSTKFLT 16 协处理器栈故障
SIGSYS 31 无效系统调用
SIGTRAP 5 调试信号,当在程序中设置断点后,该信号使得调试程序获得控制权

ProcessDump 请求结构体

接口体变量 说明
int32_t type 类型(进程/线程)
int32_t tid 线程ID
int32_t pid 进程ID
int32_t uid 用户ID
uint64_t reserved 保留字段
uint64_t timeStamp 时间戳
siginfo_t siginfo 信号值
ucontext_t context 上下文
char threadName[NAME_LEN] 线程名
char processName[NAME_LEN] 进程名

SignalHandler 运作逻辑

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void DFX_InstallSignalHandler()
{
pthread_mutex_lock(&g_signalHandlerMutex);
if (g_hasInit) {
pthread_mutex_unlock(&g_signalHandlerMutex);
return;
}

#ifdef ENABLE_DEBUG_HOOK
StartHookFunc((uintptr_t)DFX_SignalHandler);
#endif

#ifndef DFX_LOCAL_UNWIND
// reserve stack for fork
g_reservedChildStack = mmap(NULL, RESERVED_CHILD_STACK_SIZE, \
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, 1, 0);
if (g_reservedChildStack == NULL) {
DfxLogError("Failed to alloc memory for child stack.");
pthread_mutex_unlock(&g_signalHandlerMutex);
return;
}
g_reservedChildStack = (void *)(((uint8_t *)g_reservedChildStack) + RESERVED_CHILD_STACK_SIZE - 1);
#endif
ReserveMainThreadSignalStack();
struct sigaction action;
memset_s(&action, sizeof(action), 0, sizeof(action));
memset_s(&g_oldSigactionList, sizeof(g_oldSigactionList), 0, sizeof(g_oldSigactionList));
sigfillset(&action.sa_mask);
action.sa_sigaction = DFX_SignalHandler;
action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;

for (size_t i = 0; i < sizeof(g_interestedSignalList) / sizeof(g_interestedSignalList[0]); i++) {
int32_t sig = g_interestedSignalList[i];
if (sigaction(sig, &action, &(g_oldSigactionList[sig])) != 0) {
DfxLogError("Failed to register signal.");
}
}
g_hasInit = TRUE;
pthread_mutex_unlock(&g_signalHandlerMutex);
}

在任何进程拉起前,都会调用DFX_InstallSignalHandler接口用以注册信号处理器,主要干一下几个事情:

  1. 为收集崩溃日志而需要fork出来的子进程申请栈空间
  2. 为主线程信号栈申请预留空间,以防止StackOverFlow场景无法触发崩溃日志收集
  3. 对需要处理的信号以及信号处理函数DFX_SignalHandler进行注册

任何进程都在启动前安装信号处理器,是通过预先配置的export LD_PRELOAD libdfx_signalhandler.z.so环境变量,经由启动子系统的解析赋予给每一个进程的。

处理

通过信号处理器安装时设置的信号处理函数DFX_SignalHandler进行处理
处理逻辑如下:

  1. 首先识别是否是SIGDUMP信号,SIGDUMP信号为主动抓取堆栈的信号,来源为 DumpCatch接口或者kill -35命令触发,单一进程不会响应重复的SIGDUMP信号
  2. 组装ProcessDumpRequest请求结构体对象g_request,并记录当前崩溃的线程号
  3. 根据是否添加DFX_LOCAL_UNWIND编译开关用以控制是采用local回栈还是remote回栈
    1. local回栈(DFX_UnwindLocal):直接调用libunwind三方库相关接口,目前暂未开启。
    2. remote回栈:首先判断当前进程的DUMPABLEPTRACER属性进行调整,使之能够满足获取崩溃堆栈的前提条件;然后通过调用DFX_ForkAndDump接口调用clone函数fork出一个子进程来拉起processdump进行堆栈抓取;最后通过轮询机制(每10ms)检查子进程是否完成dump操作并进行回收,超时则不等待。
  4. 完成堆栈抓取后,重置信号处理器,恢复当前进程的DUMPABLEPTRACER属性,如果处理的信号不是SIGDUMP则通过syscall系统调用重发一个相同信号。

ProcessDump

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
.
├── BUILD.gn # 编译构建文件
├── cppcrash_reporter.cpp # CppCrash 上报
├── cppcrash_reporter.h # CppCrash 上报
├── dfx_config.cpp # 配置解析 faultloggerd.conf
├── dfx_config.h # 配置解析 faultloggerd.conf
├── dfx_dump_writer.cpp # 回栈数据写文件
├── dfx_dump_writer.h # 回栈数据写文件
├── dfx_elf.cpp # 解析 elf
├── dfx_elf.h # 解析 elf
├── dfx_frames.cpp # 解析 frame(栈帧)
├── dfx_frames.h # 解析 frame(栈帧)
├── dfx_log.cpp # hilog以及写文件 接口封装
├── dfx_log.h # hilog以及写文件 接口封装
├── dfx_maps.cpp # 解析 maps
├── dfx_maps.h # 解析 maps
├── dfx_process.cpp # 回栈 process
├── dfx_process.h # 回栈 process
├── dfx_regs.h # 寄存器数据
├── dfx_regs_arm.cpp # arm32 registers
├── dfx_regs_arm64.cpp # arm64 registers
├── dfx_regs_x86_64.cpp # X86_64 registers
├── dfx_ring_buffer.h # ringbuffer 数据缓存
├── dfx_ring_buffer_block.h # ringbuffer 块数据
├── dfx_signal.cpp # 崩溃信号解析与处理
├── dfx_signal.h # 崩溃信号解析与处理
├── dfx_symbols_cache.cpp # 符号表缓存
├── dfx_symbols_cache.h # 符号表缓存
├── dfx_thread.cpp # 回栈 thread
├── dfx_thread.h # 回栈 thread
├── dfx_unwind_remote.cpp # remote 回栈处理
├── dfx_unwind_remote.h # remote 回栈处理
├── dfx_util.cpp # 工具函数
├── dfx_util.h # 工具函数
├── main.cpp # 命令行回栈请求处理入口
├── process_dumper.cpp # 回栈主流程
├── process_dumper.h # 回栈主流程
└── test # [dirs] 测试用例

Dump请求

主要分为两类请求:

  • processdump -p [PID] -t [TID] 命令行请求
  • 来自SignalHandler发出的请求

请求超时时间:30s

校验:

  • 有效性校验:PID和TID的合法性
  • 权限控制:通过socket请求faultloggerd守护进程提供的服务,校验当前是否具有dump权限。

回栈逻辑入口见process_dumper.cppDump函数。

根据请求类别的不同,走不同的dump流程:

  • SignalHandler请求:DumpProcessWithSignalContext,需要打印完整信息包含有进程信息、信号信息、崩溃堆栈、寄存器、maps信息等。
  • 普通命令行请求:DumpProcess,仅只需要打印堆栈。

SignalHandler请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void ProcessDumper::DumpProcessWithSignalContext(std::shared_ptr<DfxProcess> &process,
std::shared_ptr<ProcessDumpRequest> request)
{
DfxLogDebug("Enter %s.", __func__);
ssize_t readCount = read(STDIN_FILENO, request.get(), sizeof(ProcessDumpRequest));
if (readCount != static_cast<long>(sizeof(ProcessDumpRequest))) {
DfxLogError("Fail to read DumpRequest(%d).", errno);
return;
}
std::string storeThreadName = request->GetThreadNameString();
std::string storeProcessName = request->GetProcessNameString();
FaultLoggerType type = (request->GetSiginfo().si_signo == SIGDUMP) ?
FaultLoggerType::CPP_STACKTRACE : FaultLoggerType::CPP_CRASH;
bool isLogPersist = DfxConfig::GetInstance().GetLogPersist();
InitDebugLog((int)type, request->GetPid(), request->GetTid(), request->GetUid(), isLogPersist);
// We need check pid is same with getppid().
// As in signal handler, current process is a child process, and target pid is our parent process.
if (getppid() != request->GetPid()) {
DfxLogError("Target pid is not our parent process, some un-expected happened.");
return;
}

std::shared_ptr<DfxThread> keyThread = std::make_shared<DfxThread>(request->GetPid(),
request->GetTid(),
request->GetContext());
if (!keyThread || !keyThread->IsThreadInititalized()) {
DfxLogError("Fail to init key thread.");
return;
}

keyThread->SetIsCrashThread(true);
if ((keyThread->GetThreadName()).empty()) {
keyThread->SetThreadName(storeThreadName);
}

process = DfxProcess::CreateProcessWithKeyThread(request->GetPid(), keyThread);
if (!process) {
DfxLogError("Fail to init process with key thread.");
return;
}

if ((process->GetProcessName()).empty()) {
process->UpdateProcessName(storeProcessName);
}

if (request->GetSiginfo().si_signo != SIGDUMP) {
process->SetIsSignalDump(false);
} else {
process->SetIsSignalDump(true);
}

process->InitOtherThreads();
process->SetUid(request->GetUid());
process->SetIsSignalHdlr(true);

InitPrintThread(true, request, process);
PrintDumpProcessWithSignalContextHeader(process, request->GetSiginfo());

DfxUnwindRemote::GetInstance().UnwindProcess(process);
DfxLogDebug("Exit %s.", __func__);
}
  1. 根据请求信号是否为SIGDUMP信号,区分生成的文件类型是否为CPP_STACKTRACE类型,否则就为CPP_CRASH类型。
  2. 校验PID合法性,当前dump的进程应该为父进程。
  3. 构造并初始化ProcessThread对象。
  4. InitPrintThreadfaultloggerd请求文件句柄,并初始化CppCrashReporter崩溃上报对象。
  5. PrintDumpProcessWithSignalContextHeader打印崩溃信息头等内容。
  6. UnwindProcess进入真正的回栈核心流程,依赖三方库libunwind

命令行请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void ProcessDumper::DumpProcess(std::shared_ptr<DfxProcess> &process,
std::shared_ptr<ProcessDumpRequest> request)
{
DfxLogDebug("Enter %s.", __func__);
if (request != nullptr) {
if (request->GetType() == DUMP_TYPE_PROCESS) {
process = DfxProcess::CreateProcessWithKeyThread(request->GetPid(), nullptr);
if (process) {
process->InitOtherThreads();
}
} else if (request->GetType() == DUMP_TYPE_THREAD) {
process = DfxProcess::CreateProcessWithKeyThread(request->GetTid(), nullptr);
} else {
DfxLogError("dump type is not support.");
return;
}

if (!process) {
DfxLogError("Fail to init key thread.");
return;
}

process->SetIsSignalDump(true);
process->SetIsSignalHdlr(false);
InitPrintThread(false, nullptr, process);
DfxUnwindRemote::GetInstance().UnwindProcess(process);
}

DfxLogDebug("Exit %s.", __func__);
}
  1. 构造并初始化ProcessThread对象。
  2. InitPrintThread指向标准输出
  3. UnwindProcess进入真正的回栈核心流程,依赖三方库libunwind

unwind 回栈

详见dfx_unwind_remote.cpp文件

UnwindProcess

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
bool DfxUnwindRemote::UnwindProcess(std::shared_ptr<DfxProcess> process)
{
if (!process) {
return false;
}

auto threads = process->GetThreads();
if (threads.empty()) {
return false;
}

as_ = unw_create_addr_space(&_UPT_accessors, 0);
if (!as_) {
return false;
}
unw_set_target_pid(as_, process->GetPid());
unw_set_caching_policy(as_, UNW_CACHE_GLOBAL);

// only need to unwind crash thread in crash scenario
if (process->GetIsSignalHdlr() && !process->GetIsSignalDump() && \
!DfxConfig::GetInstance().GetDumpOtherThreads()) {
bool ret = UnwindThread(process, threads[0]);
if (threads[0]->GetIsCrashThread() && (process->GetIsSignalDump() == false) && \
(process->GetIsSignalHdlr() == true)) {
process->PrintProcessMapsByConfig();
}
unw_destroy_addr_space(as_);
as_ = nullptr;
return ret;
}

size_t index = 0;
for (auto thread : threads) {
if (index == 1) {
process->PrintThreadsHeaderByConfig();
}

if (!UnwindThread(process, thread)) {
DfxLogWarn("Fail to unwind thread.");
}

if (thread->GetIsCrashThread() && (process->GetIsSignalDump() == false) && \
(process->GetIsSignalHdlr() == true)) {
process->PrintProcessMapsByConfig();
}
index++;
}

unw_destroy_addr_space(as_);
as_ = nullptr;
return true;
}
  1. unw_create_addr_space创建回栈地址空间对象,unw_set_target_pid绑定目标进PID,unw_set_caching_policy设置回栈缓存策略;
  2. 根据是否是崩溃触发的回栈以及是否打印非崩溃线程堆栈的配置,确定是否回栈所有的线程;
  3. 通过UnwindProcess函数来进行具体的线程回栈;
  4. 通过PrintProcessMapsByConfig()来确定是否打印maps信息;
  5. unw_destroy_addr_space回收地址空间对象。

UnwindThread

此处为ProcessDump回栈最核心的函数,即对具体的线程进行回栈,包含了对libunwind的直接调用,以及回栈策略的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
bool DfxUnwindRemote::UnwindThread(std::shared_ptr<DfxProcess> process, std::shared_ptr<DfxThread> thread)
{
DfxLogDebug("Enter %s.", __func__);
if (!thread) {
DfxLogWarn("NULL thread needs unwind.");
return false;
}

pid_t tid = thread->GetThreadId();
std::shared_ptr<DfxRegs> regs = thread->GetThreadRegs();

char buf[LOG_BUF_LEN] = {0};
int ret = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "Tid:%d, Name:%s\n", tid, thread->GetThreadName().c_str());
if (ret <= 0) {
DfxLogError("%s :: snprintf_s failed, line: %d.", __func__, __LINE__);
}
OHOS::HiviewDFX::ProcessDumper::GetInstance().PrintDumpProcessMsg(std::string(buf));

uintptr_t regStorePc = 0;
uintptr_t regStoreLr = 0;
uintptr_t regStoreSp = 0;
if (regs != nullptr) {
std::vector<uintptr_t> regsVector = regs->GetRegsData();
regStorePc = regsVector[REG_PC_NUM];
regStoreLr = regsVector[REG_LR_NUM];
regStoreSp = regsVector[REG_SP_NUM];
}

void *context = _UPT_create(tid);
if (!context) {
return false;
}

if (!as_) {
as_ = unw_create_addr_space(&_UPT_accessors, 0);
if (!as_) {
return false;
}
unw_set_caching_policy(as_, UNW_CACHE_GLOBAL);
}

unw_cursor_t cursor;
if (as_ && unw_init_remote(&cursor, as_, context) != 0) {
DfxLogWarn("Fail to init cursor for remote unwind.");
_UPT_destroy(context);
return false;
}

size_t index = 0;
int unwRet = 0;
unw_word_t oldPc = 0;
size_t crashUnwStepPosition = 0;
size_t skipFrames = 0;
bool useLrUnwStep = false;
bool isSigDump = process->GetIsSignalDump();
do {
unw_word_t tmpPc = 0;
unw_get_reg(&cursor, UNW_REG_IP, &tmpPc);
// Exit unwind step as pc has no change. -S-
if (oldPc == tmpPc && index != 0) {
DfxLogWarn("Break unwstep as tmpPc is same with old_ip .");
break;
}
oldPc = tmpPc;
// Exit unwind step as pc has no change. -E-

if (thread->GetIsCrashThread() && (regStorePc == tmpPc)) {
crashUnwStepPosition = index + 1;
skipFrames = index;
} else if (thread->GetIsCrashThread() && (regStoreLr == tmpPc) && (regStorePc == 0x0)) {
// Lr position found in crash thread. We need:
// 1. mark skipFrames.
// 2. Add pc zero frame.
useLrUnwStep = true;
skipFrames = index;
std::shared_ptr<DfxFrames> frame = thread->GetAvaliableFrame();
frame->SetFrameIndex(0);
frame->SetFramePc(regStorePc);
frame->SetFrameLr(regStoreLr);
frame->SetFrameSp(regStoreSp);
OHOS::HiviewDFX::ProcessDumper::GetInstance().PrintDumpProcessMsg(frame->PrintFrame());
index++;
}

if (skipFrames != 0 || thread->GetIsCrashThread() == false) {
if (!DfxUnwindRemoteDoUnwindStep((index - skipFrames), thread, cursor, process)) {
DfxLogWarn("Break unwstep as DfxUnwindRemoteDoUnwindStep failed -1.");
break;
}
}

index++;

// Add to check pc is valid in maps x segment, if check failed use lr to backtrace instead -S-.
std::shared_ptr<DfxElfMaps> processMaps = process->GetMaps();
if (!isSigDump && !processMaps->CheckPcIsValid((uint64_t)tmpPc) &&
(crashUnwStepPosition == index)) {
unw_set_reg(&cursor, UNW_REG_IP, regStoreLr);
// Add lr frame to frame list.
if (!DfxUnwindRemoteDoUnwindStep((index - skipFrames), thread, cursor, process)) {
DfxLogWarn("Break unwstep as DfxUnwindRemoteDoUnwindStep failed -2.");
break;
}
index++;
}
// Add to check pc is valid in maps x segment, if check failed use lr to backtrace instead -E-.
unwRet = unw_step(&cursor);
// if we use context's pc unwind failed, try lr -S-.
if (unwRet <= 0) {
if (!isSigDump && (crashUnwStepPosition == index)) {
unw_set_reg(&cursor, UNW_REG_IP, regStoreLr);
// Add lr frame to frame list.
if (!DfxUnwindRemoteDoUnwindStep((index - skipFrames), thread, cursor, process)) {
DfxLogWarn("Break unwstep as DfxUnwindRemoteDoUnwindStep failed -3.");
break;
}
index++;
unwRet = unw_step(&cursor);
}
}
// if we use context's pc unwind failed, try lr -E-.
} while ((unwRet > 0) && (index < BACK_STACK_MAX_STEPS));
thread->SetThreadUnwStopReason(unwRet);
_UPT_destroy(context);

if (process->GetIsSignalHdlr() && thread->GetIsCrashThread() && (process->GetIsSignalDump() == false)) {
OHOS::HiviewDFX::ProcessDumper::GetInstance().PrintDumpProcessMsg(regs->PrintRegs());
std::shared_ptr<DfxElfMaps> maps = process->GetMaps();
if (DfxConfig::GetInstance().GetDisplayFaultStack()) {
thread->CreateFaultStack(maps);
OHOS::HiviewDFX::ProcessDumper::GetInstance().PrintDumpProcessMsg(\
thread->PrintThreadFaultStackByConfig() + "\n");
}
}

DfxLogDebug("Exit %s.", __func__);
return true;
}
  1. std::shared_ptr<DfxRegs> regs = *thread*->GetThreadRegs()获取线程寄存器数据;
  2. PrintDumpProcessMsg打印具体的线程头信息,即线程tid和线程名;
  3. 获取PC、LR、SP寄存器的值:
1
2
3
4
5
6
7
8
9
uintptr_t regStorePc = 0;
uintptr_t regStoreLr = 0;
uintptr_t regStoreSp = 0;
if (regs != nullptr) {
std::vector<uintptr_t> regsVector = regs->GetRegsData();
regStorePc = regsVector[REG_PC_NUM];
regStoreLr = regsVector[REG_LR_NUM];
regStoreSp = regsVector[REG_SP_NUM];
}
  1. 初始化构造contextcursoras_对象,用于回栈;
  2. do { ... } while()结构中即为具体的回栈查询流程,包含回栈策略:
    1. 默认采用PC寄存器回栈,当上一次的pc寄存器值与本次相同时退出回栈;
    2. 当崩溃时lr寄存器的值等于当前查询得到的pc寄存器值,则采用lr回栈;
    3. 当pc寄存器值检查为无效时,采用lr回栈;
    4. 当pc回栈unwRet = unw_step(&cursor)失败时,也再尝试lr回栈;
  3. DfxUnwindRemoteDoUnwindStep函数为lr step回栈的流程。
  4. 回收资源:_UPT_destroy(context);
  5. 打印寄存器信息:PrintDumpProcessMsg(regs->PrintRegs()),打印faultstack信息:PrintDumpProcessMsg(thread->PrintThreadFaultStackByConfig());

UnwindStep

每一步回栈的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
bool DfxUnwindRemote::DfxUnwindRemoteDoUnwindStep(size_t const & index,
std::shared_ptr<DfxThread> & thread, unw_cursor_t & cursor, std::shared_ptr<DfxProcess> process)
{
DfxLogDebug("Enter %s :: index(%d).", __func__, index);
std::shared_ptr<DfxFrames> frame = thread->GetAvaliableFrame();
if (!frame) {
DfxLogWarn("Fail to create Frame.");
return false;
}

frame->SetFrameIndex(index);
std::string strSym;
uint64_t framePc = frame->GetFramePc();
if (unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t*)(&framePc))) {
DfxLogWarn("Fail to get program counter.");
return false;
}

std::shared_ptr<DfxRegs> regs = thread->GetThreadRegs();
bool isSignalHdlr = process->GetIsSignalHdlr();
if (regs != NULL) {
std::vector<uintptr_t> regsVector = regs->GetRegsData();
if (regsVector[REG_PC_NUM] != framePc) {
framePc = DfxUnwindRemoteDoAdjustPc(cursor, framePc);
}
} else {
if (!isSignalHdlr) {
framePc = DfxUnwindRemoteDoAdjustPc(cursor, framePc);
}
}

frame->SetFramePc(framePc);

uint64_t frameLr = frame->GetFrameLr();
if (unw_get_reg(&cursor, REG_LR_NUM, (unw_word_t*)(&frameLr))) {
DfxLogWarn("Fail to get lr.");
frame->SetFrameLr(0);
} else {
frame->SetFrameLr(frameLr);
}
uint64_t frameSp = frame->GetFrameSp();
if (unw_get_reg(&cursor, UNW_REG_SP, (unw_word_t*)(&frameSp))) {
DfxLogWarn("Fail to get stack pointer.");
return false;
}
frame->SetFrameSp(frameSp);

if (index != 0) {
frame->SetFrameRelativePc(unw_get_rel_pc(&cursor) - unw_get_previous_instr_sz(&cursor));
} else {
// pc frame, so needn't adjust relpc.
frame->SetFrameRelativePc(unw_get_rel_pc(&cursor));
}
struct map_info* mapInfo = unw_get_map(&cursor);
if (mapInfo != nullptr) {
frame->SetFrameMapName(mapInfo->path);
}

std::string funcName;
uint64_t funcOffset;
if (DfxSymbolsCache::GetInstance().GetNameAndOffsetByPc(&cursor, framePc, funcName, funcOffset)) {
frame->SetFrameFuncName(funcName);
frame->SetFrameFuncOffset(funcOffset);
}

OHOS::HiviewDFX::ProcessDumper::GetInstance().PrintDumpProcessMsg(frame->PrintFrame());

DfxLogDebug("Exit %s :: index(%d), framePc(0x%x), frameSp(0x%x).", __func__, index, framePc, frameSp);
return true;
}
  1. 初始化frame;
  2. 获取pc值,以及全部寄存器值;并更新pc,lr,sp的值;
  3. 计算relative pc值;
  4. 获取map信息;
  5. 通过符号表缓存获取方法名和偏移信息;
  6. 打印frame。