I have an iOS app that stores images in Core Data using External Storage. From time to time I get crash reports with the following error (in this particular instance it's running on a M1 Mac via Catalyst):
Unable to open file with path /Users/user/Library/Group Containers/group.myapp/.myapp_SUPPORT/_EXTERNAL_DATA/61681DBF-B1A4-4325-9104-E21A1218F046 (92)
The relevant stack trace bit looks like this:
0 CoreFoundation +0xfc408 __exceptionPreprocess
1 libobjc.A.dylib +0x1aea4 objc_exception_throw
2 CoreData +0x12cda0 +[_PFRoutines readExternalReferenceDataFromFile:]
3 CoreData +0xe9db8 -[_PFExternalReferenceData _attemptToMapData:]
4 CoreData +0xd7894 -[_PFExternalReferenceData _retrieveExternalData]
5 Foundation +0x46f2f8 specialized static Data._unconditionallyBridgeFromObjectiveC(_:)
6 MyApp +0x15eb80 Image.thumbnailImage.getter (Image.swift:25:22)
All I'm doing in Image.swift:25:22 is declaring a variable with the contents of the image:
let data = self.thumbnail // 'thumbnail' is my Binary Data attribute
I've tried to reproduce this crash on my machine by intentionally deleting, corrupting and changing permissions on external storage files but that doesn't work.
Does anyone have the slightest idea of what's going on, or how to prevent or fix this?
Related
I have a crash with the following log on iOS 12.0.1:
ImageIO: CFDataGetBytes: data: 0x28539b2f0 size: 154262 offset: 8 count: 8 dst: 0x16dbf86f0
External data reference cant find underlying file.
Fatal Exception: NSInternalInconsistencyException
0 CoreFoundation 0x23c2d7ef8 __exceptionPreprocess
1 libobjc.A.dylib 0x23b4a5a40 objc_exception_throw
2 CoreData 0x23efd5fc8 -[_PFExternalReferenceData getBytes:range:]
3 ImageIO 0x23e6c7178 IIOImageRead::getCFDataBytesAtOffset(void*, unsigned long, unsigned long)
4 ImageIO 0x23e6c6c38 IIOImageRead::getBytesAtOffset(void*, unsigned long, unsigned long)
5 ImageIO 0x23e711aa4 IIO_Reader_PNG::getImageCount(IIOImageReadSession*, IIODictionary*, int*, unsigned int*)
6 ImageIO 0x23e54c5d8 IIO_Reader::callGetImageCount(CGImageReadSession*, IIODictionary*, int*)
7 ImageIO 0x23e532194 IIOImageSource::updatedCount()
8 ImageIO 0x23e5367b4 CGImageSourceGetCount
9 UIKitCore 0x26960c1a4 _UIImageRefFromData
10 UIKitCore 0x268d4e15c -[UIImage(UIImagePrivate) _initWithData:preserveScale:cache:]
11 UIKitCore 0x268d48b7c +[UIImage imageWithData:]
12 MyApp 0x102239570 __48-[InfoPreviewController bindToPatient:]_block_invoke_2 (InfoPreviewController.m:83)
13 ReactiveObjC 0x1031f8004 -[RACSubscriber sendNext:] (RACSubscriber.m:72)
It seems that core data has the image, but when it tries to retrieve it, it fails.
How can I verify the data integrity in this case?
I would like to handle this failure in a user friendly way instead of crashing the app.
This is caused by a known bug in Core Data external storage in iOS 12.0.x as discussed here: https://stackoverflow.com/a/52628198/2347353. There is no workaround, but the bug appears to be fixed in iOS 12.1.
To answer your question though, with a bit of hackery-pokery, you can get the filename where the data is supposed to be stored and check to see if it exists or not. If the file is missing, then you know that the corruption has happened so you can avoid reading the attribute, and therefore prevent the app from crashing.
None of this is documented, but the files seem to be stored in the _EXTERNAL_DATA hidden directory in your app’s Documents folder with a filename that you can work out from a value that is saved to the data store.
This answer shows how to do this in Objective-C and is where I got a lot of the details from: https://stackoverflow.com/a/13497992/2347353. But for anyone trying this at home, please do heed the warnings that this is all based on Apple’s internal implementation details of Core Data and could stop working at any point in the future.
The crash can be caused by not having file access to the external storage folder.
So before accessing an object value with attribute.description?.allowsExternalBinaryDataStorage == true, make sure you have access or get access to the folder where the Core Data SQLite database files are stored.
I just received my first crash report from Crashlytics and am attempting to correct the issue. Unfortunately it is only with a line of code that runs on older devices so I can't test it on my iPhone 6.
The crash report from Crashlytics highlights two threads, the first reads:
Fatal Exception: NSInvalidArgumentException
-[CABasicAnimation altitude]: unrecognized selector sent to instance 0x17734440
While the second reads:
Crashed: Map Update :: NSOperation 0x1a839470
SIGABRT ABORT at 0x316a3dfc
The indicated line of code for both threads is:
let relativeAlt = mylocation.altitude - appDelegate.elevation
Where:
let mylocation = self.mapView.myLocation
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
I'm trying to understand what I'm reading in the crash report. The way I see it the program doesn't understand the altitude reference made for some reason? This doesn't make sense to me since this crash seems to occur after that app has been running for several minutes without error, the highlighted line of code is run possibly hundreds of times before the app crashed. What is really happening here?
Additional Information:
Since writing, I have received additional crashes that I believe stem from the same issue:
Crashed: Map Update :: NSOperation 0x19fb2d50
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x11d077ca
Crashed: Map Update :: NSOperation 0x145ced50
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x81450a64
The first highlighted the following line in my code (I believe since I had worked on the app since this beta release and the line numbers have changed slightly):
self.lastLocation = (self.mapView.myLocation as CLLocation).coordinate
While the second crash just gave me:
libobjc.A.dylib
objc_msgSend + 5
The first of the new crashes (That provided a line of code) provided this report:
Thread : Crashed: Map Update :: NSOperation 0x19fb2d50
0 libobjc.A.dylib 0x3105c708 objc_release + 7
1 FlightTracker 0x000ba830 FlightTracker.MapViewController. (locationManager (FlightTracker.MapViewController) -> (Swift.ImplicitlyUnwrappedOptional<ObjectiveC.CLLocationManager>, didUpdateLocations : Swift.ImplicitlyUnwrappedOptional<Swift.Array<Swift.AnyObject>>) -> ()).(closure #1) (MapViewController.swift:168)
2 Foundation 0x244ce0fd __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 8
3 Foundation 0x24438fc5 -[NSBlockOperation main] + 148
4 Foundation 0x2442b845 -[__NSOperationInternal _start:] + 768
5 Foundation 0x244d0a57 __NSOQSchedule_f + 186
6 libdispatch.dylib 0x315ad5d9 _dispatch_queue_drain$VARIANT$mp + 948
7 libdispatch.dylib 0x315ad0a9 _dispatch_queue_invoke$VARIANT$mp + 84
8 libdispatch.dylib 0x315af0d3 _dispatch_root_queue_drain + 330
9 libdispatch.dylib 0x315b01fb _dispatch_worker_thread3 + 106
10 libsystem_pthread.dylib 0x31720e25 _pthread_wqthread + 668
Probably not your problem, but I just had a SIGABRT that was driving me nuts (that's how I ended up looking at this question) and I'll post my solution in case it helps some future S.O. spelunker.
In my (iPad, not that it matters) app, you can push a button that results in the creation of a not-full-screen UIViewController which contains a UITableView, and this viewController is presented via UIPopoverController.
In my case I had a screw-up in my loading of the tableView items which, at the time of creating the tableView cell I ended up trying to add a null value into a dictionary. (It's a long story, having to do with an infrastructure class that expects the data to be in a certain format.)
Anyway, attempting to access newViewController.view caused the SIGABRT on that line, with no clue that the problem was related to filling the tableView cell. Nothing tableView-related was evident in the stack trace, so it took me quite a while to narrow things down. I eventually just guessed "maybe it's the tableview" and disconnected the IBOutlet and delegate/dataSource to see if the crash went away.
...And it did. Which lead me down the path of finding the real problem.
Anyway, that's my story. Hope it's helpful to someone.
Due to the lack of a full/proper crash report and the lack of more code and architecture, the following is an assumption using the little bits of information that are available.
You are accessing a variable in a background thread (NSOperation queue) that got released on another thread and now isn't available any longer, so the pointer shows to some other random object in the memory. And that random object surely has no idea what to do with the altitude message which is then causing the crash.
You have to make sure that all variables used in the background thread, are available and not released in another thread.
Wow, I just had another "impossible to track down" solution to this that was driving me bonkers.
Earlier in the day, I'd done a major refactor as some of the objects in my game had names like RFCFoo and some were like RfcBar and I wanted to standardize the capitalization.
So I used XCode's Refactor->Rename... tool, and it worked great, except for one thing:
It failed to rename one specific .xib file, which remained as "RFCBlahBlah.xib" when I was trying to load it as "RfcBlahBlah.xib"
Again, I hope this proves useful for some future SO searcher.
In a production app with the debug information stripped out, how do you convert the output of:
NSLog(#"Stack Trace: %#", [exception callStackSymbols]);
To an legible class and method name? A line number would be a blessing.
Here's the output I'm getting:
0 CoreFoundation 0x23d82f23 <redacted> + 154
1 libobjc.A.dylib 0x23519ce7 objc_exception_throw + 38
2 CoreFoundation 0x23cb92f1 <redacted> + 176
3 MyApp 0x23234815 MyApp + 440341
The final line is the bread and butter line, but when I use dwarf to find the address, nothing exists.
dwarfdump --arch armv7 MyApp.dSYM --lookup 0x00234815 | grep 'Line table'
I've read here that you need to convert the stack address to something else for dwarf or atos:
https://stackoverflow.com/a/12464678/2317728
How would I find the load address or slide address to perform the calculation? Is there not a way to calculate all this prior to sending the stacktrace to the log from within the app? If not, how would I determine and calculate these values after receiving the stack trace? Better yet, is there an easier solution I'm missing?
Note I cannot just wait for crash reports since the app is small and they will never come. I'm planning on sending the stack traces to our server to be fixed as soon as they appear.
EDITORIAL
The crash reporting tools in iOS are very rough, particularly when compared to Android. In android, the buggy lines are sent to Google analytics, you use the map to debug the line--simple (comparatively). For iOS, you are confronted with: a) waiting on user bug reports (not reasonable for a small app), b) sending stack traces to a server where there are scant tools or information on how to symbolicate the stack traces, c) relying on large quasi-commercial 3rd party libraries. This definitely makes it harder to build and scale up--hoping Apple will eventually take notice. Even more hopeful someone has spotted an easier solution I might have missed ;)
Thanks for your help!
A suggestion, you can easily get the method name, exception reason and line number using:
NSLog(#"%# Exception in %s on %d due to %#",[exception name],__PRETTY_FUNCTION__,__LINE__,[exception reason]);
I am stuck with the following crash report:
Date/Time: 2013-09-12 22:39:54 +0000
OS Version: iPhone OS 6.1.3 (10B329)
Report Version: 104
Exception Type: SIGSEGV
Exception Codes: SEGV_ACCERR at 0xa0000008
Crashed Thread: 0
Thread 0 Crashed:
0 libobjc.A.dylib 0x39a3c564 _cache_getImp + 4
1 libobjc.A.dylib 0x39a3e1d7 class_respondsToSelector + 31
2 CoreFoundation 0x31b96605 objectIsKindOfClass + 37
3 CoreFoundation 0x31b9635d __handleUncaughtException + 69
4 libobjc.A.dylib 0x39a41a65 _objc_terminate() + 129
5 libc++abi.dylib 0x3948e07b safe_handler_caller(void (*)()) + 79
6 libc++abi.dylib 0x3948e114 std::terminate() + 20
7 libc++abi.dylib 0x3948f599 __cxa_current_exception_type + 1
8 libobjc.A.dylib 0x39a419d1 objc_exception_rethrow + 13
9 CoreFoundation 0x31adcf21 CFRunLoopRunSpecific + 457
10 CoreFoundation 0x31adcd49 CFRunLoopRunInMode + 105
11 GraphicsServices 0x356a82eb GSEventRunModal + 75
12 UIKit 0x339f2301 UIApplicationMain + 1121
13 Our App 0x0003bc27 main (main.m:15)
After different attempts to fix the bug causing this error I keep receiving this crashlog again and again from PLCrashReporter (from our adhoc builds from the beta testers). The different exception codes vary from SIGSEGV/SEGV_ACCERR to SIGBUS/BUS_ADRALN to EXC_BAD_ACCESS/KERN_INVALID_ADDRESS
I am using the rapidjson library on iOS (armv7 and armv7s) with the padding fix as described here (#8) and I use the objective-c runtime functions to add method implementation on runtime (using class_addMethod).
Our codebase exists of mostly Objective-C code with some Obj-C++ and some C code. Memory Management is done by ARC except for the Obj-C++ and C parts which is handled manually. I looked into every malloc/free call and I extensively used libgmalloc to determine memory issues but there is nothing which does not seem correct to me.
I cannot reproduce this crashlogs myself, not in debug or release mode, but our beta testers keep sending me this crashlog once in a while (1 in about 50 runs). As our product (hopefully) will run on many iOS devices soon, this is not something we can left broken.
After reading a lot of memory management articles I suspect this issue is caused by bad memory alignment. Therefore I suspect rapidjson to be the cause of this bug. My lack of knowledge about memory alignment on iOS / armv7 does not allow me to fix this crashlog. Can someone explain me more about this subject on iOS? Or am I looking in the wrong place and is this another memory issue?
I hope someone can point me in the right direction.
If more info is needed I am happy to provide it.
Note: I am not looking for answers as using JSONKit or another library to replace rapidjson. Thanks :)
This issue was previously addressed here: https://devforums.apple.com/message/807860
In short, the original Objective-C exception has been released (eg, by an autorelease pool) prior to it being dereferenced in the uncaught exception handler. As such, __handleUncaughtException() dereferences a now-dead pointer, and you see the crash in your exception handler.
First, the address 0xa0000008 does not look misaligned, and SEGV_ACCERR does not mean an alignment problem, but a memory access permission problem (from sys/signal.h):
#define SEGV_ACCERR 2 /* [XSI] invalid permission for mapped object */
Considering you are using class_addMethod() and the crash is in _cache_getImp(), which is part of retrieving a method, my largely unfounded suspicion would be that you passed some invalid pointers to class_addMethod(), or overwrote that information later on.
Something to check would be whether you are using either globals or malloc()ed memory, because the runtime functions do not make copies for you.
Second, the crash you are seeing is secondary, you are crashing while running the top-level exception handler that is already terminating (_objc_terminate()) your program from an earlier exception, but in this case it's not a Unix signal, but an Objective-C exception: objc_exception_rethrow().
So you probably need to figure out that primary error first, for example from logs of the exception (just a backtrace isn't enough in many cases).
I have an app that creates a persistent store in application:didFinishLaunchingWithOptions. Adding the store can apparently take too long, which causes iOS to terminate the app before it finishes launching. The stack traces at the time of timing out looks like this. Does anyone know how to prevent this from happening?
Exception Type: 00000020
Exception Codes: 0x000000008badf00d
Highlighted Thread: 0
Application Specific Information:
com.foo.bar failed to launch in time
Elapsed total CPU time (seconds): 23.490 (user 23.490, system 0.000), 78% CPU
Elapsed application CPU time (seconds): 8.406, 28% CPU
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0:
0 libsystem_kernel.dylib 0x31b8939c pread + 20
1 libsqlite3.dylib 0x31ed95d0 unixRead
2 libsqlite3.dylib 0x31eec106 readDbPage
3 libsqlite3.dylib 0x31eeb2a2 sqlite3PagerAcquire
4 libsqlite3.dylib 0x31f04096 moveToChild
5 libsqlite3.dylib 0x31f052c6 sqlite3BtreeNext
6 libsqlite3.dylib 0x31f01490 sqlite3VdbeExec
7 libsqlite3.dylib 0x31efa48a sqlite3_step
8 CoreData 0x364f8892 _execute
9 CoreData 0x364f878c -[NSSQLiteConnection execute]
10 CoreData 0x3658bd94 -[NSSQLConnection prepareAndExecuteSQLStatement:]
11 CoreData 0x365dd4f2 -[_NSSQLiteStoreMigrator performMigration:]
12 CoreData 0x365d70dc -[NSSQLiteInPlaceMigrationManager migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:]
13 CoreData 0x36577428 -[NSMigrationManager migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:]
14 CoreData 0x365c8670 -[NSStoreMigrationPolicy(InternalMethods) migrateStoreAtURL:toURL:storeType:options:withManager:error:]
15 CoreData 0x365c79c4 -[NSStoreMigrationPolicy migrateStoreAtURL:withManager:metadata:options:error:]
16 CoreData 0x365c8ece -[NSStoreMigrationPolicy(InternalMethods) _gatherDataAndPerformMigration:]
17 CoreData 0x364ec3b0 -[NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:]
badfood is a common launch crash. You need to get the creation of the Core Data stack off the main thread. I have discussed this a few times on here and other places in the past.
You can also look at Aplle's iCloud Core Datavidros as that launch code will solve your problem as well.
Keep in mind that your app will need to be able to launch without the stack being in place. For existing apps this can be a major change.
Update 2
This has always been recommended but unfortunately only recently has it been starting to show in the templates.
There are several things that can cause the adding of the persistent store to take longer:
If you are migrating your database from one version to another; the migration occurs when you add the store to the coordinator.
If you are adding iCloud to your application it will take significantly longer on the first launch.
If Core Data determines that something needs to be done to the database for maintenance, it can take longer than expected.
The "right" answer is to add the store to the coordinator off the main thread.
The quick/bandaid answer would be to turn on sql logging for core data and then see what is going on during launch. Once you understand what is causing the delay you may be able to address it. If it is a migration then the right answer is probably the only answer.
Update 3
Profile this in instruments. That will tell you where the slowness is.
What does dataToMigrate = [self fetchSomeDataFromDatabase]; do?
Look at your time profile and that will tell you what is slow. Send me the time profile from instruments and I will look at it as well.
code is easy:
dispatch_async(DISPATCH_QUEUE_PRIORITY_HIGH, ^{
[self setupCoreDataStack]; //TODO
})
but as marcus said, make sure your app can handle not having CoreData ready at launch (it is async now) .. you could e.g. set A splash screen!? / a basic menu that doesnt use the db