toolkit/crashreporter/nsExceptionHandler.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsExceptionHandler.h"
michael@0 7 #include "nsDataHashtable.h"
michael@0 8 #include "mozilla/ArrayUtils.h"
michael@0 9 #include "mozilla/dom/CrashReporterChild.h"
michael@0 10 #include "mozilla/Services.h"
michael@0 11 #include "nsIObserverService.h"
michael@0 12 #include "mozilla/unused.h"
michael@0 13
michael@0 14 #include "nsThreadUtils.h"
michael@0 15 #include "nsXULAppAPI.h"
michael@0 16
michael@0 17 #if defined(XP_WIN32)
michael@0 18 #ifdef WIN32_LEAN_AND_MEAN
michael@0 19 #undef WIN32_LEAN_AND_MEAN
michael@0 20 #endif
michael@0 21
michael@0 22 #include "nsXULAppAPI.h"
michael@0 23 #include "nsIXULAppInfo.h"
michael@0 24 #include "nsIWindowsRegKey.h"
michael@0 25 #include "client/windows/crash_generation/client_info.h"
michael@0 26 #include "client/windows/crash_generation/crash_generation_server.h"
michael@0 27 #include "client/windows/handler/exception_handler.h"
michael@0 28 #include <dbghelp.h>
michael@0 29 #include <string.h>
michael@0 30 #include "nsDirectoryServiceUtils.h"
michael@0 31
michael@0 32 #include "nsWindowsDllInterceptor.h"
michael@0 33 #elif defined(XP_MACOSX)
michael@0 34 #include "client/mac/crash_generation/client_info.h"
michael@0 35 #include "client/mac/crash_generation/crash_generation_server.h"
michael@0 36 #include "client/mac/handler/exception_handler.h"
michael@0 37 #include <string>
michael@0 38 #include <Carbon/Carbon.h>
michael@0 39 #include <CoreFoundation/CoreFoundation.h>
michael@0 40 #include <crt_externs.h>
michael@0 41 #include <fcntl.h>
michael@0 42 #include <mach/mach.h>
michael@0 43 #include <sys/types.h>
michael@0 44 #include <spawn.h>
michael@0 45 #include <unistd.h>
michael@0 46 #include "mac_utils.h"
michael@0 47 #elif defined(XP_LINUX)
michael@0 48 #include "nsIINIParser.h"
michael@0 49 #include "common/linux/linux_libc_support.h"
michael@0 50 #include "third_party/lss/linux_syscall_support.h"
michael@0 51 #include "client/linux/crash_generation/client_info.h"
michael@0 52 #include "client/linux/crash_generation/crash_generation_server.h"
michael@0 53 #include "client/linux/handler/exception_handler.h"
michael@0 54 #include <fcntl.h>
michael@0 55 #include <sys/types.h>
michael@0 56 #include <unistd.h>
michael@0 57 #elif defined(XP_SOLARIS)
michael@0 58 #include "client/solaris/handler/exception_handler.h"
michael@0 59 #include <fcntl.h>
michael@0 60 #include <sys/types.h>
michael@0 61 #include <unistd.h>
michael@0 62 #else
michael@0 63 #error "Not yet implemented for this platform"
michael@0 64 #endif // defined(XP_WIN32)
michael@0 65
michael@0 66 #ifdef MOZ_CRASHREPORTER_INJECTOR
michael@0 67 #include "InjectCrashReporter.h"
michael@0 68 using mozilla::InjectCrashRunnable;
michael@0 69 #endif
michael@0 70
michael@0 71 #include <stdlib.h>
michael@0 72 #include <time.h>
michael@0 73 #include <prenv.h>
michael@0 74 #include <prio.h>
michael@0 75 #include <prmem.h>
michael@0 76 #include "mozilla/Mutex.h"
michael@0 77 #include "nsDebug.h"
michael@0 78 #include "nsCRT.h"
michael@0 79 #include "nsIFile.h"
michael@0 80 #include "prprf.h"
michael@0 81 #include <map>
michael@0 82 #include <vector>
michael@0 83
michael@0 84 #include "mozilla/IOInterposer.h"
michael@0 85 #include "mozilla/mozalloc_oom.h"
michael@0 86 #include "mozilla/WindowsDllBlocklist.h"
michael@0 87
michael@0 88 #if defined(XP_MACOSX)
michael@0 89 CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
michael@0 90 #endif
michael@0 91 #if defined(MOZ_WIDGET_ANDROID)
michael@0 92 #include "common/linux/file_id.h"
michael@0 93 #endif
michael@0 94
michael@0 95 using google_breakpad::CrashGenerationServer;
michael@0 96 using google_breakpad::ClientInfo;
michael@0 97 #ifdef XP_LINUX
michael@0 98 using google_breakpad::MinidumpDescriptor;
michael@0 99 #endif
michael@0 100 using namespace mozilla;
michael@0 101 using mozilla::dom::CrashReporterChild;
michael@0 102 using mozilla::dom::PCrashReporterChild;
michael@0 103
michael@0 104 namespace CrashReporter {
michael@0 105
michael@0 106 #ifdef XP_WIN32
michael@0 107 typedef wchar_t XP_CHAR;
michael@0 108 typedef std::wstring xpstring;
michael@0 109 #define CONVERT_XP_CHAR_TO_UTF16(x) x
michael@0 110 #define XP_STRLEN(x) wcslen(x)
michael@0 111 #define my_strlen strlen
michael@0 112 #define CRASH_REPORTER_FILENAME "crashreporter.exe"
michael@0 113 #define PATH_SEPARATOR "\\"
michael@0 114 #define XP_PATH_SEPARATOR L"\\"
michael@0 115 // sort of arbitrary, but MAX_PATH is kinda small
michael@0 116 #define XP_PATH_MAX 4096
michael@0 117 // "<reporter path>" "<minidump path>"
michael@0 118 #define CMDLINE_SIZE ((XP_PATH_MAX * 2) + 6)
michael@0 119 #ifdef _USE_32BIT_TIME_T
michael@0 120 #define XP_TTOA(time, buffer, base) ltoa(time, buffer, base)
michael@0 121 #else
michael@0 122 #define XP_TTOA(time, buffer, base) _i64toa(time, buffer, base)
michael@0 123 #endif
michael@0 124 #define XP_STOA(size, buffer, base) _ui64toa(size, buffer, base)
michael@0 125 #else
michael@0 126 typedef char XP_CHAR;
michael@0 127 typedef std::string xpstring;
michael@0 128 #define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x)
michael@0 129 #define CRASH_REPORTER_FILENAME "crashreporter"
michael@0 130 #define PATH_SEPARATOR "/"
michael@0 131 #define XP_PATH_SEPARATOR "/"
michael@0 132 #define XP_PATH_MAX PATH_MAX
michael@0 133 #ifdef XP_LINUX
michael@0 134 #define XP_STRLEN(x) my_strlen(x)
michael@0 135 #define XP_TTOA(time, buffer, base) my_inttostring(time, buffer, sizeof(buffer))
michael@0 136 #define XP_STOA(size, buffer, base) my_inttostring(size, buffer, sizeof(buffer))
michael@0 137 #else
michael@0 138 #define XP_STRLEN(x) strlen(x)
michael@0 139 #define XP_TTOA(time, buffer, base) sprintf(buffer, "%ld", time)
michael@0 140 #define XP_STOA(size, buffer, base) sprintf(buffer, "%zu", size)
michael@0 141 #define my_strlen strlen
michael@0 142 #define sys_close close
michael@0 143 #define sys_fork fork
michael@0 144 #define sys_open open
michael@0 145 #define sys_write write
michael@0 146 #endif
michael@0 147 #endif // XP_WIN32
michael@0 148
michael@0 149 static const XP_CHAR dumpFileExtension[] = {'.', 'd', 'm', 'p',
michael@0 150 '\0'}; // .dmp
michael@0 151 static const XP_CHAR extraFileExtension[] = {'.', 'e', 'x', 't',
michael@0 152 'r', 'a', '\0'}; // .extra
michael@0 153
michael@0 154 static const char kCrashMainID[] = "crash.main.1\n";
michael@0 155
michael@0 156 static google_breakpad::ExceptionHandler* gExceptionHandler = nullptr;
michael@0 157
michael@0 158 static XP_CHAR* pendingDirectory;
michael@0 159 static XP_CHAR* crashReporterPath;
michael@0 160
michael@0 161 // Where crash events should go.
michael@0 162 static XP_CHAR* eventsDirectory;
michael@0 163
michael@0 164 // If this is false, we don't launch the crash reporter
michael@0 165 static bool doReport = true;
michael@0 166
michael@0 167 // If this is true, we don't have a crash reporter
michael@0 168 static bool headlessClient = false;
michael@0 169
michael@0 170 // if this is true, we pass the exception on to the OS crash reporter
michael@0 171 static bool showOSCrashReporter = false;
michael@0 172
michael@0 173 // The time of the last recorded crash, as a time_t value.
michael@0 174 static time_t lastCrashTime = 0;
michael@0 175 // The pathname of a file to store the crash time in
michael@0 176 static XP_CHAR lastCrashTimeFilename[XP_PATH_MAX] = {0};
michael@0 177
michael@0 178 // A marker file to hold the path to the last dump written, which
michael@0 179 // will be checked on startup.
michael@0 180 static XP_CHAR crashMarkerFilename[XP_PATH_MAX] = {0};
michael@0 181
michael@0 182 // Whether we've already looked for the marker file.
michael@0 183 static bool lastRunCrashID_checked = false;
michael@0 184 // The minidump ID contained in the marker file.
michael@0 185 static nsString* lastRunCrashID = nullptr;
michael@0 186
michael@0 187 #if defined(MOZ_WIDGET_ANDROID)
michael@0 188 // on Android 4.2 and above there is a user serial number associated
michael@0 189 // with the current process that gets lost when we fork so we need to
michael@0 190 // explicitly pass it to am
michael@0 191 static char* androidUserSerial = nullptr;
michael@0 192 #endif
michael@0 193
michael@0 194 // these are just here for readability
michael@0 195 static const char kCrashTimeParameter[] = "CrashTime=";
michael@0 196 static const int kCrashTimeParameterLen = sizeof(kCrashTimeParameter)-1;
michael@0 197
michael@0 198 static const char kTimeSinceLastCrashParameter[] = "SecondsSinceLastCrash=";
michael@0 199 static const int kTimeSinceLastCrashParameterLen =
michael@0 200 sizeof(kTimeSinceLastCrashParameter)-1;
michael@0 201
michael@0 202 static const char kSysMemoryParameter[] = "SystemMemoryUsePercentage=";
michael@0 203 static const int kSysMemoryParameterLen = sizeof(kSysMemoryParameter)-1;
michael@0 204
michael@0 205 static const char kTotalVirtualMemoryParameter[] = "TotalVirtualMemory=";
michael@0 206 static const int kTotalVirtualMemoryParameterLen =
michael@0 207 sizeof(kTotalVirtualMemoryParameter)-1;
michael@0 208
michael@0 209 static const char kAvailableVirtualMemoryParameter[] = "AvailableVirtualMemory=";
michael@0 210 static const int kAvailableVirtualMemoryParameterLen =
michael@0 211 sizeof(kAvailableVirtualMemoryParameter)-1;
michael@0 212
michael@0 213 static const char kOOMAllocationSizeParameter[] = "OOMAllocationSize=";
michael@0 214 static const int kOOMAllocationSizeParameterLen =
michael@0 215 sizeof(kOOMAllocationSizeParameter)-1;
michael@0 216
michael@0 217 static const char kTotalPageFileParameter[] = "TotalPageFile=";
michael@0 218 static const int kTotalPageFileParameterLen =
michael@0 219 sizeof(kTotalPageFileParameter)-1;
michael@0 220
michael@0 221 static const char kAvailablePageFileParameter[] = "AvailablePageFile=";
michael@0 222 static const int kAvailablePageFileParameterLen =
michael@0 223 sizeof(kAvailablePageFileParameter)-1;
michael@0 224
michael@0 225 static const char kTotalPhysicalMemoryParameter[] = "TotalPhysicalMemory=";
michael@0 226 static const int kTotalPhysicalMemoryParameterLen =
michael@0 227 sizeof(kTotalPhysicalMemoryParameter)-1;
michael@0 228
michael@0 229 static const char kAvailablePhysicalMemoryParameter[] = "AvailablePhysicalMemory=";
michael@0 230 static const int kAvailablePhysicalMemoryParameterLen =
michael@0 231 sizeof(kAvailablePhysicalMemoryParameter)-1;
michael@0 232
michael@0 233 static const char kIsGarbageCollectingParameter[] = "IsGarbageCollecting=";
michael@0 234 static const int kIsGarbageCollectingParameterLen =
michael@0 235 sizeof(kIsGarbageCollectingParameter)-1;
michael@0 236
michael@0 237 static const char kEventLoopNestingLevelParameter[] = "EventLoopNestingLevel=";
michael@0 238 static const int kEventLoopNestingLevelParameterLen =
michael@0 239 sizeof(kEventLoopNestingLevelParameter)-1;
michael@0 240
michael@0 241 #ifdef XP_WIN
michael@0 242 static const char kBreakpadReserveAddressParameter[] = "BreakpadReserveAddress=";
michael@0 243 static const int kBreakpadReserveAddressParameterLen =
michael@0 244 sizeof(kBreakpadReserveAddressParameter)-1;
michael@0 245
michael@0 246 static const char kBreakpadReserveSizeParameter[] = "BreakpadReserveSize=";
michael@0 247 static const int kBreakpadReserveSizeParameterLen =
michael@0 248 sizeof(kBreakpadReserveSizeParameter)-1;
michael@0 249 #endif
michael@0 250
michael@0 251 // this holds additional data sent via the API
michael@0 252 static Mutex* crashReporterAPILock;
michael@0 253 static Mutex* notesFieldLock;
michael@0 254 static AnnotationTable* crashReporterAPIData_Hash;
michael@0 255 static nsCString* crashReporterAPIData = nullptr;
michael@0 256 static nsCString* notesField = nullptr;
michael@0 257 static bool isGarbageCollecting;
michael@0 258 static uint32_t eventloopNestingLevel = 0;
michael@0 259
michael@0 260 // Avoid a race during application termination.
michael@0 261 static Mutex* dumpSafetyLock;
michael@0 262 static bool isSafeToDump = false;
michael@0 263
michael@0 264 // OOP crash reporting
michael@0 265 static CrashGenerationServer* crashServer; // chrome process has this
michael@0 266
michael@0 267 # if defined(XP_WIN) || defined(XP_MACOSX)
michael@0 268 // If crash reporting is disabled, we hand out this "null" pipe to the
michael@0 269 // child process and don't attempt to connect to a parent server.
michael@0 270 static const char kNullNotifyPipe[] = "-";
michael@0 271 static char* childCrashNotifyPipe;
michael@0 272
michael@0 273 # elif defined(XP_LINUX)
michael@0 274 static int serverSocketFd = -1;
michael@0 275 static int clientSocketFd = -1;
michael@0 276 static const int kMagicChildCrashReportFd = 4;
michael@0 277
michael@0 278 # endif
michael@0 279
michael@0 280 // |dumpMapLock| must protect all access to |pidToMinidump|.
michael@0 281 static Mutex* dumpMapLock;
michael@0 282 struct ChildProcessData : public nsUint32HashKey
michael@0 283 {
michael@0 284 ChildProcessData(KeyTypePointer aKey)
michael@0 285 : nsUint32HashKey(aKey)
michael@0 286 , sequence(0)
michael@0 287 #ifdef MOZ_CRASHREPORTER_INJECTOR
michael@0 288 , callback(nullptr)
michael@0 289 #endif
michael@0 290 { }
michael@0 291
michael@0 292 nsCOMPtr<nsIFile> minidump;
michael@0 293 // Each crashing process is assigned an increasing sequence number to
michael@0 294 // indicate which process crashed first.
michael@0 295 uint32_t sequence;
michael@0 296 #ifdef MOZ_CRASHREPORTER_INJECTOR
michael@0 297 InjectorCrashCallback* callback;
michael@0 298 #endif
michael@0 299 };
michael@0 300
michael@0 301 typedef nsTHashtable<ChildProcessData> ChildMinidumpMap;
michael@0 302 static ChildMinidumpMap* pidToMinidump;
michael@0 303 static uint32_t crashSequence;
michael@0 304 static bool OOPInitialized();
michael@0 305
michael@0 306 #ifdef MOZ_CRASHREPORTER_INJECTOR
michael@0 307 static nsIThread* sInjectorThread;
michael@0 308
michael@0 309 class ReportInjectedCrash : public nsRunnable
michael@0 310 {
michael@0 311 public:
michael@0 312 ReportInjectedCrash(uint32_t pid) : mPID(pid) { }
michael@0 313
michael@0 314 NS_IMETHOD Run();
michael@0 315
michael@0 316 private:
michael@0 317 uint32_t mPID;
michael@0 318 };
michael@0 319 #endif // MOZ_CRASHREPORTER_INJECTOR
michael@0 320
michael@0 321 // Crashreporter annotations that we don't send along in subprocess
michael@0 322 // reports
michael@0 323 static const char* kSubprocessBlacklist[] = {
michael@0 324 "FramePoisonBase",
michael@0 325 "FramePoisonSize",
michael@0 326 "StartupTime",
michael@0 327 "URL"
michael@0 328 };
michael@0 329
michael@0 330 // If annotations are attempted before the crash reporter is enabled,
michael@0 331 // they queue up here.
michael@0 332 class DelayedNote;
michael@0 333 nsTArray<nsAutoPtr<DelayedNote> >* gDelayedAnnotations;
michael@0 334
michael@0 335 #if defined(XP_WIN)
michael@0 336 // the following are used to prevent other DLLs reverting the last chance
michael@0 337 // exception handler to the windows default. Any attempt to change the
michael@0 338 // unhandled exception filter or to reset it is ignored and our crash
michael@0 339 // reporter is loaded instead (in case it became unloaded somehow)
michael@0 340 typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI *SetUnhandledExceptionFilter_func)
michael@0 341 (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
michael@0 342 static SetUnhandledExceptionFilter_func stub_SetUnhandledExceptionFilter = 0;
michael@0 343 static LPTOP_LEVEL_EXCEPTION_FILTER previousUnhandledExceptionFilter = nullptr;
michael@0 344 static WindowsDllInterceptor gKernel32Intercept;
michael@0 345 static bool gBlockUnhandledExceptionFilter = true;
michael@0 346
michael@0 347 static void NotePreviousUnhandledExceptionFilter()
michael@0 348 {
michael@0 349 // Set a dummy value to get the previous filter, then restore
michael@0 350 previousUnhandledExceptionFilter = SetUnhandledExceptionFilter(nullptr);
michael@0 351 SetUnhandledExceptionFilter(previousUnhandledExceptionFilter);
michael@0 352 }
michael@0 353
michael@0 354 static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
michael@0 355 patched_SetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
michael@0 356 {
michael@0 357 if (!gBlockUnhandledExceptionFilter) {
michael@0 358 // don't intercept
michael@0 359 return stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
michael@0 360 }
michael@0 361
michael@0 362 if (lpTopLevelExceptionFilter == previousUnhandledExceptionFilter) {
michael@0 363 // OK to swap back and forth between the previous filter
michael@0 364 previousUnhandledExceptionFilter =
michael@0 365 stub_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
michael@0 366 return previousUnhandledExceptionFilter;
michael@0 367 }
michael@0 368
michael@0 369 // intercept attempts to change the filter
michael@0 370 return nullptr;
michael@0 371 }
michael@0 372
michael@0 373 /**
michael@0 374 * Reserve some VM space. In the event that we crash because VM space is
michael@0 375 * being leaked without leaking memory, freeing this space before taking
michael@0 376 * the minidump will allow us to collect a minidump.
michael@0 377 *
michael@0 378 * This size is bigger than xul.dll plus some extra for MinidumpWriteDump
michael@0 379 * allocations.
michael@0 380 */
michael@0 381 static const SIZE_T kReserveSize = 0x2800000; // 40 MB
michael@0 382 static void* gBreakpadReservedVM;
michael@0 383 #endif
michael@0 384
michael@0 385 #ifdef XP_MACOSX
michael@0 386 static cpu_type_t pref_cpu_types[2] = {
michael@0 387 #if defined(__i386__)
michael@0 388 CPU_TYPE_X86,
michael@0 389 #elif defined(__x86_64__)
michael@0 390 CPU_TYPE_X86_64,
michael@0 391 #elif defined(__ppc__)
michael@0 392 CPU_TYPE_POWERPC,
michael@0 393 #endif
michael@0 394 CPU_TYPE_ANY };
michael@0 395
michael@0 396 static posix_spawnattr_t spawnattr;
michael@0 397 #endif
michael@0 398
michael@0 399 #if defined(MOZ_WIDGET_ANDROID)
michael@0 400 // Android builds use a custom library loader,
michael@0 401 // so the embedding will provide a list of shared
michael@0 402 // libraries that are mapped into anonymous mappings.
michael@0 403 typedef struct {
michael@0 404 std::string name;
michael@0 405 uintptr_t start_address;
michael@0 406 size_t length;
michael@0 407 size_t file_offset;
michael@0 408 } mapping_info;
michael@0 409 static std::vector<mapping_info> library_mappings;
michael@0 410 typedef std::map<uint32_t,google_breakpad::MappingList> MappingMap;
michael@0 411 #endif
michael@0 412
michael@0 413 #ifdef XP_LINUX
michael@0 414 inline void
michael@0 415 my_inttostring(intmax_t t, char* buffer, size_t buffer_length)
michael@0 416 {
michael@0 417 my_memset(buffer, 0, buffer_length);
michael@0 418 my_uitos(buffer, t, my_uint_len(t));
michael@0 419 }
michael@0 420 #endif
michael@0 421
michael@0 422 #ifdef XP_WIN
michael@0 423 static void
michael@0 424 CreateFileFromPath(const xpstring& path, nsIFile** file)
michael@0 425 {
michael@0 426 NS_NewLocalFile(nsDependentString(path.c_str()), false, file);
michael@0 427 }
michael@0 428 #else
michael@0 429 static void
michael@0 430 CreateFileFromPath(const xpstring& path, nsIFile** file)
michael@0 431 {
michael@0 432 NS_NewNativeLocalFile(nsDependentCString(path.c_str()), false, file);
michael@0 433 }
michael@0 434 #endif
michael@0 435
michael@0 436 static XP_CHAR*
michael@0 437 Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size)
michael@0 438 {
michael@0 439 int appendLen = XP_STRLEN(toAppend);
michael@0 440 if (appendLen >= *size) appendLen = *size - 1;
michael@0 441
michael@0 442 memcpy(str, toAppend, appendLen * sizeof(XP_CHAR));
michael@0 443 str += appendLen;
michael@0 444 *str = '\0';
michael@0 445 *size -= appendLen;
michael@0 446
michael@0 447 return str;
michael@0 448 }
michael@0 449
michael@0 450 static size_t gOOMAllocationSize = 0;
michael@0 451
michael@0 452 void AnnotateOOMAllocationSize(size_t size)
michael@0 453 {
michael@0 454 gOOMAllocationSize = size;
michael@0 455 }
michael@0 456
michael@0 457 bool MinidumpCallback(
michael@0 458 #ifdef XP_LINUX
michael@0 459 const MinidumpDescriptor& descriptor,
michael@0 460 #else
michael@0 461 const XP_CHAR* dump_path,
michael@0 462 const XP_CHAR* minidump_id,
michael@0 463 #endif
michael@0 464 void* context,
michael@0 465 #ifdef XP_WIN32
michael@0 466 EXCEPTION_POINTERS* exinfo,
michael@0 467 MDRawAssertionInfo* assertion,
michael@0 468 #endif
michael@0 469 bool succeeded)
michael@0 470 {
michael@0 471 bool returnValue = showOSCrashReporter ? false : succeeded;
michael@0 472
michael@0 473 static XP_CHAR minidumpPath[XP_PATH_MAX];
michael@0 474 int size = XP_PATH_MAX;
michael@0 475 XP_CHAR* p;
michael@0 476 #ifndef XP_LINUX
michael@0 477 p = Concat(minidumpPath, dump_path, &size);
michael@0 478 p = Concat(p, XP_PATH_SEPARATOR, &size);
michael@0 479 p = Concat(p, minidump_id, &size);
michael@0 480 Concat(p, dumpFileExtension, &size);
michael@0 481 #else
michael@0 482 Concat(minidumpPath, descriptor.path(), &size);
michael@0 483 #endif
michael@0 484
michael@0 485 static XP_CHAR extraDataPath[XP_PATH_MAX];
michael@0 486 size = XP_PATH_MAX;
michael@0 487 #ifndef XP_LINUX
michael@0 488 p = Concat(extraDataPath, dump_path, &size);
michael@0 489 p = Concat(p, XP_PATH_SEPARATOR, &size);
michael@0 490 p = Concat(p, minidump_id, &size);
michael@0 491 #else
michael@0 492 p = Concat(extraDataPath, descriptor.path(), &size);
michael@0 493 // Skip back past the .dmp extension.
michael@0 494 p -= 4;
michael@0 495 #endif
michael@0 496 Concat(p, extraFileExtension, &size);
michael@0 497
michael@0 498 if (headlessClient) {
michael@0 499 // Leave a marker indicating that there was a crash.
michael@0 500 #if defined(XP_WIN32)
michael@0 501 HANDLE hFile = CreateFile(crashMarkerFilename, GENERIC_WRITE, 0,
michael@0 502 nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
michael@0 503 nullptr);
michael@0 504 if(hFile != INVALID_HANDLE_VALUE) {
michael@0 505 DWORD nBytes;
michael@0 506 WriteFile(hFile, minidumpPath, 2*wcslen(minidumpPath), &nBytes, nullptr);
michael@0 507 CloseHandle(hFile);
michael@0 508 }
michael@0 509 #elif defined(XP_UNIX)
michael@0 510 int fd = sys_open(crashMarkerFilename,
michael@0 511 O_WRONLY | O_CREAT | O_TRUNC,
michael@0 512 0600);
michael@0 513 if (fd != -1) {
michael@0 514 unused << sys_write(fd, minidumpPath, my_strlen(minidumpPath));
michael@0 515 sys_close(fd);
michael@0 516 }
michael@0 517 #endif
michael@0 518 }
michael@0 519
michael@0 520 char oomAllocationSizeBuffer[32];
michael@0 521 int oomAllocationSizeBufferLen = 0;
michael@0 522 if (gOOMAllocationSize) {
michael@0 523 XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
michael@0 524 oomAllocationSizeBufferLen = my_strlen(oomAllocationSizeBuffer);
michael@0 525 }
michael@0 526
michael@0 527 // calculate time since last crash (if possible), and store
michael@0 528 // the time of this crash.
michael@0 529 time_t crashTime;
michael@0 530 #ifdef XP_LINUX
michael@0 531 struct kernel_timeval tv;
michael@0 532 sys_gettimeofday(&tv, nullptr);
michael@0 533 crashTime = tv.tv_sec;
michael@0 534 #else
michael@0 535 crashTime = time(nullptr);
michael@0 536 #endif
michael@0 537 time_t timeSinceLastCrash = 0;
michael@0 538 // stringified versions of the above
michael@0 539 char crashTimeString[32];
michael@0 540 int crashTimeStringLen = 0;
michael@0 541 char timeSinceLastCrashString[32];
michael@0 542 int timeSinceLastCrashStringLen = 0;
michael@0 543
michael@0 544 XP_TTOA(crashTime, crashTimeString, 10);
michael@0 545 crashTimeStringLen = my_strlen(crashTimeString);
michael@0 546 if (lastCrashTime != 0) {
michael@0 547 timeSinceLastCrash = crashTime - lastCrashTime;
michael@0 548 XP_TTOA(timeSinceLastCrash, timeSinceLastCrashString, 10);
michael@0 549 timeSinceLastCrashStringLen = my_strlen(timeSinceLastCrashString);
michael@0 550 }
michael@0 551 // write crash time to file
michael@0 552 if (lastCrashTimeFilename[0] != 0) {
michael@0 553 #if defined(XP_WIN32)
michael@0 554 HANDLE hFile = CreateFile(lastCrashTimeFilename, GENERIC_WRITE, 0,
michael@0 555 nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
michael@0 556 nullptr);
michael@0 557 if(hFile != INVALID_HANDLE_VALUE) {
michael@0 558 DWORD nBytes;
michael@0 559 WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
michael@0 560 CloseHandle(hFile);
michael@0 561 }
michael@0 562 #elif defined(XP_UNIX)
michael@0 563 int fd = sys_open(lastCrashTimeFilename,
michael@0 564 O_WRONLY | O_CREAT | O_TRUNC,
michael@0 565 0600);
michael@0 566 if (fd != -1) {
michael@0 567 unused << sys_write(fd, crashTimeString, crashTimeStringLen);
michael@0 568 sys_close(fd);
michael@0 569 }
michael@0 570 #endif
michael@0 571 }
michael@0 572
michael@0 573 // Write crash event file.
michael@0 574
michael@0 575 // Minidump IDs are UUIDs (36) + NULL.
michael@0 576 static char id_ascii[37];
michael@0 577 #ifdef XP_LINUX
michael@0 578 const char * index = strrchr(descriptor.path(), '/');
michael@0 579 MOZ_ASSERT(index);
michael@0 580 MOZ_ASSERT(strlen(index) == 1 + 36 + 4); // "/" + UUID + ".dmp"
michael@0 581 for (uint32_t i = 0; i < 36; i++) {
michael@0 582 id_ascii[i] = *(index + 1 + i);
michael@0 583 }
michael@0 584 #else
michael@0 585 MOZ_ASSERT(XP_STRLEN(minidump_id) == 36);
michael@0 586 for (uint32_t i = 0; i < 36; i++) {
michael@0 587 id_ascii[i] = *((char *)(minidump_id + i));
michael@0 588 }
michael@0 589 #endif
michael@0 590
michael@0 591 if (eventsDirectory) {
michael@0 592 static XP_CHAR crashEventPath[XP_PATH_MAX];
michael@0 593 int size = XP_PATH_MAX;
michael@0 594 XP_CHAR* p;
michael@0 595 p = Concat(crashEventPath, eventsDirectory, &size);
michael@0 596 p = Concat(p, XP_PATH_SEPARATOR, &size);
michael@0 597 #ifdef XP_LINUX
michael@0 598 p = Concat(p, id_ascii, &size);
michael@0 599 #else
michael@0 600 p = Concat(p, minidump_id, &size);
michael@0 601 #endif
michael@0 602
michael@0 603 #if defined(XP_WIN32)
michael@0 604 HANDLE hFile = CreateFile(crashEventPath, GENERIC_WRITE, 0,
michael@0 605 nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
michael@0 606 nullptr);
michael@0 607 if (hFile != INVALID_HANDLE_VALUE) {
michael@0 608 DWORD nBytes;
michael@0 609 WriteFile(hFile, kCrashMainID, sizeof(kCrashMainID) - 1, &nBytes,
michael@0 610 nullptr);
michael@0 611 WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
michael@0 612 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
michael@0 613 WriteFile(hFile, id_ascii, strlen(id_ascii), &nBytes, nullptr);
michael@0 614 CloseHandle(hFile);
michael@0 615 }
michael@0 616 #elif defined(XP_UNIX)
michael@0 617 int fd = sys_open(crashEventPath,
michael@0 618 O_WRONLY | O_CREAT | O_TRUNC,
michael@0 619 0600);
michael@0 620 if (fd != -1) {
michael@0 621 unused << sys_write(fd, kCrashMainID, sizeof(kCrashMainID) - 1);
michael@0 622 unused << sys_write(fd, crashTimeString, crashTimeStringLen);
michael@0 623 unused << sys_write(fd, "\n", 1);
michael@0 624 unused << sys_write(fd, id_ascii, strlen(id_ascii));
michael@0 625 sys_close(fd);
michael@0 626 }
michael@0 627 #endif
michael@0 628 }
michael@0 629
michael@0 630 #if defined(XP_WIN32)
michael@0 631 if (!crashReporterAPIData->IsEmpty()) {
michael@0 632 // write out API data
michael@0 633 HANDLE hFile = CreateFile(extraDataPath, GENERIC_WRITE, 0,
michael@0 634 nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
michael@0 635 nullptr);
michael@0 636 if(hFile != INVALID_HANDLE_VALUE) {
michael@0 637 DWORD nBytes;
michael@0 638 WriteFile(hFile, crashReporterAPIData->get(),
michael@0 639 crashReporterAPIData->Length(), &nBytes, nullptr);
michael@0 640 WriteFile(hFile, kCrashTimeParameter, kCrashTimeParameterLen,
michael@0 641 &nBytes, nullptr);
michael@0 642 WriteFile(hFile, crashTimeString, crashTimeStringLen, &nBytes, nullptr);
michael@0 643 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
michael@0 644 if (timeSinceLastCrash != 0) {
michael@0 645 WriteFile(hFile, kTimeSinceLastCrashParameter,
michael@0 646 kTimeSinceLastCrashParameterLen, &nBytes, nullptr);
michael@0 647 WriteFile(hFile, timeSinceLastCrashString, timeSinceLastCrashStringLen,
michael@0 648 &nBytes, nullptr);
michael@0 649 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
michael@0 650 }
michael@0 651 if (isGarbageCollecting) {
michael@0 652 WriteFile(hFile, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen,
michael@0 653 &nBytes, nullptr);
michael@0 654 WriteFile(hFile, isGarbageCollecting ? "1" : "0", 1, &nBytes, nullptr);
michael@0 655 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
michael@0 656 }
michael@0 657
michael@0 658 char buffer[128];
michael@0 659 int bufferLen;
michael@0 660
michael@0 661 if (eventloopNestingLevel > 0) {
michael@0 662 WriteFile(hFile, kEventLoopNestingLevelParameter, kEventLoopNestingLevelParameterLen,
michael@0 663 &nBytes, nullptr);
michael@0 664 _ultoa(eventloopNestingLevel, buffer, 10);
michael@0 665 WriteFile(hFile, buffer, strlen(buffer), &nBytes, nullptr);
michael@0 666 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
michael@0 667 }
michael@0 668
michael@0 669 if (gBreakpadReservedVM) {
michael@0 670 WriteFile(hFile, kBreakpadReserveAddressParameter, kBreakpadReserveAddressParameterLen, &nBytes, nullptr);
michael@0 671 _ui64toa(uintptr_t(gBreakpadReservedVM), buffer, 10);
michael@0 672 WriteFile(hFile, buffer, strlen(buffer), &nBytes, nullptr);
michael@0 673 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
michael@0 674 WriteFile(hFile, kBreakpadReserveSizeParameter, kBreakpadReserveSizeParameterLen, &nBytes, nullptr);
michael@0 675 _ui64toa(kReserveSize, buffer, 10);
michael@0 676 WriteFile(hFile, buffer, strlen(buffer), &nBytes, nullptr);
michael@0 677 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
michael@0 678 }
michael@0 679
michael@0 680 #ifdef HAS_DLL_BLOCKLIST
michael@0 681 DllBlocklist_WriteNotes(hFile);
michael@0 682 #endif
michael@0 683
michael@0 684 // Try to get some information about memory.
michael@0 685 MEMORYSTATUSEX statex;
michael@0 686 statex.dwLength = sizeof(statex);
michael@0 687 if (GlobalMemoryStatusEx(&statex)) {
michael@0 688
michael@0 689 #define WRITE_STATEX_FIELD(field, paramName, conversionFunc) \
michael@0 690 WriteFile(hFile, k##paramName##Parameter, \
michael@0 691 k##paramName##ParameterLen, &nBytes, nullptr); \
michael@0 692 conversionFunc(statex.field, buffer, 10); \
michael@0 693 bufferLen = strlen(buffer); \
michael@0 694 WriteFile(hFile, buffer, bufferLen, &nBytes, nullptr); \
michael@0 695 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
michael@0 696
michael@0 697 WRITE_STATEX_FIELD(dwMemoryLoad, SysMemory, ltoa);
michael@0 698 WRITE_STATEX_FIELD(ullTotalVirtual, TotalVirtualMemory, _ui64toa);
michael@0 699 WRITE_STATEX_FIELD(ullAvailVirtual, AvailableVirtualMemory, _ui64toa);
michael@0 700 WRITE_STATEX_FIELD(ullTotalPageFile, TotalPageFile, _ui64toa);
michael@0 701 WRITE_STATEX_FIELD(ullAvailPageFile, AvailablePageFile, _ui64toa);
michael@0 702 WRITE_STATEX_FIELD(ullTotalPhys, TotalPhysicalMemory, _ui64toa);
michael@0 703 WRITE_STATEX_FIELD(ullAvailPhys, AvailablePhysicalMemory, _ui64toa);
michael@0 704
michael@0 705 #undef WRITE_STATEX_FIELD
michael@0 706 }
michael@0 707
michael@0 708 if (oomAllocationSizeBufferLen) {
michael@0 709 WriteFile(hFile, kOOMAllocationSizeParameter,
michael@0 710 kOOMAllocationSizeParameterLen, &nBytes, nullptr);
michael@0 711 WriteFile(hFile, oomAllocationSizeBuffer, oomAllocationSizeBufferLen,
michael@0 712 &nBytes, nullptr);
michael@0 713 WriteFile(hFile, "\n", 1, &nBytes, nullptr);
michael@0 714 }
michael@0 715 CloseHandle(hFile);
michael@0 716 }
michael@0 717 }
michael@0 718
michael@0 719 if (!doReport) {
michael@0 720 return returnValue;
michael@0 721 }
michael@0 722
michael@0 723 XP_CHAR cmdLine[CMDLINE_SIZE];
michael@0 724 size = CMDLINE_SIZE;
michael@0 725 p = Concat(cmdLine, L"\"", &size);
michael@0 726 p = Concat(p, crashReporterPath, &size);
michael@0 727 p = Concat(p, L"\" \"", &size);
michael@0 728 p = Concat(p, minidumpPath, &size);
michael@0 729 Concat(p, L"\"", &size);
michael@0 730
michael@0 731 STARTUPINFO si;
michael@0 732 PROCESS_INFORMATION pi;
michael@0 733
michael@0 734 ZeroMemory(&si, sizeof(si));
michael@0 735 si.cb = sizeof(si);
michael@0 736 si.dwFlags = STARTF_USESHOWWINDOW;
michael@0 737 si.wShowWindow = SW_SHOWNORMAL;
michael@0 738 ZeroMemory(&pi, sizeof(pi));
michael@0 739
michael@0 740 if (CreateProcess(nullptr, (LPWSTR)cmdLine, nullptr, nullptr, FALSE, 0,
michael@0 741 nullptr, nullptr, &si, &pi)) {
michael@0 742 CloseHandle( pi.hProcess );
michael@0 743 CloseHandle( pi.hThread );
michael@0 744 }
michael@0 745 // we're not really in a position to do anything if the CreateProcess fails
michael@0 746 TerminateProcess(GetCurrentProcess(), 1);
michael@0 747 #elif defined(XP_UNIX)
michael@0 748 if (!crashReporterAPIData->IsEmpty()) {
michael@0 749 // write out API data
michael@0 750 int fd = sys_open(extraDataPath,
michael@0 751 O_WRONLY | O_CREAT | O_TRUNC,
michael@0 752 0666);
michael@0 753
michael@0 754 if (fd != -1) {
michael@0 755 // not much we can do in case of error
michael@0 756 unused << sys_write(fd, crashReporterAPIData->get(),
michael@0 757 crashReporterAPIData->Length());
michael@0 758 unused << sys_write(fd, kCrashTimeParameter, kCrashTimeParameterLen);
michael@0 759 unused << sys_write(fd, crashTimeString, crashTimeStringLen);
michael@0 760 unused << sys_write(fd, "\n", 1);
michael@0 761 if (timeSinceLastCrash != 0) {
michael@0 762 unused << sys_write(fd, kTimeSinceLastCrashParameter,
michael@0 763 kTimeSinceLastCrashParameterLen);
michael@0 764 unused << sys_write(fd, timeSinceLastCrashString,
michael@0 765 timeSinceLastCrashStringLen);
michael@0 766 unused << sys_write(fd, "\n", 1);
michael@0 767 }
michael@0 768 if (isGarbageCollecting) {
michael@0 769 unused << sys_write(fd, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen);
michael@0 770 unused << sys_write(fd, isGarbageCollecting ? "1" : "0", 1);
michael@0 771 unused << sys_write(fd, "\n", 1);
michael@0 772 }
michael@0 773 if (eventloopNestingLevel > 0) {
michael@0 774 unused << sys_write(fd, kEventLoopNestingLevelParameter, kEventLoopNestingLevelParameterLen);
michael@0 775 char buffer[16];
michael@0 776 XP_TTOA(eventloopNestingLevel, buffer, 10);
michael@0 777 unused << sys_write(fd, buffer, my_strlen(buffer));
michael@0 778 unused << sys_write(fd, "\n", 1);
michael@0 779 }
michael@0 780 if (oomAllocationSizeBufferLen) {
michael@0 781 unused << sys_write(fd, kOOMAllocationSizeParameter,
michael@0 782 kOOMAllocationSizeParameterLen);
michael@0 783 unused << sys_write(fd, oomAllocationSizeBuffer,
michael@0 784 oomAllocationSizeBufferLen);
michael@0 785 unused << sys_write(fd, "\n", 1);
michael@0 786 }
michael@0 787 sys_close(fd);
michael@0 788 }
michael@0 789 }
michael@0 790
michael@0 791 if (!doReport) {
michael@0 792 return returnValue;
michael@0 793 }
michael@0 794
michael@0 795 #ifdef XP_MACOSX
michael@0 796 char* const my_argv[] = {
michael@0 797 crashReporterPath,
michael@0 798 minidumpPath,
michael@0 799 nullptr
michael@0 800 };
michael@0 801
michael@0 802 char **env = nullptr;
michael@0 803 char ***nsEnv = _NSGetEnviron();
michael@0 804 if (nsEnv)
michael@0 805 env = *nsEnv;
michael@0 806 int result = posix_spawnp(nullptr,
michael@0 807 my_argv[0],
michael@0 808 nullptr,
michael@0 809 &spawnattr,
michael@0 810 my_argv,
michael@0 811 env);
michael@0 812
michael@0 813 if (result != 0)
michael@0 814 return false;
michael@0 815
michael@0 816 #else // !XP_MACOSX
michael@0 817 pid_t pid = sys_fork();
michael@0 818
michael@0 819 if (pid == -1)
michael@0 820 return false;
michael@0 821 else if (pid == 0) {
michael@0 822 #if !defined(MOZ_WIDGET_ANDROID)
michael@0 823 // need to clobber this, as libcurl might load NSS,
michael@0 824 // and we want it to load the system NSS.
michael@0 825 unsetenv("LD_LIBRARY_PATH");
michael@0 826 unused << execl(crashReporterPath,
michael@0 827 crashReporterPath, minidumpPath, (char*)0);
michael@0 828 #else
michael@0 829 // Invoke the reportCrash activity using am
michael@0 830 if (androidUserSerial) {
michael@0 831 unused << execlp("/system/bin/am",
michael@0 832 "/system/bin/am",
michael@0 833 "start",
michael@0 834 "--user", androidUserSerial,
michael@0 835 "-a", "org.mozilla.gecko.reportCrash",
michael@0 836 "-n", crashReporterPath,
michael@0 837 "--es", "minidumpPath", minidumpPath,
michael@0 838 (char*)0);
michael@0 839 } else {
michael@0 840 unused << execlp("/system/bin/am",
michael@0 841 "/system/bin/am",
michael@0 842 "start",
michael@0 843 "-a", "org.mozilla.gecko.reportCrash",
michael@0 844 "-n", crashReporterPath,
michael@0 845 "--es", "minidumpPath", minidumpPath,
michael@0 846 (char*)0);
michael@0 847 }
michael@0 848 #endif
michael@0 849 _exit(1);
michael@0 850 }
michael@0 851 #endif // XP_MACOSX
michael@0 852 #endif // XP_UNIX
michael@0 853
michael@0 854 return returnValue;
michael@0 855 }
michael@0 856
michael@0 857 #ifdef XP_WIN
michael@0 858 static void
michael@0 859 ReserveBreakpadVM()
michael@0 860 {
michael@0 861 if (!gBreakpadReservedVM) {
michael@0 862 gBreakpadReservedVM = VirtualAlloc(nullptr, kReserveSize, MEM_RESERVE,
michael@0 863 PAGE_NOACCESS);
michael@0 864 }
michael@0 865 }
michael@0 866
michael@0 867 static void
michael@0 868 FreeBreakpadVM()
michael@0 869 {
michael@0 870 if (gBreakpadReservedVM) {
michael@0 871 VirtualFree(gBreakpadReservedVM, 0, MEM_RELEASE);
michael@0 872 }
michael@0 873 }
michael@0 874
michael@0 875 /**
michael@0 876 * Filters out floating point exceptions which are handled by nsSigHandlers.cpp
michael@0 877 * and should not be handled as crashes.
michael@0 878 *
michael@0 879 * Also calls FreeBreakpadVM if appropriate.
michael@0 880 */
michael@0 881 static bool FPEFilter(void* context, EXCEPTION_POINTERS* exinfo,
michael@0 882 MDRawAssertionInfo* assertion)
michael@0 883 {
michael@0 884 if (!exinfo) {
michael@0 885 mozilla::IOInterposer::Disable();
michael@0 886 FreeBreakpadVM();
michael@0 887 return true;
michael@0 888 }
michael@0 889
michael@0 890 PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)exinfo->ExceptionRecord;
michael@0 891 switch (e->ExceptionCode) {
michael@0 892 case STATUS_FLOAT_DENORMAL_OPERAND:
michael@0 893 case STATUS_FLOAT_DIVIDE_BY_ZERO:
michael@0 894 case STATUS_FLOAT_INEXACT_RESULT:
michael@0 895 case STATUS_FLOAT_INVALID_OPERATION:
michael@0 896 case STATUS_FLOAT_OVERFLOW:
michael@0 897 case STATUS_FLOAT_STACK_CHECK:
michael@0 898 case STATUS_FLOAT_UNDERFLOW:
michael@0 899 case STATUS_FLOAT_MULTIPLE_FAULTS:
michael@0 900 case STATUS_FLOAT_MULTIPLE_TRAPS:
michael@0 901 return false; // Don't write minidump, continue exception search
michael@0 902 }
michael@0 903 mozilla::IOInterposer::Disable();
michael@0 904 FreeBreakpadVM();
michael@0 905 return true;
michael@0 906 }
michael@0 907 #endif // XP_WIN
michael@0 908
michael@0 909 static bool ShouldReport()
michael@0 910 {
michael@0 911 // this environment variable prevents us from launching
michael@0 912 // the crash reporter client
michael@0 913 const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT");
michael@0 914 if (envvar && *envvar) {
michael@0 915 return false;
michael@0 916 }
michael@0 917
michael@0 918 envvar = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
michael@0 919 if (envvar && *envvar) {
michael@0 920 return false;
michael@0 921 }
michael@0 922
michael@0 923 return true;
michael@0 924 }
michael@0 925
michael@0 926 namespace {
michael@0 927 bool Filter(void* context) {
michael@0 928 mozilla::IOInterposer::Disable();
michael@0 929 return true;
michael@0 930 }
michael@0 931 }
michael@0 932
michael@0 933
michael@0 934 nsresult SetExceptionHandler(nsIFile* aXREDirectory,
michael@0 935 bool force/*=false*/)
michael@0 936 {
michael@0 937 if (gExceptionHandler)
michael@0 938 return NS_ERROR_ALREADY_INITIALIZED;
michael@0 939
michael@0 940 #if !defined(DEBUG) || defined(MOZ_WIDGET_GONK)
michael@0 941 // In non-debug builds, enable the crash reporter by default, and allow
michael@0 942 // disabling it with the MOZ_CRASHREPORTER_DISABLE environment variable.
michael@0 943 // Also enable it by default in debug gonk builds as it is difficult to
michael@0 944 // set environment on startup.
michael@0 945 const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
michael@0 946 if (envvar && *envvar && !force)
michael@0 947 return NS_OK;
michael@0 948 #else
michael@0 949 // In debug builds, disable the crash reporter by default, and allow to
michael@0 950 // enable it with the MOZ_CRASHREPORTER environment variable.
michael@0 951 const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER");
michael@0 952 if ((!envvar || !*envvar) && !force)
michael@0 953 return NS_OK;
michael@0 954 #endif
michael@0 955
michael@0 956 #if defined(MOZ_WIDGET_GONK)
michael@0 957 doReport = false;
michael@0 958 headlessClient = true;
michael@0 959 #elif defined(XP_WIN)
michael@0 960 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
michael@0 961 doReport = ShouldReport();
michael@0 962 } else {
michael@0 963 doReport = false;
michael@0 964 headlessClient = true;
michael@0 965 }
michael@0 966 #else
michael@0 967 // this environment variable prevents us from launching
michael@0 968 // the crash reporter client
michael@0 969 doReport = ShouldReport();
michael@0 970 #endif
michael@0 971
michael@0 972 // allocate our strings
michael@0 973 crashReporterAPIData = new nsCString();
michael@0 974 NS_ENSURE_TRUE(crashReporterAPIData, NS_ERROR_OUT_OF_MEMORY);
michael@0 975
michael@0 976 NS_ASSERTION(!crashReporterAPILock, "Shouldn't have a lock yet");
michael@0 977 crashReporterAPILock = new Mutex("crashReporterAPILock");
michael@0 978 NS_ASSERTION(!notesFieldLock, "Shouldn't have a lock yet");
michael@0 979 notesFieldLock = new Mutex("notesFieldLock");
michael@0 980
michael@0 981 crashReporterAPIData_Hash =
michael@0 982 new nsDataHashtable<nsCStringHashKey,nsCString>();
michael@0 983 NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY);
michael@0 984
michael@0 985 notesField = new nsCString();
michael@0 986 NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
michael@0 987
michael@0 988 if (!headlessClient) {
michael@0 989 // locate crashreporter executable
michael@0 990 nsCOMPtr<nsIFile> exePath;
michael@0 991 nsresult rv = aXREDirectory->Clone(getter_AddRefs(exePath));
michael@0 992 NS_ENSURE_SUCCESS(rv, rv);
michael@0 993
michael@0 994 #if defined(XP_MACOSX)
michael@0 995 exePath->Append(NS_LITERAL_STRING("crashreporter.app"));
michael@0 996 exePath->Append(NS_LITERAL_STRING("Contents"));
michael@0 997 exePath->Append(NS_LITERAL_STRING("MacOS"));
michael@0 998 #endif
michael@0 999
michael@0 1000 exePath->AppendNative(NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME));
michael@0 1001
michael@0 1002 #ifdef XP_WIN32
michael@0 1003 nsString crashReporterPath_temp;
michael@0 1004
michael@0 1005 exePath->GetPath(crashReporterPath_temp);
michael@0 1006 crashReporterPath = reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
michael@0 1007 #elif !defined(__ANDROID__)
michael@0 1008 nsCString crashReporterPath_temp;
michael@0 1009
michael@0 1010 exePath->GetNativePath(crashReporterPath_temp);
michael@0 1011 crashReporterPath = ToNewCString(crashReporterPath_temp);
michael@0 1012 #else
michael@0 1013 // On Android, we launch using the application package name
michael@0 1014 // instead of a filename, so use ANDROID_PACKAGE_NAME to do that here.
michael@0 1015 nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
michael@0 1016 crashReporterPath = ToNewCString(package);
michael@0 1017 #endif
michael@0 1018 }
michael@0 1019
michael@0 1020 // get temp path to use for minidump path
michael@0 1021 #if defined(XP_WIN32)
michael@0 1022 nsString tempPath;
michael@0 1023
michael@0 1024 // first figure out buffer size
michael@0 1025 int pathLen = GetTempPath(0, nullptr);
michael@0 1026 if (pathLen == 0)
michael@0 1027 return NS_ERROR_FAILURE;
michael@0 1028
michael@0 1029 tempPath.SetLength(pathLen);
michael@0 1030 GetTempPath(pathLen, (LPWSTR)tempPath.BeginWriting());
michael@0 1031 #elif defined(XP_MACOSX)
michael@0 1032 nsCString tempPath;
michael@0 1033 FSRef fsRef;
michael@0 1034 OSErr err = FSFindFolder(kUserDomain, kTemporaryFolderType,
michael@0 1035 kCreateFolder, &fsRef);
michael@0 1036 if (err != noErr)
michael@0 1037 return NS_ERROR_FAILURE;
michael@0 1038
michael@0 1039 char path[PATH_MAX];
michael@0 1040 OSStatus status = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
michael@0 1041 if (status != noErr)
michael@0 1042 return NS_ERROR_FAILURE;
michael@0 1043
michael@0 1044 tempPath = path;
michael@0 1045
michael@0 1046 #elif defined(__ANDROID__)
michael@0 1047 // GeckoAppShell or Gonk's init.rc sets this in the environment
michael@0 1048 const char *tempenv = PR_GetEnv("TMPDIR");
michael@0 1049 if (!tempenv)
michael@0 1050 return NS_ERROR_FAILURE;
michael@0 1051 nsCString tempPath(tempenv);
michael@0 1052 #elif defined(XP_UNIX)
michael@0 1053 // we assume it's always /tmp on unix systems
michael@0 1054 nsCString tempPath = NS_LITERAL_CSTRING("/tmp/");
michael@0 1055 #else
michael@0 1056 #error "Implement this for your platform"
michael@0 1057 #endif
michael@0 1058
michael@0 1059 #ifdef XP_MACOSX
michael@0 1060 // Initialize spawn attributes, since this calls malloc.
michael@0 1061 if (posix_spawnattr_init(&spawnattr) != 0) {
michael@0 1062 return NS_ERROR_FAILURE;
michael@0 1063 }
michael@0 1064
michael@0 1065 // Set spawn attributes.
michael@0 1066 size_t attr_count = ArrayLength(pref_cpu_types);
michael@0 1067 size_t attr_ocount = 0;
michael@0 1068 if (posix_spawnattr_setbinpref_np(&spawnattr,
michael@0 1069 attr_count,
michael@0 1070 pref_cpu_types,
michael@0 1071 &attr_ocount) != 0 ||
michael@0 1072 attr_ocount != attr_count) {
michael@0 1073 posix_spawnattr_destroy(&spawnattr);
michael@0 1074 return NS_ERROR_FAILURE;
michael@0 1075 }
michael@0 1076 #endif
michael@0 1077
michael@0 1078 #ifdef XP_WIN32
michael@0 1079 ReserveBreakpadVM();
michael@0 1080
michael@0 1081 MINIDUMP_TYPE minidump_type = MiniDumpNormal;
michael@0 1082
michael@0 1083 // Try to determine what version of dbghelp.dll we're using.
michael@0 1084 // MinidumpWithFullMemoryInfo is only available in 6.1.x or newer.
michael@0 1085
michael@0 1086 DWORD version_size = GetFileVersionInfoSizeW(L"dbghelp.dll", nullptr);
michael@0 1087 if (version_size > 0) {
michael@0 1088 std::vector<BYTE> buffer(version_size);
michael@0 1089 if (GetFileVersionInfoW(L"dbghelp.dll",
michael@0 1090 0,
michael@0 1091 version_size,
michael@0 1092 &buffer[0])) {
michael@0 1093 UINT len;
michael@0 1094 VS_FIXEDFILEINFO* file_info;
michael@0 1095 VerQueryValue(&buffer[0], L"\\", (void**)&file_info, &len);
michael@0 1096 WORD major = HIWORD(file_info->dwFileVersionMS),
michael@0 1097 minor = LOWORD(file_info->dwFileVersionMS),
michael@0 1098 revision = HIWORD(file_info->dwFileVersionLS);
michael@0 1099 if (major > 6 || (major == 6 && minor > 1) ||
michael@0 1100 (major == 6 && minor == 1 && revision >= 7600)) {
michael@0 1101 minidump_type = MiniDumpWithFullMemoryInfo;
michael@0 1102 }
michael@0 1103 }
michael@0 1104 }
michael@0 1105
michael@0 1106 const char* e = PR_GetEnv("MOZ_CRASHREPORTER_FULLDUMP");
michael@0 1107 if (e && *e) {
michael@0 1108 minidump_type = MiniDumpWithFullMemory;
michael@0 1109 }
michael@0 1110 #endif // XP_WIN32
michael@0 1111
michael@0 1112 #ifdef MOZ_WIDGET_ANDROID
michael@0 1113 androidUserSerial = getenv("MOZ_ANDROID_USER_SERIAL_NUMBER");
michael@0 1114 #endif
michael@0 1115
michael@0 1116 // Initialize the flag and mutex used to avoid dump processing
michael@0 1117 // once browser termination has begun.
michael@0 1118 NS_ASSERTION(!dumpSafetyLock, "Shouldn't have a lock yet");
michael@0 1119 // Do not deallocate this lock while it is still possible for
michael@0 1120 // isSafeToDump to be tested on another thread.
michael@0 1121 dumpSafetyLock = new Mutex("dumpSafetyLock");
michael@0 1122 MutexAutoLock lock(*dumpSafetyLock);
michael@0 1123 isSafeToDump = true;
michael@0 1124
michael@0 1125 // now set the exception handler
michael@0 1126 #ifdef XP_LINUX
michael@0 1127 MinidumpDescriptor descriptor(tempPath.get());
michael@0 1128 #endif
michael@0 1129
michael@0 1130 #ifdef XP_WIN
michael@0 1131 NotePreviousUnhandledExceptionFilter();
michael@0 1132 #endif
michael@0 1133
michael@0 1134 gExceptionHandler = new google_breakpad::
michael@0 1135 ExceptionHandler(
michael@0 1136 #ifdef XP_LINUX
michael@0 1137 descriptor,
michael@0 1138 #else
michael@0 1139 tempPath.get(),
michael@0 1140 #endif
michael@0 1141
michael@0 1142 #ifdef XP_WIN
michael@0 1143 FPEFilter,
michael@0 1144 #else
michael@0 1145 Filter,
michael@0 1146 #endif
michael@0 1147 MinidumpCallback,
michael@0 1148 nullptr,
michael@0 1149 #ifdef XP_WIN32
michael@0 1150 google_breakpad::ExceptionHandler::HANDLER_ALL,
michael@0 1151 minidump_type,
michael@0 1152 (const wchar_t*) nullptr,
michael@0 1153 nullptr);
michael@0 1154 #else
michael@0 1155 true
michael@0 1156 #ifdef XP_MACOSX
michael@0 1157 , nullptr
michael@0 1158 #endif
michael@0 1159 #ifdef XP_LINUX
michael@0 1160 , -1
michael@0 1161 #endif
michael@0 1162 );
michael@0 1163 #endif // XP_WIN32
michael@0 1164
michael@0 1165 if (!gExceptionHandler)
michael@0 1166 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1167
michael@0 1168 #ifdef XP_WIN
michael@0 1169 gExceptionHandler->set_handle_debug_exceptions(true);
michael@0 1170
michael@0 1171 // protect the crash reporter from being unloaded
michael@0 1172 gBlockUnhandledExceptionFilter = true;
michael@0 1173 gKernel32Intercept.Init("kernel32.dll");
michael@0 1174 bool ok = gKernel32Intercept.AddHook("SetUnhandledExceptionFilter",
michael@0 1175 reinterpret_cast<intptr_t>(patched_SetUnhandledExceptionFilter),
michael@0 1176 (void**) &stub_SetUnhandledExceptionFilter);
michael@0 1177
michael@0 1178 #ifdef DEBUG
michael@0 1179 if (!ok)
michael@0 1180 printf_stderr ("SetUnhandledExceptionFilter hook failed; crash reporter is vulnerable.\n");
michael@0 1181 #endif
michael@0 1182 #endif
michael@0 1183
michael@0 1184 // store application start time
michael@0 1185 char timeString[32];
michael@0 1186 time_t startupTime = time(nullptr);
michael@0 1187 XP_TTOA(startupTime, timeString, 10);
michael@0 1188 AnnotateCrashReport(NS_LITERAL_CSTRING("StartupTime"),
michael@0 1189 nsDependentCString(timeString));
michael@0 1190
michael@0 1191 #if defined(XP_MACOSX)
michael@0 1192 // On OS X, many testers like to see the OS crash reporting dialog
michael@0 1193 // since it offers immediate stack traces. We allow them to set
michael@0 1194 // a default to pass exceptions to the OS handler.
michael@0 1195 Boolean keyExistsAndHasValidFormat = false;
michael@0 1196 Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("OSCrashReporter"),
michael@0 1197 kCFPreferencesCurrentApplication,
michael@0 1198 &keyExistsAndHasValidFormat);
michael@0 1199 if (keyExistsAndHasValidFormat)
michael@0 1200 showOSCrashReporter = prefValue;
michael@0 1201 #endif
michael@0 1202
michael@0 1203 #if defined(MOZ_WIDGET_ANDROID)
michael@0 1204 for (unsigned int i = 0; i < library_mappings.size(); i++) {
michael@0 1205 u_int8_t guid[sizeof(MDGUID)];
michael@0 1206 google_breakpad::FileID::ElfFileIdentifierFromMappedFile((void const *)library_mappings[i].start_address, guid);
michael@0 1207 gExceptionHandler->AddMappingInfo(library_mappings[i].name,
michael@0 1208 guid,
michael@0 1209 library_mappings[i].start_address,
michael@0 1210 library_mappings[i].length,
michael@0 1211 library_mappings[i].file_offset);
michael@0 1212 }
michael@0 1213 #endif
michael@0 1214
michael@0 1215 mozalloc_set_oom_abort_handler(AnnotateOOMAllocationSize);
michael@0 1216
michael@0 1217 return NS_OK;
michael@0 1218 }
michael@0 1219
michael@0 1220 bool GetEnabled()
michael@0 1221 {
michael@0 1222 return gExceptionHandler != nullptr;
michael@0 1223 }
michael@0 1224
michael@0 1225 bool GetMinidumpPath(nsAString& aPath)
michael@0 1226 {
michael@0 1227 if (!gExceptionHandler)
michael@0 1228 return false;
michael@0 1229
michael@0 1230 #ifndef XP_LINUX
michael@0 1231 aPath = CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler->dump_path().c_str());
michael@0 1232 #else
michael@0 1233 aPath = CONVERT_XP_CHAR_TO_UTF16(
michael@0 1234 gExceptionHandler->minidump_descriptor().directory().c_str());
michael@0 1235 #endif
michael@0 1236 return true;
michael@0 1237 }
michael@0 1238
michael@0 1239 nsresult SetMinidumpPath(const nsAString& aPath)
michael@0 1240 {
michael@0 1241 if (!gExceptionHandler)
michael@0 1242 return NS_ERROR_NOT_INITIALIZED;
michael@0 1243
michael@0 1244 #ifdef XP_WIN32
michael@0 1245 gExceptionHandler->set_dump_path(char16ptr_t(aPath.BeginReading()));
michael@0 1246 #elif defined(XP_LINUX)
michael@0 1247 gExceptionHandler->set_minidump_descriptor(
michael@0 1248 MinidumpDescriptor(NS_ConvertUTF16toUTF8(aPath).BeginReading()));
michael@0 1249 #else
michael@0 1250 gExceptionHandler->set_dump_path(NS_ConvertUTF16toUTF8(aPath).BeginReading());
michael@0 1251 #endif
michael@0 1252 return NS_OK;
michael@0 1253 }
michael@0 1254
michael@0 1255 static nsresult
michael@0 1256 WriteDataToFile(nsIFile* aFile, const nsACString& data)
michael@0 1257 {
michael@0 1258 PRFileDesc* fd;
michael@0 1259 nsresult rv = aFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 00600, &fd);
michael@0 1260 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1261
michael@0 1262 rv = NS_OK;
michael@0 1263 if (PR_Write(fd, data.Data(), data.Length()) == -1) {
michael@0 1264 rv = NS_ERROR_FAILURE;
michael@0 1265 }
michael@0 1266 PR_Close(fd);
michael@0 1267 return rv;
michael@0 1268 }
michael@0 1269
michael@0 1270 static nsresult
michael@0 1271 GetFileContents(nsIFile* aFile, nsACString& data)
michael@0 1272 {
michael@0 1273 PRFileDesc* fd;
michael@0 1274 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
michael@0 1275 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1276
michael@0 1277 rv = NS_OK;
michael@0 1278 int32_t filesize = PR_Available(fd);
michael@0 1279 if (filesize <= 0) {
michael@0 1280 rv = NS_ERROR_FILE_NOT_FOUND;
michael@0 1281 }
michael@0 1282 else {
michael@0 1283 data.SetLength(filesize);
michael@0 1284 if (PR_Read(fd, data.BeginWriting(), filesize) == -1) {
michael@0 1285 rv = NS_ERROR_FAILURE;
michael@0 1286 }
michael@0 1287 }
michael@0 1288 PR_Close(fd);
michael@0 1289 return rv;
michael@0 1290 }
michael@0 1291
michael@0 1292 // Function typedef for initializing a piece of data that we
michael@0 1293 // don't already have.
michael@0 1294 typedef nsresult (*InitDataFunc)(nsACString&);
michael@0 1295
michael@0 1296 // Attempt to read aFile's contents into aContents, if aFile
michael@0 1297 // does not exist, create it and initialize its contents
michael@0 1298 // by calling aInitFunc for the data.
michael@0 1299 static nsresult
michael@0 1300 GetOrInit(nsIFile* aDir, const nsACString& filename,
michael@0 1301 nsACString& aContents, InitDataFunc aInitFunc)
michael@0 1302 {
michael@0 1303 bool exists;
michael@0 1304
michael@0 1305 nsCOMPtr<nsIFile> dataFile;
michael@0 1306 nsresult rv = aDir->Clone(getter_AddRefs(dataFile));
michael@0 1307 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1308
michael@0 1309 rv = dataFile->AppendNative(filename);
michael@0 1310 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1311
michael@0 1312 rv = dataFile->Exists(&exists);
michael@0 1313 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1314
michael@0 1315 if (!exists) {
michael@0 1316 if (aInitFunc) {
michael@0 1317 // get the initial value and write it to the file
michael@0 1318 rv = aInitFunc(aContents);
michael@0 1319 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1320 rv = WriteDataToFile(dataFile, aContents);
michael@0 1321 }
michael@0 1322 else {
michael@0 1323 // didn't pass in an init func
michael@0 1324 rv = NS_ERROR_FAILURE;
michael@0 1325 }
michael@0 1326 }
michael@0 1327 else {
michael@0 1328 // just get the file's contents
michael@0 1329 rv = GetFileContents(dataFile, aContents);
michael@0 1330 }
michael@0 1331
michael@0 1332 return rv;
michael@0 1333 }
michael@0 1334
michael@0 1335 // Init the "install time" data. We're taking an easy way out here
michael@0 1336 // and just setting this to "the time when this version was first run".
michael@0 1337 static nsresult
michael@0 1338 InitInstallTime(nsACString& aInstallTime)
michael@0 1339 {
michael@0 1340 time_t t = time(nullptr);
michael@0 1341 char buf[16];
michael@0 1342 sprintf(buf, "%ld", t);
michael@0 1343 aInstallTime = buf;
michael@0 1344
michael@0 1345 return NS_OK;
michael@0 1346 }
michael@0 1347
michael@0 1348 // Ensure a directory exists and create it if missing.
michael@0 1349 static nsresult
michael@0 1350 EnsureDirectoryExists(nsIFile* dir)
michael@0 1351 {
michael@0 1352 nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
michael@0 1353
michael@0 1354 if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
michael@0 1355 return rv;
michael@0 1356 }
michael@0 1357
michael@0 1358 return NS_OK;
michael@0 1359 }
michael@0 1360
michael@0 1361 // Annotate the crash report with a Unique User ID and time
michael@0 1362 // since install. Also do some prep work for recording
michael@0 1363 // time since last crash, which must be calculated at
michael@0 1364 // crash time.
michael@0 1365 // If any piece of data doesn't exist, initialize it first.
michael@0 1366 nsresult SetupExtraData(nsIFile* aAppDataDirectory,
michael@0 1367 const nsACString& aBuildID)
michael@0 1368 {
michael@0 1369 nsCOMPtr<nsIFile> dataDirectory;
michael@0 1370 nsresult rv = aAppDataDirectory->Clone(getter_AddRefs(dataDirectory));
michael@0 1371 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1372
michael@0 1373 rv = dataDirectory->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
michael@0 1374 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1375
michael@0 1376 EnsureDirectoryExists(dataDirectory);
michael@0 1377
michael@0 1378 #if defined(XP_WIN32)
michael@0 1379 nsAutoString dataDirEnv(NS_LITERAL_STRING("MOZ_CRASHREPORTER_DATA_DIRECTORY="));
michael@0 1380
michael@0 1381 nsAutoString dataDirectoryPath;
michael@0 1382 rv = dataDirectory->GetPath(dataDirectoryPath);
michael@0 1383 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1384
michael@0 1385 dataDirEnv.Append(dataDirectoryPath);
michael@0 1386
michael@0 1387 _wputenv(dataDirEnv.get());
michael@0 1388 #else
michael@0 1389 // Save this path in the environment for the crash reporter application.
michael@0 1390 nsAutoCString dataDirEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY=");
michael@0 1391
michael@0 1392 nsAutoCString dataDirectoryPath;
michael@0 1393 rv = dataDirectory->GetNativePath(dataDirectoryPath);
michael@0 1394 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1395
michael@0 1396 dataDirEnv.Append(dataDirectoryPath);
michael@0 1397
michael@0 1398 char* env = ToNewCString(dataDirEnv);
michael@0 1399 NS_ENSURE_TRUE(env, NS_ERROR_OUT_OF_MEMORY);
michael@0 1400
michael@0 1401 PR_SetEnv(env);
michael@0 1402 #endif
michael@0 1403
michael@0 1404 nsAutoCString data;
michael@0 1405 if(NS_SUCCEEDED(GetOrInit(dataDirectory,
michael@0 1406 NS_LITERAL_CSTRING("InstallTime") + aBuildID,
michael@0 1407 data, InitInstallTime)))
michael@0 1408 AnnotateCrashReport(NS_LITERAL_CSTRING("InstallTime"), data);
michael@0 1409
michael@0 1410 // this is a little different, since we can't init it with anything,
michael@0 1411 // since it's stored at crash time, and we can't annotate the
michael@0 1412 // crash report with the stored value, since we really want
michael@0 1413 // (now - LastCrash), so we just get a value if it exists,
michael@0 1414 // and store it in a time_t value.
michael@0 1415 if(NS_SUCCEEDED(GetOrInit(dataDirectory, NS_LITERAL_CSTRING("LastCrash"),
michael@0 1416 data, nullptr))) {
michael@0 1417 lastCrashTime = (time_t)atol(data.get());
michael@0 1418 }
michael@0 1419
michael@0 1420 // not really the best place to init this, but I have the path I need here
michael@0 1421 nsCOMPtr<nsIFile> lastCrashFile;
michael@0 1422 rv = dataDirectory->Clone(getter_AddRefs(lastCrashFile));
michael@0 1423 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1424
michael@0 1425 rv = lastCrashFile->AppendNative(NS_LITERAL_CSTRING("LastCrash"));
michael@0 1426 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1427 memset(lastCrashTimeFilename, 0, sizeof(lastCrashTimeFilename));
michael@0 1428
michael@0 1429 #if defined(XP_WIN32)
michael@0 1430 nsAutoString filename;
michael@0 1431 rv = lastCrashFile->GetPath(filename);
michael@0 1432 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1433
michael@0 1434 if (filename.Length() < XP_PATH_MAX)
michael@0 1435 wcsncpy(lastCrashTimeFilename, filename.get(), filename.Length());
michael@0 1436 #else
michael@0 1437 nsAutoCString filename;
michael@0 1438 rv = lastCrashFile->GetNativePath(filename);
michael@0 1439 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1440
michael@0 1441 if (filename.Length() < XP_PATH_MAX)
michael@0 1442 strncpy(lastCrashTimeFilename, filename.get(), filename.Length());
michael@0 1443 #endif
michael@0 1444
michael@0 1445 if (headlessClient) {
michael@0 1446 nsCOMPtr<nsIFile> markerFile;
michael@0 1447 rv = dataDirectory->Clone(getter_AddRefs(markerFile));
michael@0 1448 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1449
michael@0 1450 rv = markerFile->AppendNative(NS_LITERAL_CSTRING("LastCrashFilename"));
michael@0 1451 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1452 memset(crashMarkerFilename, 0, sizeof(crashMarkerFilename));
michael@0 1453
michael@0 1454 #if defined(XP_WIN32)
michael@0 1455 nsAutoString markerFilename;
michael@0 1456 rv = markerFile->GetPath(markerFilename);
michael@0 1457 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1458
michael@0 1459 if (markerFilename.Length() < XP_PATH_MAX)
michael@0 1460 wcsncpy(crashMarkerFilename, markerFilename.get(),
michael@0 1461 markerFilename.Length());
michael@0 1462 #else
michael@0 1463 nsAutoCString markerFilename;
michael@0 1464 rv = markerFile->GetNativePath(markerFilename);
michael@0 1465 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1466
michael@0 1467 if (markerFilename.Length() < XP_PATH_MAX)
michael@0 1468 strncpy(crashMarkerFilename, markerFilename.get(),
michael@0 1469 markerFilename.Length());
michael@0 1470 #endif
michael@0 1471 }
michael@0 1472
michael@0 1473 return NS_OK;
michael@0 1474 }
michael@0 1475
michael@0 1476 static void OOPDeinit();
michael@0 1477
michael@0 1478 nsresult UnsetExceptionHandler()
michael@0 1479 {
michael@0 1480 if (isSafeToDump) {
michael@0 1481 MutexAutoLock lock(*dumpSafetyLock);
michael@0 1482 isSafeToDump = false;
michael@0 1483 }
michael@0 1484
michael@0 1485 #ifdef XP_WIN
michael@0 1486 // allow SetUnhandledExceptionFilter
michael@0 1487 gBlockUnhandledExceptionFilter = false;
michael@0 1488 #endif
michael@0 1489
michael@0 1490 delete gExceptionHandler;
michael@0 1491
michael@0 1492 // do this here in the unlikely case that we succeeded in allocating
michael@0 1493 // our strings but failed to allocate gExceptionHandler.
michael@0 1494 delete crashReporterAPIData_Hash;
michael@0 1495 crashReporterAPIData_Hash = nullptr;
michael@0 1496
michael@0 1497 delete crashReporterAPILock;
michael@0 1498 crashReporterAPILock = nullptr;
michael@0 1499
michael@0 1500 delete notesFieldLock;
michael@0 1501 notesFieldLock = nullptr;
michael@0 1502
michael@0 1503 delete crashReporterAPIData;
michael@0 1504 crashReporterAPIData = nullptr;
michael@0 1505
michael@0 1506 delete notesField;
michael@0 1507 notesField = nullptr;
michael@0 1508
michael@0 1509 delete lastRunCrashID;
michael@0 1510 lastRunCrashID = nullptr;
michael@0 1511
michael@0 1512 if (pendingDirectory) {
michael@0 1513 NS_Free(pendingDirectory);
michael@0 1514 pendingDirectory = nullptr;
michael@0 1515 }
michael@0 1516
michael@0 1517 if (crashReporterPath) {
michael@0 1518 NS_Free(crashReporterPath);
michael@0 1519 crashReporterPath = nullptr;
michael@0 1520 }
michael@0 1521
michael@0 1522 if (eventsDirectory) {
michael@0 1523 NS_Free(eventsDirectory);
michael@0 1524 eventsDirectory = nullptr;
michael@0 1525 }
michael@0 1526
michael@0 1527 #ifdef XP_MACOSX
michael@0 1528 posix_spawnattr_destroy(&spawnattr);
michael@0 1529 #endif
michael@0 1530
michael@0 1531 if (!gExceptionHandler)
michael@0 1532 return NS_ERROR_NOT_INITIALIZED;
michael@0 1533
michael@0 1534 gExceptionHandler = nullptr;
michael@0 1535
michael@0 1536 OOPDeinit();
michael@0 1537
michael@0 1538 delete dumpSafetyLock;
michael@0 1539 dumpSafetyLock = nullptr;
michael@0 1540
michael@0 1541 return NS_OK;
michael@0 1542 }
michael@0 1543
michael@0 1544 static void ReplaceChar(nsCString& str, const nsACString& character,
michael@0 1545 const nsACString& replacement)
michael@0 1546 {
michael@0 1547 nsCString::const_iterator start, end;
michael@0 1548
michael@0 1549 str.BeginReading(start);
michael@0 1550 str.EndReading(end);
michael@0 1551
michael@0 1552 while (FindInReadable(character, start, end)) {
michael@0 1553 int32_t pos = end.size_backward();
michael@0 1554 str.Replace(pos - 1, 1, replacement);
michael@0 1555
michael@0 1556 str.BeginReading(start);
michael@0 1557 start.advance(pos + replacement.Length() - 1);
michael@0 1558 str.EndReading(end);
michael@0 1559 }
michael@0 1560 }
michael@0 1561
michael@0 1562 static bool DoFindInReadable(const nsACString& str, const nsACString& value)
michael@0 1563 {
michael@0 1564 nsACString::const_iterator start, end;
michael@0 1565 str.BeginReading(start);
michael@0 1566 str.EndReading(end);
michael@0 1567
michael@0 1568 return FindInReadable(value, start, end);
michael@0 1569 }
michael@0 1570
michael@0 1571 static PLDHashOperator EnumerateEntries(const nsACString& key,
michael@0 1572 nsCString entry,
michael@0 1573 void* userData)
michael@0 1574 {
michael@0 1575 crashReporterAPIData->Append(key + NS_LITERAL_CSTRING("=") + entry +
michael@0 1576 NS_LITERAL_CSTRING("\n"));
michael@0 1577 return PL_DHASH_NEXT;
michael@0 1578 }
michael@0 1579
michael@0 1580 // This function is miscompiled with MSVC 2005/2008 when PGO is on.
michael@0 1581 #ifdef _MSC_VER
michael@0 1582 #pragma optimize("", off)
michael@0 1583 #endif
michael@0 1584 static nsresult
michael@0 1585 EscapeAnnotation(const nsACString& key, const nsACString& data, nsCString& escapedData)
michael@0 1586 {
michael@0 1587 if (DoFindInReadable(key, NS_LITERAL_CSTRING("=")) ||
michael@0 1588 DoFindInReadable(key, NS_LITERAL_CSTRING("\n")))
michael@0 1589 return NS_ERROR_INVALID_ARG;
michael@0 1590
michael@0 1591 if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
michael@0 1592 return NS_ERROR_INVALID_ARG;
michael@0 1593
michael@0 1594 escapedData = data;
michael@0 1595
michael@0 1596 // escape backslashes
michael@0 1597 ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
michael@0 1598 NS_LITERAL_CSTRING("\\\\"));
michael@0 1599 // escape newlines
michael@0 1600 ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
michael@0 1601 NS_LITERAL_CSTRING("\\n"));
michael@0 1602 return NS_OK;
michael@0 1603 }
michael@0 1604 #ifdef _MSC_VER
michael@0 1605 #pragma optimize("", on)
michael@0 1606 #endif
michael@0 1607
michael@0 1608 class DelayedNote
michael@0 1609 {
michael@0 1610 public:
michael@0 1611 DelayedNote(const nsACString& aKey, const nsACString& aData)
michael@0 1612 : mKey(aKey), mData(aData), mType(Annotation) {}
michael@0 1613
michael@0 1614 DelayedNote(const nsACString& aData)
michael@0 1615 : mData(aData), mType(AppNote) {}
michael@0 1616
michael@0 1617 void Run()
michael@0 1618 {
michael@0 1619 if (mType == Annotation) {
michael@0 1620 AnnotateCrashReport(mKey, mData);
michael@0 1621 } else {
michael@0 1622 AppendAppNotesToCrashReport(mData);
michael@0 1623 }
michael@0 1624 }
michael@0 1625
michael@0 1626 private:
michael@0 1627 nsCString mKey;
michael@0 1628 nsCString mData;
michael@0 1629 enum AnnotationType { Annotation, AppNote } mType;
michael@0 1630 };
michael@0 1631
michael@0 1632 static void
michael@0 1633 EnqueueDelayedNote(DelayedNote* aNote)
michael@0 1634 {
michael@0 1635 if (!gDelayedAnnotations) {
michael@0 1636 gDelayedAnnotations = new nsTArray<nsAutoPtr<DelayedNote> >();
michael@0 1637 }
michael@0 1638 gDelayedAnnotations->AppendElement(aNote);
michael@0 1639 }
michael@0 1640
michael@0 1641 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
michael@0 1642 {
michael@0 1643 if (!GetEnabled())
michael@0 1644 return NS_ERROR_NOT_INITIALIZED;
michael@0 1645
michael@0 1646 nsCString escapedData;
michael@0 1647 nsresult rv = EscapeAnnotation(key, data, escapedData);
michael@0 1648 if (NS_FAILED(rv))
michael@0 1649 return rv;
michael@0 1650
michael@0 1651 if (XRE_GetProcessType() != GeckoProcessType_Default) {
michael@0 1652 if (!NS_IsMainThread()) {
michael@0 1653 NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread.");
michael@0 1654 return NS_ERROR_FAILURE;
michael@0 1655 }
michael@0 1656 PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
michael@0 1657 if (!reporter) {
michael@0 1658 EnqueueDelayedNote(new DelayedNote(key, data));
michael@0 1659 return NS_OK;
michael@0 1660 }
michael@0 1661 if (!reporter->SendAnnotateCrashReport(nsCString(key), escapedData))
michael@0 1662 return NS_ERROR_FAILURE;
michael@0 1663 return NS_OK;
michael@0 1664 }
michael@0 1665
michael@0 1666 MutexAutoLock lock(*crashReporterAPILock);
michael@0 1667
michael@0 1668 crashReporterAPIData_Hash->Put(key, escapedData);
michael@0 1669
michael@0 1670 // now rebuild the file contents
michael@0 1671 crashReporterAPIData->Truncate(0);
michael@0 1672 crashReporterAPIData_Hash->EnumerateRead(EnumerateEntries,
michael@0 1673 crashReporterAPIData);
michael@0 1674
michael@0 1675 return NS_OK;
michael@0 1676 }
michael@0 1677
michael@0 1678 nsresult SetGarbageCollecting(bool collecting)
michael@0 1679 {
michael@0 1680 if (!GetEnabled())
michael@0 1681 return NS_ERROR_NOT_INITIALIZED;
michael@0 1682
michael@0 1683 isGarbageCollecting = collecting;
michael@0 1684
michael@0 1685 return NS_OK;
michael@0 1686 }
michael@0 1687
michael@0 1688 void SetEventloopNestingLevel(uint32_t level)
michael@0 1689 {
michael@0 1690 eventloopNestingLevel = level;
michael@0 1691 }
michael@0 1692
michael@0 1693 nsresult AppendAppNotesToCrashReport(const nsACString& data)
michael@0 1694 {
michael@0 1695 if (!GetEnabled())
michael@0 1696 return NS_ERROR_NOT_INITIALIZED;
michael@0 1697
michael@0 1698 if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
michael@0 1699 return NS_ERROR_INVALID_ARG;
michael@0 1700
michael@0 1701 if (XRE_GetProcessType() != GeckoProcessType_Default) {
michael@0 1702 if (!NS_IsMainThread()) {
michael@0 1703 NS_ERROR("Cannot call AnnotateCrashReport in child processes from non-main thread.");
michael@0 1704 return NS_ERROR_FAILURE;
michael@0 1705 }
michael@0 1706 PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
michael@0 1707 if (!reporter) {
michael@0 1708 EnqueueDelayedNote(new DelayedNote(data));
michael@0 1709 return NS_OK;
michael@0 1710 }
michael@0 1711
michael@0 1712 // Since we don't go through AnnotateCrashReport in the parent process,
michael@0 1713 // we must ensure that the data is escaped and valid before the parent
michael@0 1714 // sees it.
michael@0 1715 nsCString escapedData;
michael@0 1716 nsresult rv = EscapeAnnotation(NS_LITERAL_CSTRING("Notes"), data, escapedData);
michael@0 1717 if (NS_FAILED(rv))
michael@0 1718 return rv;
michael@0 1719
michael@0 1720 if (!reporter->SendAppendAppNotes(escapedData))
michael@0 1721 return NS_ERROR_FAILURE;
michael@0 1722 return NS_OK;
michael@0 1723 }
michael@0 1724
michael@0 1725 MutexAutoLock lock(*notesFieldLock);
michael@0 1726
michael@0 1727 notesField->Append(data);
michael@0 1728 return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField);
michael@0 1729 }
michael@0 1730
michael@0 1731 // Returns true if found, false if not found.
michael@0 1732 bool GetAnnotation(const nsACString& key, nsACString& data)
michael@0 1733 {
michael@0 1734 if (!gExceptionHandler)
michael@0 1735 return false;
michael@0 1736
michael@0 1737 nsAutoCString entry;
michael@0 1738 if (!crashReporterAPIData_Hash->Get(key, &entry))
michael@0 1739 return false;
michael@0 1740
michael@0 1741 data = entry;
michael@0 1742 return true;
michael@0 1743 }
michael@0 1744
michael@0 1745 nsresult RegisterAppMemory(void* ptr, size_t length)
michael@0 1746 {
michael@0 1747 if (!GetEnabled())
michael@0 1748 return NS_ERROR_NOT_INITIALIZED;
michael@0 1749
michael@0 1750 #if defined(XP_LINUX) || defined(XP_WIN32)
michael@0 1751 gExceptionHandler->RegisterAppMemory(ptr, length);
michael@0 1752 return NS_OK;
michael@0 1753 #else
michael@0 1754 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 1755 #endif
michael@0 1756 }
michael@0 1757
michael@0 1758 nsresult UnregisterAppMemory(void* ptr)
michael@0 1759 {
michael@0 1760 if (!GetEnabled())
michael@0 1761 return NS_ERROR_NOT_INITIALIZED;
michael@0 1762
michael@0 1763 #if defined(XP_LINUX) || defined(XP_WIN32)
michael@0 1764 gExceptionHandler->UnregisterAppMemory(ptr);
michael@0 1765 return NS_OK;
michael@0 1766 #else
michael@0 1767 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 1768 #endif
michael@0 1769 }
michael@0 1770
michael@0 1771 bool GetServerURL(nsACString& aServerURL)
michael@0 1772 {
michael@0 1773 if (!gExceptionHandler)
michael@0 1774 return false;
michael@0 1775
michael@0 1776 return GetAnnotation(NS_LITERAL_CSTRING("ServerURL"), aServerURL);
michael@0 1777 }
michael@0 1778
michael@0 1779 nsresult SetServerURL(const nsACString& aServerURL)
michael@0 1780 {
michael@0 1781 // store server URL with the API data
michael@0 1782 // the client knows to handle this specially
michael@0 1783 return AnnotateCrashReport(NS_LITERAL_CSTRING("ServerURL"),
michael@0 1784 aServerURL);
michael@0 1785 }
michael@0 1786
michael@0 1787 nsresult
michael@0 1788 SetRestartArgs(int argc, char** argv)
michael@0 1789 {
michael@0 1790 if (!gExceptionHandler)
michael@0 1791 return NS_OK;
michael@0 1792
michael@0 1793 int i;
michael@0 1794 nsAutoCString envVar;
michael@0 1795 char *env;
michael@0 1796 char *argv0 = getenv("MOZ_APP_LAUNCHER");
michael@0 1797 for (i = 0; i < argc; i++) {
michael@0 1798 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
michael@0 1799 envVar.AppendInt(i);
michael@0 1800 envVar += "=";
michael@0 1801 if (argv0 && i == 0) {
michael@0 1802 // Is there a request to suppress default binary launcher?
michael@0 1803 envVar += argv0;
michael@0 1804 } else {
michael@0 1805 envVar += argv[i];
michael@0 1806 }
michael@0 1807
michael@0 1808 // PR_SetEnv() wants the string to be available for the lifetime
michael@0 1809 // of the app, so dup it here
michael@0 1810 env = ToNewCString(envVar);
michael@0 1811 if (!env)
michael@0 1812 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1813
michael@0 1814 PR_SetEnv(env);
michael@0 1815 }
michael@0 1816
michael@0 1817 // make sure the arg list is terminated
michael@0 1818 envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
michael@0 1819 envVar.AppendInt(i);
michael@0 1820 envVar += "=";
michael@0 1821
michael@0 1822 // PR_SetEnv() wants the string to be available for the lifetime
michael@0 1823 // of the app, so dup it here
michael@0 1824 env = ToNewCString(envVar);
michael@0 1825 if (!env)
michael@0 1826 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1827
michael@0 1828 PR_SetEnv(env);
michael@0 1829
michael@0 1830 // make sure we save the info in XUL_APP_FILE for the reporter
michael@0 1831 const char *appfile = PR_GetEnv("XUL_APP_FILE");
michael@0 1832 if (appfile && *appfile) {
michael@0 1833 envVar = "MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE=";
michael@0 1834 envVar += appfile;
michael@0 1835 env = ToNewCString(envVar);
michael@0 1836 PR_SetEnv(env);
michael@0 1837 }
michael@0 1838
michael@0 1839 return NS_OK;
michael@0 1840 }
michael@0 1841
michael@0 1842 #ifdef XP_WIN32
michael@0 1843 nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo)
michael@0 1844 {
michael@0 1845 if (!gExceptionHandler)
michael@0 1846 return NS_ERROR_NOT_INITIALIZED;
michael@0 1847
michael@0 1848 return gExceptionHandler->WriteMinidumpForException(aExceptionInfo) ? NS_OK : NS_ERROR_FAILURE;
michael@0 1849 }
michael@0 1850 #endif
michael@0 1851
michael@0 1852 #ifdef XP_LINUX
michael@0 1853 bool WriteMinidumpForSigInfo(int signo, siginfo_t* info, void* uc)
michael@0 1854 {
michael@0 1855 return gExceptionHandler->HandleSignal(signo, info, uc);
michael@0 1856 }
michael@0 1857 #endif
michael@0 1858
michael@0 1859 #ifdef XP_MACOSX
michael@0 1860 nsresult AppendObjCExceptionInfoToAppNotes(void *inException)
michael@0 1861 {
michael@0 1862 nsAutoCString excString;
michael@0 1863 GetObjCExceptionInfo(inException, excString);
michael@0 1864 AppendAppNotesToCrashReport(excString);
michael@0 1865 return NS_OK;
michael@0 1866 }
michael@0 1867 #endif
michael@0 1868
michael@0 1869 /*
michael@0 1870 * Combined code to get/set the crash reporter submission pref on
michael@0 1871 * different platforms.
michael@0 1872 */
michael@0 1873 static nsresult PrefSubmitReports(bool* aSubmitReports, bool writePref)
michael@0 1874 {
michael@0 1875 nsresult rv;
michael@0 1876 #if defined(XP_WIN32)
michael@0 1877 /*
michael@0 1878 * NOTE! This needs to stay in sync with the preference checking code
michael@0 1879 * in toolkit/crashreporter/client/crashreporter_win.cpp
michael@0 1880 */
michael@0 1881 nsCOMPtr<nsIXULAppInfo> appinfo =
michael@0 1882 do_GetService("@mozilla.org/xre/app-info;1", &rv);
michael@0 1883 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1884
michael@0 1885 nsAutoCString appVendor, appName;
michael@0 1886 rv = appinfo->GetVendor(appVendor);
michael@0 1887 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1888 rv = appinfo->GetName(appName);
michael@0 1889 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1890
michael@0 1891 nsCOMPtr<nsIWindowsRegKey> regKey
michael@0 1892 (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
michael@0 1893 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1894
michael@0 1895 nsAutoCString regPath;
michael@0 1896
michael@0 1897 regPath.AppendLiteral("Software\\");
michael@0 1898
michael@0 1899 // We need to ensure the registry keys are created so we can properly
michael@0 1900 // write values to it
michael@0 1901
michael@0 1902 // Create appVendor key
michael@0 1903 if(!appVendor.IsEmpty()) {
michael@0 1904 regPath.Append(appVendor);
michael@0 1905 regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
michael@0 1906 NS_ConvertUTF8toUTF16(regPath),
michael@0 1907 nsIWindowsRegKey::ACCESS_SET_VALUE);
michael@0 1908 regPath.AppendLiteral("\\");
michael@0 1909 }
michael@0 1910
michael@0 1911 // Create appName key
michael@0 1912 regPath.Append(appName);
michael@0 1913 regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
michael@0 1914 NS_ConvertUTF8toUTF16(regPath),
michael@0 1915 nsIWindowsRegKey::ACCESS_SET_VALUE);
michael@0 1916 regPath.AppendLiteral("\\");
michael@0 1917
michael@0 1918 // Create Crash Reporter key
michael@0 1919 regPath.AppendLiteral("Crash Reporter");
michael@0 1920 regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
michael@0 1921 NS_ConvertUTF8toUTF16(regPath),
michael@0 1922 nsIWindowsRegKey::ACCESS_SET_VALUE);
michael@0 1923
michael@0 1924 // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
michael@0 1925 // and we're done.
michael@0 1926 if (writePref) {
michael@0 1927 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
michael@0 1928 NS_ConvertUTF8toUTF16(regPath),
michael@0 1929 nsIWindowsRegKey::ACCESS_SET_VALUE);
michael@0 1930 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1931
michael@0 1932 uint32_t value = *aSubmitReports ? 1 : 0;
michael@0 1933 rv = regKey->WriteIntValue(NS_LITERAL_STRING("SubmitCrashReport"), value);
michael@0 1934 regKey->Close();
michael@0 1935 return rv;
michael@0 1936 }
michael@0 1937
michael@0 1938 // We're reading the pref value, so we need to first look under
michael@0 1939 // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to
michael@0 1940 // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults
michael@0 1941 // to "true".
michael@0 1942 uint32_t value;
michael@0 1943 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
michael@0 1944 NS_ConvertUTF8toUTF16(regPath),
michael@0 1945 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 1946 if (NS_SUCCEEDED(rv)) {
michael@0 1947 rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
michael@0 1948 regKey->Close();
michael@0 1949 if (NS_SUCCEEDED(rv)) {
michael@0 1950 *aSubmitReports = !!value;
michael@0 1951 return NS_OK;
michael@0 1952 }
michael@0 1953 }
michael@0 1954
michael@0 1955 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
michael@0 1956 NS_ConvertUTF8toUTF16(regPath),
michael@0 1957 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 1958 if (NS_FAILED(rv)) {
michael@0 1959 *aSubmitReports = true;
michael@0 1960 return NS_OK;
michael@0 1961 }
michael@0 1962
michael@0 1963 rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
michael@0 1964 // default to true on failure
michael@0 1965 if (NS_FAILED(rv)) {
michael@0 1966 value = 1;
michael@0 1967 rv = NS_OK;
michael@0 1968 }
michael@0 1969 regKey->Close();
michael@0 1970
michael@0 1971 *aSubmitReports = !!value;
michael@0 1972 return NS_OK;
michael@0 1973 #elif defined(XP_MACOSX)
michael@0 1974 rv = NS_OK;
michael@0 1975 if (writePref) {
michael@0 1976 CFPropertyListRef cfValue = (CFPropertyListRef)(*aSubmitReports ? kCFBooleanTrue : kCFBooleanFalse);
michael@0 1977 ::CFPreferencesSetAppValue(CFSTR("submitReport"),
michael@0 1978 cfValue,
michael@0 1979 reporterClientAppID);
michael@0 1980 if (!::CFPreferencesAppSynchronize(reporterClientAppID))
michael@0 1981 rv = NS_ERROR_FAILURE;
michael@0 1982 }
michael@0 1983 else {
michael@0 1984 *aSubmitReports = true;
michael@0 1985 Boolean keyExistsAndHasValidFormat = false;
michael@0 1986 Boolean prefValue = ::CFPreferencesGetAppBooleanValue(CFSTR("submitReport"),
michael@0 1987 reporterClientAppID,
michael@0 1988 &keyExistsAndHasValidFormat);
michael@0 1989 if (keyExistsAndHasValidFormat)
michael@0 1990 *aSubmitReports = !!prefValue;
michael@0 1991 }
michael@0 1992 return rv;
michael@0 1993 #elif defined(XP_UNIX)
michael@0 1994 /*
michael@0 1995 * NOTE! This needs to stay in sync with the preference checking code
michael@0 1996 * in toolkit/crashreporter/client/crashreporter_linux.cpp
michael@0 1997 */
michael@0 1998 nsCOMPtr<nsIFile> reporterINI;
michael@0 1999 rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI));
michael@0 2000 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2001 reporterINI->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
michael@0 2002 reporterINI->AppendNative(NS_LITERAL_CSTRING("crashreporter.ini"));
michael@0 2003
michael@0 2004 bool exists;
michael@0 2005 rv = reporterINI->Exists(&exists);
michael@0 2006 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2007 if (!exists) {
michael@0 2008 if (!writePref) {
michael@0 2009 // If reading the pref, default to true if .ini doesn't exist.
michael@0 2010 *aSubmitReports = true;
michael@0 2011 return NS_OK;
michael@0 2012 }
michael@0 2013 // Create the file so the INI processor can write to it.
michael@0 2014 rv = reporterINI->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
michael@0 2015 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2016 }
michael@0 2017
michael@0 2018 nsCOMPtr<nsIINIParserFactory> iniFactory =
michael@0 2019 do_GetService("@mozilla.org/xpcom/ini-processor-factory;1", &rv);
michael@0 2020 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2021
michael@0 2022 nsCOMPtr<nsIINIParser> iniParser;
michael@0 2023 rv = iniFactory->CreateINIParser(reporterINI,
michael@0 2024 getter_AddRefs(iniParser));
michael@0 2025 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2026
michael@0 2027 // If we're writing the pref, just set and we're done.
michael@0 2028 if (writePref) {
michael@0 2029 nsCOMPtr<nsIINIParserWriter> iniWriter = do_QueryInterface(iniParser);
michael@0 2030 NS_ENSURE_TRUE(iniWriter, NS_ERROR_FAILURE);
michael@0 2031
michael@0 2032 rv = iniWriter->SetString(NS_LITERAL_CSTRING("Crash Reporter"),
michael@0 2033 NS_LITERAL_CSTRING("SubmitReport"),
michael@0 2034 *aSubmitReports ? NS_LITERAL_CSTRING("1") :
michael@0 2035 NS_LITERAL_CSTRING("0"));
michael@0 2036 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2037 rv = iniWriter->WriteFile(nullptr, 0);
michael@0 2038 return rv;
michael@0 2039 }
michael@0 2040
michael@0 2041 nsAutoCString submitReportValue;
michael@0 2042 rv = iniParser->GetString(NS_LITERAL_CSTRING("Crash Reporter"),
michael@0 2043 NS_LITERAL_CSTRING("SubmitReport"),
michael@0 2044 submitReportValue);
michael@0 2045
michael@0 2046 // Default to "true" if the pref can't be found.
michael@0 2047 if (NS_FAILED(rv))
michael@0 2048 *aSubmitReports = true;
michael@0 2049 else if (submitReportValue.EqualsASCII("0"))
michael@0 2050 *aSubmitReports = false;
michael@0 2051 else
michael@0 2052 *aSubmitReports = true;
michael@0 2053
michael@0 2054 return NS_OK;
michael@0 2055 #else
michael@0 2056 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 2057 #endif
michael@0 2058 }
michael@0 2059
michael@0 2060 nsresult GetSubmitReports(bool* aSubmitReports)
michael@0 2061 {
michael@0 2062 return PrefSubmitReports(aSubmitReports, false);
michael@0 2063 }
michael@0 2064
michael@0 2065 nsresult SetSubmitReports(bool aSubmitReports)
michael@0 2066 {
michael@0 2067 nsresult rv;
michael@0 2068
michael@0 2069 nsCOMPtr<nsIObserverService> obsServ =
michael@0 2070 mozilla::services::GetObserverService();
michael@0 2071 if (!obsServ) {
michael@0 2072 return NS_ERROR_FAILURE;
michael@0 2073 }
michael@0 2074
michael@0 2075 rv = PrefSubmitReports(&aSubmitReports, true);
michael@0 2076 if (NS_FAILED(rv)) {
michael@0 2077 return rv;
michael@0 2078 }
michael@0 2079
michael@0 2080 obsServ->NotifyObservers(nullptr, "submit-reports-pref-changed", nullptr);
michael@0 2081 return NS_OK;
michael@0 2082 }
michael@0 2083
michael@0 2084 void
michael@0 2085 UpdateCrashEventsDir()
michael@0 2086 {
michael@0 2087 nsCOMPtr<nsIFile> eventsDir;
michael@0 2088
michael@0 2089 // We prefer the following locations in order:
michael@0 2090 //
michael@0 2091 // 1. If environment variable is present, use it. We don't expect
michael@0 2092 // the environment variable except for tests and other atypical setups.
michael@0 2093 // 2. Inside the profile directory.
michael@0 2094 // 3. Inside the user application data directory (no profile available).
michael@0 2095 // 4. A temporary directory (setup likely is invalid / application is buggy).
michael@0 2096 const char *env = PR_GetEnv("CRASHES_EVENTS_DIR");
michael@0 2097 if (env) {
michael@0 2098 eventsDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
michael@0 2099 if (!eventsDir) {
michael@0 2100 return;
michael@0 2101 }
michael@0 2102 eventsDir->InitWithNativePath(nsDependentCString(env));
michael@0 2103 EnsureDirectoryExists(eventsDir);
michael@0 2104 } else {
michael@0 2105 nsresult rv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(eventsDir));
michael@0 2106 if (NS_SUCCEEDED(rv)) {
michael@0 2107 eventsDir->Append(NS_LITERAL_STRING("crashes"));
michael@0 2108 EnsureDirectoryExists(eventsDir);
michael@0 2109 eventsDir->Append(NS_LITERAL_STRING("events"));
michael@0 2110 EnsureDirectoryExists(eventsDir);
michael@0 2111 } else {
michael@0 2112 rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(eventsDir));
michael@0 2113 if (NS_SUCCEEDED(rv)) {
michael@0 2114 eventsDir->Append(NS_LITERAL_STRING("Crash Reports"));
michael@0 2115 EnsureDirectoryExists(eventsDir);
michael@0 2116 eventsDir->Append(NS_LITERAL_STRING("events"));
michael@0 2117 EnsureDirectoryExists(eventsDir);
michael@0 2118 } else {
michael@0 2119 NS_WARNING("Couldn't get the user appdata directory. Crash events may not be produced.");
michael@0 2120 return;
michael@0 2121 }
michael@0 2122 }
michael@0 2123 }
michael@0 2124
michael@0 2125 #ifdef XP_WIN
michael@0 2126 nsString path;
michael@0 2127 eventsDir->GetPath(path);
michael@0 2128 eventsDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
michael@0 2129 #else
michael@0 2130 nsCString path;
michael@0 2131 eventsDir->GetNativePath(path);
michael@0 2132 eventsDirectory = ToNewCString(path);
michael@0 2133 #endif
michael@0 2134 }
michael@0 2135
michael@0 2136 bool GetCrashEventsDir(nsAString& aPath)
michael@0 2137 {
michael@0 2138 if (!eventsDirectory) {
michael@0 2139 return false;
michael@0 2140 }
michael@0 2141
michael@0 2142 aPath = CONVERT_XP_CHAR_TO_UTF16(eventsDirectory);
michael@0 2143 return true;
michael@0 2144 }
michael@0 2145
michael@0 2146 static void
michael@0 2147 FindPendingDir()
michael@0 2148 {
michael@0 2149 if (pendingDirectory)
michael@0 2150 return;
michael@0 2151
michael@0 2152 nsCOMPtr<nsIFile> pendingDir;
michael@0 2153 nsresult rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(pendingDir));
michael@0 2154 if (NS_FAILED(rv)) {
michael@0 2155 NS_WARNING("Couldn't get the user appdata directory, crash dumps will go in an unusual location");
michael@0 2156 }
michael@0 2157 else {
michael@0 2158 pendingDir->Append(NS_LITERAL_STRING("Crash Reports"));
michael@0 2159 pendingDir->Append(NS_LITERAL_STRING("pending"));
michael@0 2160
michael@0 2161 #ifdef XP_WIN
michael@0 2162 nsString path;
michael@0 2163 pendingDir->GetPath(path);
michael@0 2164 pendingDirectory = reinterpret_cast<wchar_t*>(ToNewUnicode(path));
michael@0 2165 #else
michael@0 2166 nsCString path;
michael@0 2167 pendingDir->GetNativePath(path);
michael@0 2168 pendingDirectory = ToNewCString(path);
michael@0 2169 #endif
michael@0 2170 }
michael@0 2171 }
michael@0 2172
michael@0 2173 // The "pending" dir is Crash Reports/pending, from which minidumps
michael@0 2174 // can be submitted. Because this method may be called off the main thread,
michael@0 2175 // we store the pending directory as a path.
michael@0 2176 static bool
michael@0 2177 GetPendingDir(nsIFile** dir)
michael@0 2178 {
michael@0 2179 MOZ_ASSERT(OOPInitialized());
michael@0 2180 if (!pendingDirectory) {
michael@0 2181 return false;
michael@0 2182 }
michael@0 2183
michael@0 2184 nsCOMPtr<nsIFile> pending = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
michael@0 2185 if (!pending) {
michael@0 2186 NS_WARNING("Can't set up pending directory during shutdown.");
michael@0 2187 return false;
michael@0 2188 }
michael@0 2189 #ifdef XP_WIN
michael@0 2190 pending->InitWithPath(nsDependentString(pendingDirectory));
michael@0 2191 #else
michael@0 2192 pending->InitWithNativePath(nsDependentCString(pendingDirectory));
michael@0 2193 #endif
michael@0 2194 pending.swap(*dir);
michael@0 2195 return true;
michael@0 2196 }
michael@0 2197
michael@0 2198 // The "limbo" dir is where minidumps go to wait for something else to
michael@0 2199 // use them. If we're |ShouldReport()|, then the "something else" is
michael@0 2200 // a minidump submitter, and they're coming from the
michael@0 2201 // Crash Reports/pending/ dir. Otherwise, we don't know what the
michael@0 2202 // "somthing else" is, but the minidumps stay in [profile]/minidumps/
michael@0 2203 // limbo.
michael@0 2204 static bool
michael@0 2205 GetMinidumpLimboDir(nsIFile** dir)
michael@0 2206 {
michael@0 2207 if (ShouldReport()) {
michael@0 2208 return GetPendingDir(dir);
michael@0 2209 }
michael@0 2210 else {
michael@0 2211 #ifndef XP_LINUX
michael@0 2212 CreateFileFromPath(gExceptionHandler->dump_path(), dir);
michael@0 2213 #else
michael@0 2214 CreateFileFromPath(gExceptionHandler->minidump_descriptor().directory(),
michael@0 2215 dir);
michael@0 2216 #endif
michael@0 2217 return nullptr != *dir;
michael@0 2218 }
michael@0 2219 }
michael@0 2220
michael@0 2221 bool
michael@0 2222 GetMinidumpForID(const nsAString& id, nsIFile** minidump)
michael@0 2223 {
michael@0 2224 if (!GetMinidumpLimboDir(minidump))
michael@0 2225 return false;
michael@0 2226 (*minidump)->Append(id + NS_LITERAL_STRING(".dmp"));
michael@0 2227 return true;
michael@0 2228 }
michael@0 2229
michael@0 2230 bool
michael@0 2231 GetIDFromMinidump(nsIFile* minidump, nsAString& id)
michael@0 2232 {
michael@0 2233 if (NS_SUCCEEDED(minidump->GetLeafName(id))) {
michael@0 2234 id.Replace(id.Length() - 4, 4, NS_LITERAL_STRING(""));
michael@0 2235 return true;
michael@0 2236 }
michael@0 2237 return false;
michael@0 2238 }
michael@0 2239
michael@0 2240 bool
michael@0 2241 GetExtraFileForID(const nsAString& id, nsIFile** extraFile)
michael@0 2242 {
michael@0 2243 if (!GetMinidumpLimboDir(extraFile))
michael@0 2244 return false;
michael@0 2245 (*extraFile)->Append(id + NS_LITERAL_STRING(".extra"));
michael@0 2246 return true;
michael@0 2247 }
michael@0 2248
michael@0 2249 bool
michael@0 2250 GetExtraFileForMinidump(nsIFile* minidump, nsIFile** extraFile)
michael@0 2251 {
michael@0 2252 nsAutoString leafName;
michael@0 2253 nsresult rv = minidump->GetLeafName(leafName);
michael@0 2254 if (NS_FAILED(rv))
michael@0 2255 return false;
michael@0 2256
michael@0 2257 nsCOMPtr<nsIFile> extraF;
michael@0 2258 rv = minidump->Clone(getter_AddRefs(extraF));
michael@0 2259 if (NS_FAILED(rv))
michael@0 2260 return false;
michael@0 2261
michael@0 2262 leafName.Replace(leafName.Length() - 3, 3,
michael@0 2263 NS_LITERAL_STRING("extra"));
michael@0 2264 rv = extraF->SetLeafName(leafName);
michael@0 2265 if (NS_FAILED(rv))
michael@0 2266 return false;
michael@0 2267
michael@0 2268 *extraFile = nullptr;
michael@0 2269 extraF.swap(*extraFile);
michael@0 2270 return true;
michael@0 2271 }
michael@0 2272
michael@0 2273 bool
michael@0 2274 AppendExtraData(const nsAString& id, const AnnotationTable& data)
michael@0 2275 {
michael@0 2276 nsCOMPtr<nsIFile> extraFile;
michael@0 2277 if (!GetExtraFileForID(id, getter_AddRefs(extraFile)))
michael@0 2278 return false;
michael@0 2279 return AppendExtraData(extraFile, data);
michael@0 2280 }
michael@0 2281
michael@0 2282 //-----------------------------------------------------------------------------
michael@0 2283 // Helpers for AppendExtraData()
michael@0 2284 //
michael@0 2285 struct Blacklist {
michael@0 2286 Blacklist() : mItems(nullptr), mLen(0) { }
michael@0 2287 Blacklist(const char** items, int len) : mItems(items), mLen(len) { }
michael@0 2288
michael@0 2289 bool Contains(const nsACString& key) const {
michael@0 2290 for (int i = 0; i < mLen; ++i)
michael@0 2291 if (key.EqualsASCII(mItems[i]))
michael@0 2292 return true;
michael@0 2293 return false;
michael@0 2294 }
michael@0 2295
michael@0 2296 const char** mItems;
michael@0 2297 const int mLen;
michael@0 2298 };
michael@0 2299
michael@0 2300 struct EnumerateAnnotationsContext {
michael@0 2301 const Blacklist& blacklist;
michael@0 2302 PRFileDesc* fd;
michael@0 2303 };
michael@0 2304
michael@0 2305 static void
michael@0 2306 WriteAnnotation(PRFileDesc* fd, const nsACString& key, const nsACString& value)
michael@0 2307 {
michael@0 2308 PR_Write(fd, key.BeginReading(), key.Length());
michael@0 2309 PR_Write(fd, "=", 1);
michael@0 2310 PR_Write(fd, value.BeginReading(), value.Length());
michael@0 2311 PR_Write(fd, "\n", 1);
michael@0 2312 }
michael@0 2313
michael@0 2314 static PLDHashOperator
michael@0 2315 EnumerateAnnotations(const nsACString& key,
michael@0 2316 nsCString entry,
michael@0 2317 void* userData)
michael@0 2318 {
michael@0 2319 EnumerateAnnotationsContext* ctx =
michael@0 2320 static_cast<EnumerateAnnotationsContext*>(userData);
michael@0 2321 const Blacklist& blacklist = ctx->blacklist;
michael@0 2322
michael@0 2323 // skip entries in the blacklist
michael@0 2324 if (blacklist.Contains(key))
michael@0 2325 return PL_DHASH_NEXT;
michael@0 2326
michael@0 2327 WriteAnnotation(ctx->fd, key, entry);
michael@0 2328
michael@0 2329 return PL_DHASH_NEXT;
michael@0 2330 }
michael@0 2331
michael@0 2332 static bool
michael@0 2333 WriteExtraData(nsIFile* extraFile,
michael@0 2334 const AnnotationTable& data,
michael@0 2335 const Blacklist& blacklist,
michael@0 2336 bool writeCrashTime=false,
michael@0 2337 bool truncate=false)
michael@0 2338 {
michael@0 2339 PRFileDesc* fd;
michael@0 2340 int truncOrAppend = truncate ? PR_TRUNCATE : PR_APPEND;
michael@0 2341 nsresult rv =
michael@0 2342 extraFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | truncOrAppend,
michael@0 2343 0600, &fd);
michael@0 2344 if (NS_FAILED(rv))
michael@0 2345 return false;
michael@0 2346
michael@0 2347 EnumerateAnnotationsContext ctx = { blacklist, fd };
michael@0 2348 data.EnumerateRead(EnumerateAnnotations, &ctx);
michael@0 2349
michael@0 2350 if (writeCrashTime) {
michael@0 2351 time_t crashTime = time(nullptr);
michael@0 2352 char crashTimeString[32];
michael@0 2353 XP_TTOA(crashTime, crashTimeString, 10);
michael@0 2354
michael@0 2355 WriteAnnotation(fd,
michael@0 2356 nsDependentCString("CrashTime"),
michael@0 2357 nsDependentCString(crashTimeString));
michael@0 2358 }
michael@0 2359
michael@0 2360 PR_Close(fd);
michael@0 2361 return true;
michael@0 2362 }
michael@0 2363
michael@0 2364 bool
michael@0 2365 AppendExtraData(nsIFile* extraFile, const AnnotationTable& data)
michael@0 2366 {
michael@0 2367 return WriteExtraData(extraFile, data, Blacklist());
michael@0 2368 }
michael@0 2369
michael@0 2370
michael@0 2371 static bool
michael@0 2372 WriteExtraForMinidump(nsIFile* minidump,
michael@0 2373 const Blacklist& blacklist,
michael@0 2374 nsIFile** extraFile)
michael@0 2375 {
michael@0 2376 nsCOMPtr<nsIFile> extra;
michael@0 2377 if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra)))
michael@0 2378 return false;
michael@0 2379
michael@0 2380 if (!WriteExtraData(extra, *crashReporterAPIData_Hash,
michael@0 2381 blacklist,
michael@0 2382 true /*write crash time*/,
michael@0 2383 true /*truncate*/))
michael@0 2384 return false;
michael@0 2385
michael@0 2386 *extraFile = nullptr;
michael@0 2387 extra.swap(*extraFile);
michael@0 2388
michael@0 2389 return true;
michael@0 2390 }
michael@0 2391
michael@0 2392 // It really only makes sense to call this function when
michael@0 2393 // ShouldReport() is true.
michael@0 2394 static bool
michael@0 2395 MoveToPending(nsIFile* dumpFile, nsIFile* extraFile)
michael@0 2396 {
michael@0 2397 nsCOMPtr<nsIFile> pendingDir;
michael@0 2398 if (!GetPendingDir(getter_AddRefs(pendingDir)))
michael@0 2399 return false;
michael@0 2400
michael@0 2401 if (NS_FAILED(dumpFile->MoveTo(pendingDir, EmptyString()))) {
michael@0 2402 return false;
michael@0 2403 }
michael@0 2404
michael@0 2405 if (extraFile && NS_FAILED(extraFile->MoveTo(pendingDir, EmptyString()))) {
michael@0 2406 return false;
michael@0 2407 }
michael@0 2408
michael@0 2409 return true;
michael@0 2410 }
michael@0 2411
michael@0 2412 static void
michael@0 2413 OnChildProcessDumpRequested(void* aContext,
michael@0 2414 #ifdef XP_MACOSX
michael@0 2415 const ClientInfo& aClientInfo,
michael@0 2416 const xpstring& aFilePath
michael@0 2417 #else
michael@0 2418 const ClientInfo* aClientInfo,
michael@0 2419 const xpstring* aFilePath
michael@0 2420 #endif
michael@0 2421 )
michael@0 2422 {
michael@0 2423 nsCOMPtr<nsIFile> minidump;
michael@0 2424 nsCOMPtr<nsIFile> extraFile;
michael@0 2425
michael@0 2426 // Hold the mutex until the current dump request is complete, to
michael@0 2427 // prevent UnsetExceptionHandler() from pulling the rug out from
michael@0 2428 // under us.
michael@0 2429 MutexAutoLock lock(*dumpSafetyLock);
michael@0 2430 if (!isSafeToDump)
michael@0 2431 return;
michael@0 2432
michael@0 2433 CreateFileFromPath(
michael@0 2434 #ifdef XP_MACOSX
michael@0 2435 aFilePath,
michael@0 2436 #else
michael@0 2437 *aFilePath,
michael@0 2438 #endif
michael@0 2439 getter_AddRefs(minidump));
michael@0 2440
michael@0 2441 if (!WriteExtraForMinidump(minidump,
michael@0 2442 Blacklist(kSubprocessBlacklist,
michael@0 2443 ArrayLength(kSubprocessBlacklist)),
michael@0 2444 getter_AddRefs(extraFile)))
michael@0 2445 return;
michael@0 2446
michael@0 2447 if (ShouldReport())
michael@0 2448 MoveToPending(minidump, extraFile);
michael@0 2449
michael@0 2450 {
michael@0 2451 uint32_t pid =
michael@0 2452 #ifdef XP_MACOSX
michael@0 2453 aClientInfo.pid();
michael@0 2454 #else
michael@0 2455 aClientInfo->pid();
michael@0 2456 #endif
michael@0 2457
michael@0 2458 #ifdef MOZ_CRASHREPORTER_INJECTOR
michael@0 2459 bool runCallback;
michael@0 2460 #endif
michael@0 2461 {
michael@0 2462 MutexAutoLock lock(*dumpMapLock);
michael@0 2463 ChildProcessData* pd = pidToMinidump->PutEntry(pid);
michael@0 2464 MOZ_ASSERT(!pd->minidump);
michael@0 2465 pd->minidump = minidump;
michael@0 2466 pd->sequence = ++crashSequence;
michael@0 2467 #ifdef MOZ_CRASHREPORTER_INJECTOR
michael@0 2468 runCallback = nullptr != pd->callback;
michael@0 2469 #endif
michael@0 2470 }
michael@0 2471 #ifdef MOZ_CRASHREPORTER_INJECTOR
michael@0 2472 if (runCallback)
michael@0 2473 NS_DispatchToMainThread(new ReportInjectedCrash(pid));
michael@0 2474 #endif
michael@0 2475 }
michael@0 2476 }
michael@0 2477
michael@0 2478 static bool
michael@0 2479 OOPInitialized()
michael@0 2480 {
michael@0 2481 return pidToMinidump != nullptr;
michael@0 2482 }
michael@0 2483
michael@0 2484 #ifdef XP_MACOSX
michael@0 2485 static bool ChildFilter(void *context) {
michael@0 2486 mozilla::IOInterposer::Disable();
michael@0 2487 return true;
michael@0 2488 }
michael@0 2489 #endif
michael@0 2490
michael@0 2491 void
michael@0 2492 OOPInit()
michael@0 2493 {
michael@0 2494 if (OOPInitialized())
michael@0 2495 return;
michael@0 2496
michael@0 2497 MOZ_ASSERT(NS_IsMainThread());
michael@0 2498
michael@0 2499 NS_ABORT_IF_FALSE(gExceptionHandler != nullptr,
michael@0 2500 "attempt to initialize OOP crash reporter before in-process crashreporter!");
michael@0 2501
michael@0 2502 #if defined(XP_WIN)
michael@0 2503 childCrashNotifyPipe =
michael@0 2504 PR_smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
michael@0 2505 static_cast<int>(::GetCurrentProcessId()));
michael@0 2506
michael@0 2507 const std::wstring dumpPath = gExceptionHandler->dump_path();
michael@0 2508 crashServer = new CrashGenerationServer(
michael@0 2509 NS_ConvertASCIItoUTF16(childCrashNotifyPipe).get(),
michael@0 2510 nullptr, // default security attributes
michael@0 2511 nullptr, nullptr, // we don't care about process connect here
michael@0 2512 OnChildProcessDumpRequested, nullptr,
michael@0 2513 nullptr, nullptr, // we don't care about process exit here
michael@0 2514 nullptr, nullptr, // we don't care about upload request here
michael@0 2515 true, // automatically generate dumps
michael@0 2516 &dumpPath);
michael@0 2517
michael@0 2518 #elif defined(XP_LINUX)
michael@0 2519 if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd,
michael@0 2520 &clientSocketFd))
michael@0 2521 NS_RUNTIMEABORT("can't create crash reporter socketpair()");
michael@0 2522
michael@0 2523 const std::string dumpPath =
michael@0 2524 gExceptionHandler->minidump_descriptor().directory();
michael@0 2525 crashServer = new CrashGenerationServer(
michael@0 2526 serverSocketFd,
michael@0 2527 OnChildProcessDumpRequested, nullptr,
michael@0 2528 nullptr, nullptr, // we don't care about process exit here
michael@0 2529 true,
michael@0 2530 &dumpPath);
michael@0 2531
michael@0 2532 #elif defined(XP_MACOSX)
michael@0 2533 childCrashNotifyPipe =
michael@0 2534 PR_smprintf("gecko-crash-server-pipe.%i",
michael@0 2535 static_cast<int>(getpid()));
michael@0 2536 const std::string dumpPath = gExceptionHandler->dump_path();
michael@0 2537
michael@0 2538 crashServer = new CrashGenerationServer(
michael@0 2539 childCrashNotifyPipe,
michael@0 2540 ChildFilter,
michael@0 2541 nullptr,
michael@0 2542 OnChildProcessDumpRequested, nullptr,
michael@0 2543 nullptr, nullptr,
michael@0 2544 true, // automatically generate dumps
michael@0 2545 dumpPath);
michael@0 2546 #endif
michael@0 2547
michael@0 2548 if (!crashServer->Start())
michael@0 2549 NS_RUNTIMEABORT("can't start crash reporter server()");
michael@0 2550
michael@0 2551 pidToMinidump = new ChildMinidumpMap();
michael@0 2552
michael@0 2553 dumpMapLock = new Mutex("CrashReporter::dumpMapLock");
michael@0 2554
michael@0 2555 FindPendingDir();
michael@0 2556 UpdateCrashEventsDir();
michael@0 2557 }
michael@0 2558
michael@0 2559 static void
michael@0 2560 OOPDeinit()
michael@0 2561 {
michael@0 2562 if (!OOPInitialized()) {
michael@0 2563 NS_WARNING("OOPDeinit() without successful OOPInit()");
michael@0 2564 return;
michael@0 2565 }
michael@0 2566
michael@0 2567 #ifdef MOZ_CRASHREPORTER_INJECTOR
michael@0 2568 if (sInjectorThread) {
michael@0 2569 sInjectorThread->Shutdown();
michael@0 2570 NS_RELEASE(sInjectorThread);
michael@0 2571 }
michael@0 2572 #endif
michael@0 2573
michael@0 2574 delete crashServer;
michael@0 2575 crashServer = nullptr;
michael@0 2576
michael@0 2577 delete dumpMapLock;
michael@0 2578 dumpMapLock = nullptr;
michael@0 2579
michael@0 2580 delete pidToMinidump;
michael@0 2581 pidToMinidump = nullptr;
michael@0 2582
michael@0 2583 #if defined(XP_WIN)
michael@0 2584 PR_Free(childCrashNotifyPipe);
michael@0 2585 childCrashNotifyPipe = nullptr;
michael@0 2586 #endif
michael@0 2587 }
michael@0 2588
michael@0 2589 #if defined(XP_WIN) || defined(XP_MACOSX)
michael@0 2590 // Parent-side API for children
michael@0 2591 const char*
michael@0 2592 GetChildNotificationPipe()
michael@0 2593 {
michael@0 2594 if (!GetEnabled())
michael@0 2595 return kNullNotifyPipe;
michael@0 2596
michael@0 2597 MOZ_ASSERT(OOPInitialized());
michael@0 2598
michael@0 2599 return childCrashNotifyPipe;
michael@0 2600 }
michael@0 2601 #endif
michael@0 2602
michael@0 2603 #ifdef MOZ_CRASHREPORTER_INJECTOR
michael@0 2604 void
michael@0 2605 InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb)
michael@0 2606 {
michael@0 2607 if (!GetEnabled())
michael@0 2608 return;
michael@0 2609
michael@0 2610 if (!OOPInitialized())
michael@0 2611 OOPInit();
michael@0 2612
michael@0 2613 if (!sInjectorThread) {
michael@0 2614 if (NS_FAILED(NS_NewThread(&sInjectorThread)))
michael@0 2615 return;
michael@0 2616 }
michael@0 2617
michael@0 2618 {
michael@0 2619 MutexAutoLock lock(*dumpMapLock);
michael@0 2620 ChildProcessData* pd = pidToMinidump->PutEntry(processID);
michael@0 2621 MOZ_ASSERT(!pd->minidump && !pd->callback);
michael@0 2622 pd->callback = cb;
michael@0 2623 }
michael@0 2624
michael@0 2625 nsCOMPtr<nsIRunnable> r = new InjectCrashRunnable(processID);
michael@0 2626 sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL);
michael@0 2627 }
michael@0 2628
michael@0 2629 NS_IMETHODIMP
michael@0 2630 ReportInjectedCrash::Run()
michael@0 2631 {
michael@0 2632 // Crash reporting may have been disabled after this method was dispatched
michael@0 2633 if (!OOPInitialized())
michael@0 2634 return NS_OK;
michael@0 2635
michael@0 2636 InjectorCrashCallback* cb;
michael@0 2637 {
michael@0 2638 MutexAutoLock lock(*dumpMapLock);
michael@0 2639 ChildProcessData* pd = pidToMinidump->GetEntry(mPID);
michael@0 2640 if (!pd || !pd->callback)
michael@0 2641 return NS_OK;
michael@0 2642
michael@0 2643 MOZ_ASSERT(pd->minidump);
michael@0 2644
michael@0 2645 cb = pd->callback;
michael@0 2646 }
michael@0 2647
michael@0 2648 cb->OnCrash(mPID);
michael@0 2649 return NS_OK;
michael@0 2650 }
michael@0 2651
michael@0 2652 void
michael@0 2653 UnregisterInjectorCallback(DWORD processID)
michael@0 2654 {
michael@0 2655 if (!OOPInitialized())
michael@0 2656 return;
michael@0 2657
michael@0 2658 MutexAutoLock lock(*dumpMapLock);
michael@0 2659 pidToMinidump->RemoveEntry(processID);
michael@0 2660 }
michael@0 2661
michael@0 2662 #endif // MOZ_CRASHREPORTER_INJECTOR
michael@0 2663
michael@0 2664 bool
michael@0 2665 CheckForLastRunCrash()
michael@0 2666 {
michael@0 2667 if (lastRunCrashID)
michael@0 2668 return true;
michael@0 2669
michael@0 2670 // The exception handler callback leaves the filename of the
michael@0 2671 // last minidump in a known file.
michael@0 2672 nsCOMPtr<nsIFile> lastCrashFile;
michael@0 2673 CreateFileFromPath(crashMarkerFilename,
michael@0 2674 getter_AddRefs(lastCrashFile));
michael@0 2675
michael@0 2676 bool exists;
michael@0 2677 if (NS_FAILED(lastCrashFile->Exists(&exists)) || !exists) {
michael@0 2678 return false;
michael@0 2679 }
michael@0 2680
michael@0 2681 nsAutoCString lastMinidump_contents;
michael@0 2682 if (NS_FAILED(GetFileContents(lastCrashFile, lastMinidump_contents))) {
michael@0 2683 return false;
michael@0 2684 }
michael@0 2685 lastCrashFile->Remove(false);
michael@0 2686
michael@0 2687 #ifdef XP_WIN
michael@0 2688 // Ugly but effective.
michael@0 2689 nsDependentString lastMinidump(
michael@0 2690 reinterpret_cast<const char16_t*>(lastMinidump_contents.get()));
michael@0 2691 #else
michael@0 2692 nsAutoCString lastMinidump = lastMinidump_contents;
michael@0 2693 #endif
michael@0 2694 nsCOMPtr<nsIFile> lastMinidumpFile;
michael@0 2695 CreateFileFromPath(lastMinidump.get(),
michael@0 2696 getter_AddRefs(lastMinidumpFile));
michael@0 2697
michael@0 2698 if (!lastMinidumpFile || NS_FAILED(lastMinidumpFile->Exists(&exists)) || !exists) {
michael@0 2699 return false;
michael@0 2700 }
michael@0 2701
michael@0 2702 nsCOMPtr<nsIFile> lastExtraFile;
michael@0 2703 if (!GetExtraFileForMinidump(lastMinidumpFile,
michael@0 2704 getter_AddRefs(lastExtraFile))) {
michael@0 2705 return false;
michael@0 2706 }
michael@0 2707
michael@0 2708 FindPendingDir();
michael@0 2709
michael@0 2710 // Move {dump,extra} to pending folder
michael@0 2711 if (!MoveToPending(lastMinidumpFile, lastExtraFile)) {
michael@0 2712 return false;
michael@0 2713 }
michael@0 2714
michael@0 2715 lastRunCrashID = new nsString();
michael@0 2716 return GetIDFromMinidump(lastMinidumpFile, *lastRunCrashID);
michael@0 2717 }
michael@0 2718
michael@0 2719 bool
michael@0 2720 GetLastRunCrashID(nsAString& id)
michael@0 2721 {
michael@0 2722 if (!lastRunCrashID_checked) {
michael@0 2723 CheckForLastRunCrash();
michael@0 2724 lastRunCrashID_checked = true;
michael@0 2725 }
michael@0 2726
michael@0 2727 if (!lastRunCrashID) {
michael@0 2728 return false;
michael@0 2729 }
michael@0 2730
michael@0 2731 id = *lastRunCrashID;
michael@0 2732 return true;
michael@0 2733 }
michael@0 2734
michael@0 2735 #if defined(XP_WIN)
michael@0 2736 // Child-side API
michael@0 2737 bool
michael@0 2738 SetRemoteExceptionHandler(const nsACString& crashPipe)
michael@0 2739 {
michael@0 2740 // crash reporting is disabled
michael@0 2741 if (crashPipe.Equals(kNullNotifyPipe))
michael@0 2742 return true;
michael@0 2743
michael@0 2744 NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
michael@0 2745
michael@0 2746 gExceptionHandler = new google_breakpad::
michael@0 2747 ExceptionHandler(L"",
michael@0 2748 FPEFilter,
michael@0 2749 nullptr, // no minidump callback
michael@0 2750 nullptr, // no callback context
michael@0 2751 google_breakpad::ExceptionHandler::HANDLER_ALL,
michael@0 2752 MiniDumpNormal,
michael@0 2753 NS_ConvertASCIItoUTF16(crashPipe).get(),
michael@0 2754 nullptr);
michael@0 2755 #ifdef XP_WIN
michael@0 2756 gExceptionHandler->set_handle_debug_exceptions(true);
michael@0 2757 #endif
michael@0 2758
michael@0 2759 // we either do remote or nothing, no fallback to regular crash reporting
michael@0 2760 return gExceptionHandler->IsOutOfProcess();
michael@0 2761 }
michael@0 2762
michael@0 2763 //--------------------------------------------------
michael@0 2764 #elif defined(XP_LINUX)
michael@0 2765
michael@0 2766 // Parent-side API for children
michael@0 2767 bool
michael@0 2768 CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd)
michael@0 2769 {
michael@0 2770 if (!GetEnabled()) {
michael@0 2771 *childCrashFd = -1;
michael@0 2772 *childCrashRemapFd = -1;
michael@0 2773 return true;
michael@0 2774 }
michael@0 2775
michael@0 2776 MOZ_ASSERT(OOPInitialized());
michael@0 2777
michael@0 2778 *childCrashFd = clientSocketFd;
michael@0 2779 *childCrashRemapFd = kMagicChildCrashReportFd;
michael@0 2780
michael@0 2781 return true;
michael@0 2782 }
michael@0 2783
michael@0 2784 // Child-side API
michael@0 2785 bool
michael@0 2786 SetRemoteExceptionHandler()
michael@0 2787 {
michael@0 2788 NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
michael@0 2789
michael@0 2790 #ifndef XP_LINUX
michael@0 2791 xpstring path = "";
michael@0 2792 #else
michael@0 2793 // MinidumpDescriptor requires a non-empty path.
michael@0 2794 google_breakpad::MinidumpDescriptor path(".");
michael@0 2795 #endif
michael@0 2796 gExceptionHandler = new google_breakpad::
michael@0 2797 ExceptionHandler(path,
michael@0 2798 nullptr, // no filter callback
michael@0 2799 nullptr, // no minidump callback
michael@0 2800 nullptr, // no callback context
michael@0 2801 true, // install signal handlers
michael@0 2802 kMagicChildCrashReportFd);
michael@0 2803
michael@0 2804 if (gDelayedAnnotations) {
michael@0 2805 for (uint32_t i = 0; i < gDelayedAnnotations->Length(); i++) {
michael@0 2806 gDelayedAnnotations->ElementAt(i)->Run();
michael@0 2807 }
michael@0 2808 delete gDelayedAnnotations;
michael@0 2809 }
michael@0 2810
michael@0 2811 // we either do remote or nothing, no fallback to regular crash reporting
michael@0 2812 return gExceptionHandler->IsOutOfProcess();
michael@0 2813 }
michael@0 2814
michael@0 2815 //--------------------------------------------------
michael@0 2816 #elif defined(XP_MACOSX)
michael@0 2817 // Child-side API
michael@0 2818 bool
michael@0 2819 SetRemoteExceptionHandler(const nsACString& crashPipe)
michael@0 2820 {
michael@0 2821 // crash reporting is disabled
michael@0 2822 if (crashPipe.Equals(kNullNotifyPipe))
michael@0 2823 return true;
michael@0 2824
michael@0 2825 NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
michael@0 2826
michael@0 2827 gExceptionHandler = new google_breakpad::
michael@0 2828 ExceptionHandler("",
michael@0 2829 Filter,
michael@0 2830 nullptr, // no minidump callback
michael@0 2831 nullptr, // no callback context
michael@0 2832 true, // install signal handlers
michael@0 2833 crashPipe.BeginReading());
michael@0 2834
michael@0 2835 // we either do remote or nothing, no fallback to regular crash reporting
michael@0 2836 return gExceptionHandler->IsOutOfProcess();
michael@0 2837 }
michael@0 2838 #endif // XP_WIN
michael@0 2839
michael@0 2840
michael@0 2841 bool
michael@0 2842 TakeMinidumpForChild(uint32_t childPid, nsIFile** dump, uint32_t* aSequence)
michael@0 2843 {
michael@0 2844 if (!GetEnabled())
michael@0 2845 return false;
michael@0 2846
michael@0 2847 MutexAutoLock lock(*dumpMapLock);
michael@0 2848
michael@0 2849 ChildProcessData* pd = pidToMinidump->GetEntry(childPid);
michael@0 2850 if (!pd)
michael@0 2851 return false;
michael@0 2852
michael@0 2853 NS_IF_ADDREF(*dump = pd->minidump);
michael@0 2854 if (aSequence) {
michael@0 2855 *aSequence = pd->sequence;
michael@0 2856 }
michael@0 2857
michael@0 2858 pidToMinidump->RemoveEntry(childPid);
michael@0 2859
michael@0 2860 return !!*dump;
michael@0 2861 }
michael@0 2862
michael@0 2863 //-----------------------------------------------------------------------------
michael@0 2864 // CreatePairedMinidumps() and helpers
michael@0 2865 //
michael@0 2866
michael@0 2867 void
michael@0 2868 RenameAdditionalHangMinidump(nsIFile* minidump, nsIFile* childMinidump,
michael@0 2869 const nsACString& name)
michael@0 2870 {
michael@0 2871 nsCOMPtr<nsIFile> directory;
michael@0 2872 childMinidump->GetParent(getter_AddRefs(directory));
michael@0 2873 if (!directory)
michael@0 2874 return;
michael@0 2875
michael@0 2876 nsAutoCString leafName;
michael@0 2877 childMinidump->GetNativeLeafName(leafName);
michael@0 2878
michael@0 2879 // turn "<id>.dmp" into "<id>-<name>.dmp
michael@0 2880 leafName.Insert(NS_LITERAL_CSTRING("-") + name, leafName.Length() - 4);
michael@0 2881
michael@0 2882 minidump->MoveToNative(directory, leafName);
michael@0 2883 }
michael@0 2884
michael@0 2885 static bool
michael@0 2886 PairedDumpCallback(
michael@0 2887 #ifdef XP_LINUX
michael@0 2888 const MinidumpDescriptor& descriptor,
michael@0 2889 #else
michael@0 2890 const XP_CHAR* dump_path,
michael@0 2891 const XP_CHAR* minidump_id,
michael@0 2892 #endif
michael@0 2893 void* context,
michael@0 2894 #ifdef XP_WIN32
michael@0 2895 EXCEPTION_POINTERS* /*unused*/,
michael@0 2896 MDRawAssertionInfo* /*unused*/,
michael@0 2897 #endif
michael@0 2898 bool succeeded)
michael@0 2899 {
michael@0 2900 nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
michael@0 2901
michael@0 2902 xpstring dump;
michael@0 2903 #ifdef XP_LINUX
michael@0 2904 dump = descriptor.path();
michael@0 2905 #else
michael@0 2906 dump = dump_path;
michael@0 2907 dump += XP_PATH_SEPARATOR;
michael@0 2908 dump += minidump_id;
michael@0 2909 dump += dumpFileExtension;
michael@0 2910 #endif
michael@0 2911
michael@0 2912 CreateFileFromPath(dump, getter_AddRefs(minidump));
michael@0 2913 return true;
michael@0 2914 }
michael@0 2915
michael@0 2916 static bool
michael@0 2917 PairedDumpCallbackExtra(
michael@0 2918 #ifdef XP_LINUX
michael@0 2919 const MinidumpDescriptor& descriptor,
michael@0 2920 #else
michael@0 2921 const XP_CHAR* dump_path,
michael@0 2922 const XP_CHAR* minidump_id,
michael@0 2923 #endif
michael@0 2924 void* context,
michael@0 2925 #ifdef XP_WIN32
michael@0 2926 EXCEPTION_POINTERS* /*unused*/,
michael@0 2927 MDRawAssertionInfo* /*unused*/,
michael@0 2928 #endif
michael@0 2929 bool succeeded)
michael@0 2930 {
michael@0 2931 PairedDumpCallback(
michael@0 2932 #ifdef XP_LINUX
michael@0 2933 descriptor,
michael@0 2934 #else
michael@0 2935 dump_path, minidump_id,
michael@0 2936 #endif
michael@0 2937 context,
michael@0 2938 #ifdef XP_WIN32
michael@0 2939 nullptr, nullptr,
michael@0 2940 #endif
michael@0 2941 succeeded);
michael@0 2942
michael@0 2943 nsCOMPtr<nsIFile>& minidump = *static_cast< nsCOMPtr<nsIFile>* >(context);
michael@0 2944
michael@0 2945 nsCOMPtr<nsIFile> extra;
michael@0 2946 return WriteExtraForMinidump(minidump, Blacklist(), getter_AddRefs(extra));
michael@0 2947 }
michael@0 2948
michael@0 2949 ThreadId
michael@0 2950 CurrentThreadId()
michael@0 2951 {
michael@0 2952 #if defined(XP_WIN)
michael@0 2953 return ::GetCurrentThreadId();
michael@0 2954 #elif defined(XP_LINUX)
michael@0 2955 return sys_gettid();
michael@0 2956 #elif defined(XP_MACOSX)
michael@0 2957 // Just return an index, since Mach ports can't be directly serialized
michael@0 2958 thread_act_port_array_t threads_for_task;
michael@0 2959 mach_msg_type_number_t thread_count;
michael@0 2960
michael@0 2961 if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
michael@0 2962 return -1;
michael@0 2963
michael@0 2964 for (unsigned int i = 0; i < thread_count; ++i) {
michael@0 2965 if (threads_for_task[i] == mach_thread_self())
michael@0 2966 return i;
michael@0 2967 }
michael@0 2968 abort();
michael@0 2969 #else
michael@0 2970 # error "Unsupported platform"
michael@0 2971 #endif
michael@0 2972 }
michael@0 2973
michael@0 2974 #ifdef XP_MACOSX
michael@0 2975 static mach_port_t
michael@0 2976 GetChildThread(ProcessHandle childPid, ThreadId childBlamedThread)
michael@0 2977 {
michael@0 2978 mach_port_t childThread = MACH_PORT_NULL;
michael@0 2979 thread_act_port_array_t threads_for_task;
michael@0 2980 mach_msg_type_number_t thread_count;
michael@0 2981
michael@0 2982 if (task_threads(childPid, &threads_for_task, &thread_count)
michael@0 2983 == KERN_SUCCESS && childBlamedThread < thread_count) {
michael@0 2984 childThread = threads_for_task[childBlamedThread];
michael@0 2985 }
michael@0 2986
michael@0 2987 return childThread;
michael@0 2988 }
michael@0 2989 #endif
michael@0 2990
michael@0 2991 bool
michael@0 2992 CreatePairedMinidumps(ProcessHandle childPid,
michael@0 2993 ThreadId childBlamedThread,
michael@0 2994 nsIFile** childDump)
michael@0 2995 {
michael@0 2996 if (!GetEnabled())
michael@0 2997 return false;
michael@0 2998
michael@0 2999 #ifdef XP_MACOSX
michael@0 3000 mach_port_t childThread = GetChildThread(childPid, childBlamedThread);
michael@0 3001 #else
michael@0 3002 ThreadId childThread = childBlamedThread;
michael@0 3003 #endif
michael@0 3004
michael@0 3005 xpstring dump_path;
michael@0 3006 #ifndef XP_LINUX
michael@0 3007 dump_path = gExceptionHandler->dump_path();
michael@0 3008 #else
michael@0 3009 dump_path = gExceptionHandler->minidump_descriptor().directory();
michael@0 3010 #endif
michael@0 3011
michael@0 3012 // dump the child
michael@0 3013 nsCOMPtr<nsIFile> childMinidump;
michael@0 3014 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
michael@0 3015 childPid,
michael@0 3016 childThread,
michael@0 3017 dump_path,
michael@0 3018 PairedDumpCallbackExtra,
michael@0 3019 static_cast<void*>(&childMinidump)))
michael@0 3020 return false;
michael@0 3021
michael@0 3022 nsCOMPtr<nsIFile> childExtra;
michael@0 3023 GetExtraFileForMinidump(childMinidump, getter_AddRefs(childExtra));
michael@0 3024
michael@0 3025 // dump the parent
michael@0 3026 nsCOMPtr<nsIFile> parentMinidump;
michael@0 3027 if (!google_breakpad::ExceptionHandler::WriteMinidump(
michael@0 3028 dump_path,
michael@0 3029 #ifdef XP_MACOSX
michael@0 3030 true, // write exception stream
michael@0 3031 #endif
michael@0 3032 PairedDumpCallback,
michael@0 3033 static_cast<void*>(&parentMinidump))) {
michael@0 3034
michael@0 3035 childMinidump->Remove(false);
michael@0 3036 childExtra->Remove(false);
michael@0 3037
michael@0 3038 return false;
michael@0 3039 }
michael@0 3040
michael@0 3041 // success
michael@0 3042 RenameAdditionalHangMinidump(parentMinidump, childMinidump,
michael@0 3043 NS_LITERAL_CSTRING("browser"));
michael@0 3044
michael@0 3045 if (ShouldReport()) {
michael@0 3046 MoveToPending(childMinidump, childExtra);
michael@0 3047 MoveToPending(parentMinidump, nullptr);
michael@0 3048 }
michael@0 3049
michael@0 3050 childMinidump.forget(childDump);
michael@0 3051
michael@0 3052 return true;
michael@0 3053 }
michael@0 3054
michael@0 3055 bool
michael@0 3056 CreateAdditionalChildMinidump(ProcessHandle childPid,
michael@0 3057 ThreadId childBlamedThread,
michael@0 3058 nsIFile* parentMinidump,
michael@0 3059 const nsACString& name)
michael@0 3060 {
michael@0 3061 if (!GetEnabled())
michael@0 3062 return false;
michael@0 3063
michael@0 3064 #ifdef XP_MACOSX
michael@0 3065 mach_port_t childThread = GetChildThread(childPid, childBlamedThread);
michael@0 3066 #else
michael@0 3067 ThreadId childThread = childBlamedThread;
michael@0 3068 #endif
michael@0 3069
michael@0 3070 xpstring dump_path;
michael@0 3071 #ifndef XP_LINUX
michael@0 3072 dump_path = gExceptionHandler->dump_path();
michael@0 3073 #else
michael@0 3074 dump_path = gExceptionHandler->minidump_descriptor().directory();
michael@0 3075 #endif
michael@0 3076
michael@0 3077 // dump the child
michael@0 3078 nsCOMPtr<nsIFile> childMinidump;
michael@0 3079 if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild(
michael@0 3080 childPid,
michael@0 3081 childThread,
michael@0 3082 dump_path,
michael@0 3083 PairedDumpCallback,
michael@0 3084 static_cast<void*>(&childMinidump))) {
michael@0 3085 return false;
michael@0 3086 }
michael@0 3087
michael@0 3088 RenameAdditionalHangMinidump(childMinidump, parentMinidump, name);
michael@0 3089
michael@0 3090 return true;
michael@0 3091 }
michael@0 3092
michael@0 3093 bool
michael@0 3094 UnsetRemoteExceptionHandler()
michael@0 3095 {
michael@0 3096 delete gExceptionHandler;
michael@0 3097 gExceptionHandler = nullptr;
michael@0 3098 return true;
michael@0 3099 }
michael@0 3100
michael@0 3101 #if defined(MOZ_WIDGET_ANDROID)
michael@0 3102 void AddLibraryMapping(const char* library_name,
michael@0 3103 uintptr_t start_address,
michael@0 3104 size_t mapping_length,
michael@0 3105 size_t file_offset)
michael@0 3106 {
michael@0 3107 if (!gExceptionHandler) {
michael@0 3108 mapping_info info;
michael@0 3109 info.name = library_name;
michael@0 3110 info.start_address = start_address;
michael@0 3111 info.length = mapping_length;
michael@0 3112 info.file_offset = file_offset;
michael@0 3113 library_mappings.push_back(info);
michael@0 3114 }
michael@0 3115 else {
michael@0 3116 u_int8_t guid[sizeof(MDGUID)];
michael@0 3117 google_breakpad::FileID::ElfFileIdentifierFromMappedFile((void const *)start_address, guid);
michael@0 3118 gExceptionHandler->AddMappingInfo(library_name,
michael@0 3119 guid,
michael@0 3120 start_address,
michael@0 3121 mapping_length,
michael@0 3122 file_offset);
michael@0 3123 }
michael@0 3124 }
michael@0 3125 #endif
michael@0 3126
michael@0 3127 } // namespace CrashReporter

mercurial