Bug 452317 - FeedConverter.js: QueryInterface should throw NS_ERROR_NO_INTERFACE...
[wine-gecko.git] / toolkit / crashreporter / nsExceptionHandler.cpp
blobbe4c5b91b1585eff6cce40a98df4c4510d983b48
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
13 * License.
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.
22 * Contributor(s):
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"
41 #if defined(XP_WIN32)
42 #ifdef WIN32_LEAN_AND_MEAN
43 #undef WIN32_LEAN_AND_MEAN
44 #endif
46 #include "client/windows/handler/exception_handler.h"
47 #include <string.h>
48 #elif defined(XP_MACOSX)
49 #include "client/mac/handler/exception_handler.h"
50 #include <string>
51 #include <Carbon/Carbon.h>
52 #include <fcntl.h>
53 #include <sys/types.h>
54 #include <unistd.h>
55 #include "mac_utils.h"
56 #elif defined(XP_LINUX)
57 #include "client/linux/handler/exception_handler.h"
58 #include <fcntl.h>
59 #include <sys/types.h>
60 #include <unistd.h>
61 #elif defined(XP_SOLARIS)
62 #include "client/solaris/handler/exception_handler.h"
63 #include <fcntl.h>
64 #include <sys/types.h>
65 #include <unistd.h>
66 #else
67 #error "Not yet implemented for this platform"
68 #endif // defined(XP_WIN32)
70 #include <stdlib.h>
71 #include <time.h>
72 #include <prenv.h>
73 #include <prio.h>
74 #include <prmem.h>
75 #include "nsDebug.h"
76 #include "nsCRT.h"
77 #include "nsILocalFile.h"
78 #include "nsDataHashtable.h"
80 namespace CrashReporter {
82 #ifdef XP_WIN32
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)
95 #else
96 #define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
97 #endif
98 #else
99 typedef char XP_CHAR;
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)
107 #endif // XP_WIN32
109 static const XP_CHAR dumpFileExtension[] = {'.', 'd', 'm', 'p',
110 '\0'}; // .dmp
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;
142 static XP_CHAR*
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));
149 str += appendLen;
150 *str = '\0';
151 *size -= appendLen;
153 return str;
156 bool MinidumpCallback(const XP_CHAR* dump_path,
157 const XP_CHAR* minidump_id,
158 void* context,
159 #ifdef XP_WIN32
160 EXCEPTION_POINTERS* exinfo,
161 MDRawAssertionInfo* assertion,
162 #endif
163 bool succeeded)
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];
175 size = 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,
203 NULL);
204 if(hFile != INVALID_HANDLE_VALUE) {
205 DWORD nBytes;
206 WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, NULL);
207 CloseHandle(hFile);
209 #elif defined(XP_UNIX)
210 int fd = open(lastCrashTimeFilename,
211 O_WRONLY | O_CREAT | O_TRUNC,
212 0600);
213 if (fd != -1) {
214 write(fd, crashTimeString, crashTimeStringLen);
215 close(fd);
217 #endif
220 #if defined(XP_WIN32)
221 XP_CHAR cmdLine[CMDLINE_SIZE];
222 size = 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,
233 NULL);
234 if(hFile != INVALID_HANDLE_VALUE) {
235 DWORD nBytes;
236 WriteFile(hFile, crashReporterAPIData->get(),
237 crashReporterAPIData->Length(), &nBytes, NULL);
238 WriteFile(hFile, kCrashTimeParameter, kCrashTimeParameterLen,
239 &nBytes, NULL);
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,
246 &nBytes, NULL);
247 WriteFile(hFile, "\n", 1, &nBytes, NULL);
249 CloseHandle(hFile);
253 if (!doReport) {
254 return returnValue;
257 STARTUPINFO si;
258 PROCESS_INFORMATION pi;
260 ZeroMemory(&si, sizeof(si));
261 si.cb = 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,
278 0666);
280 if (fd != -1) {
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);
285 write(fd, "\n", 1);
286 if (timeSinceLastCrash != 0) {
287 write(fd, kTimeSinceLastCrashParameter,kTimeSinceLastCrashParameterLen);
288 write(fd, timeSinceLastCrashString, timeSinceLastCrashStringLen);
289 write(fd, "\n", 1);
291 close (fd);
295 if (!doReport) {
296 return returnValue;
299 pid_t pid = fork();
301 if (pid == -1)
302 return false;
303 else if (pid == 0) {
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);
309 _exit(1);
311 #endif
313 return returnValue;
316 nsresult SetExceptionHandler(nsILocalFile* aXREDirectory,
317 const char* aServerURL)
319 nsresult rv;
321 if (gExceptionHandler)
322 return NS_ERROR_ALREADY_INITIALIZED;
324 const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
325 if (envvar && *envvar)
326 return NS_OK;
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)
332 doReport = false;
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"));
357 #endif
359 exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
361 #ifdef XP_WIN32
362 nsString crashReporterPath_temp;
363 exePath->GetPath(crashReporterPath_temp);
365 crashReporterPath = ToNewUnicode(crashReporterPath_temp);
366 #else
367 nsCString crashReporterPath_temp;
368 exePath->GetNativePath(crashReporterPath_temp);
370 crashReporterPath = ToNewCString(crashReporterPath_temp);
371 #endif
373 // get temp path to use for minidump path
374 #if defined(XP_WIN32)
375 nsString tempPath;
377 // first figure out buffer size
378 int pathLen = GetTempPath(0, NULL);
379 if (pathLen == 0)
380 return NS_ERROR_FAILURE;
382 tempPath.SetLength(pathLen);
383 GetTempPath(pathLen, (LPWSTR)tempPath.BeginWriting());
384 #elif defined(XP_MACOSX)
385 nsCString tempPath;
386 FSRef fsRef;
387 OSErr err = FSFindFolder(kUserDomain, kTemporaryFolderType,
388 kCreateFolder, &fsRef);
389 if (err != noErr)
390 return NS_ERROR_FAILURE;
392 char path[PATH_MAX];
393 OSStatus status = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
394 if (status != noErr)
395 return NS_ERROR_FAILURE;
397 tempPath = path;
399 #elif defined(XP_UNIX)
400 // we assume it's always /tmp on unix systems
401 nsCString tempPath = NS_LITERAL_CSTRING("/tmp/");
402 #else
403 #error "Implement this for your platform"
404 #endif
406 // now set the exception handler
407 gExceptionHandler = new google_breakpad::
408 ExceptionHandler(tempPath.get(),
409 nsnull,
410 MinidumpCallback,
411 nsnull,
412 #if defined(XP_WIN32)
413 google_breakpad::ExceptionHandler::HANDLER_ALL);
414 #else
415 true);
416 #endif
418 if (!gExceptionHandler)
419 return NS_ERROR_OUT_OF_MEMORY;
421 // store server URL with the API data
422 if (aServerURL)
423 AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
424 nsDependentCString(aServerURL));
426 // store application start time
427 char timeString[32];
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();
437 #endif
439 return NS_OK;
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());
449 return NS_OK;
452 static nsresult
453 WriteDataToFile(nsIFile* aFile, const nsACString& data)
455 nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(aFile);
456 NS_ENSURE_TRUE(localFile, NS_ERROR_FAILURE);
458 PRFileDesc* fd;
459 nsresult rv = localFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 00600,
460 &fd);
461 NS_ENSURE_SUCCESS(rv, rv);
463 rv = NS_OK;
464 if (PR_Write(fd, data.Data(), data.Length()) == -1) {
465 rv = NS_ERROR_FAILURE;
467 PR_Close(fd);
468 return rv;
471 static nsresult
472 GetFileContents(nsIFile* aFile, nsACString& data)
474 nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(aFile);
475 NS_ENSURE_TRUE(localFile, NS_ERROR_FAILURE);
477 PRFileDesc* fd;
478 nsresult rv = localFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
479 NS_ENSURE_SUCCESS(rv, rv);
481 rv = NS_OK;
482 PRInt32 filesize = PR_Available(fd);
483 if (filesize <= 0) {
484 rv = NS_ERROR_FILE_NOT_FOUND;
486 else {
487 data.SetLength(filesize);
488 if (PR_Read(fd, data.BeginWriting(), filesize) == -1) {
489 rv = NS_ERROR_FAILURE;
492 PR_Close(fd);
493 return rv;
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.
503 static nsresult
504 GetOrInit(nsIFile* aDir, const nsACString& filename,
505 nsACString& aContents, InitDataFunc aInitFunc)
507 PRBool exists;
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);
519 if (!exists) {
520 if (aInitFunc) {
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);
526 else {
527 // didn't pass in an init func
528 rv = NS_ERROR_FAILURE;
531 else {
532 // just get the file's contents
533 rv = GetFileContents(dataFile, aContents);
536 return rv;
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.
542 static nsresult
543 InitUserID(nsACString& aUserID)
545 nsID id;
547 // copied shamelessly from nsUUIDGenerator.cpp
548 #if defined(XP_WIN)
549 HRESULT hr = CoCreateGuid((GUID*)&id);
550 if (NS_FAILED(hr))
551 return NS_ERROR_FAILURE;
552 #elif defined(XP_MACOSX)
553 CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
554 if (!uuid)
555 return NS_ERROR_FAILURE;
557 CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
558 memcpy(&id, &bytes, sizeof(nsID));
560 CFRelease(uuid);
561 #else
562 // UNIX or some such thing
563 id.m0 = random();
564 id.m1 = random();
565 id.m2 = random();
566 *reinterpret_cast<PRUint32*>(&id.m3[0]) = random();
567 *reinterpret_cast<PRUint32*>(&id.m3[4]) = random();
568 #endif
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);
575 PR_Free(id_cstr);
576 return NS_OK;
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".
581 static nsresult
582 InitInstallTime(nsACString& aInstallTime)
584 time_t t = time(NULL);
585 char buf[16];
586 sprintf(buf, "%ld", t);
587 aInstallTime = buf;
589 return NS_OK;
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
595 // crash time.
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);
607 PRBool exists;
608 rv = dataDirectory->Exists(&exists);
609 NS_ENSURE_SUCCESS(rv, rv);
611 if (!exists) {
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());
626 #else
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);
639 PR_SetEnv(env);
640 #endif
642 nsCAutoString data;
643 if(NS_SUCCEEDED(GetOrInit(dataDirectory, NS_LITERAL_CSTRING("UserID"),
644 data, InitUserID)))
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"),
658 data, NULL))) {
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());
678 #else
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());
685 #endif
687 return NS_OK;
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;
704 if (notesField) {
705 delete notesField;
706 notesField = 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;
720 return NS_OK;
723 static void ReplaceChar(nsCString& str, const nsACString& character,
724 const nsACString& replacement)
726 nsCString::const_iterator start, end;
728 str.BeginReading(start);
729 str.EndReading(end);
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);
737 str.EndReading(end);
741 static PRBool DoFindInReadable(const nsACString& str, const nsACString& value)
743 nsACString::const_iterator start, end;
744 str.BeginReading(start);
745 str.EndReading(end);
747 return FindInReadable(value, start, end);
750 static PLDHashOperator PR_CALLBACK EnumerateEntries(const nsACString& key,
751 nsCString entry,
752 void* userData)
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("\\\\"));
776 // escape newlines
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);
788 return NS_OK;
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;
809 nsCAutoString entry;
810 if (!crashReporterAPIData_Hash->Get(key, &entry))
811 return false;
813 data = entry;
814 return true;
817 nsresult
818 SetRestartArgs(int argc, char** argv)
820 if (!gExceptionHandler)
821 return NS_OK;
823 int i;
824 nsCAutoString envVar;
825 char *env;
826 for (i = 0; i < argc; i++) {
827 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
828 envVar.AppendInt(i);
829 envVar += "=";
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
834 int arg_len = 0;
835 if (i == 0 &&
836 (arg_len = strlen(argv[i])) > 4 &&
837 strcmp(argv[i] + arg_len - 4, "-bin") == 0) {
838 envVar.Append(argv[i], arg_len - 4);
839 } else
840 #endif
842 envVar += argv[i];
845 // PR_SetEnv() wants the string to be available for the lifetime
846 // of the app, so dup it here
847 env = ToNewCString(envVar);
848 if (!env)
849 return NS_ERROR_OUT_OF_MEMORY;
851 PR_SetEnv(env);
854 // make sure the arg list is terminated
855 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
856 envVar.AppendInt(i);
857 envVar += "=";
859 // PR_SetEnv() wants the string to be available for the lifetime
860 // of the app, so dup it here
861 env = ToNewCString(envVar);
862 if (!env)
863 return NS_ERROR_OUT_OF_MEMORY;
865 PR_SetEnv(env);
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=";
871 envVar += appfile;
872 env = ToNewCString(envVar);
873 PR_SetEnv(env);
876 return NS_OK;
879 #ifdef XP_WIN32
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;
887 #endif
889 #ifdef XP_MACOSX
890 nsresult AppendObjCExceptionInfoToAppNotes(void *inException)
892 nsCAutoString excString;
893 GetObjCExceptionInfo(inException, excString);
894 AppendAppNotesToCrashReport(excString);
895 return NS_OK;
897 #endif
899 } // namespace CrashReporter