OpenHarmony HiviewDFX Faultloggerd 模块解析
本文档已过时,计划删除
FaultloggerdClient 客户端
接口
1 | int32_t RequestFileDescriptor(int32_t type); // 通过socket请求文件句柄 |
请求结构体(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 | void HandleDefaultClientReqeust(int32_t connectionFd, const FaultLoggerdRequest* request); |
异常处理
1 | void FaultLoggerDaemon::GcZStatProcess(void) |
faultloggerd起一个线程专门用于回收fork出来的请求处理子线程,一秒处理一个,避免环境中存在Z状态的faultloggerd进程。
FaultloggerConfig
- crash日志管理,包含路径、数量、老化
- debug crash日志管理,包含路径
FaultloggerSecure
- 校验调用进程是否有dump权限
DumpCatcher(SDK API)
接口
1 | bool DumpCatch(int pid, int tid, std::string& msg); |
根据
pid
和tid
来区分使用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 | void DFX_InstallSignalHandler() |
在任何进程拉起前,都会调用DFX_InstallSignalHandler
接口用以注册信号处理器,主要干一下几个事情:
- 为收集崩溃日志而需要
fork
出来的子进程申请栈空间 - 为主线程信号栈申请预留空间,以防止
StackOverFlow
场景无法触发崩溃日志收集 - 对需要处理的信号以及信号处理函数
DFX_SignalHandler
进行注册
任何进程都在启动前安装信号处理器,是通过预先配置的
export LD_PRELOAD libdfx_signalhandler.z.so
环境变量,经由启动子系统的解析赋予给每一个进程的。
处理
通过信号处理器安装时设置的信号处理函数
DFX_SignalHandler
进行处理
处理逻辑如下:
- 首先识别是否是
SIGDUMP
信号,SIGDUMP
信号为主动抓取堆栈的信号,来源为DumpCatch
接口或者kill -35
命令触发,单一进程不会响应重复的SIGDUMP
信号 - 组装
ProcessDumpRequest
请求结构体对象g_request
,并记录当前崩溃的线程号 - 根据是否添加
DFX_LOCAL_UNWIND
编译开关用以控制是采用local回栈还是remote回栈- local回栈(
DFX_UnwindLocal
):直接调用libunwind
三方库相关接口,目前暂未开启。 - remote回栈:首先判断当前进程的
DUMPABLE
和PTRACER
属性进行调整,使之能够满足获取崩溃堆栈的前提条件;然后通过调用DFX_ForkAndDump
接口调用clone
函数fork出一个子进程来拉起processdump
进行堆栈抓取;最后通过轮询机制(每10ms)检查子进程是否完成dump操作并进行回收,超时则不等待。
- local回栈(
- 完成堆栈抓取后,重置信号处理器,恢复当前进程的
DUMPABLE
和PTRACER
属性,如果处理的信号不是SIGDUMP
则通过syscall
系统调用重发一个相同信号。
ProcessDump
1 | . |
Dump请求
主要分为两类请求:
processdump -p [PID] -t [TID]
命令行请求- 来自
SignalHandler
发出的请求
请求超时时间:30s
校验:
- 有效性校验:PID和TID的合法性
- 权限控制:通过socket请求
faultloggerd
守护进程提供的服务,校验当前是否具有dump权限。
回栈逻辑入口见
process_dumper.cpp
的Dump
函数。
根据请求类别的不同,走不同的dump流程:
- SignalHandler请求:
DumpProcessWithSignalContext
,需要打印完整信息包含有进程信息、信号信息、崩溃堆栈、寄存器、maps信息等。 - 普通命令行请求:
DumpProcess
,仅只需要打印堆栈。
SignalHandler请求
1 | void ProcessDumper::DumpProcessWithSignalContext(std::shared_ptr<DfxProcess> &process, |
- 根据请求信号是否为
SIGDUMP
信号,区分生成的文件类型是否为CPP_STACKTRACE
类型,否则就为CPP_CRASH
类型。 - 校验PID合法性,当前dump的进程应该为父进程。
- 构造并初始化
Process
和Thread
对象。 InitPrintThread
向faultloggerd
请求文件句柄,并初始化CppCrashReporter
崩溃上报对象。PrintDumpProcessWithSignalContextHeader
打印崩溃信息头等内容。UnwindProcess
进入真正的回栈核心流程,依赖三方库libunwind
。
命令行请求
1 | void ProcessDumper::DumpProcess(std::shared_ptr<DfxProcess> &process, |
- 构造并初始化
Process
和Thread
对象。 InitPrintThread
指向标准输出UnwindProcess
进入真正的回栈核心流程,依赖三方库libunwind
。
unwind 回栈
详见
dfx_unwind_remote.cpp
文件
UnwindProcess
1 | bool DfxUnwindRemote::UnwindProcess(std::shared_ptr<DfxProcess> process) |
unw_create_addr_space
创建回栈地址空间对象,unw_set_target_pid
绑定目标进PID,unw_set_caching_policy
设置回栈缓存策略;- 根据是否是崩溃触发的回栈以及是否打印非崩溃线程堆栈的配置,确定是否回栈所有的线程;
- 通过
UnwindProcess
函数来进行具体的线程回栈; - 通过
PrintProcessMapsByConfig()
来确定是否打印maps信息; unw_destroy_addr_space
回收地址空间对象。
UnwindThread
此处为ProcessDump回栈最核心的函数,即对具体的线程进行回栈,包含了对libunwind的直接调用,以及回栈策略的实现。
1 | bool DfxUnwindRemote::UnwindThread(std::shared_ptr<DfxProcess> process, std::shared_ptr<DfxThread> thread) |
std::shared_ptr<DfxRegs> regs = *thread*->GetThreadRegs()
获取线程寄存器数据;PrintDumpProcessMsg
打印具体的线程头信息,即线程tid和线程名;- 获取PC、LR、SP寄存器的值:
1 | uintptr_t regStorePc = 0; |
- 初始化构造
context
、cursor
、as_
对象,用于回栈; do { ... } while()
结构中即为具体的回栈查询流程,包含回栈策略:- 默认采用PC寄存器回栈,当上一次的pc寄存器值与本次相同时退出回栈;
- 当崩溃时lr寄存器的值等于当前查询得到的pc寄存器值,则采用lr回栈;
- 当pc寄存器值检查为无效时,采用lr回栈;
- 当pc回栈
unwRet = unw_step(&cursor)
失败时,也再尝试lr回栈;
DfxUnwindRemoteDoUnwindStep
函数为lr step回栈的流程。- 回收资源:
_UPT_destroy(context)
; - 打印寄存器信息:
PrintDumpProcessMsg(regs->PrintRegs())
,打印faultstack信息:PrintDumpProcessMsg(thread->PrintThreadFaultStackByConfig())
;
UnwindStep
每一步回栈的实现
1 | bool DfxUnwindRemote::DfxUnwindRemoteDoUnwindStep(size_t const & index, |
- 初始化frame;
- 获取pc值,以及全部寄存器值;并更新pc,lr,sp的值;
- 计算relative pc值;
- 获取map信息;
- 通过符号表缓存获取方法名和偏移信息;
- 打印frame。