__abort_with_payload + 8 崩溃
崩溃一
1 | Thread 0 Crashed: |
分析:
从 CFAutoreleasePoolPop 开始,进入了 error 的函数调用。
在 objc 的源码中找到 objc_fatalv 的实现,这是一个不会返回的函数调用。
static __attribute__((noreturn))
void _objc_fatalv(uint64_t reason, uint64_t flags, const char *fmt, va_list ap)
{
char *buf1;
vasprintf(&buf1, fmt, ap);
char *buf2;
asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
_objc_syslog(buf2);
if (DebugDontCrash) {
char *buf3;
asprintf(&buf3, "objc[%d]: HALTED\n", getpid());
_objc_syslog(buf3);
_Exit(1);
}
else {
abort_with_reason(OS_REASON_OBJC, reason, buf1, flags);
}
}
abort_with_reason 找不到实现,调用栈最后的abort_with_payload,这个的实现在内核中。
关注到栈上的内存
1 | "stack@0x16d53a5f8": { |
有 error 日志的信息,对应起来是 Autorelease 中的内存错误。
static void badPop(void *token)
{
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
// OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
_objc_fatal
("Invalid or prematurely-freed autorelease pool %p.", token);
}
// Old SDK. Bad pop is warned once.
static bool complained = false;
if (!complained) {
complained = true;
_objc_inform_now_and_on_crash
("Invalid or prematurely-freed autorelease pool %p. "
"Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
"Proceeding anyway because the app is old "
"(SDK version " SDK_FORMAT "). Memory errors are likely.",
token, FORMAT_SDK(sdkVersion()));
}
objc_autoreleasePoolInvalid(token);
}
崩溃二
1 | Thread 0 Crashed: |
1 | "stack@0x16f391388": { |
简单分析这就是在 UITouch 的一次触摸事件中发生的崩溃,看栈上的内存,是跟 UIRemoteInputViewController 有关。问题根因是对一个对象做 weak 引用时,对象本身已经释放或者正在释放。
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
SEL_allowsWeakReference);
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
}
if (deallocating) {
if (crashIfDeallocating) {
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
} else {
return nil;
}
}
// now remember it and where it is being stored
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
else {
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
简单搜索下关键词 UIRemoteInputViewController,猜测可能是键盘问题。或者第三方键盘问题。第三方键盘老有唤醒失败的情况,闪现一下就没了。对一个正在释放的对象做 weak 引用发生的崩溃。
总结
0 libsystem_kernel.dylib __abort_with_payload + 8
1 libsystem_kernel.dylib abort_with_payload_wrapper_internal + 100
2 libsystem_kernel.dylib abort_with_payload_wrapper_internal + 0
3 libobjc.A.dylib _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 112
4 libobjc.A.dylib __objc_error + 0
abort_with_payload 崩溃的调用栈都应该是相似的,主要看栈上的 error 信息。因为这里的字符串刚巧会在栈上,如果你的崩溃收集器有对栈上的内存做处理收集起来了,那应该多关注这里,这会是突破口。