Permalink
0ff15b4 Sep 22, 2015
1 contributor

Users who have contributed to this file

185 lines (138 sloc) 5.93 KB

How To Add Breakpad To Your Mac Client Application

This document is a step-by-step recipe to get your Mac client app to build with Breakpad.

Preparing a binary build of Breakpad for use in your tree

You can either check in a binary build of the Breakpad framework & tools or build it as a dependency of your project. The former is recommended, and detailed here, since building dependencies through other projects is problematic(matching up configuration names), and the Breakpad code doesn't change nearly often enough as your application's will.

Building the requisite targets

All directories are relative to the src directory of the Breakpad checkout.

  • Build the 'All' target of client/mac/Breakpad.xcodeproj in Release mode.
  • Execute cp -R client/mac/build/Release/Breakpad.framework <location in your source tree>
  • Inside tools/mac/dump_syms directory, build dump_syms.xcodeproj, and copy tools/mac/dump_syms/build/Release/dump_syms to a safe location where it can be run during the build process.

Adding Breakpad.framework

Inside your application's framework, add the Breakpad.Framework to your project's framework settings. When you select it from the file chooser, it will let you pick a target to add it to; go ahead and check the one that's relevant to your application.

Copy Breakpad into your Application Package

Copy Breakpad into your Application Package, so it will be around at run time.

Go to the Targets section of your Xcode Project window. Hit the disclosure triangle to reveal the build phases of your application. Add a new Copy Files phase using the Contextual menu (Control Click). On the General panel of the new 'Get Info' of this new phase, set the destination to 'Frameworks' Close the 'Info' panel. Use the Contextual Menu to Rename your new phase 'Copy Frameworks' Now drag Breakpad again into this Copy Frameworks phase. Drag it from whereever it appears in the project file tree.

Add a New Run Script build phase

Near the end of the build phases, add a new Run Script build phase. This will be run before Xcode calls /usr/bin/strip on your project. This is where you'll be calling dump_sym to output the symbols for each architecture of your build. In my case, the relevant lines read:

#!/bin/sh
$TOOL_DIR=<location of dump_syms from step 3 above>

"$TOOL_DIR/dump_syms" -a ppc "$PROD" > "$TARGET_NAME ppc.breakpad"

"$TOOL_DIR/dump_syms" -a i386 "$PROD" > "$TARGET_NAME i386.breakpad"

Adjust the Project Settings

  • Turn on Separate Strip,
  • Set the Strip Style to Non-Global Symbols.

Write Code!

You'll need to have an object that acts as the delegate for NSApplication. Inside this object's header, you'll need to add

  1. add an ivar for Breakpad and
  2. a declaration for the applicationShouldTerminate:(NSApplication* sender) message.
#import <Breakpad/Breakpad.h>

@interface BreakpadTest : NSObject {
   .
   .
   .
   BreakpadRef breakpad;
   .
   .
   .
}
.
.
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
.
.
@end

Inside your object's implementation file,

  1. add the following method InitBreakpad
  2. modify your awakeFromNib method to look like the one below,
  3. modify/add your application's delegate method to look like the one below
static BreakpadRef InitBreakpad(void) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  BreakpadRef breakpad = 0;
  NSDictionary *plist = [[NSBundle mainBundle] infoDictionary];
  if (plist) {
    // Note: version 1.0.0.4 of the framework changed the type of the argument 
    // from CFDictionaryRef to NSDictionary * on the next line:
    breakpad = BreakpadCreate(plist);
  }
  [pool release];
  return breakpad;
}

- (void)awakeFromNib {
  breakpad = InitBreakpad();
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
  BreakpadRelease(breakpad);
  return NSTerminateNow;
}

Configure Breakpad

Configure Breakpad for your application.

  1. Take a look inside the Breakpad.framework at the Breakpad.h file for the keys, default values, and descriptions to be passed to BreakpadCreate().
  2. Add/Edit the Breakpad specific entries in the dictionary passed to BreakpadCreate() -- typically your application's info plist.

Example from the Notifier Info.plist: <key>BreakpadProduct</key><string>Google_Notifier_Mac</string> <key>BreakpadProductDisplay</key><string>${PRODUCT_NAME}</string>

Build Your Application

Almost done!

Verify

Double-check:

Your app should have in its package contents: myApp.app/Contents/Frameworks/Breakpad.framework.

The symbol files have reasonable contents (you can look at them with a text editor.)

Look again at the Copy Frameworks phase of your project. Are you leaking .h files? Select them and delete them. (If you drag a bunch of files into your project, Xcode often wants to copy your .h files into the build, revealing Google secrets. Be vigilant!)

Upload the symbol file

You'll need to configure your build process to store symbols in a location that is accessible by the minidump processor. There is a tool in tools/mac/symupload that can be used to send the symbol file via HTTP post.

  1. Test

Configure breakpad to send reports to a URL by adding to your app's Info.plist:

<key>BreakpadURL</key>
<string>upload URL</string>
<key>BreakpadReportInterval</key>
<string>30</string>

Final Notes

Breakpad checks whether it is being run under a debugger, and if so, normally does nothing. But, you can force Breakpad to function under a debugger by setting the Unix shell variable BREAKPAD_IGNORE_DEBUGGER to a non-zero value. You can bracket the source code in the above Write The Code step with #if DEBUG to completely eliminate it from Debug builds. See //depot/googlemac/GoogleNotifier/main.m for an example. FYI, when your process forks(), exception handlers are reset to the default for child processes. So they must reinitialize Breakpad, otherwise exceptions will be handled by Apple's Crash Reporter.