Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Mobile Client Crash Logs

Mobile Client Crash Logs

Presented at the Futurice Mobile Fortnightly in August 2013.

Ali Rantakari

August 09, 2013
Tweet

More Decks by Ali Rantakari

Other Decks in Programming

Transcript

  1. Why • You might have bugs that you don’t know

    about • Users will not send you “steps to reproduce” — they will leave a zero-star review that says “shit dont work” • If you find out (or the client finds out) that there is a crasher in the released build, you’ll want to fix it ASAP • If you don’t know how to reproduce it, you’ll need the stack trace
  2. Why use a service • Aggregation — a single bug

    can generate thousands of crash log reports, but you only want to look at a single instance and ignore the rest • (Semi)automatic symbolication for iOS (in some services, just upload the .dSYM of each build — in others, even this is not required) • Features for searching, browsing, and filtering crashes based on OS/app version, or other device state variables • Project management tool integrations (bug trackers etc.)
  3. Services • Mobile OS SDKs for iOS, Android, WP… •

    Symbolication • Custom report metadata • Looks like they are trying to be more “enterprise- like” • My limited experience: seemed pretty good • 3rd party SDKs for iOS and Android • Symbolication (client-side) • Custom report metadata • Pretty much meant for server-side (Ruby) apps; mobile apps not a “good fit” • My experience: I wouldn’t recommend for mobile apps • Only iOS SDK (Android coming later) • Symbolication • Custom report metadata • I have no experience
  4. Services • Mobile OS SDKs for iOS, Android, WP… •

    Symbolication (paying customers only; server- or client-side) • Custom report metadata • My limited experience: seemed pretty good • Mobile OS SDKs for iOS, Android, WP… • SaaS or self-hosted (QuincyKit) • Custom report metadata: one string (iOS) or none (WP) • Well-known • I have no experience • SDKs for iOS and Android only • Symbolication (client-side) • Custom report metadata • They act like they’re “Invite only” and make you jump through hoops to start using it • Owned by Twitter nowadays (WTF.) • Well-known • Based on some quick testing, seems very polished
  5. Which one to try? Go ahead Go ahead (if only

    iOS + Android) Go ahead Go ahead (Futu has an account?) Forget it. Go ahead (if only iOS) FREE $ $ SELF HOST FREE BASIC FREE BASIC FREE
  6. Which one to try? Go ahead Go ahead (if only

    iOS + Android) Go ahead Go ahead (Futu has an account?) Forget it. Go ahead (if only iOS) FREE $ $ SELF HOST FREE BASIC FREE BASIC FREE Android
  7. Which one to try? Go ahead Go ahead (if only

    iOS + Android) Go ahead Go ahead (Futu has an account?) Forget it. Go ahead (if only iOS) FREE $ $ SELF HOST FREE BASIC FREE BASIC FREE Windows Phone
  8. Why do it manually • No existing services fulfill requirements,

    whatever they may be • Client doesn’t want to use a service for whatever reason • Be a good consultant and help them make a good decision • “We want it in our own datacenter” is not reason enough to do it manually → HockeyApp (QuincyKit) can be self-hosted
  9. PLCrashReporter Catch Crashes void myprefix_initCrashReporting() { if (PLCrashReporter.sharedReporter.hasPendingCrashReport) myprefix_handlePendingCrashReport(); NSError

    *crashLogEnablingError; if (![PLCrashReporter.sharedReporter enableCrashReporterAndReturnError:&crashLogEnablingError]) { // Deal with error } } Manually
  10. PLCrashReporter Catch Crashes void myprefix_handlePendingCrashReport() { NSError *crashLoadError; NSData *crashData

    = [PLCrashReporter.sharedReporter loadPendingCrashReportDataAndReturnError:&crashLoadError]; if (crashData == nil) { // Handle the error [PLCrashReporter.sharedReporter purgePendingCrashReport]; return; } NSError *reportGenerationError; PLCrashReport *crashReport = [[PLCrashReport alloc] initWithData:crashData error:&reportGenerationError]; if (crashReport == nil) { // Handle the error [PLCrashReporter.sharedReporter purgePendingCrashReport]; return; } NSString *formattedCrashReport = [PLCrashReportTextFormatter stringValueForCrashReport:crashReport withTextFormat:PLCrashReportTextFormatiOS]; [PLCrashReporter.sharedReporter purgePendingCrashReport]; // Do something with the crash report } Manually
  11. Symbolication This is not very useful: Thread 0: 0 libsystem_kernel.dylib

    0x3af45eb4 0x3af45000 + 3764 1 CoreFoundation 0x32d88045 0x32cf1000 + 618565 2 CoreFoundation 0x32d86d5f 0x32cf1000 + 613727 3 CoreFoundation 0x32cf9ebd 0x32cf1000 + 36541 4 CoreFoundation 0x32cf9d49 0x32cf1000 + 36169 5 GraphicsServices 0x368bd2eb 0x368b8000 + 21227 6 UIKit 0x34c0f301 0x34bb8000 + 357121 7 myappname 0x00089ebf 0x7d000 + 52927 Manually
  12. Symbolication Save the .xcarchive of each public release! (put them

    into the VCS) • This preserves the debug symbols for the binary • Dropping an .xcarchive into the Xcode organizer allows the auto-symbolication features to work Manually
  13. Symbolication #!/bin/bash # # Symbolicates a single crash log to

    stdout. # export DEVELOPER_DIR="`xcode-select -print-path`" "$DEVELOPER_DIR/Platforms/iPhoneOS.platform/Developer/Library/ PrivateFrameworks/DTDeviceKit.framework/Versions/A/Resources/ symbolicatecrash" "$@" Manually
  14. Symbolication 11 myappname 0x000ada99 my_reticulateSplines() (MYReticulation.m:18) 11 myappname 0x000ada99 0xab000

    + 10905 Instead of this: you’ll see this: $ symbolicatecrash.sh crashlogfile.crash Manually
  15. Catch Crashes private void RootFrame_NavigationFailed( object sender, NavigationFailedEventArgs e) {

    if (System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Break(); } else { // Do something with `e.Exception` // (use .StackTrace) } } private void Application_UnhandledException( object sender, ApplicationUnhandledExceptionEventArgs e) { if (System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debugger.Break(); } else { // Do something with `e.ExceptionObject` // (use .StackTrace) } } Manually
  16. Symbolication • StackFrame::GetFileName() is marked “security critical” and thus cannot

    be used ☹ • StackFrame::GetFileLineNumber() either throws an exception or always returns zero ☹ • Exception::StackTrace might contain file names and line numbers (?) but it’s just a string Manually var trace = new StackTrace(exception); StackFrame[] frames = trace.GetFrames();