Bug 368206 - Breakpad client for Mac OS X, patch by dcamp, r=luser+mento

This commit is contained in:
benjamin%smedbergs.us 2007-07-25 01:06:37 +00:00
Родитель 1877501ee2
Коммит be0a828e4f
15 изменённых файлов: 790 добавлений и 139 удалений

Просмотреть файл

@ -60,6 +60,12 @@ ifeq ($(OS_ARCH),WINNT)
$(NULL)
endif
ifeq ($(OS_ARCH),Darwin)
DIRS += airbag/src/common/mac \
airbag/src/client/mac/handler \
$(NULL)
endif
DIRS += client
LOCAL_INCLUDES = -I$(srcdir)/airbag/src

Просмотреть файл

@ -55,8 +55,10 @@ LOCAL_INCLUDES = -I$(srcdir)/../airbag/src
ifeq ($(OS_ARCH),WINNT)
CPPSRCS = crashreporter_win.cpp
LIBS += $(DEPTH)/toolkit/airbag/airbag/src/client/windows/sender/$(LIB_PREFIX)crash_report_sender_s.$(LIB_SUFFIX)
LIBS += $(DEPTH)/toolkit/airbag/airbag/src/common/windows/$(LIB_PREFIX)breakpad_windows_common_s.$(LIB_SUFFIX)
LIBS += \
$(DEPTH)/toolkit/airbag/airbag/src/client/windows/sender/$(LIB_PREFIX)crash_report_sender_s.$(LIB_SUFFIX) \
$(DEPTH)/toolkit/airbag/airbag/src/common/windows/$(LIB_PREFIX)breakpad_windows_common_s.$(LIB_SUFFIX) \
$(NULL)
LOCAL_INCLUDES += -I$(srcdir)
RCINCLUDE = crashreporter.rc
DEFINES += -DUNICODE -D_UNICODE
@ -64,4 +66,28 @@ OS_LIBS += $(call EXPAND_LIBNAME,comctl32 shell32 wininet shlwapi)
MOZ_WINCONSOLE = 0
endif
ifeq ($(OS_ARCH),Darwin)
CMMSRCS += crashreporter_osx.mm
OS_LIBS += -framework Cocoa
LIBS += \
$(DEPTH)/toolkit/airbag/airbag/src/client/mac/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \
$(DEPTH)/toolkit/airbag/airbag/src/common/mac/$(LIB_PREFIX)breakpad_mac_common_s.$(LIB_SUFFIX) \
$(NULL)
LOCAL_INCLUDES += -I$(srcdir) -I$(srcdir)/../airbag/src/common/mac/
endif
include $(topsrcdir)/config/rules.mk
ifeq ($(OS_ARCH),Darwin)
libs::
$(NSINSTALL) -D $(DIST)/bin/crashreporter.app
rsync -a -C --exclude "*.in" $(srcdir)/macbuild/Contents $(DIST)/bin/crashreporter.app
sed -e "s/@APP_NAME@/$(MOZ_APP_DISPLAYNAME)/" $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | \
iconv -f UTF-8 -t UTF-16 > $(DIST)/bin/crashreporter.app/Contents/Resources/English.lproj/InfoPlist.strings
$(NSINSTALL) -D $(DIST)/bin/crashreporter.app/Contents/MacOS
$(NSINSTALL) $(DIST)/bin/crashreporter $(DIST)/bin/crashreporter.app/Contents/MacOS
rm -f $(DIST)/bin/crashreporter
$(NSINSTALL) $(DIST)/bin/crashreporter.ini $(DIST)/bin/crashreporter.app/Contents/MacOS
rm -f $(DIST)/bin/crashreporter.ini
endif

Просмотреть файл

@ -2,6 +2,9 @@
[Strings]
Ok=Ok
Cancel=Cancel
Send=Send
DontSend=Don't Send
Close=Close
CrashReporterTitle=Mozilla Crash Reporter
CrashReporterDescription=Crash reporting blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah
RadioEnable=Enable Crash Reporting

