iOS objc_msgSend 野指针Crash 从 Log 提取 Crash 时 selector 的地址和名字并打印

时间:2023-03-09 08:32:34
iOS objc_msgSend 野指针Crash 从 Log 提取 Crash 时 selector 的地址和名字并打印

从 crash stack log 里面,提取 objc_msgSend 关键字,定位是否是野指针问题导致的crash,如果是则打印 crash 时的 objc_msgSend 调用的第二个参数,即 selector 的地址和名字String,方便定位和 fix 此类型的 crash。

具体提取方法:
1. 提取字符串地址:
32bit 机器,读取 crash 时 log 里面 r1: 0x 后面的 8 个 16 进制数字字符串;
64bit 机器,读取 crash 时 log 里面 x1: 0x 后面的 16 个 16 进制数字字符串;
2. 通过 strtoul 将字符串地址转化为无符号整型地址;
3. 同时打印 selector 的 “地址” 和 selector “字符串” 即 selectorName。

static void printLastSelectorName(NSString *crashStackString)
{
// print registers
QZLOG_INFO(@"*** print registers begin. ***"); if (crashStackString.length > 0 && ([crashStackString rangeOfString:@"objc_msgSend"].location != NSNotFound)) { QZLOG_INFO(@"*** have found objc_msgSend. ***"); NSString *r1Flag = @"r1: 0x";
NSString *x1Flag = @"x1: 0x";
NSRange rangeContainsR1Reg = [crashStackString rangeOfString:r1Flag];
NSRange rangeContainsX1Reg = [crashStackString rangeOfString:x1Flag]; NSString *valOfR1X1 = nil; @try {
if ((rangeContainsR1Reg.location != NSNotFound) && (crashStackString.length >= rangeContainsR1Reg.location + r1Flag.length + 8)) { // 32-bit
valOfR1X1 = [crashStackString substringWithRange:NSMakeRange(rangeContainsR1Reg.location + r1Flag.length, 8)];
}
else if ((rangeContainsX1Reg.location != NSNotFound) && (crashStackString.length >= rangeContainsX1Reg.location + x1Flag.length + 16)) { // 64-bit
valOfR1X1 = [crashStackString substringWithRange:NSMakeRange(rangeContainsX1Reg.location + x1Flag.length, 16)];
}
}
@catch (NSException *exception) {
QZLOG_ERROR(@"*** exception: %@", exception);
} if (valOfR1X1.length > 0) {
unsigned long val = strtoul([[valOfR1X1 substringWithRange:NSMakeRange(0, valOfR1X1.length)] UTF8String], 0, 16);
if (val != 0 && val != ULONG_MAX) { QZLOG_INFO(@"*** r1(x1) val = %lx", val);
QZLOG_INFO(@"*** r1(x1): %@", NSStringFromSelector((SEL)val));
}
}
} QZLOG_INFO(@"*** print registers end. ***");
}