mac_breakpad_starter_guide.md
1 # How To Add Breakpad To Your Mac Client Application 2 3 This document is a step-by-step recipe to get your Mac client app to build with 4 Breakpad. 5 6 ## Preparing a binary build of Breakpad for use in your tree 7 8 You can either check in a binary build of the Breakpad framework & tools or 9 build it as a dependency of your project. The former is recommended, and 10 detailed here, since building dependencies through other projects is 11 problematic(matching up configuration names), and the Breakpad code doesn't 12 change nearly often enough as your application's will. 13 14 ## Building the requisite targets 15 16 All directories are relative to the `src` directory of the Breakpad checkout. 17 18 * Build the 'All' target of `client/mac/Breakpad.xcodeproj` in Release mode. 19 * Execute `cp -R client/mac/build/Release/Breakpad.framework <location in your 20 source tree>` 21 * Inside `tools/mac/dump_syms` directory, build dump\_syms.xcodeproj, and copy 22 tools/mac/dump\_syms/build/Release/dump\_syms to a safe location where it 23 can be run during the build process. 24 25 ## Adding Breakpad.framework 26 27 Inside your application's framework, add the Breakpad.Framework to your 28 project's framework settings. When you select it from the file chooser, it will 29 let you pick a target to add it to; go ahead and check the one that's relevant 30 to your application. 31 32 ## Copy Breakpad into your Application Package 33 34 Copy Breakpad into your Application Package, so it will be around at run time. 35 36 Go to the Targets section of your Xcode Project window. Hit the disclosure 37 triangle to reveal the build phases of your application. Add a new Copy Files 38 phase using the Contextual menu (Control Click). On the General panel of the new 39 'Get Info' of this new phase, set the destination to 'Frameworks' Close the 40 'Info' panel. Use the Contextual Menu to Rename your new phase 'Copy Frameworks' 41 Now drag Breakpad again into this Copy Frameworks phase. Drag it from whereever 42 it appears in the project file tree. 43 44 ## Add a New Run Script build phase 45 46 Near the end of the build phases, add a new Run Script build phase. This will be 47 run before Xcode calls /usr/bin/strip on your project. This is where you'll be 48 calling dump\_sym to output the symbols for each architecture of your build. In 49 my case, the relevant lines read: 50 51 ``` 52 #!/bin/sh 53 $TOOL_DIR=<location of dump_syms from step 3 above> 54 55 "$TOOL_DIR/dump_syms" -a ppc "$PROD" > "$TARGET_NAME ppc.breakpad" 56 57 "$TOOL_DIR/dump_syms" -a i386 "$PROD" > "$TARGET_NAME i386.breakpad" 58 ``` 59 60 ## Adjust the Project Settings 61 62 * Turn on Separate Strip, 63 * Set the Strip Style to Non-Global Symbols. 64 65 ## Write Code! 66 67 You'll need to have an object that acts as the delegate for NSApplication. 68 Inside this object's header, you'll need to add 69 70 1. add an ivar for Breakpad and 71 2. a declaration for the applicationShouldTerminate:(NSApplication`*` sender) 72 message. 73 74 ``` 75 #import <Breakpad/Breakpad.h> 76 77 @interface BreakpadTest : NSObject { 78 . 79 . 80 . 81 BreakpadRef breakpad; 82 . 83 . 84 . 85 } 86 . 87 . 88 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; 89 . 90 . 91 @end 92 ``` 93 94 Inside your object's implementation file, 95 96 1. add the following method InitBreakpad 97 2. modify your awakeFromNib method to look like the one below, 98 3. modify/add your application's delegate method to look like the one below 99 100 ``` 101 static BreakpadRef InitBreakpad(void) { 102 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 103 BreakpadRef breakpad = 0; 104 NSDictionary *plist = [[NSBundle mainBundle] infoDictionary]; 105 if (plist) { 106 // Note: version 1.0.0.4 of the framework changed the type of the argument 107 // from CFDictionaryRef to NSDictionary * on the next line: 108 breakpad = BreakpadCreate(plist); 109 } 110 [pool release]; 111 return breakpad; 112 } 113 114 - (void)awakeFromNib { 115 breakpad = InitBreakpad(); 116 } 117 118 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { 119 BreakpadRelease(breakpad); 120 return NSTerminateNow; 121 } 122 ``` 123 124 ## Configure Breakpad 125 126 Configure Breakpad for your application. 127 128 1. Take a look inside the Breakpad.framework at the Breakpad.h file for the 129 keys, default values, and descriptions to be passed to BreakpadCreate(). 130 2. Add/Edit the Breakpad specific entries in the dictionary passed to 131 BreakpadCreate() -- typically your application's info plist. 132 133 Example from the Notifier Info.plist: 134 `<key>BreakpadProduct</key><string>Google_Notifier_Mac</string> 135 <key>BreakpadProductDisplay</key><string>${PRODUCT_NAME}</string> 136 ` 137 138 ## Build Your Application 139 140 Almost done! 141 142 ## Verify 143 144 Double-check: 145 146 Your app should have in its package contents: 147 myApp.app/Contents/Frameworks/Breakpad.framework. 148 149 The symbol files have reasonable contents (you can look at them with a text 150 editor.) 151 152 Look again at the Copy Frameworks phase of your project. Are you leaking .h 153 files? Select them and delete them. (If you drag a bunch of files into your 154 project, Xcode often wants to copy your .h files into the build, revealing 155 Google secrets. Be vigilant!) 156 157 ## Upload the symbol file 158 159 You'll need to configure your build process to store symbols in a location that 160 is accessible by the minidump processor. There is a tool in tools/mac/symupload 161 that can be used to send the symbol file via HTTP post. 162 163 1. Test 164 165 Configure breakpad to send reports to a URL by adding to your app's Info.plist: 166 167 ``` 168 <key>BreakpadURL</key> 169 <string>upload URL</string> 170 <key>BreakpadReportInterval</key> 171 <string>30</string> 172 ``` 173 174 ## Final Notes 175 176 Breakpad checks whether it is being run under a debugger, and if so, normally 177 does nothing. But, you can force Breakpad to function under a debugger by 178 setting the Unix shell variable BREAKPAD\_IGNORE\_DEBUGGER to a non-zero value. 179 You can bracket the source code in the above Write The Code step with #if DEBUG 180 to completely eliminate it from Debug builds. See 181 //depot/googlemac/GoogleNotifier/main.m for an example. FYI, when your process 182 forks(), exception handlers are reset to the default for child processes. So 183 they must reinitialize Breakpad, otherwise exceptions will be handled by Apple's 184 Crash Reporter.