Просмотреть файл

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Toolkit Crash Reporter
*
* The Initial Developer of the Original Code is
* Mozilla Corporation
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dave Camp <dcamp@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef CRASHREPORTER_OSX_H__
#define CRASHREPORTER_OSX_H__
#include <Cocoa/Cocoa.h>
#include "HTTPMultipartUpload.h"
@interface CrashReporterUI : NSObject
{
/* Enabled view */
IBOutlet NSView *enableView;
IBOutlet NSTextField *descriptionLabel;
IBOutlet NSButton *disableReportingButton;
IBOutlet NSButton *dontSendButton;
IBOutlet NSButton *sendButton;
/* Upload progress view */
IBOutlet NSView *uploadingView;
IBOutlet NSTextField *progressLabel;
IBOutlet NSProgressIndicator *progressBar;
IBOutlet NSButton *closeButton;
HTTPMultipartUpload *mPost;
}
- (IBAction)closeClicked:(id)sender;
- (IBAction)sendClicked:(id)sender;
- (void)setView:(NSWindow *)w newView: (NSView *)v animate: (BOOL) animate;
- (void)setupPost;
- (void)uploadThread:(id)post;
- (void)uploadComplete:(id)error;
@end
#endif

Просмотреть файл

@ -0,0 +1,273 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Toolkit Crash Reporter
*
* The Initial Developer of the Original Code is
* Mozilla Corporation
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dave Camp <dcamp@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#import <Cocoa/Cocoa.h>
#include "crashreporter_osx.h"
#include <string>
#include <map>
#include <fstream>
using std::string;
using std::map;
using std::ifstream;
typedef map<string,string> StringTable;
typedef map<string,StringTable> StringTableSections;
static string kExtraDataExtension = ".extra";
static string gMinidumpPath;
static string gExtraDataPath;
static StringTableSections gStrings;
static StringTable gExtraData;
static BOOL gSendFailed;
static NSString *
Str(const char *aName, const char *aSection="Strings")
{
string str = gStrings[aSection][aName];
if (str.empty()) str = "?";
return [NSString stringWithUTF8String:str.c_str()];
}
static bool
ReadStrings(const string &aPath, StringTableSections *aSections)
{
StringTableSections &sections = *aSections;
ifstream f(aPath.c_str());
if (!f.is_open()) return false;
string currentSection;
while (!f.eof()) {
string line;
std::getline(f, line);
if (line[0] == ';') continue;
if (line[0] == '[') {
int close = line.find(']');
if (close >= 0)
currentSection = line.substr(1, close - 1);
continue;
}
int sep = line.find('=');
if (sep >= 0)
sections[currentSection][line.substr(0, sep)] = line.substr(sep + 1);
}
return f.eof();
}
@implementation CrashReporterUI
-(void)awakeFromNib
{
NSWindow *w = [descriptionLabel window];
[w center];
[w setTitle:[[NSBundle mainBundle]
objectForInfoDictionaryKey:@"CFBundleName"]];
[descriptionLabel setStringValue:Str("CrashReporterDescription")];
[disableReportingButton setTitle:Str("RadioDisable")];
if (!gMinidumpPath.empty()) {
[sendButton setTitle:Str("Send")];
[sendButton setKeyEquivalent:@"\r"];
[dontSendButton setTitle:Str("DontSend")];
} else {
[dontSendButton setFrame:[sendButton frame]];
[dontSendButton setTitle:Str("Close")];
[dontSendButton setKeyEquivalent:@"\r"];
[sendButton removeFromSuperview];
}
[closeButton setTitle:Str("Close")];
[w makeKeyAndOrderFront:nil];
}
-(IBAction)closeClicked:(id)sender
{
[NSApp terminate: self];
}
-(IBAction)sendClicked:(id)sender
{
NSWindow *w = [descriptionLabel window];
[progressBar startAnimation: self];
[progressLabel setStringValue:Str("SendTitle")];
[self setupPost];
if (mPost) {
[self setView: w newView: uploadingView animate: YES];
[progressBar startAnimation: self];
[NSThread detachNewThreadSelector:@selector(uploadThread:)
toTarget:self
withObject:nil];
}
}
-(void)setView:(NSWindow *)w newView: (NSView *)v animate: (BOOL)animate
{
NSRect frame = [w frame];
NSRect oldViewFrame = [[w contentView] frame];
NSRect newViewFrame = [uploadingView frame];
frame.origin.y += oldViewFrame.size.height - newViewFrame.size.height;
frame.size.height -= oldViewFrame.size.height - newViewFrame.size.height;
frame.origin.x += oldViewFrame.size.width - newViewFrame.size.width;
frame.size.width -= oldViewFrame.size.width - newViewFrame.size.width;
[w setContentView: v];
[w setFrame: frame display: true animate: animate];
}
-(void)setupPost
{
NSURL *url = [NSURL URLWithString:Str("URL", "Settings")];
if (!url) return;
mPost = [[HTTPMultipartUpload alloc] initWithURL: url];
if (!mPost) return;
NSMutableDictionary *parameters =
[[NSMutableDictionary alloc] initWithCapacity: gExtraData.size()];
StringTable::const_iterator end = gExtraData.end();
for (StringTable::const_iterator i = gExtraData.begin(); i != end; i++) {
NSString *key = [NSString stringWithUTF8String: i->first.c_str()];
NSString *value = [NSString stringWithUTF8String: i->second.c_str()];
[parameters setObject: value forKey: key];
}
[mPost setParameters: parameters];
[mPost addFileAtPath: [NSString stringWithUTF8String: gMinidumpPath.c_str()]
name: @"upload_file_minidump"];
}
-(void)uploadComplete:(id)error
{
[progressBar stopAnimation: self];
NSHTTPURLResponse *response = [mPost response];
NSString *status;
if (error || !response || [response statusCode] != 200) {
status = Str("SubmitFailed");
gSendFailed = YES;
} else
status = Str("SubmitSuccess");
[progressLabel setStringValue: status];
[closeButton setEnabled: true];
[closeButton setKeyEquivalent:@"\r"];
}
-(void)uploadThread:(id)post
{
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
NSError *error = nil;
[mPost send: &error];
[self performSelectorOnMainThread: @selector(uploadComplete:)
withObject: error
waitUntilDone: nil];
[autoreleasepool release];
}
@end
string
GetExtraDataFilename(const string& dumpfile)
{
string filename(dumpfile);
int dot = filename.rfind('.');
if (dot < 0)
return "";
filename.replace(dot, filename.length() - dot, kExtraDataExtension);
return filename;
}
int
main(int argc, char *argv[])
{
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
string iniPath(argv[0]);
iniPath.append(".ini");
if (!ReadStrings(iniPath, &gStrings)) {
printf("couldn't read strings\n");
return -1;
}
if (argc > 1) {
gMinidumpPath = argv[1];
gExtraDataPath = GetExtraDataFilename(gMinidumpPath);
if (!gExtraDataPath.empty()) {
StringTableSections table;
ReadStrings(gExtraDataPath, &table);
gExtraData = table[""];
}
}
gSendFailed = NO;
int ret = NSApplicationMain(argc, (const char **) argv);
string deleteSetting = gStrings["Settings"]["Delete"];
if (!gSendFailed &&
(deleteSetting.empty() || atoi(deleteSetting.c_str()) > 0)) {
remove(gMinidumpPath.c_str());
if (!gExtraDataPath.empty())
remove(gExtraDataPath.c_str());
}
[autoreleasepool release];
return ret;
}

