Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Messages from fatalError/etc are no longer propagated on Bugsnag reports #318

Closed
edenman opened this issue Nov 5, 2018 · 30 comments
Closed
Labels
bug Confirmed bug released This feature/bug fix has been released

Comments

@edenman
Copy link

edenman commented Nov 5, 2018

Expected behavior

Forcing the app to crash by calling fatalError("i am a message") includes the "i am a message" text in the bugsnag report.

Observed behavior

No message is included:
https://app.bugsnag.com/recharge-labs/ios-consumer/errors/5bbe93a371c2fc001a3e9ba8

Version

Bugsnag 5.17.0
Xcode Version 10.0 (10A255)

Additional information

Original feature request (#159) and PR to add the feature (#188). I'm guessing that something in Swift 4.2 or Xcode 10 changed the way those notable addresses are attached to the crash report? Or maybe broke the heuristics in some other way?

kattrali added a commit that referenced this issue Nov 29, 2018
In the new Xcode 10 build system, Swift object register values have the
top bit used as a flag. This change strips the flag while not losing
anything relevant to us in our quest to see error messages for assertion
failures.

Fixes #318
kattrali added a commit that referenced this issue Nov 29, 2018
In the new Xcode 10 build system, Swift object register values have the
top bit used as a flag. This change strips the flag while not losing
anything relevant to us in our quest to see error messages for assertion
failures.

Fixes #318
kattrali added a commit that referenced this issue Nov 29, 2018
In the new Xcode 10 build system, Swift object register values have the
top bit used as a flag. This change strips the flag while not losing
anything relevant to us in our quest to see error messages for assertion
failures.

This technique does not capture messages which are less than 16
characters, as short strings are stored as raw char arrays on the stack
rather than being allocated. (See WWDC 2018 #401 for more info on new
string optimizations)

While it is possible to check for char arrays as well as pointers when
searching for notable address values, sweeping up local variables has a
likely chance of capturing unintended data as well from the surrounding
code, some of which may be sensitive. It is also not guaranteed that the
value would still be on the stack after the message is logged, so it is
possible to get only unrelated string values as the message.

In the current Swift stdlib, the following messages passed to
fatalError, preconditionFailure, and precondition (and their internal
func counterparts) are less than 16 characters:

* empty string
* `unavailable`
* `not implemented`
* `abstract method`
* `unknown value`
* `invalid count` (where a dictionary contains < 0 items(?))
* `invalid index` (where a dictionary ceases to be a dictionary)
* `don't touch me` (from SpriteKit)
* `close() failed` (from the private Subprocess implementation)

The vast majority have more meaningful messages.

Reference:
* https://asciiwwdc.com/2018/sessions/401

Fixes #318
kattrali added a commit that referenced this issue Nov 30, 2018
In the new Xcode 10 build system, Swift object register values have the
top bit used as a flag. This change strips the flag while not losing
anything relevant to us in our quest to see error messages for assertion
failures.

This technique does not capture messages which are less than 16
characters, as short strings are stored as raw char arrays on the stack
rather than being allocated. (See WWDC 2018 #401 for more info on new
string optimizations)

While it is possible to check for char arrays as well as pointers when
searching for notable address values, sweeping up local variables has a
likely chance of capturing unintended data as well from the surrounding
code, some of which may be sensitive. It is also not guaranteed that the
value would still be on the stack after the message is logged, so it is
possible to get only unrelated string values as the message.

In the current Swift stdlib, the following messages passed to
fatalError, preconditionFailure, and precondition (and their internal
func counterparts) are less than 16 characters:

* empty string
* `unavailable`
* `not implemented`
* `abstract method`
* `unknown value`
* `invalid count` (where a dictionary contains < 0 items(?))
* `invalid index` (where a dictionary ceases to be a dictionary)
* `don't touch me` (from SpriteKit)
* `close() failed` (from the private Subprocess implementation)

The vast majority have more meaningful messages.

Reference:
* https://asciiwwdc.com/2018/sessions/401

Fixes #318
@edenman
Copy link
Author

edenman commented Dec 3, 2018

I just updated to Bugsnag 5.17.1 and the issue is still not fixed. I tried both on a dev build and an ad-hoc distribution, no luck getting the fatalError message on either:

dev build https://app.bugsnag.com/recharge-labs/ios-consumer/errors/5c05587150c99b001aa35ac0
production https://app.bugsnag.com/recharge-labs/ios-consumer/errors/5c055ca9638ce3001a10ce8d

@snmaynard
Copy link
Contributor

"i am a message" is less than 16 characters and is no longer possible to show. If you have a message of more than 16 characters it should work

@edenman
Copy link
Author

edenman commented Dec 3, 2018

Same issue, even with a message longer than 16 characters ("Eric test crash..") https://app.bugsnag.com/recharge-labs/ios-consumer/errors/5c0560f209f364001a4947be

@kattrali
Copy link
Contributor

kattrali commented Dec 3, 2018

@edenman What device? I can try to reproduce and figure out what's going on.

@edenman
Copy link
Author

edenman commented Dec 3, 2018

@kattrali iPhone SE. Swift 4.2, Xcode 10.0 (10A255)

@kattrali
Copy link
Contributor

kattrali commented Dec 3, 2018

It seems to be working for me in a reduced test case using:

  • iPhone SE + Swift 4.2 + Xcode 10.0 (10A255)
  • iPhone SE + Swift 4.2 + Xcode 10.1 (10B61)

In both debug and production builds, and I tried a few different strings to rule out any weirdness.

I updated the test project here (on the kattrali/upgrade-swift-ios-project branch) with the settings I'm using. Can you try it on your machine to see if it also works for you? If it does, perhaps we can see if a particular setting is causing different behavior.

screenshot

@edenman
Copy link
Author

edenman commented Dec 3, 2018

I just ran the test project, although I had to tweak a few things to get it working. Do you see the crashes I forced? I'm guessing they're being uploaded to a project that I don't have access to.

@kattrali
Copy link
Contributor

kattrali commented Dec 3, 2018

By default there's no API key set, so you'd have to add one in the app delegate.

@edenman
Copy link
Author

edenman commented Dec 3, 2018

Derp. Ok, yeah, it's working fine in the sample app. Not sure why it's not working in my actual app.

@sjmueller
Copy link

Can this be re-opened? I have same problem, anytime we invoke fatalError("custom message"), we get a blank message in bugsnag:

Our release builds to the App Store use bitcode, could that be affecting things?

@snmaynard
Copy link
Contributor

@sjmueller "custom message" is less than 16 characters so would be known not to work. Are you actually trying with that string or a longer one?

@sjmueller
Copy link

Correct, we are experiencing this with more than 16 characters. Example: this is the line for Actions.swift:742:

guard routes.last != route.rawValue else { fatalError("Attempted to push the same route twice") }

@tomlongridge tomlongridge reopened this Jun 6, 2019
@sjmueller
Copy link

Any update here? This is leaving us in the dark with our bug reports.

@edenman
Copy link
Author

edenman commented Jul 10, 2019

@sjmueller I wasn't able to figure out what was causing this. I ended up just wrapping all fatalError calls in my own helper method that first adds a breadcrumb and then calls fatalError. It sucks, but /shrug

@snmaynard
Copy link
Contributor

The problem is with different versions of swift or the compiler that the memory location that this information in changes. I believe that we have to reach into specific registers to find it, and it depends on how long the string is as to where the information is. The reality is that swift needs to make an API for this so we can fetch it which I believe @kattrali is thinking about.

@abigailbramble abigailbramble added needs discussion Requires internal analysis/discussion scheduled Work is starting on this feature/bug labels Sep 3, 2019
@sjmueller
Copy link

@snmaynard but that doesn't make too much sense if @edenman is writing the error to a breadcrumb before invoking the fatalError. If he's able to write the information to the breadcrumbs portion of the api, then why can't it be written to the right place automatically?

@snmaynard
Copy link
Contributor

That's because he is doing it before calling fatalError. There is no API from a crash handler to get access to this information. We have to inspect registers to get it - and it's not reliable.

@sjmueller
Copy link

Thanks for the response. Do you have an alternative suggestion to invoking fatalError that would enable bugsnag to capture exceptions in these scenarios? In the case of fatalError it's our code invoking, so we could change to another pattern pretty quickly.

@snmaynard
Copy link
Contributor

@kattrali may have some ideas - but what @edenman has done with wrapping it and adding a breadcrumb doesn't seem terrible to me. I'll see if I can think of other ideas - its annoying we cant get access to the data in the crash handler - I think @kattrali was looking to see if a proposal could be made to swift to change that.

@edenman
Copy link
Author

edenman commented Sep 14, 2019

My workaround works but it breaks grouping. And the issue I opened on that problem is stuck too: #226

@Nemixdre
Copy link

Hi. Is there any news on that? It's been on hold for a while now.
Thanks :)

