For iOS engineers, the following Swift crash reports might look all too familiar.
Instead of seeing a file name and line number in your symbolicated stack trace, you’re greeted with an odd entry labeled
<compiler-generated>. This is incredibly frustrating, as it doesn’t provide enough information to effectively debug the crash.
In this post, we’ll cover why these entries show up, what you can do to debug them manually, and how Embrace helps automatically find the most relevant file name and line number in these situations.
Why Do Compiler-Generated Lines Show Up in iOS Crash Reports?
Swift generates a fair amount of code automatically during compile time, and this is undoubtedly a good thing. It allows the language to be more expressive, forgoing imperative instructions for leaner, easier to read code. The Swift compiler is very good at taking human-readable code and transforming it into machine code that executes in the least number of operations so that the code runs as fast as possible.
Some examples include:
- Inlining function calls to reduce call-linkage overhead
- Optimizing loops to improve cache performance and make effective use of parallel processing capabilities
- Generating code to effectively bridge between Objective-C and Swift
In addition, iOS engineers can configure the Swift compiler to optimize for runtime performance at the cost of increased code size (or vice versa). In other words, there is a tradeoff between the level of optimization the Swift compiler can do automatically and the “cleanliness” of the resulting binary. The more changes and transformations the compiler does, the more likely the symbolication process (mapping from memory addresses and offsets back to file names and line numbers) will result in missing information.
What this means is that when symbolicating a crash report with the corresponding dSYM, these
<compiler-generated> entries can appear. This doesn’t mean that the code that developer wrote was transformed and there was a problem when deobfuscating it. It means that the Swift compiler created brand new code and inserted it into the binary. When trying to translate the symbols back into human-readable form, the symbolication service could only determine that it was compiler-generated.
What Can iOS Engineers Do to Debug These Issues Manually?
<compiler-generated> file names are intentionally emitted by the Swift compiler. The compiler produces debug information for the code it generates, which typically includes the file that caused the code to be generated. In other words,
<compiler-generated> is added because the code in question simply does not exist in your source code.
There are a few things iOS engineers can do to maximize the debugging information they have for such a crash. First, they can make sure they have a fully symbolicated crash report. After all, any additional missing information only makes the debugging process more difficult. The image below demonstrates a partially symbolicated crash report that only has memory addresses for several frames.
You can check out these posts from Apple for how to automatically symbolicate crash reports in Xcode and how to download dSYM files from App Store Connect if you have a bitcode-enabled app. By following these steps, you should be able to get a fully symbolicated crash report and use the information from the other frames to guide debugging efforts.
You can also manually symbolicate a crash report using some of Apple’s many developer tools, including dwarfdump, atos, and symbolicatecrash. There’s also a helpful talk from Apple from this year’s WWDC called “Symbolication: Beyond the basics.”
If none of these solutions seem too appealing, there is another option that you can consider to get more visibility into
<compiler-generated> stack trace entries.
How Embrace Helps Automatically Find the Most Relevant File Name and Line Number
Embrace symbolicates all iOS crash reports that we receive if you upload the dSYM file to us. Our platform also allows iOS engineers to go from seeing stack traces like this:
That’s right! Embrace automatically translates the compiler-generated line into the most likely file name and line number. This works for both Objective-C and Swift crash reports. We do this by analyzing the surrounding code to see if we can find code that maps back to a file in the project, and, if this analysis meets certain requirements, we reference this file and line number in the stack trace. While not guaranteed to be the correct line, it will definitely be close and will guide further troubleshooting efforts.
To provide a bit more color (but still simplified), you can picture what we do as taking the memory address and offset for the compiler-generated frame and intelligently searching the surrounding addresses for the closest corresponding line in source code. The goal is to provide as much context as possible for these difficult to solve crashes.
Mobile teams that use Firebase Crashlytics are no doubt familiar with the pain of debugging with compiler-generated lines in stack traces. Now, they can investigate crashes and errors they previously had given up on, and they can do so faster and with more actionable information than ever before.
Summing It All Up
Swift is a powerful programming language that allows iOS engineers to write concise, expressive code. The compiler generates a fair amount of code during compile time, which sometimes leads to difficulties when investigating crash reports with
Short of digging into the binary files themselves, iOS engineers have not had a great solution for translating these stack trace entries into useful information. With Embrace, they can be automatically pointed to the most likely file name and line number. Our goal is to get you from issue to resolution as quickly as possible, and this functionality allows your team to save time when debugging crashes and errors.
How Embrace Helps Mobile Teams
Embrace is a mobile data platform that provides observability, debugging, and proactive alerting for mobile teams. We are a comprehensive solution that fully reproduces every user experience from every single session. Your team gets the data it needs to proactively identify, prioritize, and solve any issue that’s costing you users or revenue.
We're hiring for many different roles! Check out our openings and see if you'd be a good fit for our growing team!
Want to see how Embrace can help your team grow your mobile applications with best-in-class tooling and world-class support? Request a customized demo and see how we help teams set and exceed the KPIs that matter for their business!
Need help improving the performance and stability of your Unity games? Take our SDK for a spin!
Want to learn best practices for scaling your mobile applications? Check out these helpful eBooks!