1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Chromium headers must come before Mozilla headers.
8 #include "base/process_util.h"
10 #include "mozilla/Atomics.h"
11 #include "mozilla/IntentionalCrash.h"
12 #include "mozilla/Printf.h"
14 #include "MainThreadUtils.h"
15 #include "nsDebugImpl.h"
17 #include "nsExceptionHandler.h"
19 #include "nsXULAppAPI.h"
27 # include <android/log.h>
35 #include "nsTraceRefcnt.h"
43 # include "nsString.h"
46 #if defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) || \
47 defined(__NetBSD__) || defined(__OpenBSD__)
50 # include <sys/param.h>
51 # include <sys/sysctl.h>
54 #if defined(__OpenBSD__)
55 # include <sys/proc.h>
58 #if defined(__DragonFly__) || defined(__FreeBSD__)
59 # include <sys/user.h>
62 #if defined(__NetBSD__)
64 # define KERN_PROC KERN_PROC2
65 # define KINFO_PROC struct kinfo_proc2
67 # define KINFO_PROC struct kinfo_proc
70 #if defined(XP_MACOSX)
71 # define KP_FLAGS kp_proc.p_flag
72 #elif defined(__DragonFly__)
73 # define KP_FLAGS kp_flags
74 #elif defined(__FreeBSD__)
75 # define KP_FLAGS ki_flag
76 #elif defined(__OpenBSD__) && !defined(_P_TRACED)
77 # define KP_FLAGS p_psflags
78 # define P_TRACED PS_TRACED
80 # define KP_FLAGS p_flag
83 #include "mozilla/mozalloc_abort.h"
85 static void Abort(const char* aMsg
);
87 static void RealBreak();
89 static void Break(const char* aMsg
);
94 # include <malloc.h> // for _alloca
95 #elif defined(XP_UNIX)
99 using namespace mozilla
;
101 static const char* sMultiprocessDescription
= nullptr;
103 static Atomic
<int32_t> gAssertionCount
;
105 NS_IMPL_QUERY_INTERFACE(nsDebugImpl
, nsIDebug2
)
107 NS_IMETHODIMP_(MozExternalRefCountType
)
108 nsDebugImpl::AddRef() { return 2; }
110 NS_IMETHODIMP_(MozExternalRefCountType
)
111 nsDebugImpl::Release() { return 1; }
114 nsDebugImpl::Assertion(const char* aStr
, const char* aExpr
, const char* aFile
,
116 NS_DebugBreak(NS_DEBUG_ASSERTION
, aStr
, aExpr
, aFile
, aLine
);
121 nsDebugImpl::Warning(const char* aStr
, const char* aFile
, int32_t aLine
) {
122 NS_DebugBreak(NS_DEBUG_WARNING
, aStr
, nullptr, aFile
, aLine
);
127 nsDebugImpl::Break(const char* aFile
, int32_t aLine
) {
128 NS_DebugBreak(NS_DEBUG_BREAK
, nullptr, nullptr, aFile
, aLine
);
133 nsDebugImpl::Abort(const char* aFile
, int32_t aLine
) {
134 NS_DebugBreak(NS_DEBUG_ABORT
, nullptr, nullptr, aFile
, aLine
);
139 nsDebugImpl::CrashWithOOM() {
144 // From toolkit/library/rust/lib.rs
145 extern "C" void intentional_panic(const char* message
);
148 nsDebugImpl::RustPanic(const char* aMessage
) {
149 intentional_panic(aMessage
);
153 // From toolkit/library/rust/lib.rs
154 extern "C" void debug_log(const char* target
, const char* message
);
157 nsDebugImpl::RustLog(const char* aTarget
, const char* aMessage
) {
158 debug_log(aTarget
, aMessage
);
163 nsDebugImpl::GetIsDebugBuild(bool* aResult
) {
173 nsDebugImpl::GetAssertionCount(int32_t* aResult
) {
174 *aResult
= gAssertionCount
;
179 nsDebugImpl::GetIsDebuggerAttached(bool* aResult
) {
182 #if defined(__OpenBSD__) && defined(MOZ_SANDBOX)
183 // no access to KERN_PROC_PID sysctl when pledge'd
187 *aResult
= ::IsDebuggerPresent();
188 #elif defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) || \
189 defined(__NetBSD__) || defined(__OpenBSD__)
190 // Specify the info we're looking for
196 # if defined(__NetBSD__) || defined(__OpenBSD__)
201 u_int mibSize
= sizeof(mib
) / sizeof(int);
204 size_t infoSize
= sizeof(info
);
205 memset(&info
, 0, infoSize
);
207 if (sysctl(mib
, mibSize
, &info
, &infoSize
, nullptr, 0)) {
208 // if the call fails, default to false
213 if (info
.KP_FLAGS
& P_TRACED
) {
222 void nsDebugImpl::SetMultiprocessMode(const char* aDesc
) {
223 sMultiprocessDescription
= aDesc
;
226 /* static */ const char* nsDebugImpl::GetMultiprocessMode() {
227 return sMultiprocessDescription
;
231 * Implementation of the nsDebug methods. Note that this code is
232 * always compiled in, in case some other module that uses it is
233 * compiled with debugging even if this library is not.
235 enum nsAssertBehavior
{
236 NS_ASSERT_UNINITIALIZED
,
242 NS_ASSERT_STACK_AND_ABORT
245 static nsAssertBehavior
GetAssertBehavior() {
246 static nsAssertBehavior gAssertBehavior
= NS_ASSERT_UNINITIALIZED
;
247 if (gAssertBehavior
!= NS_ASSERT_UNINITIALIZED
) {
248 return gAssertBehavior
;
251 gAssertBehavior
= NS_ASSERT_WARN
;
253 const char* assertString
= PR_GetEnv("XPCOM_DEBUG_BREAK");
254 if (!assertString
|| !*assertString
) {
255 return gAssertBehavior
;
257 if (!strcmp(assertString
, "warn")) {
258 return gAssertBehavior
= NS_ASSERT_WARN
;
260 if (!strcmp(assertString
, "suspend")) {
261 return gAssertBehavior
= NS_ASSERT_SUSPEND
;
263 if (!strcmp(assertString
, "stack")) {
264 return gAssertBehavior
= NS_ASSERT_STACK
;
266 if (!strcmp(assertString
, "abort")) {
267 return gAssertBehavior
= NS_ASSERT_ABORT
;
269 if (!strcmp(assertString
, "trap") || !strcmp(assertString
, "break")) {
270 return gAssertBehavior
= NS_ASSERT_TRAP
;
272 if (!strcmp(assertString
, "stack-and-abort")) {
273 return gAssertBehavior
= NS_ASSERT_STACK_AND_ABORT
;
276 fprintf(stderr
, "Unrecognized value of XPCOM_DEBUG_BREAK\n");
277 return gAssertBehavior
;
280 struct FixedBuffer final
: public mozilla::PrintfTarget
{
281 FixedBuffer() : curlen(0) { buffer
[0] = '\0'; }
286 bool append(const char* sp
, size_t len
) override
;
289 bool FixedBuffer::append(const char* aBuf
, size_t aLen
) {
294 if (curlen
+ aLen
>= sizeof(buffer
)) {
295 aLen
= sizeof(buffer
) - curlen
- 1;
299 memcpy(buffer
+ curlen
, aBuf
, aLen
);
301 buffer
[curlen
] = '\0';
307 EXPORT_XPCOM_API(void)
308 NS_DebugBreak(uint32_t aSeverity
, const char* aStr
, const char* aExpr
,
309 const char* aFile
, int32_t aLine
) {
310 FixedBuffer nonPIDBuf
;
312 const char* sevString
= "WARNING";
315 case NS_DEBUG_ASSERTION
:
316 sevString
= "###!!! ASSERTION";
320 sevString
= "###!!! BREAK";
324 sevString
= "###!!! ABORT";
328 aSeverity
= NS_DEBUG_WARNING
;
331 nonPIDBuf
.print("%s: ", sevString
);
333 nonPIDBuf
.print("%s: ", aStr
);
336 nonPIDBuf
.print("'%s', ", aExpr
);
338 if (aFile
|| aLine
!= -1) {
339 nonPIDBuf
.print("file %s:%d", aFile
? aFile
: "<unknown>",
340 aLine
!= -1 ? aLine
: 0);
343 // Print "[PID]" or "[Desc PID]" at the beginning of the message.
345 if (sMultiprocessDescription
) {
346 buf
.print("%s ", sMultiprocessDescription
);
349 bool isMainthread
= (NS_IsMainThreadTLSInitialized() && NS_IsMainThread());
350 PRThread
* currentThread
= PR_GetCurrentThread();
351 const char* currentThreadName
=
352 isMainthread
? "Main Thread" : PR_GetThreadName(currentThread
);
353 if (currentThreadName
) {
354 buf
.print("%d, %s] %s", base::GetCurrentProcId(), currentThreadName
,
357 buf
.print("%d, Unnamed thread %p] %s", base::GetCurrentProcId(),
358 currentThread
, nonPIDBuf
.buffer
);
361 // errors on platforms without a debugdlg ring a bell on stderr
363 if (aSeverity
!= NS_DEBUG_WARNING
) {
364 fprintf(stderr
, "\07");
369 __android_log_print(ANDROID_LOG_INFO
, "Gecko", "%s", buf
.buffer
);
372 // Write the message to stderr unless it's a warning and MOZ_IGNORE_WARNINGS
374 if (!(PR_GetEnv("MOZ_IGNORE_WARNINGS") && aSeverity
== NS_DEBUG_WARNING
)) {
375 fprintf(stderr
, "%s\n", buf
.buffer
);
380 case NS_DEBUG_WARNING
:
387 case NS_DEBUG_ABORT
: {
388 // Updating crash annotations in the child causes us to do IPC. This can
389 // really cause trouble if we're asserting from within IPC code. So we
390 // have to do without the annotations in that case.
391 if (XRE_IsParentProcess()) {
392 // Don't include the PID in the crash report annotation to
393 // allow faceting on crash-stats.mozilla.org.
394 nsCString
note("xpcom_runtime_abort(");
395 note
+= nonPIDBuf
.buffer
;
397 CrashReporter::AppendAppNotesToCrashReport(note
);
398 CrashReporter::AnnotateCrashReport(
399 CrashReporter::Annotation::AbortMessage
,
400 nsDependentCString(nonPIDBuf
.buffer
));
403 #if defined(DEBUG) && defined(_WIN32)
407 nsTraceRefcnt::WalkTheStack(stderr
);
414 // Now we deal with assertions
417 switch (GetAssertBehavior()) {
421 case NS_ASSERT_SUSPEND
:
423 fprintf(stderr
, "Suspending process; attach with the debugger.\n");
430 case NS_ASSERT_STACK
:
431 nsTraceRefcnt::WalkTheStack(stderr
);
434 case NS_ASSERT_STACK_AND_ABORT
:
435 nsTraceRefcnt::WalkTheStack(stderr
);
436 // Fall through to abort
439 case NS_ASSERT_ABORT
:
444 case NS_ASSERT_UNINITIALIZED
: // Default to "trap" behavior
450 static void Abort(const char* aMsg
) {
451 NoteIntentionalCrash(XRE_GetProcessTypeString());
452 mozalloc_abort(aMsg
);
455 static void RealBreak() {
458 #elif defined(XP_MACOSX)
460 #elif defined(__GNUC__) && \
461 (defined(__i386__) || defined(__i386) || defined(__x86_64__))
463 #elif defined(__arm__)
465 # ifdef __ARM_ARCH_4T__
466 /* ARMv4T doesn't support the BKPT instruction, so if the compiler target
467 * is ARMv4T, we want to ensure the assembler will understand that ARMv5T
468 * instruction, while keeping the resulting object tagged as ARMv4T.
471 ".object_arch armv4t\n"
474 #elif defined(__aarch64__)
476 #elif defined(SOLARIS)
477 # if defined(__i386__) || defined(__i386) || defined(__x86_64__)
483 # warning do not know how to break on this platform
487 // Abort() calls this function, don't call it!
488 static void Break(const char* aMsg
) {
490 static int ignoreDebugger
;
491 if (!ignoreDebugger
) {
492 const char* shouldIgnoreDebugger
= getenv("XPCOM_DEBUG_DLG");
494 1 + (shouldIgnoreDebugger
&& !strcmp(shouldIgnoreDebugger
, "1"));
496 if ((ignoreDebugger
== 2) || !::IsDebuggerPresent()) {
497 DWORD code
= IDRETRY
;
499 /* Create the debug dialog out of process to avoid the crashes caused by
500 * Windows events leaking into our event loop from an in process dialog.
501 * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
502 * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
504 PROCESS_INFORMATION pi
;
506 wchar_t executable
[MAX_PATH
];
509 memset(&pi
, 0, sizeof(pi
));
511 memset(&si
, 0, sizeof(si
));
513 si
.wShowWindow
= SW_SHOW
;
515 // 2nd arg of CreateProcess is in/out
516 wchar_t* msgCopy
= (wchar_t*)_alloca((strlen(aMsg
) + 1) * sizeof(wchar_t));
517 wcscpy(msgCopy
, NS_ConvertUTF8toUTF16(aMsg
).get());
519 if (GetModuleFileNameW(GetModuleHandleW(L
"xpcom.dll"), executable
,
521 (pName
= wcsrchr(executable
, '\\')) != nullptr &&
522 wcscpy(pName
+ 1, L
"windbgdlg.exe") &&
523 CreateProcessW(executable
, msgCopy
, nullptr, nullptr, false,
524 DETACHED_PROCESS
| NORMAL_PRIORITY_CLASS
, nullptr,
525 nullptr, &si
, &pi
)) {
526 WaitForSingleObject(pi
.hProcess
, INFINITE
);
527 GetExitCodeProcess(pi
.hProcess
, &code
);
528 CloseHandle(pi
.hProcess
);
529 CloseHandle(pi
.hThread
);
534 // This should exit us
536 // If we are ignored exit this way..
545 #elif defined(XP_MACOSX)
546 /* Note that we put this Mac OS X test above the GNUC/x86 test because the
547 * GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86
548 * impls to be the same.
551 #elif defined(__GNUC__) && \
552 (defined(__i386__) || defined(__i386) || defined(__x86_64__))
554 #elif defined(__arm__) || defined(__aarch64__)
556 #elif defined(SOLARIS)
559 # warning do not know how to break on this platform
563 nsresult
nsDebugImpl::Create(nsISupports
* aOuter
, const nsIID
& aIID
,
564 void** aInstancePtr
) {
565 static const nsDebugImpl
* sImpl
;
567 if (NS_WARN_IF(aOuter
)) {
568 return NS_ERROR_NO_AGGREGATION
;
572 sImpl
= new nsDebugImpl();
575 return const_cast<nsDebugImpl
*>(sImpl
)->QueryInterface(aIID
, aInstancePtr
);
578 ////////////////////////////////////////////////////////////////////////////////
580 nsresult
NS_ErrorAccordingToNSPR() {
581 PRErrorCode err
= PR_GetError();
583 case PR_OUT_OF_MEMORY_ERROR
:
584 return NS_ERROR_OUT_OF_MEMORY
;
585 case PR_WOULD_BLOCK_ERROR
:
586 return NS_BASE_STREAM_WOULD_BLOCK
;
587 case PR_FILE_NOT_FOUND_ERROR
:
588 return NS_ERROR_FILE_NOT_FOUND
;
589 case PR_READ_ONLY_FILESYSTEM_ERROR
:
590 return NS_ERROR_FILE_READ_ONLY
;
591 case PR_NOT_DIRECTORY_ERROR
:
592 return NS_ERROR_FILE_NOT_DIRECTORY
;
593 case PR_IS_DIRECTORY_ERROR
:
594 return NS_ERROR_FILE_IS_DIRECTORY
;
596 return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK
;
597 case PR_FILE_EXISTS_ERROR
:
598 return NS_ERROR_FILE_ALREADY_EXISTS
;
599 case PR_FILE_IS_LOCKED_ERROR
:
600 return NS_ERROR_FILE_IS_LOCKED
;
601 case PR_FILE_TOO_BIG_ERROR
:
602 return NS_ERROR_FILE_TOO_BIG
;
603 case PR_NO_DEVICE_SPACE_ERROR
:
604 return NS_ERROR_FILE_NO_DEVICE_SPACE
;
605 case PR_NAME_TOO_LONG_ERROR
:
606 return NS_ERROR_FILE_NAME_TOO_LONG
;
607 case PR_DIRECTORY_NOT_EMPTY_ERROR
:
608 return NS_ERROR_FILE_DIR_NOT_EMPTY
;
609 case PR_NO_ACCESS_RIGHTS_ERROR
:
610 return NS_ERROR_FILE_ACCESS_DENIED
;
612 return NS_ERROR_FAILURE
;
616 void NS_ABORT_OOM(size_t aSize
) {
617 CrashReporter::AnnotateOOMAllocationSize(aSize
);