@tiwoc
Copy link

tiwoc commented Mar 2, 2020

I just stumbled upon the same issue. The message we pass to fatalError is 18 characters long. There's a message included in the crash report, but its not the one we passed to fatalError.

To debug this, I took a look at the crashing binary with Hopper Disassembler and found that the error message that was passed to fatalError ("Test for Log.fatal") is stored at 0x1006c28f0, while the message that the crash handler included into the report ("al data for MSCB ") is located at 0x1006c28d0, which is just 32 (0x20) less than the correct address. There seems to be an error in the offset that the crash handler uses to determine the location of the message.

This is in a simulator build for x86_64. Compiler is swiftlang-1100.0.282.1 clang-1100.0.33.15 from Xcode 11.3.1 (11C505).

@xljones
Copy link
Contributor

xljones commented Mar 10, 2020

Hey @Nemixdre, until we've got something more concrete from Swift, this isn't going to be progressed, unfortunately. As noted above, the locations for this data moves depending on a few factors, without a single and robust method for us to gather the data. We've made a proposal to Swift to make this change and are following any responses closely, we'll report back here as soon as there are any updates! :)

@sjmueller
Copy link

If we were to run Instabug in parallel, would they have the same exact problems? In other words, is this a Bugsnag issue or one that is unsolvable across the entire space?

