iosfirebasecrashlytics

iOS: Crashlytics pointing to wrong line in symbolicated crashlog


We got a crashlog that was pointing to a line that didn't make sense for the kind of crash we were seeing.

These were the top lines in the crash report:

Crashed: com.apple.main-thread
0  MyApp    0x676634 specialized static LevelsSessionBeginCoordinator.buildUnitPracticeSession(user:practiceLevelSpecifics:pathLevel:mistakesPracticeTracker:experimentProvider:) + 48 (ClientSizeConstraintsCalculator.swift:48)
1  MyApp    0xd2e58c SkillTreeSessionPreparer.prepareUnitPractice(practiceLevelSpecifics:pathLevel:) + 560 (SkillTreeSessionPreparer.swift:560)

Checking out line 48 of ClientConstraintsCalculator.swift, there's where a property is being defined:

// ClientSizeConstraintsCalculator.swift

48:  private static var maxEmsForMatch: Float = {
49:    // pretty straightforward math
...
70:  }()

But it's weird because that line is never even called from the LevelsSessionBeginCoordinator.buildUnitPracticeSession(...) function mentioned.

This was confusing to members of the team, because it was leading them in the wrong direction with no idea what could be going wrong.

Looking in the crashlogs included within Xcode, they have the same stack trace, but with the top 2 memory addresses have been expanded into multiple stack frames, and it even gives us a very plain message about what was wrong:

Thread 0 Crashed:
0   MyApp    0x0000000104982634 Swift runtime failure: Can't get random value with an empty range + 0 (<compiler-generated>:0)
1   MyApp    0x0000000104982634 specialized static FixedWidthInteger.random<A>(in:using:) + 0 (<compiler-generated>:0)
2   MyApp    0x0000000104982634 specialized static FixedWidthInteger.random(in:) + 0 (LevelsSessionBeginCoordinator.swift:705)
3   MyApp    0x0000000104982634 specialized static LevelsSessionBeginCoordinator.buildUnitPracticeSession(user:practiceLevelSpecifics:pathLevel:mistakesPracticeTracker:experimentProvider:) + 768
4   MyApp    0x000000010503a58c specialized static LevelsSessionBeginCoordinator.buildUnitPracticeSession(user:practiceLevelSpecifics:pathLevel:mistakesPracticeTracker:experimentProvider:) + 16 (<compiler-generated>:0)
5   MyApp    0x000000010503a58c static LevelsSessionBeginCoordinator.buildUnitPracticeSession(user:practiceLevelSpecifics:pathLevel:mistakesPracticeTracker:experimentProvider:) + 16 (<compiler-generated>:0)
6   MyApp    0x000000010503a58c SkillTreeSessionPreparer.prepareUnitPractice(practiceLevelSpecifics:pathLevel:) + 80 (SkillTreeSessionPreparer.swift:555)

Not only that, but if we go to the line mentioned, LevelsSessionBeginCoordinator.swift:705, we see this:

704:  let totalRegularSessions = pathLevel.hasLevelReview ? pathLevel.totalSessions - 1 : pathLevel.totalSessions
705:  levelSessionIndex = Int.random(in: 0..<totalRegularSessions)

So yeah, this lines up perfectly with what Apple's tooling is telling us.

Something is going wrong here. Something isn't working.

Looking at what doing things manually would show me, I decided to pull up atos. Finding the offset of the memory address for the line that is being called, we can see that it's the same as what Crashlytics is telling us:

Binary Images:
    0x10430c000 - 0x10681ffff MyApp arm64

Crashing Line: 0x0000000104982634

Offset: 0x104982634 - 0x10430c000 = 0x676634

So let's see what atos will tell us:

> atos -o dSYMs/MyApp.dSYM -offset 0x676634
specialized static LevelsSessionBeginCoordinator.buildUnitPracticeSession(user:practiceLevelSpecifics:pathLevel:mistakesPracticeTracker:experimentProvider:) (in MyApp) (LevelsSessionBeginCoordinator.swift:705)

Which is the line that was crashing!

I'd like to submit a bug report, but they say you need something "clearly defined bug (with a Minimal, Complete, and Verifiable example) that is not specific to your project." This seems pretty specific to this one instance, so I'm not sure it's what they are looking for. So sorry, I'm abusing stack overflow for this.


Solution

  • I got a response from them. Apparently this is expected and all is going according to plan:

    As per our engineers, current behavior is working as intended. The atos command does expand 1 address to multiple lines sometimes. However, because Crashlytics converts from gSYM to cSYM, we are only able to get one symbol per address.

    When you upload dSYM to Crashlytics we re-format the DWARF content in dSYM file to a simple format which is easier to lookup on the backend when doing the symbolication. When a stack trace frame address hits a compiler-generated frame, sometimes we are able to find the nearest address containing a similar symbol with more accurate file and line number information. So instead of using the compiler-generate we swap to the closest line in your application file we can get.

    With current strategy on how we store code mapping data, this is probably the closest we can get. The atos command probably is able to get more information from DWARF or there might be some internal apple tools to facilitate generating diagnostic messages.

    Which is basically what I was expecting, but was just wondering if anyone else had any insight.