1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Breakpad integration
17 * The Initial Developer of the Original Code is
18 * Ted Mielczarek <ted.mielczarek@gmail.com>
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
23 * Josh Aas <josh@mozilla.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsExceptionHandler.h"
42 #ifdef WIN32_LEAN_AND_MEAN
43 #undef WIN32_LEAN_AND_MEAN
46 #include "client/windows/handler/exception_handler.h"
48 #elif defined(XP_MACOSX)
49 #include "client/mac/handler/exception_handler.h"
51 #include <Carbon/Carbon.h>
53 #include <sys/types.h>
55 #include "mac_utils.h"
56 #elif defined(XP_LINUX)
57 #include "client/linux/handler/exception_handler.h"
59 #include <sys/types.h>
61 #elif defined(XP_SOLARIS)
62 #include "client/solaris/handler/exception_handler.h"
64 #include <sys/types.h>
67 #error "Not yet implemented for this platform"
68 #endif // defined(XP_WIN32)
77 #include "nsILocalFile.h"
78 #include "nsDataHashtable.h"
80 namespace CrashReporter
{
83 typedef wchar_t XP_CHAR
;
84 #define CONVERT_UTF16_TO_XP_CHAR(x) x
85 #define XP_STRLEN(x) wcslen(x)
86 #define CRASH_REPORTER_FILENAME "crashreporter.exe"
87 #define PATH_SEPARATOR "\\"
88 #define XP_PATH_SEPARATOR L"\\"
89 // sort of arbitrary, but MAX_PATH is kinda small
90 #define XP_PATH_MAX 4096
91 // "<reporter path>" "<minidump path>"
92 #define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
93 #ifdef _USE_32BIT_TIME_T
94 #define XP_TTOA(time, buffer, base) ltoa(time, buffer, base)
96 #define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
100 #define CONVERT_UTF16_TO_XP_CHAR(x) NS_ConvertUTF16toUTF8(x)
101 #define XP_STRLEN(x) strlen(x)
102 #define CRASH_REPORTER_FILENAME "crashreporter"
103 #define PATH_SEPARATOR "/"
104 #define XP_PATH_SEPARATOR "/"
105 #define XP_PATH_MAX PATH_MAX
106 #define XP_TTOA(time, buffer, base) sprintf(buffer, "%ld", time)
109 static const XP_CHAR dumpFileExtension
[] = {'.', 'd', 'm', 'p',
111 static const XP_CHAR extraFileExtension
[] = {'.', 'e', 'x', 't',
112 'r', 'a', '\0'}; // .extra
114 static google_breakpad::ExceptionHandler
* gExceptionHandler
= nsnull
;
116 static XP_CHAR
* crashReporterPath
;
118 // if this is false, we don't launch the crash reporter
119 static bool doReport
= true;
121 // if this is true, we pass the exception on to the OS crash reporter
122 static bool showOSCrashReporter
= false;
124 // The time of the last recorded crash, as a time_t value.
125 static time_t lastCrashTime
= 0;
126 // The pathname of a file to store the crash time in
127 static XP_CHAR lastCrashTimeFilename
[XP_PATH_MAX
] = {0};
129 // these are just here for readability
130 static const char kCrashTimeParameter
[] = "CrashTime=";
131 static const int kCrashTimeParameterLen
= sizeof(kCrashTimeParameter
)-1;
133 static const char kTimeSinceLastCrashParameter
[] = "SecondsSinceLastCrash=";
134 static const int kTimeSinceLastCrashParameterLen
=
135 sizeof(kTimeSinceLastCrashParameter
)-1;
137 // this holds additional data sent via the API
138 static nsDataHashtable
<nsCStringHashKey
,nsCString
>* crashReporterAPIData_Hash
;
139 static nsCString
* crashReporterAPIData
= nsnull
;
140 static nsCString
* notesField
= nsnull
;
143 Concat(XP_CHAR
* str
, const XP_CHAR
* toAppend
, int* size
)
145 int appendLen
= XP_STRLEN(toAppend
);
146 if (appendLen
>= *size
) appendLen
= *size
- 1;
148 memcpy(str
, toAppend
, appendLen
* sizeof(XP_CHAR
));
156 bool MinidumpCallback(const XP_CHAR
* dump_path
,
157 const XP_CHAR
* minidump_id
,
160 EXCEPTION_POINTERS
* exinfo
,
161 MDRawAssertionInfo
* assertion
,
165 bool returnValue
= showOSCrashReporter
? false : succeeded
;
167 XP_CHAR minidumpPath
[XP_PATH_MAX
];
168 int size
= XP_PATH_MAX
;
169 XP_CHAR
* p
= Concat(minidumpPath
, dump_path
, &size
);
170 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
171 p
= Concat(p
, minidump_id
, &size
);
172 Concat(p
, dumpFileExtension
, &size
);
174 XP_CHAR extraDataPath
[XP_PATH_MAX
];
176 p
= Concat(extraDataPath
, dump_path
, &size
);
177 p
= Concat(p
, XP_PATH_SEPARATOR
, &size
);
178 p
= Concat(p
, minidump_id
, &size
);
179 Concat(p
, extraFileExtension
, &size
);
181 // calculate time since last crash (if possible), and store
182 // the time of this crash.
183 time_t crashTime
= time(NULL
);
184 time_t timeSinceLastCrash
= 0;
185 // stringified versions of the above
186 char crashTimeString
[32];
187 int crashTimeStringLen
= 0;
188 char timeSinceLastCrashString
[32];
189 int timeSinceLastCrashStringLen
= 0;
191 XP_TTOA(crashTime
, crashTimeString
, 10);
192 crashTimeStringLen
= strlen(crashTimeString
);
193 if (lastCrashTime
!= 0) {
194 timeSinceLastCrash
= crashTime
- lastCrashTime
;
195 XP_TTOA(timeSinceLastCrash
, timeSinceLastCrashString
, 10);
196 timeSinceLastCrashStringLen
= strlen(timeSinceLastCrashString
);
198 // write crash time to file
199 if (lastCrashTimeFilename
[0] != 0) {
200 #if defined(XP_WIN32)
201 HANDLE hFile
= CreateFile(lastCrashTimeFilename
, GENERIC_WRITE
, 0,
202 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
,
204 if(hFile
!= INVALID_HANDLE_VALUE
) {
206 WriteFile(hFile
, crashTimeString
, crashTimeStringLen
, &nBytes
, NULL
);
209 #elif defined(XP_UNIX)
210 int fd
= open(lastCrashTimeFilename
,
211 O_WRONLY
| O_CREAT
| O_TRUNC
,
214 write(fd
, crashTimeString
, crashTimeStringLen
);
220 #if defined(XP_WIN32)
221 XP_CHAR cmdLine
[CMDLINE_SIZE
];
223 p
= Concat(cmdLine
, L
"\"", &size
);
224 p
= Concat(p
, crashReporterPath
, &size
);
225 p
= Concat(p
, L
"\" \"", &size
);
226 p
= Concat(p
, minidumpPath
, &size
);
227 Concat(p
, L
"\"", &size
);
229 if (!crashReporterAPIData
->IsEmpty()) {
230 // write out API data
231 HANDLE hFile
= CreateFile(extraDataPath
, GENERIC_WRITE
, 0,
232 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
,
234 if(hFile
!= INVALID_HANDLE_VALUE
) {
236 WriteFile(hFile
, crashReporterAPIData
->get(),
237 crashReporterAPIData
->Length(), &nBytes
, NULL
);
238 WriteFile(hFile
, kCrashTimeParameter
, kCrashTimeParameterLen
,
240 WriteFile(hFile
, crashTimeString
, crashTimeStringLen
, &nBytes
, NULL
);
241 WriteFile(hFile
, "\n", 1, &nBytes
, NULL
);
242 if (timeSinceLastCrash
!= 0) {
243 WriteFile(hFile
, kTimeSinceLastCrashParameter
,
244 kTimeSinceLastCrashParameterLen
, &nBytes
, NULL
);
245 WriteFile(hFile
, timeSinceLastCrashString
, timeSinceLastCrashStringLen
,
247 WriteFile(hFile
, "\n", 1, &nBytes
, NULL
);
258 PROCESS_INFORMATION pi
;
260 ZeroMemory(&si
, sizeof(si
));
262 si
.dwFlags
= STARTF_USESHOWWINDOW
;
263 si
.wShowWindow
= SW_SHOWNORMAL
;
264 ZeroMemory(&pi
, sizeof(pi
));
266 if (CreateProcess(NULL
, (LPWSTR
)cmdLine
, NULL
, NULL
, FALSE
, 0,
267 NULL
, NULL
, &si
, &pi
)) {
268 CloseHandle( pi
.hProcess
);
269 CloseHandle( pi
.hThread
);
271 // we're not really in a position to do anything if the CreateProcess fails
272 TerminateProcess(GetCurrentProcess(), 1);
273 #elif defined(XP_UNIX)
274 if (!crashReporterAPIData
->IsEmpty()) {
275 // write out API data
276 int fd
= open(extraDataPath
,
277 O_WRONLY
| O_CREAT
| O_TRUNC
,
281 // not much we can do in case of error
282 write(fd
, crashReporterAPIData
->get(), crashReporterAPIData
->Length());
283 write(fd
, kCrashTimeParameter
, kCrashTimeParameterLen
);
284 write(fd
, crashTimeString
, crashTimeStringLen
);
286 if (timeSinceLastCrash
!= 0) {
287 write(fd
, kTimeSinceLastCrashParameter
,kTimeSinceLastCrashParameterLen
);
288 write(fd
, timeSinceLastCrashString
, timeSinceLastCrashStringLen
);
304 // need to clobber this, as libcurl might load NSS,
305 // and we want it to load the system NSS.
306 unsetenv("LD_LIBRARY_PATH");
307 (void) execl(crashReporterPath
,
308 crashReporterPath
, minidumpPath
, (char*)0);
316 nsresult
SetExceptionHandler(nsILocalFile
* aXREDirectory
,
317 const char* aServerURL
)
321 if (gExceptionHandler
)
322 return NS_ERROR_ALREADY_INITIALIZED
;
324 const char *envvar
= PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
325 if (envvar
&& *envvar
)
328 // this environment variable prevents us from launching
329 // the crash reporter client
330 envvar
= PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
331 if (envvar
&& *envvar
)
334 // allocate our strings
335 crashReporterAPIData
= new nsCString();
336 NS_ENSURE_TRUE(crashReporterAPIData
, NS_ERROR_OUT_OF_MEMORY
);
338 crashReporterAPIData_Hash
=
339 new nsDataHashtable
<nsCStringHashKey
,nsCString
>();
340 NS_ENSURE_TRUE(crashReporterAPIData_Hash
, NS_ERROR_OUT_OF_MEMORY
);
342 rv
= crashReporterAPIData_Hash
->Init();
343 NS_ENSURE_SUCCESS(rv
, rv
);
345 notesField
= new nsCString();
346 NS_ENSURE_TRUE(notesField
, NS_ERROR_OUT_OF_MEMORY
);
348 // locate crashreporter executable
349 nsCOMPtr
<nsIFile
> exePath
;
350 rv
= aXREDirectory
->Clone(getter_AddRefs(exePath
));
351 NS_ENSURE_SUCCESS(rv
, rv
);
353 #if defined(XP_MACOSX)
354 exePath
->Append(NS_LITERAL_STRING("crashreporter.app"));
355 exePath
->Append(NS_LITERAL_STRING("Contents"));
356 exePath
->Append(NS_LITERAL_STRING("MacOS"));
359 exePath
->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME
));
362 nsString crashReporterPath_temp
;
363 exePath
->GetPath(crashReporterPath_temp
);
365 crashReporterPath
= ToNewUnicode(crashReporterPath_temp
);
367 nsCString crashReporterPath_temp
;
368 exePath
->GetNativePath(crashReporterPath_temp
);
370 crashReporterPath
= ToNewCString(crashReporterPath_temp
);
373 // get temp path to use for minidump path
374 #if defined(XP_WIN32)
377 // first figure out buffer size
378 int pathLen
= GetTempPath(0, NULL
);
380 return NS_ERROR_FAILURE
;
382 tempPath
.SetLength(pathLen
);
383 GetTempPath(pathLen
, (LPWSTR
)tempPath
.BeginWriting());
384 #elif defined(XP_MACOSX)
387 OSErr err
= FSFindFolder(kUserDomain
, kTemporaryFolderType
,
388 kCreateFolder
, &fsRef
);
390 return NS_ERROR_FAILURE
;
393 OSStatus status
= FSRefMakePath(&fsRef
, (UInt8
*)path
, PATH_MAX
);
395 return NS_ERROR_FAILURE
;
399 #elif defined(XP_UNIX)
400 // we assume it's always /tmp on unix systems
401 nsCString tempPath
= NS_LITERAL_CSTRING("/tmp/");
403 #error "Implement this for your platform"
406 // now set the exception handler
407 gExceptionHandler
= new google_breakpad::
408 ExceptionHandler(tempPath
.get(),
412 #if defined(XP_WIN32)
413 google_breakpad::ExceptionHandler::HANDLER_ALL
);
418 if (!gExceptionHandler
)
419 return NS_ERROR_OUT_OF_MEMORY
;
421 // store server URL with the API data
423 AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
424 nsDependentCString(aServerURL
));
426 // store application start time
428 XP_TTOA(time(NULL
), timeString
, 10);
429 AnnotateCrashReport(NS_LITERAL_CSTRING("StartupTime"),
430 nsDependentCString(timeString
));
432 #if defined(XP_MACOSX)
433 // On OS X, many testers like to see the OS crash reporting dialog
434 // since it offers immediate stack traces. We allow them to set
435 // a default to pass exceptions to the OS handler.
436 showOSCrashReporter
= PassToOSCrashReporter();
442 nsresult
SetMinidumpPath(const nsAString
& aPath
)
444 if (!gExceptionHandler
)
445 return NS_ERROR_NOT_INITIALIZED
;
447 gExceptionHandler
->set_dump_path(CONVERT_UTF16_TO_XP_CHAR(aPath
).BeginReading());
453 WriteDataToFile(nsIFile
* aFile
, const nsACString
& data
)
455 nsCOMPtr
<nsILocalFile
> localFile
= do_QueryInterface(aFile
);
456 NS_ENSURE_TRUE(localFile
, NS_ERROR_FAILURE
);
459 nsresult rv
= localFile
->OpenNSPRFileDesc(PR_WRONLY
| PR_CREATE_FILE
, 00600,
461 NS_ENSURE_SUCCESS(rv
, rv
);
464 if (PR_Write(fd
, data
.Data(), data
.Length()) == -1) {
465 rv
= NS_ERROR_FAILURE
;
472 GetFileContents(nsIFile
* aFile
, nsACString
& data
)
474 nsCOMPtr
<nsILocalFile
> localFile
= do_QueryInterface(aFile
);
475 NS_ENSURE_TRUE(localFile
, NS_ERROR_FAILURE
);
478 nsresult rv
= localFile
->OpenNSPRFileDesc(PR_RDONLY
, 0, &fd
);
479 NS_ENSURE_SUCCESS(rv
, rv
);
482 PRInt32 filesize
= PR_Available(fd
);
484 rv
= NS_ERROR_FILE_NOT_FOUND
;
487 data
.SetLength(filesize
);
488 if (PR_Read(fd
, data
.BeginWriting(), filesize
) == -1) {
489 rv
= NS_ERROR_FAILURE
;
496 // Function typedef for initializing a piece of data that we
497 // don't already have.
498 typedef nsresult (*InitDataFunc
)(nsACString
&);
500 // Attempt to read aFile's contents into aContents, if aFile
501 // does not exist, create it and initialize its contents
502 // by calling aInitFunc for the data.
504 GetOrInit(nsIFile
* aDir
, const nsACString
& filename
,
505 nsACString
& aContents
, InitDataFunc aInitFunc
)
509 nsCOMPtr
<nsIFile
> dataFile
;
510 nsresult rv
= aDir
->Clone(getter_AddRefs(dataFile
));
511 NS_ENSURE_SUCCESS(rv
, rv
);
513 rv
= dataFile
->AppendNative(filename
);
514 NS_ENSURE_SUCCESS(rv
, rv
);
516 rv
= dataFile
->Exists(&exists
);
517 NS_ENSURE_SUCCESS(rv
, rv
);
521 // get the initial value and write it to the file
522 rv
= aInitFunc(aContents
);
523 NS_ENSURE_SUCCESS(rv
, rv
);
524 rv
= WriteDataToFile(dataFile
, aContents
);
527 // didn't pass in an init func
528 rv
= NS_ERROR_FAILURE
;
532 // just get the file's contents
533 rv
= GetFileContents(dataFile
, aContents
);
539 // Generate a unique user ID. We're using a GUID form,
540 // but not jumping through hoops to make it cryptographically
541 // secure. We just want it to distinguish unique users.
543 InitUserID(nsACString
& aUserID
)
547 // copied shamelessly from nsUUIDGenerator.cpp
549 HRESULT hr
= CoCreateGuid((GUID
*)&id
);
551 return NS_ERROR_FAILURE
;
552 #elif defined(XP_MACOSX)
553 CFUUIDRef uuid
= CFUUIDCreate(kCFAllocatorDefault
);
555 return NS_ERROR_FAILURE
;
557 CFUUIDBytes bytes
= CFUUIDGetUUIDBytes(uuid
);
558 memcpy(&id
, &bytes
, sizeof(nsID
));
562 // UNIX or some such thing
566 *reinterpret_cast<PRUint32
*>(&id
.m3
[0]) = random();
567 *reinterpret_cast<PRUint32
*>(&id
.m3
[4]) = random();
570 char* id_cstr
= id
.ToString();
571 NS_ENSURE_TRUE(id_cstr
, NS_ERROR_OUT_OF_MEMORY
);
572 nsDependentCString
id_str(id_cstr
);
573 aUserID
= Substring(id_str
, 1, id_str
.Length()-2);
579 // Init the "install time" data. We're taking an easy way out here
580 // and just setting this to "the time when this version was first run".
582 InitInstallTime(nsACString
& aInstallTime
)
584 time_t t
= time(NULL
);
586 sprintf(buf
, "%ld", t
);
592 // Annotate the crash report with a Unique User ID and time
593 // since install. Also do some prep work for recording
594 // time since last crash, which must be calculated at
596 // If any piece of data doesn't exist, initialize it first.
597 nsresult
SetupExtraData(nsILocalFile
* aAppDataDirectory
,
598 const nsACString
& aBuildID
)
600 nsCOMPtr
<nsIFile
> dataDirectory
;
601 nsresult rv
= aAppDataDirectory
->Clone(getter_AddRefs(dataDirectory
));
602 NS_ENSURE_SUCCESS(rv
, rv
);
604 rv
= dataDirectory
->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
605 NS_ENSURE_SUCCESS(rv
, rv
);
608 rv
= dataDirectory
->Exists(&exists
);
609 NS_ENSURE_SUCCESS(rv
, rv
);
612 rv
= dataDirectory
->Create(nsIFile::DIRECTORY_TYPE
, 0700);
613 NS_ENSURE_SUCCESS(rv
, rv
);
616 #if defined(XP_WIN32)
617 nsAutoString
dataDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_DATA_DIRECTORY="));
619 nsAutoString dataDirectoryPath
;
620 rv
= dataDirectory
->GetPath(dataDirectoryPath
);
621 NS_ENSURE_SUCCESS(rv
, rv
);
623 dataDirEnv
.Append(dataDirectoryPath
);
625 _wputenv(dataDirEnv
.get());
627 // Save this path in the environment for the crash reporter application.
628 nsCAutoString
dataDirEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY=");
630 nsCAutoString dataDirectoryPath
;
631 rv
= dataDirectory
->GetNativePath(dataDirectoryPath
);
632 NS_ENSURE_SUCCESS(rv
, rv
);
634 dataDirEnv
.Append(dataDirectoryPath
);
636 char* env
= ToNewCString(dataDirEnv
);
637 NS_ENSURE_TRUE(env
, NS_ERROR_OUT_OF_MEMORY
);
643 if(NS_SUCCEEDED(GetOrInit(dataDirectory
, NS_LITERAL_CSTRING("UserID"),
645 AnnotateCrashReport(NS_LITERAL_CSTRING("UserID"), data
);
647 if(NS_SUCCEEDED(GetOrInit(dataDirectory
,
648 NS_LITERAL_CSTRING("InstallTime") + aBuildID
,
649 data
, InitInstallTime
)))
650 AnnotateCrashReport(NS_LITERAL_CSTRING("InstallTime"), data
);
652 // this is a little different, since we can't init it with anything,
653 // since it's stored at crash time, and we can't annotate the
654 // crash report with the stored value, since we really want
655 // (now - LastCrash), so we just get a value if it exists,
656 // and store it in a time_t value.
657 if(NS_SUCCEEDED(GetOrInit(dataDirectory
, NS_LITERAL_CSTRING("LastCrash"),
659 lastCrashTime
= (time_t)atol(data
.get());
662 // not really the best place to init this, but I have the path I need here
663 nsCOMPtr
<nsIFile
> lastCrashFile
;
664 rv
= dataDirectory
->Clone(getter_AddRefs(lastCrashFile
));
665 NS_ENSURE_SUCCESS(rv
, rv
);
667 rv
= lastCrashFile
->AppendNative(NS_LITERAL_CSTRING("LastCrash"));
668 NS_ENSURE_SUCCESS(rv
, rv
);
669 memset(lastCrashTimeFilename
, 0, sizeof(lastCrashTimeFilename
));
671 #if defined(XP_WIN32)
672 nsAutoString filename
;
673 rv
= lastCrashFile
->GetPath(filename
);
674 NS_ENSURE_SUCCESS(rv
, rv
);
676 if (filename
.Length() < XP_PATH_MAX
)
677 wcsncpy(lastCrashTimeFilename
, filename
.get(), filename
.Length());
679 nsCAutoString filename
;
680 rv
= lastCrashFile
->GetNativePath(filename
);
681 NS_ENSURE_SUCCESS(rv
, rv
);
683 if (filename
.Length() < XP_PATH_MAX
)
684 strncpy(lastCrashTimeFilename
, filename
.get(), filename
.Length());
690 nsresult
UnsetExceptionHandler()
692 // do this here in the unlikely case that we succeeded in allocating
693 // our strings but failed to allocate gExceptionHandler.
694 if (crashReporterAPIData_Hash
) {
695 delete crashReporterAPIData_Hash
;
696 crashReporterAPIData_Hash
= nsnull
;
699 if (crashReporterAPIData
) {
700 delete crashReporterAPIData
;
701 crashReporterAPIData
= nsnull
;
709 if (crashReporterPath
) {
710 NS_Free(crashReporterPath
);
711 crashReporterPath
= nsnull
;
714 if (!gExceptionHandler
)
715 return NS_ERROR_NOT_INITIALIZED
;
717 delete gExceptionHandler
;
718 gExceptionHandler
= nsnull
;
723 static void ReplaceChar(nsCString
& str
, const nsACString
& character
,
724 const nsACString
& replacement
)
726 nsCString::const_iterator start
, end
;
728 str
.BeginReading(start
);
731 while (FindInReadable(character
, start
, end
)) {
732 PRInt32 pos
= end
.size_backward();
733 str
.Replace(pos
- 1, 1, replacement
);
735 str
.BeginReading(start
);
736 start
.advance(pos
+ replacement
.Length() - 1);
741 static PRBool
DoFindInReadable(const nsACString
& str
, const nsACString
& value
)
743 nsACString::const_iterator start
, end
;
744 str
.BeginReading(start
);
747 return FindInReadable(value
, start
, end
);
750 static PLDHashOperator PR_CALLBACK
EnumerateEntries(const nsACString
& key
,
754 crashReporterAPIData
->Append(key
+ NS_LITERAL_CSTRING("=") + entry
+
755 NS_LITERAL_CSTRING("\n"));
756 return PL_DHASH_NEXT
;
759 nsresult
AnnotateCrashReport(const nsACString
& key
, const nsACString
& data
)
761 if (!gExceptionHandler
)
762 return NS_ERROR_NOT_INITIALIZED
;
764 if (DoFindInReadable(key
, NS_LITERAL_CSTRING("=")) ||
765 DoFindInReadable(key
, NS_LITERAL_CSTRING("\n")))
766 return NS_ERROR_INVALID_ARG
;
768 if (DoFindInReadable(data
, NS_LITERAL_CSTRING("\0")))
769 return NS_ERROR_INVALID_ARG
;
771 nsCString
escapedData(data
);
773 // escape backslashes
774 ReplaceChar(escapedData
, NS_LITERAL_CSTRING("\\"),
775 NS_LITERAL_CSTRING("\\\\"));
777 ReplaceChar(escapedData
, NS_LITERAL_CSTRING("\n"),
778 NS_LITERAL_CSTRING("\\n"));
780 nsresult rv
= crashReporterAPIData_Hash
->Put(key
, escapedData
);
781 NS_ENSURE_SUCCESS(rv
, rv
);
783 // now rebuild the file contents
784 crashReporterAPIData
->Truncate(0);
785 crashReporterAPIData_Hash
->EnumerateRead(EnumerateEntries
,
786 crashReporterAPIData
);
791 nsresult
AppendAppNotesToCrashReport(const nsACString
& data
)
793 if (!gExceptionHandler
)
794 return NS_ERROR_NOT_INITIALIZED
;
796 if (DoFindInReadable(data
, NS_LITERAL_CSTRING("\0")))
797 return NS_ERROR_INVALID_ARG
;
799 notesField
->Append(data
);
800 return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField
);
803 // Returns true if found, false if not found.
804 bool GetAnnotation(const nsACString
& key
, nsACString
& data
)
806 if (!gExceptionHandler
)
807 return NS_ERROR_NOT_INITIALIZED
;
810 if (!crashReporterAPIData_Hash
->Get(key
, &entry
))
818 SetRestartArgs(int argc
, char** argv
)
820 if (!gExceptionHandler
)
824 nsCAutoString envVar
;
826 for (i
= 0; i
< argc
; i
++) {
827 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
830 #if defined(XP_UNIX) && !defined(XP_MACOSX)
831 // we'd like to run the script around the binary
832 // instead of the binary itself, so remove the -bin
833 // if it exists on the first argument
836 (arg_len
= strlen(argv
[i
])) > 4 &&
837 strcmp(argv
[i
] + arg_len
- 4, "-bin") == 0) {
838 envVar
.Append(argv
[i
], arg_len
- 4);
845 // PR_SetEnv() wants the string to be available for the lifetime
846 // of the app, so dup it here
847 env
= ToNewCString(envVar
);
849 return NS_ERROR_OUT_OF_MEMORY
;
854 // make sure the arg list is terminated
855 envVar
= "MOZ_CRASHREPORTER_RESTART_ARG_";
859 // PR_SetEnv() wants the string to be available for the lifetime
860 // of the app, so dup it here
861 env
= ToNewCString(envVar
);
863 return NS_ERROR_OUT_OF_MEMORY
;
867 // make sure we save the info in XUL_APP_FILE for the reporter
868 const char *appfile
= PR_GetEnv("XUL_APP_FILE");
869 if (appfile
&& *appfile
) {
870 envVar
= "MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE=";
872 env
= ToNewCString(envVar
);
880 nsresult
WriteMinidumpForException(EXCEPTION_POINTERS
* aExceptionInfo
)
882 if (!gExceptionHandler
)
883 return NS_ERROR_NOT_INITIALIZED
;
885 return gExceptionHandler
->WriteMinidumpForException(aExceptionInfo
) ? NS_OK
: NS_ERROR_FAILURE
;
890 nsresult
AppendObjCExceptionInfoToAppNotes(void *inException
)
892 nsCAutoString excString
;
893 GetObjCExceptionInfo(inException
, excString
);
894 AppendAppNotesToCrashReport(excString
);
899 } // namespace CrashReporter