@tiwoc
Copy link

tiwoc commented Jun 4, 2020

For anyone wondering, the Swift proposal that was mentioned earlier in this thread is likely this one: Cleanup callback for fatal Swift errors

@sjmueller
Copy link

From that thread:

The Swift compiler in development now emits fatal error traps with an artificial inline frame, which will allow crash reporters than understand debug info to show the error directly as part of the backtrace. Future toolchains that include this change should be able to do better.

Does this mean BugSnag has the ability to fix this now? If so, how long will it take?

@xljones
Copy link
Contributor

xljones commented Jun 9, 2020

Hey @sjmueller, it looks like this may now be possible. We'll take a look into this as priorities allow.

@sethfri
Copy link

sethfri commented Dec 15, 2020

Hi there, is there any update on this? This is something Crashlytics already does correctly, and we're disappointed to see Bugsnag doesn't

@johnkiely1
Copy link
Member

Hi @sethfri,

We are working on this at the moment, although we don't have any definite timeframe as yet. We will share any updates here as we have them.

@mattdyoung
Copy link

We've now implemented a new approach to extracting the error messages from Swift's assertionFailure, fatalError, and preconditionFailure functions, which has been released in v6.5.0.

For further details see:
#948

@mattdyoung mattdyoung added bug Confirmed bug released This feature/bug fix has been released and removed needs discussion Requires internal analysis/discussion scheduled Work is starting on this feature/bug labels Jan 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Confirmed bug released This feature/bug fix has been released
Projects
None yet
Development

No branches or pull requests