Просмотреть файл

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>crashreporter</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>org.mozilla.crashreporter</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,2 @@
APPL????

Просмотреть файл

@ -0,0 +1,4 @@
/* Localized versions of Info.plist keys */
CFBundleName = "@APP_NAME@ Crash Reporter";
NSHumanReadableCopyright = "Copyright © 2007 Mozilla Foundation";

Просмотреть файл

@ -0,0 +1,23 @@
{
IBClasses = (
{
ACTIONS = {closeClicked = id; sendClicked = id; };
CLASS = CrashReporterUI;
LANGUAGE = ObjC;
OUTLETS = {
closeButton = NSButton;
descriptionLabel = NSTextField;
disableReportingButton = NSButton;
dontSendButton = NSButton;
enableView = NSView;
progressBar = NSProgressIndicator;
progressLabel = NSTextField;
sendButton = NSButton;
uploadingView = NSView;
};
SUPERCLASS = NSObject;
},
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
);
IBVersion = 1;
}

Просмотреть файл

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBDocumentLocation</key>
<string>299 87 356 240 0 0 1440 878 </string>
<key>IBEditorPositions</key>
<dict>
<key>282</key>
<string>445 520 550 163 0 0 1440 878 </string>
<key>29</key>
<string>447 315 137 44 0 0 1440 878 </string>
<key>356</key>
<string>643 213 551 213 0 0 1440 878 </string>
</dict>
<key>IBFramework Version</key>
<string>446.1</string>
<key>IBLockedObjects</key>
<array>
<integer>282</integer>
<integer>356</integer>
</array>
<key>IBOldestOS</key>
<integer>2</integer>
<key>IBOpenObjects</key>
<array>
<integer>21</integer>
<integer>29</integer>
<integer>282</integer>
</array>
<key>IBSystem Version</key>
<string>8P2137</string>
</dict>
</plist>

