mac_breakpad_starter_guide.md 5.93 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
# 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.