赞
踩
// 保存注册的 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. 执行了非法指令 2. 通常是因为可执行文件本身出现错误或者试图执行数据段 3. 堆栈溢出时也有可能产生这个信号 |
SIGABRT | 6 | 调用abort | 程序自己发现错误并调用 abort 时产生,一些 C 库函数(如:strlen) |
SIGSFPE | 8 | 浮点运算错误 | 如:除 0 操作 |
SIGSEGV | 11 | 段非法错误 | 1. 试图访问未分配给自己的内存 2. 或试图往没有写权限的内存地址写数据 3. 空指针 4. 数组越界 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)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。