文章40 | 阅读 19083 | 点赞0
// 保存注册的 exception 捕获方法
NSUncaughtExceptionHandler * oldExceptionHandler;
// 自定义的 exception 异常处理
void ExceptionHandler(NSException * exception);
void RegisterExceptionHandler() {
if(NSGetUncaughtExceptionHandler() != ExceptionHandler) {
oldExceptionHandler = NSGetUncaughtExceptionHandler();
}
NSSetUncaughtExceptionHandler(ExceptionHandler);
}
/**
* @brief exception 崩溃处理
*/
void ExceptionHandler(NSException * exception) {
// 使 UncaughtExceptionCount 递增
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
// 超出允许捕获错误的次数
if (exceptionCount > UncaughtExceptionMaximum) {
return;
}
// 获取调用堆栈
NSMutableDictionary * userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
userInfo[kUncaughtCallStackKey] = [exception callStackSymbols];
NSException * exp = [NSException exceptionWithName:exception.name
reason:exception.reason
userInfo:userInfo];
// 在主线程中执行方法
[[[UncaughtExceptionHandler alloc] init] performSelectorOnMainThread:@selector(dealException:)
withObject:exp
waitUntilDone:YES];
// 调用保存的 handler
if (oldExceptionHandler) {
oldExceptionHandler(exception);
}
}
信号 | 值 | 介绍 | 场景 |
---|---|---|---|
SIGILL | 4 | 非法指令 | 1. 执行了非法指令 <br>2. 通常是因为可执行文件本身出现错误或者试图执行数据段 <br>3. 堆栈溢出时也有可能产生这个信号 |
SIGABRT | 6 | 调用abort | 程序自己发现错误并调用 abort 时产生,一些 C 库函数(如:strlen) |
SIGSFPE | 8 | 浮点运算错误 | 如:除 0 操作 |
SIGSEGV | 11 | 段非法错误 | 1. 试图访问未分配给自己的内存 <br>2. 或试图往没有写权限的内存地址写数据 <br>3. 空指针<br>4. 数组越界<br>5. 栈溢出等 |
typedef void (* SignalHandlerClass)(int, struct __siginfo *, void *);
// 已注册的 singal 捕获方法
SignalHandlerClass oldSignalHandler;
static void MySignalHandler(int signal, siginfo_t* info, void* context) {
// do something。。。
if (signal == SIGABRT) {
if (oldSignalHandler) {
oldSignalHandler(signal, info, context);
}
}
}
void registerSignalHandler() {
// 获取已注册的 handler
struct sigaction old_action;
sigaction(SIGABRT, NULL, &old_action);
if (old_action.sa_flags & SA_SIGINFO) {
SignalHandlerClass handler = old_action.sa_sigaction;
if (handler != MySignalHandler) {
oldSignalHandler = handler;
}
}
struct sigaction action;
action.sa_sigaction = MySignalHandler;
action.sa_flags = SA_NODEFER | SA_SIGINFO;
sigemptyset(&action.sa_mask);
sigaction(signal, &action, 0);
}
+ (NSArray *)backtrace {
/* 指针列表。
①、backtrace 用来获取当前线程的调用堆栈,获取的信息存放在这里的 callstack 中
②、128 用来指定当前的 buffer 中可以保存多少个 void* 元素
*/
void * callstack[128];
// 返回值是实际获取的指针个数
int frames = backtrace(callstack, 128);
// backtrace_symbols 将从 backtrace 函数获取的信息转化为一个字符串数组,每个字符串包含了一个相对于 callstack 中对应元素的可打印信息,包括函数名、偏移地址、实际返回地址。
// 返回一个指向字符串数组的指针
char **strs = backtrace_symbols(callstack, frames);
NSMutableArray * backtrace = [NSMutableArray arrayWithCapacity:frames];
for (int i = 0; i < frames; i++) {
[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
return backtrace;
}
// 未符号化前
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x000000018b816f30 0x18b7fc000 + 110384 (objc_msgSend + 16)
1 UIKit 0x0000000192e0a79c 0x192c05000 + 2119580 (<redacted> + 72)
2 UIKit 0x0000000192c4db48 0x192c05000 + 297800 (<redacted> + 312)
3 UIKit 0x0000000192c4d988 0x192c05000 + 297352 (<redacted> + 160)
4 QuartzCore 0x00000001900d6404 0x18ffc5000 + 1119236 (<redacted> + 260)
// 符号化后
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x000000018b816f30 objc_msgSend + 16
1 UIKit 0x0000000192e0a79c -[UISearchDisplayController _sendDelegateDidBeginDidEndSearch] + 72
2 UIKit 0x0000000192c4db48 -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 312
3 UIKit 0x0000000192c4d988 -[UIViewAnimationState animationDidStop:finished:] + 160
4 QuartzCore 0x00000001900d6404 CA::Layer::run_animation_callbacks(void*) + 260
/*
* The 64-bit mach header appears at the very beginning of object files for
* 64-bit architectures.
*/
struct mach_header_64 {
uint32_t magic; /* mach magic number identifier */
cpu_type_t cputype; /* cpu specifier */
cpu_subtype_t cpusubtype; /* machine specifier */
uint32_t filetype; /* type of file */
uint32_t ncmds; /* number of load commands */
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
uint32_t reserved; /* reserved */
};
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
Stack Address = Symbol Address + Slide
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
uint64_t vmbase = 0;
uint64_t vmslide = 0;
uint64_t vmsize = 0;
uint64_t loadAddress = 0;
uint64_t loadEndAddress = 0;
NSString *imageName = @"";
NSString *uuid;
const struct mach_header *header = _dyld_get_image_header(i);
const char *name = _dyld_get_image_name(i);
vmslide = (i);
imageName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
BOOL is64bit = header->magic == MH_MAGIC_64 || header->magic == MH_CIGAM_64;
uintptr_t cursor = (uintptr_t)header + (is64bit ? sizeof(struct mach_header_64) : sizeof(struct mach_header));
struct load_command *loadCommand = NULL;
for (uint32_t i = 0; i < header->ncmds; i++, cursor += loadCommand->cmdsize) {
loadCommand = (struct load_command *)cursor;
if(loadCommand->cmd == LC_SEGMENT) {
const struct segment_command* segmentCommand = (struct segment_command*)loadCommand;
if (strcmp(segmentCommand->segname, SEG_TEXT) == 0) {
vmsize = segmentCommand->vmsize;
vmbase = segmentCommand->vmaddr;
}
} else if(loadCommand->cmd == LC_SEGMENT_64) {
const struct segment_command_64* segmentCommand = (struct segment_command_64*)loadCommand;
if (strcmp(segmentCommand->segname, SEG_TEXT) == 0) {
vmsize = segmentCommand->vmsize;
vmbase = (uintptr_t)(segmentCommand->vmaddr);
}
}
else if (loadCommand->cmd == LC_UUID) {
const struct uuid_command *uuidCommand = (const struct uuid_command *)loadCommand;
NSString *uuidString = [[[NSUUID alloc] initWithUUIDBytes:uuidCommand->uuid] UUIDString];
uuid = [[uuidString stringByReplacingOccurrencesOfString:@"-" withString:@""] lowercaseString];
}
}
loadAddress = vmbase + vmslide;
loadEndAddress = loadAddress + vmsize - 1;
}
// do something...
$ dwarfdump --uuid mytest.app.dSYM
UUID: B4217D5B-0349-3D9F-9D70-BC7DD60DA121 (armv7) mytest.app.dSYM/Contents/Resources/DWARF/mytest
UUID: A52E3452-C2EF-3291-AE37-9392EDCCE572 (arm64) mytest.app.dSYM/Contents/Resources/DWARF/mytest
dwarfdump --arch [arch type] --lookup [Symbol Address] [dsym file path]
$ dwarfdump --arch arm64 --lookup 0x100006a14 mytest.app.dSYM
----------------------------------------------------------------------
File: mytest.app.dSYM/Contents/Resources/DWARF/mytest (arm64)
----------------------------------------------------------------------
Looking up address: 0x0000000100006a14 in .debug_info... found!
0x0003ebb7: Compile Unit: length = 0x000000d4 version = 0x0004 abbr_offset = 0x00000000 addr_size = 0x08 (next CU at 0x0003ec8f)
0x0003ebc2: TAG_compile_unit [120] *
AT_producer( "Apple LLVM version 9.1.0 (clang-902.0.39.2)" )
AT_language( DW_LANG_ObjC )
AT_name( "/Users/worthyzhang/Desktop/mytest/mytest/ViewController.m" )
AT_stmt_list( 0x00009151 )
AT_comp_dir( "/Users/worthyzhang/Desktop/mytest" )
AT_APPLE_optimized( true )
AT_APPLE_major_runtime_vers( 0x02 )
AT_low_pc( 0x00000001000069bc )
AT_high_pc( 0x000000a4 )
0x0003ebf9: TAG_subprogram [122] *
AT_low_pc( 0x00000001000069bc )
AT_high_pc( 0x00000070 )
AT_frame_base( reg29 )
AT_object_pointer( {0x0003ec12} )
AT_name( "-[ViewController viewDidLoad]" )
AT_decl_file( "/Users/worthyzhang/Desktop/mytest/mytest/ViewController.m" )
AT_decl_line( 17 )
AT_prototyped( true )
AT_APPLE_optimized( true )
Line table dir : '/Users/worthyzhang/Desktop/mytest/mytest'
Line table file: 'ViewController.m' line 25, column 1 with start address 0x0000000100006a14
Looking up address: 0x0000000100006a14 in .debug_frame... not found.
atos -o [dsym file path] -l [Load Address] -arch [arch type] [Stack Address]
$ atos -o mytest.app.dSYM/Contents/Resources/DWARF/mytest -l 0x1046e8000 --arch arm64 0x1046eea14
-[ViewController viewDidLoad] (in mytest) (ViewController.m:25)
# 获取 crash 文件的 UUID
grep "appName armv" *crash
# 或者
grep --after-context=2 "Binary Images:" *crash
# 获取 app 的 UUID
xcrun dwarfdump --uuid appName.app/appName
# 获取 dSYM 的 UUID
xcrun dwarfdump --uuid appName.dSYM
# 对比 app 和 crash 的 UUID 进行匹配
# 用 atos 命令来符号化某个特定模块加载地址 (3种方式都可以)
# 0x4000 是模块的加载地址(必须是DWARF文件地址,而不是dSYM地址,dSYM只是一个bundle)
xcrun atos -o appName.app.dSYM/Contents/Resources/DWARF/appName -l 0x4000 -arch armv7
xcrun atos -o appName.app.dSYM/Contents/Resources/DWARF/appName -arch armv7
xcrun atos -o appName.app/appName -arch armv7
# 另外,应用内 获取 UUID 的方法
#import <mach-o/ldsyms.h>
NSString *executableUUID() {
const uint8_t *command = (const uint8_t *)(&_mh_execute_header + 1);
for (uint32_t idx = 0; idx < _mh_execute_header.ncmds; ++idx) {
if (((const struct load_command *)command)->cmd == LC_UUID) {
command += sizeof(struct load_command);
return [NSString stringWithFormat:@"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
command[0], command[1], command[2], command[3],
command[4], command[5],
command[6], command[7],
command[8], command[9],
command[10], command[11], command[12], command[13], command[14], command[15]];
} else {
command += ((const struct load_command *)command)->cmdsize;
}
}
return nil;
}
# 通过 iTunes Connect 网站来下载 dSYM 的话,对下载下来的每个 dSYM 文件都执行一次
xcrun dsymutil -symbol-map ~/Library/Developer/Xcode/Archives/[...]/BCSymbolMaps [UUID].dSYM
# 有两行未符号化的 crash log
* 3 appName 0x000f462a 0x4000 + 984618
* 4 appName **0x00352aee** 0x4000 + 3468014
# 1. 执行
xcrun atos -o appName.app.dSYM/Contents/Resources/DWARF/appName -l 0x4000 -arch armv7
# 2. 然后输入 0x00352aee
# 3. 符号化结果:
-[UIScrollView(UITouch) touchesEnded:withEvent:] (in appName) (UIScrollView+UITouch.h:26)
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/Forever_wj/article/details/120068863
内容来源于网络,如有侵权,请联系作者删除!