This Session introduces iOS Crash Logs: How to analyze Crash Logs, and how to debug and fix Crash problems, such as hard-to-repeat memory and multithreading issues. This is also a study note series. Please read it with happy water on ice if you are allowed. (A moderate amount of sugar will cause a high and make you enjoy studying even more.
What is a Crash? Why does a Crash happen?
When the App crashes, the Attached debugger suspends the App and locates the crash location.
Take a closer look at crash stack. Here is the App startup entryHere is the location of crashIn the event of a crash, the debugger receives the signal, pauses the App, and displays the call stack for the crashIf there is no attached debugger, the system dumps the crash stack information to a log fileWhen a crash occurs in a Release app, the call stack of the log file only has address informationXcode symbolized crash log
Accessing crash logs
When the online APP crashes, the system will upload the log file to the cloud, and then the crash can be obtained and analyzed through xcode’s Organizer.
The Crash Organizer contains the crash data of the App on TestFlight and App Store. Including recent crash statistics, the most affected devices information, crash location information and so on
The “Show In Finder” button will open a text file to get the full Log
The file starts with some summary information at the top. This includes the name of your app, the version number of the operating system running it, and the date and time it crashed
How to read Carsh?
For example, as shown in figure! In this case, the exception type is EXC_BAD_INSTRUCTION. The SIGILL signal is an invalid instruction signal, which means that the CPU is trying to execute an instruction that for some reason does not exist or is invalid and that is why the process is abortingBecause this is a symbolic stack trace with full debug information we can see a file and a line number that indicates where the crash occurred Swift line 26 so we can look at that code and we open up the project and this is recipeImage.swift line 26 is the line that was marked in the crash
Experience with CARSH solutions
Most programmers run into a lot of Carsh at work and when you look at enough crash logs you can start to pick up pattern examples of error values.
In this case, the error is using free space.So we have the address range that the memory allocator uses and our invalid address looks like it’s in the malloc range but it’s been offset by 4 bits and it’s been rotated by 4 bits so it looks like it’s a rotated valid MALloc address and that’s a clue from the memory allocator itself
Why is this address offset by 4 bits?
An object starts with an ISA field that points to the object’s class and that’s the structure of objective-C objects and that’s the structure of some Swift objects.
The objc_release function does:
Read the ISA field and dereference the ISA field so that you canGo to that class object and look up its methods in general and of course that’s valid and that’s what happens in the normal case
But what happens if the object is already released? When a release function deletes an object it inserts it into a release list of other invalid objects and it writes a pointer to the release list to the next object in the list where the ISA field used to be
But in a slightly twisted way it’s not going to write a pointer directly into that field it’s going to write a rotated pointer into that field and it wants to make sure that the value that’s written there is not a valid memory address and that’s why it crashes when it’s used incorrectly
When objc_release reads the ISA field it gets a rotated free list pointer and when it dereferences the rotated free list pointer it crashes
The memory allocator does this for us by deliberately rotating the pointer to ensure that a crash occurs if we try to use it again
This is the signature we see in this crash log our invalid address field looks like a pointer in the Malloc region but rotates in the same way that Malloc rotates its free list pointer
Find specific objects
Is there any way to know which object was released multiple times causing the crash? Although there is call stack information in the log, all the functions are generated by the compiler. There is no specific information related to crash. Here’s a concrete example of how to find objects in LoginViewController that have been released multiple times.
// LoginViewController.swift
class LoginViewController: UIViewController {
var userName: String
var database: DatabaseProxy
var views: [UIView]
...
}
Copy the code
Use disassembly to find specific information to locate carsh
Run the LLDB command script import lldb.macosx.crashlog command on the CLI or xcode to load the crashlog file
Find the address of the __ivar_DESTROYER function and disassemble it
Disassembly instruction
Disassemble -A (function address for XXX)Copy the code
This shows us the assembly code for the function
I don’t have time to teach you how to read assembly code but fortunately for crash logs you don’t really need to be able to read assembly code completely fluently usually it’s enough to just skim through assembly code and get a sense of what’s going on you don’t have to understand every single instruction to get useful information out of the crash log, okay
Now let’s go back to the information in the crash log which is the __ivar_DESTROYER function plus 42 which called objc_release, There’s an instruction at +42 but there’s another problem and that is that in the stack trace most stack frames have assembly level offsets that are the return address and that’s the instruction that comes after the function call so the instruction that calls objc_release is the previous instruction which is the instruction that if we read that it’s correct Objc_release call this is good and this is consistent with what we saw in the crash log stack trace the call to objc_Release at this offsetThe release function is releasing the database property
(Note: offset here, why there is offset here, and then how much offset, or know this offset, we just need to locate a assembly instruction, the author is not very clear, if any friends know, can add wechat, thank you very much)
Exception Codes 0x8BadF00D Error Codes: Watchdog timeout, which means “ate bad food”. 0xdeadFA11 Error Code: User forcibly exits, which means “Dead Fall”. 0xBaAAAAAD Error Code: The user holds down the Home and volume keys to obtain the current memory status. 0xBAD22222 Error Code: VoIP Application (Too frequent?) Got killed by iOS. 0xC00010FF error code: Killed because it was too hot, meaning “cool off” 0xdead10CC Error code: Dead lock for holding system resources (e.g. address book) while in the background.
Crash Log Analysis Summary
- I Understand the reason for the crash
- Examine the Crashed thread’s stack trace: Examine the crashed thread’s stack trace
- Look for more Clues in bad address and Disassembly: Look for more clues in bad addresses and disassembly
Crash Analysis Tips
- Look at code other than the line that crashed: Look at code other than the line that crashed
- Look at thread stack traces other than the crashed thread
- Look at more than one crash log: View multiple crash logs
- Use Address Sanitizer and Zombies to reproduce memory errors: Use Address Sanitizer and Zombies to reproduce memory errors
Log diagram of common memory error crashes
Multithreaded Carsh
Multithreading is particularly difficult to reproduce because they only occasionally cause crashes so your code seems to work 99% of the time and these bugs go undetected for a long time
Symptoms of multithreaded CARsh
There are also some unique symptoms of multithreaded errors and crashing threads usually contain sorry crash logs and usually contain multiple threads that are executing code that is related to each other so if a particular class or method appears in the crash logs of multiple threads that means there could be multithreaded errors
Symptoms of multithreaded memory CARsh
Memory corruption due to multithreading problems is usually very random so you might find that crashes happen on slightly different lines of code or at slightly different addresses and as Greg said you can see that they show up as different crash points in Xcode even though they’re part of the same vulnerability
Use Thread Sanitizer to solve multithreading problems
Symptoms of Multithreading Bugs in Crash Logs One of the hardest bug types to reproduce and diagnose: One of the most difficult types of errors to replicate and diagnose is Multithreading bugs and often cause memory corruptions: A thread thread error usually causes memory corruption. One bug can appear as different crash points: A bug can appear as different crash points
Edit Scheme → Dignostics → Thread Sanitizer → finding buffer overflows
Tips
- Test your app on real devices
- Try to reproduce crashes
- Use bug-finding tools on hard-to-reproduce crashes
- Address Sanitizer for Memory Corruption Bugs: Use the Address Sanitizer to debug memory problems
- Thread Sanitizer for Multithreading problems: Uses Thread Sanitizer to debug multithreading problems
Summary
- User Organizer to access crash logs: Focus on crashes in the Organizer
- Analyze reproducible crahses: Analysis of repeated crahses
- Look for signs of Memory corruption and threading issues: Look for signs of memory corruption and threading issues
- Reproduce: Use bug-finding tools to help reproduce
- Give each thread a name to make it easier to locate a crash
For example, if naming is used. So the broken information is
Isn’t that a lot easier to find threads?