Двоичные данные
toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib сгенерированный Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,59 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla Breakpad integration
#
# The Initial Developer of the Original Code is
# Ted Mielczarek <ted.mielczarek@gmail.com>
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = handler
LIBRARY_NAME = exception_handler_s
XPI_NAME = crashreporter
LOCAL_INCLUDES = -I$(srcdir)/../../..
CPPSRCS = \
exception_handler.cc \
minidump_generator.cc \
../../minidump_file_writer.cc \
$(NULL)
# need static lib
FORCE_STATIC_LIB = 1
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -0,0 +1,68 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla Breakpad integration
#
# The Initial Developer of the Original Code is
# Ted Mielczarek <ted.mielczarek@gmail.com>
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = breakpad_mac_common
LIBRARY_NAME = breakpad_mac_common_s
LOCAL_INCLUDES = -I$(srcdir)/../..
CPPSRCS = \
file_id.cc \
macho_id.cc \
macho_walker.cc \
string_utilities.cc \
../string_conversion.cc \
$(NULL)
CMSRCS = \
HTTPMultipartUpload.m \
$(NULL)
CSRCS = \
../convert_UTF.c
# need static lib
FORCE_STATIC_LIB = 1
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -37,16 +37,21 @@
#include "nsAirbagExceptionHandler.h"
#ifdef XP_WIN32
#if defined(XP_WIN32)
#ifdef WIN32_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#endif
#include "client/windows/handler/exception_handler.h"
#include <string.h>
#elif defined(XP_MACOSX)
#include "client/mac/handler/exception_handler.h"
#include <string>
#include <Carbon/Carbon.h>
#include <fcntl.h>
#else
#error "Not yet implemented for this platform"
#endif // XP_WIN32
#endif // defined(XP_WIN32)
#ifndef HAVE_CPP_2BYTE_WCHAR_T
#error "This code expects a 2 byte wchar_t. You should --disable-airbag."
@ -59,72 +64,93 @@
#include "nsILocalFile.h"
#include "nsDataHashtable.h"
#ifdef XP_WIN32
#define CRASH_REPORTER_FILENAME "crashreporter.exe"
#define PATH_SEPARATOR "\\"
#else
#define CRASH_REPORTER_FILENAME "crashreporter"
#define PATH_SEPARATOR "/"
#endif
namespace CrashReporter {
using std::wstring;
#ifdef XP_WIN32
typedef wchar_t XP_CHAR;
#define TO_NEW_XP_CHAR(x) ToNewUnicode(x)
#define CONVERT_UTF16_TO_XP_CHAR(x) x
#define XP_STRLEN(x) wcslen(x)
#define CRASH_REPORTER_FILENAME "crashreporter.exe"
#define PATH_SEPARATOR "\\"
#define XP_PATH_SEPARATOR L"\\"
// sort of arbitrary, but MAX_PATH is kinda small
#define XP_PATH_MAX 4096
// "<reporter path>" "<minidump path>"
#define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
#else
typedef char XP_CHAR;
#define TO_NEW_XP_CHAR(x) ToNewUTF8String(x)
#define CONVERT_UTF16_TO_XP_CHAR(x) NS_ConvertUTF16toUTF8(x)
#define XP_STRLEN(x) strlen(x)
#define CRASH_REPORTER_FILENAME "crashreporter"
#define PATH_SEPARATOR "/"
#define XP_PATH_SEPARATOR "/"
#define XP_PATH_MAX PATH_MAX
#endif // XP_WIN32
static const PRUnichar dumpFileExtension[] = {'.', 'd', 'm', 'p',
'\"', '\0'}; // .dmp"
static const PRUnichar extraFileExtension[] = {'.', 'e', 'x', 't',
'r', 'a', '\0'}; // .extra
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
static const PRInt32 kGUIDLength = 36;
// length of a GUID + .dmp" (yes, trailing double quote)
static const PRInt32 kMinidumpFilenameLength =
kGUIDLength + sizeof(dumpFileExtension) / sizeof(dumpFileExtension[0]);
static const XP_CHAR dumpFileExtension[] = {'.', 'd', 'm', 'p',
'\0'}; // .dmp
static const XP_CHAR extraFileExtension[] = {'.', 'e', 'x', 't',
'r', 'a', '\0'}; // .extra
static google_breakpad::ExceptionHandler* gExceptionHandler = nsnull;
// for ease of replacing the dump path when someone
// calls SetMinidumpPath
static nsString* crashReporterCmdLine_withoutDumpPath = nsnull;
// this is set up so we don't have to do heap allocation in the handler
static PRUnichar* crashReporterCmdLine = nsnull;
// points at the end of the previous string
// so we can append the minidump filename
static PRUnichar* crashReporterCmdLineEnd = nsnull;
// space to hold a filename for the API data
static PRUnichar* crashReporterAPIDataFilename = nsnull;
static PRUnichar* crashReporterAPIDataFilenameEnd = nsnull;
static XP_CHAR* crashReporterPath;
// this holds additional data sent via the API
static nsDataHashtable<nsCStringHashKey,nsCString>* crashReporterAPIData_Hash;
static nsCString* crashReporterAPIData = nsnull;
bool MinidumpCallback(const wchar_t *dump_path,
const wchar_t *minidump_id,
void *context,
EXCEPTION_POINTERS *exinfo,
MDRawAssertionInfo *assertion,
static XP_CHAR*
Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
{
int appendLen = XP_STRLEN(toAppend);
if (appendLen >= *size) appendLen = *size - 1;
memcpy(str, toAppend, appendLen * sizeof(XP_CHAR));
str += appendLen;
*str = '\0';
*size -= appendLen;
return str;
}
bool MinidumpCallback(const XP_CHAR* dump_path,
const XP_CHAR* minidump_id,
void* context,
#ifdef XP_WIN32
EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion,
#endif
bool succeeded)
{
// append minidump filename to command line
memcpy(crashReporterCmdLineEnd, minidump_id,
kGUIDLength * sizeof(PRUnichar));
// this will copy the null terminator as well
memcpy(crashReporterCmdLineEnd + kGUIDLength,
dumpFileExtension, sizeof(dumpFileExtension));
XP_CHAR minidumpPath[XP_PATH_MAX];
int size = XP_PATH_MAX;
XP_CHAR* p = Concat(minidumpPath, dump_path, &size);
p = Concat(p, XP_PATH_SEPARATOR, &size);
p = Concat(p, minidump_id, &size);
Concat(p, dumpFileExtension, &size);
// append minidump filename to API data filename
memcpy(crashReporterAPIDataFilenameEnd, minidump_id,
kGUIDLength * sizeof(PRUnichar));
// this will copy the null terminator as well
memcpy(crashReporterAPIDataFilenameEnd + kGUIDLength,
extraFileExtension, sizeof(extraFileExtension));
XP_CHAR extraDataPath[XP_PATH_MAX];
size = XP_PATH_MAX;
p = Concat(extraDataPath, dump_path, &size);
p = Concat(p, XP_PATH_SEPARATOR, &size);
p = Concat(p, minidump_id, &size);
Concat(p, extraFileExtension, &size);
#ifdef XP_WIN32
XP_CHAR cmdLine[CMDLINE_SIZE];
size = CMDLINE_SIZE;
p = Concat(cmdLine, L"\"", &size);
p = Concat(p, crashReporterPath, &size);
p = Concat(p, L"\" \"", &size);
p = Concat(p, minidumpPath, &size);
Concat(p, L"\"", &size);
if (!crashReporterAPIData->IsEmpty()) {
// write out API data
HANDLE hFile = CreateFile(crashReporterAPIDataFilename, GENERIC_WRITE, 0,
HANDLE hFile = CreateFile(extraDataPath, GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL);
if(hFile != INVALID_HANDLE_VALUE) {
@ -144,51 +170,48 @@ bool MinidumpCallback(const wchar_t *dump_path,
si.wShowWindow = SW_SHOWNORMAL;
ZeroMemory(&pi, sizeof(pi));
if (CreateProcess(NULL, (LPWSTR)crashReporterCmdLine, NULL, NULL, FALSE, 0,
if (CreateProcess(NULL, (LPWSTR)cmdLine, NULL, NULL, FALSE, 0,
NULL, NULL, &si, &pi)) {
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
// we're not really in a position to do anything if the CreateProcess fails
TerminateProcess(GetCurrentProcess(), 1);
#endif
return succeeded;
}
#elif defined(XP_UNIX)
if (!crashReporterAPIData->IsEmpty()) {
// write out API data
int fd = open(extraDataPath,
O_WRONLY | O_CREAT | O_TRUNC,
0666);
static nsresult BuildCommandLine(const nsAString &tempPath)
{
nsString crashReporterCmdLine_temp =
*crashReporterCmdLine_withoutDumpPath + NS_LITERAL_STRING(" \"") + tempPath;
PRInt32 cmdLineLength = crashReporterCmdLine_temp.Length();
// allocate extra space for minidump file name
crashReporterCmdLine_temp.SetLength(cmdLineLength + kMinidumpFilenameLength
+ 1);
crashReporterCmdLine = ToNewUnicode(crashReporterCmdLine_temp);
crashReporterCmdLineEnd = crashReporterCmdLine + cmdLineLength;
// build API data filename
if(crashReporterAPIDataFilename != nsnull) {
NS_Free(crashReporterAPIDataFilename);
crashReporterAPIDataFilename = nsnull;
if (fd != -1) {
// not much we can do in case of error
write(fd, crashReporterAPIData->get(), crashReporterAPIData->Length());
close (fd);
}
}
nsString apiDataFilename_temp(tempPath);
PRInt32 filenameLength = apiDataFilename_temp.Length();
apiDataFilename_temp.SetLength(filenameLength + kMinidumpFilenameLength + 1);
crashReporterAPIDataFilename = ToNewUnicode(apiDataFilename_temp);
crashReporterAPIDataFilenameEnd =
crashReporterAPIDataFilename + filenameLength;
pid_t pid = fork();
return NS_OK;
if (pid == -1)
return false;
else if (pid == 0) {
(void) execl(crashReporterPath,
crashReporterPath, minidumpPath, (char*)0);
_exit(1);
}
#endif
return succeeded;
}
static nsresult GetExecutablePath(nsString& exePath)
{
#if !defined(XP_MACOSX)
#ifdef XP_WIN32
// sort of arbitrary, but MAX_PATH is kinda small
exePath.SetLength(4096);
if (!GetModuleFileName(NULL, (LPWSTR)exePath.BeginWriting(), 4096))
exePath.SetLength(XP_PATH_MAX);
if (!GetModuleFileName(NULL, (LPWSTR)exePath.BeginWriting(), XP_PATH_MAX))
return NS_ERROR_FAILURE;
#else
return NS_ERROR_NOT_IMPLEMENTED;
@ -203,6 +226,55 @@ static nsresult GetExecutablePath(nsString& exePath)
exePath.Truncate(lastSlash + 1);
return NS_OK;
#else // !defined(XP_MACOSX)
CFBundleRef appBundle = CFBundleGetMainBundle();
if (!appBundle)
return NS_ERROR_FAILURE;
CFURLRef executableURL = CFBundleCopyExecutableURL(appBundle);
if (!executableURL)
return NS_ERROR_FAILURE;
CFURLRef bundleURL = CFURLCreateCopyDeletingLastPathComponent(NULL,
executableURL);
CFRelease(executableURL);
if (!bundleURL)
return NS_ERROR_FAILURE;
CFURLRef reporterURL = CFURLCreateCopyAppendingPathComponent(
NULL,
bundleURL,
CFSTR("crashreporter.app/Contents/MacOS/"),
false);
CFRelease(bundleURL);
if (!reporterURL)
return NS_ERROR_FAILURE;
FSRef fsRef;
if (!CFURLGetFSRef(reporterURL, &fsRef)) {
CFRelease(reporterURL);
return NS_ERROR_FAILURE;
}
CFRelease(reporterURL);
char path[PATH_MAX + 1];
OSStatus status = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
if (status != noErr)
return NS_ERROR_FAILURE;
int len = strlen(path);
path[len] = '/';
path[len + 1] = '\0';
exePath = NS_ConvertUTF8toUTF16(path);
return NS_OK;
#endif
}
nsresult SetExceptionHandler(nsILocalFile* aXREDirectory)
@ -216,14 +288,11 @@ nsresult SetExceptionHandler(nsILocalFile* aXREDirectory)
// we're off by default until we sort out the
// rest of the infrastructure,
// so it must exist and be set to a non-zero value.
const char *airbagEnv = PR_GetEnv("MOZ_AIRBAG");
const char* airbagEnv = PR_GetEnv("MOZ_AIRBAG");
if (airbagEnv == NULL || atoi(airbagEnv) == 0)
return NS_ERROR_NOT_AVAILABLE;
// allocate our strings
crashReporterCmdLine_withoutDumpPath = new nsString();
NS_ENSURE_TRUE(crashReporterCmdLine_withoutDumpPath, NS_ERROR_OUT_OF_MEMORY);
crashReporterAPIData = new nsCString();
NS_ENSURE_TRUE(crashReporterAPIData, NS_ERROR_OUT_OF_MEMORY);
@ -247,11 +316,7 @@ nsresult SetExceptionHandler(nsILocalFile* aXREDirectory)
NS_NAMED_LITERAL_STRING(crashReporterFilename, CRASH_REPORTER_FILENAME);
// note that we enclose the exe filename in double quotes
crashReporterCmdLine_withoutDumpPath->Assign(NS_LITERAL_STRING("\"") +
exePath +
crashReporterFilename +
NS_LITERAL_STRING("\""));
crashReporterPath = TO_NEW_XP_CHAR(exePath + crashReporterFilename);
// get temp path to use for minidump path
nsString tempPath;
@ -263,18 +328,30 @@ nsresult SetExceptionHandler(nsILocalFile* aXREDirectory)
tempPath.SetLength(pathLen);
GetTempPath(pathLen, (LPWSTR)tempPath.BeginWriting());
#elif defined(XP_MACOSX)
FSRef fsRef;
OSErr err = FSFindFolder(kUserDomain, kTemporaryFolderType,
kCreateFolder, &fsRef);
if (err != noErr)
return NS_ERROR_FAILURE;
tempPath.SetLength(PATH_MAX);
OSStatus status = FSRefMakePath(&fsRef,
(UInt8*)tempPath.BeginWriting(), PATH_MAX);
if (status != noErr)
return NS_ERROR_FAILURE;
#else
//XXX: implement get temp path on other platforms
return NS_ERROR_NOT_IMPLEMENTED;
#endif
rv = BuildCommandLine(tempPath);
NS_ENSURE_SUCCESS(rv, rv);
// finally, set the exception handler
gExceptionHandler = new google_breakpad::ExceptionHandler(
PromiseFlatString(tempPath).get(),
nsnull,
MinidumpCallback,
nsnull,
true);
gExceptionHandler = new google_breakpad::
ExceptionHandler(CONVERT_UTF16_TO_XP_CHAR(tempPath).get(),
nsnull,
MinidumpCallback,
nsnull,
true);
if (!gExceptionHandler)
return NS_ERROR_OUT_OF_MEMORY;
@ -287,25 +364,8 @@ nsresult SetMinidumpPath(const nsAString& aPath)
if (!gExceptionHandler)
return NS_ERROR_NOT_INITIALIZED;
if(crashReporterCmdLine != nsnull) {
NS_Free(crashReporterCmdLine);
crashReporterCmdLine = nsnull;
}
gExceptionHandler->set_dump_path(CONVERT_UTF16_TO_XP_CHAR(aPath).BeginReading());
NS_NAMED_LITERAL_STRING(pathSep, PATH_SEPARATOR);
nsresult rv;
if(!StringEndsWith(aPath, pathSep)) {
rv = BuildCommandLine(aPath + pathSep);
}
else {
rv = BuildCommandLine(aPath);
}
NS_ENSURE_SUCCESS(rv, rv);
gExceptionHandler->set_dump_path(PromiseFlatString(aPath).get());
return NS_OK;
}
@ -313,18 +373,14 @@ nsresult UnsetExceptionHandler()
{
// do this here in the unlikely case that we succeeded in allocating
// our strings but failed to allocate gExceptionHandler.
if (crashReporterCmdLine_withoutDumpPath) {
delete crashReporterCmdLine_withoutDumpPath;
crashReporterCmdLine_withoutDumpPath = nsnull;
}
if (crashReporterAPIData) {
delete crashReporterAPIData;
crashReporterAPIData = nsnull;
}
if (crashReporterAPIData_Hash) {
delete crashReporterAPIData_Hash;
crashReporterAPIData_Hash = nsnull;
}
if (crashReporterPath) {
NS_Free(crashReporterPath);
crashReporterPath = nsnull;
}
if (!gExceptionHandler)
return NS_ERROR_NOT_INITIALIZED;
@ -332,18 +388,6 @@ nsresult UnsetExceptionHandler()
delete gExceptionHandler;
gExceptionHandler = nsnull;
if(crashReporterCmdLine != nsnull) {
NS_Free(crashReporterCmdLine);
crashReporterCmdLine = nsnull;
}
if(crashReporterAPIDataFilename != nsnull) {
NS_Free(crashReporterAPIDataFilename);
crashReporterAPIDataFilename = nsnull;
}
crashReporterCmdLineEnd = nsnull;
return NS_OK;
}

Просмотреть файл

@ -71,9 +71,17 @@ LIBS += \
$(DEPTH)/toolkit/airbag/airbag/src/client/windows/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \
$(DEPTH)/toolkit/airbag/airbag/src/common/windows/$(LIB_PREFIX)breakpad_windows_common_s.$(LIB_SUFFIX)
LIBS += $(call expand_libname shell32)
OS_LIBS += $(call EXPAND_LIBNAME,ole32)
endif
OS_LIBS += $(call EXPAND_LIBNAME,ole32)
ifeq ($(OS_ARCH),Darwin)
OS_LIBS += -framework Cocoa -lcrypto
LIBS += \
$(DEPTH)/toolkit/airbag/airbag/src/client/mac/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \
$(DEPTH)/toolkit/airbag/airbag/src/common/mac/$(LIB_PREFIX)breakpad_mac_common_s.$(LIB_SUFFIX) \
$(NULL)
LOCAL_INCLUDES += -I$(srcdir) -I$(srcdir)/../airbag/src/common/mac/
endif
ifndef MOZ_ENABLE_LIBXUL
check:: $(PROGRAM)