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"
13 #include "mozilla/ProfilerMarkers.h"
15 #include "MainThreadUtils.h"
16 #include "nsDebugImpl.h"
18 #include "nsExceptionHandler.h"
20 #include "nsXULAppAPI.h"
26 # include <android/log.h>
34 #include "mozilla/StackWalk.h"
42 # include "nsString.h"
45 #if defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) || \
46 defined(__NetBSD__) || defined(__OpenBSD__)
49 # include <sys/param.h>
50 # include <sys/sysctl.h>
53 #if defined(__OpenBSD__)
54 # include <sys/proc.h>
57 #if defined(__DragonFly__) || defined(__FreeBSD__)
58 # include <sys/user.h>
61 #if defined(__NetBSD__)
63 # define KERN_PROC KERN_PROC2
64 # define KINFO_PROC struct kinfo_proc2
66 # define KINFO_PROC struct kinfo_proc
69 #if defined(XP_MACOSX)
70 # define KP_FLAGS kp_proc.p_flag
71 #elif defined(__DragonFly__)
72 # define KP_FLAGS kp_flags
73 #elif defined(__FreeBSD__)
74 # define KP_FLAGS ki_flag
75 #elif defined(__OpenBSD__) && !defined(_P_TRACED)
76 # define KP_FLAGS p_psflags
77 # define P_TRACED PS_TRACED
79 # define KP_FLAGS p_flag
82 static void Abort(const char* aMsg
);
84 static void RealBreak();
86 static void Break(const char* aMsg
);
91 # include <malloc.h> // for _alloca
94 using namespace mozilla
;
96 static const char* sMultiprocessDescription
= nullptr;
98 static Atomic
<int32_t> gAssertionCount
;
100 NS_IMPL_QUERY_INTERFACE(nsDebugImpl
, nsIDebug2
)
102 NS_IMETHODIMP_(MozExternalRefCountType
)
103 nsDebugImpl::AddRef() { return 2; }
105 NS_IMETHODIMP_(MozExternalRefCountType
)
106 nsDebugImpl::Release() { return 1; }
109 nsDebugImpl::Assertion(const char* aStr
, const char* aExpr
, const char* aFile
,
111 NS_DebugBreak(NS_DEBUG_ASSERTION
, aStr
, aExpr
, aFile
, aLine
);
116 nsDebugImpl::Warning(const char* aStr
, const char* aFile
, int32_t aLine
) {
117 NS_DebugBreak(NS_DEBUG_WARNING
, aStr
, nullptr, aFile
, aLine
);
122 nsDebugImpl::Break(const char* aFile
, int32_t aLine
) {
123 NS_DebugBreak(NS_DEBUG_BREAK
, nullptr, nullptr, aFile
, aLine
);
128 nsDebugImpl::Abort(const char* aFile
, int32_t aLine
) {
129 NS_DebugBreak(NS_DEBUG_ABORT
, nullptr, nullptr, aFile
, aLine
);
134 nsDebugImpl::CrashWithOOM() {
139 // From toolkit/library/rust/lib.rs
140 extern "C" void intentional_panic(const char* message
);
143 nsDebugImpl::RustPanic(const char* aMessage
) {
144 intentional_panic(aMessage
);
148 // From toolkit/library/rust/lib.rs
149 extern "C" void debug_log(const char* target
, const char* message
);
152 nsDebugImpl::RustLog(const char* aTarget
, const char* aMessage
) {
153 debug_log(aTarget
, aMessage
);
158 nsDebugImpl::GetIsDebugBuild(bool* aResult
) {
168 nsDebugImpl::GetAssertionCount(int32_t* aResult
) {
169 *aResult
= gAssertionCount
;
174 nsDebugImpl::GetIsDebuggerAttached(bool* aResult
) {
177 #if defined(__OpenBSD__) && defined(MOZ_SANDBOX)
178 // no access to KERN_PROC_PID sysctl when pledge'd
182 *aResult
= ::IsDebuggerPresent();
183 #elif defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) || \
184 defined(__NetBSD__) || defined(__OpenBSD__)
185 // Specify the info we're looking for
191 # if defined(__NetBSD__) || defined(__OpenBSD__)
196 u_int mibSize
= sizeof(mib
) / sizeof(int);
199 size_t infoSize
= sizeof(info
);
200 memset(&info
, 0, infoSize
);
202 if (sysctl(mib
, mibSize
, &info
, &infoSize
, nullptr, 0)) {
203 // if the call fails, default to false
208 if (info
.KP_FLAGS
& P_TRACED
) {
217 void nsDebugImpl::SetMultiprocessMode(const char* aDesc
) {
218 sMultiprocessDescription
= aDesc
;
221 /* static */ const char* nsDebugImpl::GetMultiprocessMode() {
222 return sMultiprocessDescription
;
226 * Implementation of the nsDebug methods. Note that this code is
227 * always compiled in, in case some other module that uses it is
228 * compiled with debugging even if this library is not.
230 enum nsAssertBehavior
{
231 NS_ASSERT_UNINITIALIZED
,
237 NS_ASSERT_STACK_AND_ABORT
240 static nsAssertBehavior
GetAssertBehavior() {
241 static nsAssertBehavior gAssertBehavior
= NS_ASSERT_UNINITIALIZED
;
242 if (gAssertBehavior
!= NS_ASSERT_UNINITIALIZED
) {
243 return gAssertBehavior
;
246 gAssertBehavior
= NS_ASSERT_WARN
;
248 const char* assertString
= PR_GetEnv("XPCOM_DEBUG_BREAK");
249 if (!assertString
|| !*assertString
) {
250 return gAssertBehavior
;
252 if (!strcmp(assertString
, "warn")) {
253 return gAssertBehavior
= NS_ASSERT_WARN
;
255 if (!strcmp(assertString
, "suspend")) {
256 return gAssertBehavior
= NS_ASSERT_SUSPEND
;
258 if (!strcmp(assertString
, "stack")) {
259 return gAssertBehavior
= NS_ASSERT_STACK
;
261 if (!strcmp(assertString
, "abort")) {
262 return gAssertBehavior
= NS_ASSERT_ABORT
;
264 if (!strcmp(assertString
, "trap") || !strcmp(assertString
, "break")) {
265 return gAssertBehavior
= NS_ASSERT_TRAP
;
267 if (!strcmp(assertString
, "stack-and-abort")) {
268 return gAssertBehavior
= NS_ASSERT_STACK_AND_ABORT
;
271 fprintf(stderr
, "Unrecognized value of XPCOM_DEBUG_BREAK\n");
272 return gAssertBehavior
;
275 struct FixedBuffer final
: public mozilla::PrintfTarget
{
276 FixedBuffer() : curlen(0) { buffer
[0] = '\0'; }
281 bool append(const char* sp
, size_t len
) override
;
284 bool FixedBuffer::append(const char* aBuf
, size_t aLen
) {
289 if (curlen
+ aLen
>= sizeof(buffer
)) {
290 aLen
= sizeof(buffer
) - curlen
- 1;
294 memcpy(buffer
+ curlen
, aBuf
, aLen
);
296 buffer
[curlen
] = '\0';
302 namespace geckoprofiler::markers
{
304 struct DebugBreakMarker
{
305 static constexpr Span
<const char> MarkerTypeName() {
306 return MakeStringSpan("DebugBreak");
308 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter
& aWriter
,
310 const ProfilerString8View
& aStr
,
311 const ProfilerString8View
& aExpr
,
312 const ProfilerString8View
& aFile
,
314 nsAutoCString
sevString("WARNING");
316 case NS_DEBUG_ASSERTION
:
317 sevString
= "ASSERTION";
328 aWriter
.StringProperty("Severity", sevString
);
329 // The 'name' property is searchable on the front-end.
330 if (aStr
.Length() != 0) {
331 aWriter
.StringProperty("Message", aStr
);
332 aWriter
.StringProperty("name", aStr
);
333 } else if (aExpr
.Length() != 0) {
334 aWriter
.StringProperty("name", aExpr
);
336 if (aExpr
.Length() != 0) {
337 aWriter
.StringProperty("Expression", aExpr
);
339 if (aFile
.Length() != 0) {
340 aWriter
.StringProperty("File", aFile
);
343 aWriter
.IntProperty("Line", aLine
);
346 static MarkerSchema
MarkerTypeDisplay() {
347 using MS
= MarkerSchema
;
348 MS schema
{MS::Location::TimelineOverview
, MS::Location::MarkerChart
,
349 MS::Location::MarkerTable
};
350 schema
.SetAllLabels("{marker.data.Severity}: {marker.data.name}");
351 schema
.AddKeyFormat("Message", MS::Format::String
);
352 schema
.AddKeyFormat("Severity", MS::Format::String
);
353 schema
.AddKeyFormat("Expression", MS::Format::String
);
354 schema
.AddKeyFormat("File", MS::Format::String
);
355 schema
.AddKeyFormat("Line", MS::Format::Integer
);
360 } // namespace geckoprofiler::markers
362 EXPORT_XPCOM_API(void)
363 NS_DebugBreak(uint32_t aSeverity
, const char* aStr
, const char* aExpr
,
364 const char* aFile
, int32_t aLine
) {
365 FixedBuffer nonPIDBuf
;
367 const char* sevString
= "WARNING";
370 case NS_DEBUG_ASSERTION
:
371 sevString
= "###!!! ASSERTION";
375 sevString
= "###!!! BREAK";
379 sevString
= "###!!! ABORT";
383 aSeverity
= NS_DEBUG_WARNING
;
386 nonPIDBuf
.print("%s: ", sevString
);
388 nonPIDBuf
.print("%s: ", aStr
);
391 nonPIDBuf
.print("'%s', ", aExpr
);
393 if (aFile
|| aLine
!= -1) {
394 nonPIDBuf
.print("file %s:%d", aFile
? aFile
: "<unknown>",
395 aLine
!= -1 ? aLine
: 0);
398 // Print "[PID]" or "[Desc PID]" at the beginning of the message.
400 if (sMultiprocessDescription
) {
401 buf
.print("%s ", sMultiprocessDescription
);
404 bool isMainthread
= (NS_IsMainThreadTLSInitialized() && NS_IsMainThread());
405 PRThread
* currentThread
= PR_GetCurrentThread();
406 const char* currentThreadName
=
407 isMainthread
? "Main Thread" : PR_GetThreadName(currentThread
);
408 if (currentThreadName
) {
409 buf
.print("%" PRIPID
", %s] %s", base::GetCurrentProcId(),
410 currentThreadName
, nonPIDBuf
.buffer
);
412 buf
.print("%" PRIPID
", Unnamed thread %p] %s", base::GetCurrentProcId(),
413 currentThread
, nonPIDBuf
.buffer
);
416 // errors on platforms without a debugdlg ring a bell on stderr
418 if (aSeverity
!= NS_DEBUG_WARNING
) {
419 fprintf(stderr
, "\07");
424 __android_log_print(ANDROID_LOG_INFO
, "Gecko", "%s", buf
.buffer
);
427 PROFILER_MARKER("NS_DebugBreak", OTHER
, MarkerStack::Capture(),
428 DebugBreakMarker
, aSeverity
,
429 ProfilerString8View::WrapNullTerminatedString(aStr
),
430 ProfilerString8View::WrapNullTerminatedString(aExpr
),
431 ProfilerString8View::WrapNullTerminatedString(aFile
), aLine
);
433 // Write the message to stderr unless it's a warning and MOZ_IGNORE_WARNINGS
435 if (!(PR_GetEnv("MOZ_IGNORE_WARNINGS") && aSeverity
== NS_DEBUG_WARNING
)) {
436 fprintf(stderr
, "%s\n", buf
.buffer
);
441 case NS_DEBUG_WARNING
:
448 case NS_DEBUG_ABORT
: {
449 // Updating crash annotations in the child causes us to do IPC. This can
450 // really cause trouble if we're asserting from within IPC code. So we
451 // have to do without the annotations in that case.
452 if (XRE_IsParentProcess()) {
453 // Don't include the PID in the crash report annotation to
454 // allow faceting on crash-stats.mozilla.org.
455 nsCString
note("xpcom_runtime_abort(");
456 note
+= nonPIDBuf
.buffer
;
458 CrashReporter::AppendAppNotesToCrashReport(note
);
459 CrashReporter::AnnotateCrashReport(
460 CrashReporter::Annotation::AbortMessage
,
461 nsDependentCString(nonPIDBuf
.buffer
));
464 #if defined(DEBUG) && defined(_WIN32)
468 MozWalkTheStack(stderr
);
475 // Now we deal with assertions
478 switch (GetAssertBehavior()) {
482 case NS_ASSERT_SUSPEND
:
484 fprintf(stderr
, "Suspending process; attach with the debugger.\n");
491 case NS_ASSERT_STACK
:
492 MozWalkTheStack(stderr
);
495 case NS_ASSERT_STACK_AND_ABORT
:
496 MozWalkTheStack(stderr
);
497 // Fall through to abort
500 case NS_ASSERT_ABORT
:
505 case NS_ASSERT_UNINITIALIZED
: // Default to "trap" behavior
511 static void Abort(const char* aMsg
) {
512 NoteIntentionalCrash(XRE_GetProcessTypeString());
513 MOZ_CRASH_UNSAFE(aMsg
);
516 static void RealBreak() {
519 #elif defined(XP_MACOSX)
521 #elif defined(__GNUC__) && \
522 (defined(__i386__) || defined(__i386) || defined(__x86_64__))
524 #elif defined(__arm__)
526 # ifdef __ARM_ARCH_4T__
527 /* ARMv4T doesn't support the BKPT instruction, so if the compiler target
528 * is ARMv4T, we want to ensure the assembler will understand that ARMv5T
529 * instruction, while keeping the resulting object tagged as ARMv4T.
532 ".object_arch armv4t\n"
535 #elif defined(__aarch64__)
537 #elif defined(SOLARIS)
538 # if defined(__i386__) || defined(__i386) || defined(__x86_64__)
544 # warning do not know how to break on this platform
548 // Abort() calls this function, don't call it!
549 static void Break(const char* aMsg
) {
551 static int ignoreDebugger
;
552 if (!ignoreDebugger
) {
553 const char* shouldIgnoreDebugger
= getenv("XPCOM_DEBUG_DLG");
555 1 + (shouldIgnoreDebugger
&& !strcmp(shouldIgnoreDebugger
, "1"));
557 if ((ignoreDebugger
== 2) || !::IsDebuggerPresent()) {
558 DWORD code
= IDRETRY
;
560 /* Create the debug dialog out of process to avoid the crashes caused by
561 * Windows events leaking into our event loop from an in process dialog.
562 * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
563 * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
565 PROCESS_INFORMATION pi
;
567 wchar_t executable
[MAX_PATH
];
570 memset(&pi
, 0, sizeof(pi
));
572 memset(&si
, 0, sizeof(si
));
574 si
.wShowWindow
= SW_SHOW
;
576 // 2nd arg of CreateProcess is in/out
577 wchar_t* msgCopy
= (wchar_t*)_alloca((strlen(aMsg
) + 1) * sizeof(wchar_t));
578 wcscpy(msgCopy
, NS_ConvertUTF8toUTF16(aMsg
).get());
580 if (GetModuleFileNameW(GetModuleHandleW(L
"xpcom.dll"), executable
,
582 (pName
= wcsrchr(executable
, '\\')) != nullptr &&
583 wcscpy(pName
+ 1, L
"windbgdlg.exe") &&
584 CreateProcessW(executable
, msgCopy
, nullptr, nullptr, false,
585 DETACHED_PROCESS
| NORMAL_PRIORITY_CLASS
, nullptr,
586 nullptr, &si
, &pi
)) {
587 WaitForSingleObject(pi
.hProcess
, INFINITE
);
588 GetExitCodeProcess(pi
.hProcess
, &code
);
589 CloseHandle(pi
.hProcess
);
590 CloseHandle(pi
.hThread
);
595 // This should exit us
597 // If we are ignored exit this way..
606 #elif defined(XP_MACOSX)
607 /* Note that we put this Mac OS X test above the GNUC/x86 test because the
608 * GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86
609 * impls to be the same.
612 #elif defined(__GNUC__) && \
613 (defined(__i386__) || defined(__i386) || defined(__x86_64__))
615 #elif defined(__arm__) || defined(__aarch64__)
617 #elif defined(SOLARIS)
620 # warning do not know how to break on this platform
624 nsresult
nsDebugImpl::Create(const nsIID
& aIID
, void** aInstancePtr
) {
625 static const nsDebugImpl
* sImpl
;
628 sImpl
= new nsDebugImpl();
631 return const_cast<nsDebugImpl
*>(sImpl
)->QueryInterface(aIID
, aInstancePtr
);
634 ////////////////////////////////////////////////////////////////////////////////
636 nsresult
NS_ErrorAccordingToNSPR() {
637 PRErrorCode err
= PR_GetError();
639 case PR_OUT_OF_MEMORY_ERROR
:
640 return NS_ERROR_OUT_OF_MEMORY
;
641 case PR_WOULD_BLOCK_ERROR
:
642 return NS_BASE_STREAM_WOULD_BLOCK
;
643 case PR_FILE_NOT_FOUND_ERROR
:
644 return NS_ERROR_FILE_NOT_FOUND
;
645 case PR_READ_ONLY_FILESYSTEM_ERROR
:
646 return NS_ERROR_FILE_READ_ONLY
;
647 case PR_NOT_DIRECTORY_ERROR
:
648 return NS_ERROR_FILE_NOT_DIRECTORY
;
649 case PR_IS_DIRECTORY_ERROR
:
650 return NS_ERROR_FILE_IS_DIRECTORY
;
652 return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK
;
653 case PR_FILE_EXISTS_ERROR
:
654 return NS_ERROR_FILE_ALREADY_EXISTS
;
655 case PR_FILE_IS_LOCKED_ERROR
:
656 return NS_ERROR_FILE_IS_LOCKED
;
657 case PR_FILE_TOO_BIG_ERROR
:
658 return NS_ERROR_FILE_TOO_BIG
;
659 case PR_NO_DEVICE_SPACE_ERROR
:
660 return NS_ERROR_FILE_NO_DEVICE_SPACE
;
661 case PR_NAME_TOO_LONG_ERROR
:
662 return NS_ERROR_FILE_NAME_TOO_LONG
;
663 case PR_DIRECTORY_NOT_EMPTY_ERROR
:
664 return NS_ERROR_FILE_DIR_NOT_EMPTY
;
665 case PR_NO_ACCESS_RIGHTS_ERROR
:
666 return NS_ERROR_FILE_ACCESS_DENIED
;
668 return NS_ERROR_FAILURE
;
672 void NS_ABORT_OOM(size_t aSize
) {
673 CrashReporter::AnnotateOOMAllocationSize(aSize
);