iOS奔溃信息收集
来源:互联网 发布:淘宝美姿堂meiji真假 编辑:程序博客网 时间:2024/06/05 07:59
概述
app 奔溃主要有两种原因:
1,程序某处抛了一个异常,却未被捕获,会导致std::terminate
函数被调用,std::terminate
调用std::terminate_handler
类型的终止处理器,默认的终止处理器调用abort
函数终止程序
2,进程收到一个默认终止进程的信号,大多数信号的默认行为都是终止进程
针对第一种情况,可以安装一个终止处理器来来执行异常信息收集
针对第二种情况,可以安装信号处理器来执行信号信息收集
异常信息收集
void terminate_handler(){ std::string name, reason; std::vector<std::string> symbols; auto except = std::current_exception(); if (except) { try { std::rethrow_exception(except); } catch(const NSException *e) { name = e.name.UTF8String; reason = e.reason.UTF8String; for (NSString *sym in e.callStackSymbols) symbols.push_back(sym.UTF8String); } catch(const std::exception &e) { name = "unknown"; reason = e.what() ?: "unknown"; } catch(...) { } } // send exception info // ... raise(SIGKILL);}
std::set_terminate(terminate_handler);
在终止处理器里把异常重新抛出来,分别捕获两个异常基类,从基类里获取异常信息,然后发送给server。
信号信息收集
首先设置信号处理器的执行栈,如果不设置信号处理器的执行栈的话,当进程栈溢出时(比如无穷递归),信号处理器就没法执行了。
void setup_stack(){ stack_t sigstack; sigstack.ss_sp = ::operator new(SIGSTKSZ, std::nothrow); sigstack.ss_size = SIGSTKSZ; sigstack.ss_flags = 0; sigaltstack(&sigstack, nullptr);}
安装信号处理器
void install_sig_handler(int signo, void (*handler)(int, siginfo_t *, void *)){ struct sigaction action, old_action; sigset_t sigset; sigemptyset(&sigset); action.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK; action.sa_sigaction = handler; action.sa_mask = sigset; sigaction(signo, &action, &old_action);}
void sig_handler(int signo, siginfo_t *info, void *context){}
install_sig_handler(SIGSYS, sig_handler);install_sig_handler(SIGPIPE, sig_handler);install_sig_handler(SIGTERM, sig_handler);install_sig_handler(SIGBUS, sig_handler);install_sig_handler(SIGFPE, sig_handler);install_sig_handler(SIGTRAP, sig_handler);install_sig_handler(SIGILL, sig_handler);install_sig_handler(SIGSEGV, sig_handler);install_sig_handler(SIGABRT, sig_handler);
把常见导致奔溃的信号都捕获了
信号处理器
看一下信号处理器的函数原型:
void sig_handler(int signo, siginfo_t *info, void *context);
参数说明:
signo: 捕获的 signal number
info
typedef struct __siginfo { int si_signo; /* signal number */ int si_errno; /* errno association */ int si_code; /* signal code */ pid_t si_pid; /* sending process */ uid_t si_uid; /* sender's ruid */ int si_status; /* exit value */ void *si_addr; /* faulting instruction */ union sigval si_value; /* signal value */ long si_band; /* band event for SIGPOLL */ unsigned long __pad[7]; /* Reserved for Future Use */} siginfo_t;
其中 si_addr 是奔溃的地址
context 这个参数的类型是:ucontext_t,这个类型是硬件相关的,不可移植
在iOS上这个类型里包含了一些寄存器信息:
// arm32struct __darwin_arm_thread_state { __uint32_t __r[13]; /* General purpose register r0-r12 */ __uint32_t __sp; /* Stack pointer r13 */ __uint32_t __lr; /* Link register r14 */ __uint32_t __pc; /* Program counter r15 */ __uint32_t __cpsr; /* Current program status register */};
// arm64struct __darwin_arm_thread_state64 { __uint64_t __x[29]; /* General purpose registers x0-x28 */ __uint64_t __fp; /* Frame pointer x29 */ __uint64_t __lr; /* Link register x30 */ __uint64_t __sp; /* Stack pointer x31 */ __uint64_t __pc; /* Program counter */ __uint32_t __cpsr; /* Current program status register */ __uint32_t __pad; /* Same size for 32-bit or 64-bit clients */};
这些寄存器保存了程序奔溃时的值,其中比较重要的两个寄存器是lr
和fp
lr
是 link register
,即函数的返回地址 fp
是 frame pointer
,即当前栈帧的指针
arm32的fp
是r7
iOS 上函数调用栈的栈帧结构如下:
/** * stack frame * * ---------- * | | * ---------- * | lr | <- fp[1] * ---------- * | old fp | <- fp * ---------- * | | * ---------- * | | * ---------- */
每一个栈帧中都保存了old fp
和lr
,可以通过栈帧指针回溯整个backtrace
然后通过lr
和dladdr
函数查询函数符号名等信息
Dl_info dlinfo;dladdr(lr, &dlinfo);
Dl_info dlinfo;void **fp = reinterpret_cast<void **>(__fp);while (fp) { void *lr = fp[1]; dladdr(lr, &dlinfo); // dlinfo 里包含函数符号名,函数地址,动态库(image)地址,动态库路径等信息 // ... fp = reinterpret_cast<void **>(fp[0]); // fp = old_fp}
这样就可以拿到崩溃时的函数backtrace
注意,通过C库函数backtrace
, backtrace_symbols
或(Objc的[NSThread callStackSymbols]
)拿到的backtrace是不对的,库函数是拿不到奔溃时的栈回溯信息的。在异常信息收集时也是一样,捕获异常时通过库函数backtrace
, backtrace_symbols
也是拿不到抛异常时的栈回溯信息的,除非在抛异常前调用backtrace
, backtrace_symbols
拿到栈回溯信息,然后保存到即将要抛出的异常对象中,就像NSException那样。
总结
奔溃信息收集,主要是收集奔溃时的奔溃地址,以及栈回溯信息等。
像原始C数组越界会产生信号
而Objc的数组越界会抛出异常
注意:
在开发信号相关的程序时,不能连接调试器,否则,信号处理器不会被调用,因为调试器会捕获信号,并且覆盖了我们的信号处理器
参考资料:
《The Linux Programming Interface》
man page
- iOS奔溃信息收集
- IOS 收集崩溃信息 NSException类
- IOS crash信息收集(swift)
- iOS 抛出异常 收集奔溃信息处理
- 信息收集
- 信息收集
- 信息收集
- 信息收集
- 信息收集
- 信息收集
- 信息收集
- 信息收集
- ios程序发布后,收集Crash崩溃信息
- iOS Bugly定位收集真机崩溃日志信息
- [IOS笔记][Bugly]首次使用bugly收集app崩溃信息
- ios收集
- 被动信息收集之DNS信息收集
- 被动信息收集:信息收集内容、信息用途、信息收集DNS、DNS信息收集-NSLOOKUP
- inotify与rsync同步机制
- 低通、高通、带通、带阻、状态可调滤波器【3】
- Batch Normalization
- 唯一插件化Replugin源码及原理深度剖析--初始化之框架核心
- 如何终止java线程
- iOS奔溃信息收集
- Oracle 数据库启动过程
- nginx服务器添加密码访问
- Md5算法代码(c++):
- java访问权限
- CentOS 7.*版本安装MariaDB数据库
- 日志监控filebeat中文指南
- Collections.sort()和Arrays.sort()排序算法选择
- 【分块】