summaryrefslogtreecommitdiff
path: root/xpcom/base/nsDebugImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/base/nsDebugImpl.cpp')
-rw-r--r--xpcom/base/nsDebugImpl.cpp607
1 files changed, 607 insertions, 0 deletions
diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp
new file mode 100644
index 0000000000..36288d2037
--- /dev/null
+++ b/xpcom/base/nsDebugImpl.cpp
@@ -0,0 +1,607 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Chromium headers must come before Mozilla headers.
+#include "base/process_util.h"
+
+#include "mozilla/Atomics.h"
+
+#include "nsDebugImpl.h"
+#include "nsDebug.h"
+#ifdef MOZ_CRASHREPORTER
+# include "nsExceptionHandler.h"
+#endif
+#include "nsString.h"
+#include "nsXULAppAPI.h"
+#include "prprf.h"
+#include "nsError.h"
+#include "prerror.h"
+#include "prerr.h"
+#include "prenv.h"
+
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
+#ifdef _WIN32
+/* for getenv() */
+#include <stdlib.h>
+#endif
+
+#include "nsTraceRefcnt.h"
+
+#if defined(XP_UNIX)
+#include <signal.h>
+#endif
+
+#if defined(XP_WIN)
+#include <tchar.h>
+#include "nsString.h"
+#endif
+
+#if defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#endif
+
+#if defined(__OpenBSD__)
+#include <sys/proc.h>
+#endif
+
+#if defined(__DragonFly__) || defined(__FreeBSD__)
+#include <sys/user.h>
+#endif
+
+#if defined(__NetBSD__)
+#undef KERN_PROC
+#define KERN_PROC KERN_PROC2
+#define KINFO_PROC struct kinfo_proc2
+#else
+#define KINFO_PROC struct kinfo_proc
+#endif
+
+#if defined(XP_MACOSX)
+#define KP_FLAGS kp_proc.p_flag
+#elif defined(__DragonFly__)
+#define KP_FLAGS kp_flags
+#elif defined(__FreeBSD__)
+#define KP_FLAGS ki_flag
+#elif defined(__OpenBSD__) && !defined(_P_TRACED)
+#define KP_FLAGS p_psflags
+#define P_TRACED PS_TRACED
+#else
+#define KP_FLAGS p_flag
+#endif
+
+#include "mozilla/mozalloc_abort.h"
+
+static void
+Abort(const char* aMsg);
+
+static void
+RealBreak();
+
+static void
+Break(const char* aMsg);
+
+#if defined(_WIN32)
+#include <windows.h>
+#include <signal.h>
+#include <malloc.h> // for _alloca
+#elif defined(XP_UNIX)
+#include <stdlib.h>
+#endif
+
+using namespace mozilla;
+
+static const char* sMultiprocessDescription = nullptr;
+
+static Atomic<int32_t> gAssertionCount;
+
+NS_IMPL_QUERY_INTERFACE(nsDebugImpl, nsIDebug2)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsDebugImpl::AddRef()
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsDebugImpl::Release()
+{
+ return 1;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Assertion(const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_ASSERTION, aStr, aExpr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Warning(const char* aStr, const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_WARNING, aStr, nullptr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Break(const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_BREAK, nullptr, nullptr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::Abort(const char* aFile, int32_t aLine)
+{
+ NS_DebugBreak(NS_DEBUG_ABORT, nullptr, nullptr, aFile, aLine);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::GetIsDebugBuild(bool* aResult)
+{
+#ifdef DEBUG
+ *aResult = true;
+#else
+ *aResult = false;
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::GetAssertionCount(int32_t* aResult)
+{
+ *aResult = gAssertionCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDebugImpl::GetIsDebuggerAttached(bool* aResult)
+{
+ *aResult = false;
+
+#if defined(XP_WIN)
+ *aResult = ::IsDebuggerPresent();
+#elif defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+ // Specify the info we're looking for
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sizeof(KINFO_PROC),
+ 1,
+#endif
+ };
+ u_int mibSize = sizeof(mib) / sizeof(int);
+
+ KINFO_PROC info;
+ size_t infoSize = sizeof(info);
+ memset(&info, 0, infoSize);
+
+ if (sysctl(mib, mibSize, &info, &infoSize, nullptr, 0)) {
+ // if the call fails, default to false
+ *aResult = false;
+ return NS_OK;
+ }
+
+ if (info.KP_FLAGS & P_TRACED) {
+ *aResult = true;
+ }
+#endif
+
+ return NS_OK;
+}
+
+/* static */ void
+nsDebugImpl::SetMultiprocessMode(const char* aDesc)
+{
+ sMultiprocessDescription = aDesc;
+}
+
+/**
+ * Implementation of the nsDebug methods. Note that this code is
+ * always compiled in, in case some other module that uses it is
+ * compiled with debugging even if this library is not.
+ */
+enum nsAssertBehavior
+{
+ NS_ASSERT_UNINITIALIZED,
+ NS_ASSERT_WARN,
+ NS_ASSERT_SUSPEND,
+ NS_ASSERT_STACK,
+ NS_ASSERT_TRAP,
+ NS_ASSERT_ABORT,
+ NS_ASSERT_STACK_AND_ABORT
+};
+
+static nsAssertBehavior
+GetAssertBehavior()
+{
+ static nsAssertBehavior gAssertBehavior = NS_ASSERT_UNINITIALIZED;
+ if (gAssertBehavior != NS_ASSERT_UNINITIALIZED) {
+ return gAssertBehavior;
+ }
+
+ gAssertBehavior = NS_ASSERT_WARN;
+
+ const char* assertString = PR_GetEnv("XPCOM_DEBUG_BREAK");
+ if (!assertString || !*assertString) {
+ return gAssertBehavior;
+ }
+ if (!strcmp(assertString, "warn")) {
+ return gAssertBehavior = NS_ASSERT_WARN;
+ }
+ if (!strcmp(assertString, "suspend")) {
+ return gAssertBehavior = NS_ASSERT_SUSPEND;
+ }
+ if (!strcmp(assertString, "stack")) {
+ return gAssertBehavior = NS_ASSERT_STACK;
+ }
+ if (!strcmp(assertString, "abort")) {
+ return gAssertBehavior = NS_ASSERT_ABORT;
+ }
+ if (!strcmp(assertString, "trap") || !strcmp(assertString, "break")) {
+ return gAssertBehavior = NS_ASSERT_TRAP;
+ }
+ if (!strcmp(assertString, "stack-and-abort")) {
+ return gAssertBehavior = NS_ASSERT_STACK_AND_ABORT;
+ }
+
+ fprintf(stderr, "Unrecognized value of XPCOM_DEBUG_BREAK\n");
+ return gAssertBehavior;
+}
+
+struct FixedBuffer
+{
+ FixedBuffer() : curlen(0)
+ {
+ buffer[0] = '\0';
+ }
+
+ char buffer[500];
+ uint32_t curlen;
+};
+
+static int
+StuffFixedBuffer(void* aClosure, const char* aBuf, uint32_t aLen)
+{
+ if (!aLen) {
+ return 0;
+ }
+
+ FixedBuffer* fb = (FixedBuffer*)aClosure;
+
+ // strip the trailing null, we add it again later
+ if (aBuf[aLen - 1] == '\0') {
+ --aLen;
+ }
+
+ if (fb->curlen + aLen >= sizeof(fb->buffer)) {
+ aLen = sizeof(fb->buffer) - fb->curlen - 1;
+ }
+
+ if (aLen) {
+ memcpy(fb->buffer + fb->curlen, aBuf, aLen);
+ fb->curlen += aLen;
+ fb->buffer[fb->curlen] = '\0';
+ }
+
+ return aLen;
+}
+
+EXPORT_XPCOM_API(void)
+NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine)
+{
+ FixedBuffer nonPIDBuf;
+ FixedBuffer buf;
+ const char* sevString = "WARNING";
+
+ switch (aSeverity) {
+ case NS_DEBUG_ASSERTION:
+ sevString = "###!!! ASSERTION";
+ break;
+
+ case NS_DEBUG_BREAK:
+ sevString = "###!!! BREAK";
+ break;
+
+ case NS_DEBUG_ABORT:
+ sevString = "###!!! ABORT";
+ break;
+
+ default:
+ aSeverity = NS_DEBUG_WARNING;
+ }
+
+#define PRINT_TO_NONPID_BUFFER(...) PR_sxprintf(StuffFixedBuffer, &nonPIDBuf, __VA_ARGS__)
+ PRINT_TO_NONPID_BUFFER("%s: ", sevString);
+ if (aStr) {
+ PRINT_TO_NONPID_BUFFER("%s: ", aStr);
+ }
+ if (aExpr) {
+ PRINT_TO_NONPID_BUFFER("'%s', ", aExpr);
+ }
+ if (aFile) {
+ PRINT_TO_NONPID_BUFFER("file %s, ", aFile);
+ }
+ if (aLine != -1) {
+ PRINT_TO_NONPID_BUFFER("line %d", aLine);
+ }
+#undef PRINT_TO_NONPID_BUFFER
+
+ // Print "[PID]" or "[Desc PID]" at the beginning of the message.
+#define PRINT_TO_BUFFER(...) PR_sxprintf(StuffFixedBuffer, &buf, __VA_ARGS__)
+ PRINT_TO_BUFFER("[");
+ if (sMultiprocessDescription) {
+ PRINT_TO_BUFFER("%s ", sMultiprocessDescription);
+ }
+ PRINT_TO_BUFFER("%d] %s", base::GetCurrentProcId(), nonPIDBuf.buffer);
+#undef PRINT_TO_BUFFER
+
+
+ // errors on platforms without a debugdlg ring a bell on stderr
+#if !defined(XP_WIN)
+ if (aSeverity != NS_DEBUG_WARNING) {
+ fprintf(stderr, "\07");
+ }
+#endif
+
+#ifdef ANDROID
+ __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer);
+#endif
+
+ // Write the message to stderr unless it's a warning and MOZ_IGNORE_WARNINGS
+ // is set.
+ if (!(PR_GetEnv("MOZ_IGNORE_WARNINGS") && aSeverity == NS_DEBUG_WARNING)) {
+ fprintf(stderr, "%s\n", buf.buffer);
+ fflush(stderr);
+ }
+
+ switch (aSeverity) {
+ case NS_DEBUG_WARNING:
+ return;
+
+ case NS_DEBUG_BREAK:
+ Break(buf.buffer);
+ return;
+
+ case NS_DEBUG_ABORT: {
+#if defined(MOZ_CRASHREPORTER)
+ // Updating crash annotations in the child causes us to do IPC. This can
+ // really cause trouble if we're asserting from within IPC code. So we
+ // have to do without the annotations in that case.
+ if (XRE_IsParentProcess()) {
+ // Don't include the PID in the crash report annotation to
+ // allow faceting on crash-stats.mozilla.org.
+ nsCString note("xpcom_runtime_abort(");
+ note += nonPIDBuf.buffer;
+ note += ")";
+ CrashReporter::AppendAppNotesToCrashReport(note);
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AbortMessage"),
+ nsDependentCString(nonPIDBuf.buffer));
+ }
+#endif // MOZ_CRASHREPORTER
+
+#if defined(DEBUG) && defined(_WIN32)
+ RealBreak();
+#endif
+#if defined(DEBUG)
+ nsTraceRefcnt::WalkTheStack(stderr);
+#endif
+ Abort(buf.buffer);
+ return;
+ }
+ }
+
+ // Now we deal with assertions
+ gAssertionCount++;
+
+ switch (GetAssertBehavior()) {
+ case NS_ASSERT_WARN:
+ return;
+
+ case NS_ASSERT_SUSPEND:
+#ifdef XP_UNIX
+ fprintf(stderr, "Suspending process; attach with the debugger.\n");
+ kill(0, SIGSTOP);
+#else
+ Break(buf.buffer);
+#endif
+ return;
+
+ case NS_ASSERT_STACK:
+ nsTraceRefcnt::WalkTheStack(stderr);
+ return;
+
+ case NS_ASSERT_STACK_AND_ABORT:
+ nsTraceRefcnt::WalkTheStack(stderr);
+ // Fall through to abort
+ MOZ_FALLTHROUGH;
+
+ case NS_ASSERT_ABORT:
+ Abort(buf.buffer);
+ return;
+
+ case NS_ASSERT_TRAP:
+ case NS_ASSERT_UNINITIALIZED: // Default to "trap" behavior
+ Break(buf.buffer);
+ return;
+ }
+}
+
+static void
+Abort(const char* aMsg)
+{
+ mozalloc_abort(aMsg);
+}
+
+static void
+RealBreak()
+{
+#if defined(_WIN32)
+ ::DebugBreak();
+#elif defined(XP_MACOSX)
+ raise(SIGTRAP);
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
+ asm("int $3");
+#elif defined(__arm__)
+ asm(
+#ifdef __ARM_ARCH_4T__
+ /* ARMv4T doesn't support the BKPT instruction, so if the compiler target
+ * is ARMv4T, we want to ensure the assembler will understand that ARMv5T
+ * instruction, while keeping the resulting object tagged as ARMv4T.
+ */
+ ".arch armv5t\n"
+ ".object_arch armv4t\n"
+#endif
+ "BKPT #0");
+#elif defined(SOLARIS)
+#if defined(__i386__) || defined(__i386) || defined(__x86_64__)
+ asm("int $3");
+#else
+ raise(SIGTRAP);
+#endif
+#else
+#warning do not know how to break on this platform
+#endif
+}
+
+// Abort() calls this function, don't call it!
+static void
+Break(const char* aMsg)
+{
+#if defined(_WIN32)
+ static int ignoreDebugger;
+ if (!ignoreDebugger) {
+ const char* shouldIgnoreDebugger = getenv("XPCOM_DEBUG_DLG");
+ ignoreDebugger =
+ 1 + (shouldIgnoreDebugger && !strcmp(shouldIgnoreDebugger, "1"));
+ }
+ if ((ignoreDebugger == 2) || !::IsDebuggerPresent()) {
+ DWORD code = IDRETRY;
+
+ /* Create the debug dialog out of process to avoid the crashes caused by
+ * Windows events leaking into our event loop from an in process dialog.
+ * We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
+ * See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
+ */
+ PROCESS_INFORMATION pi;
+ STARTUPINFOW si;
+ wchar_t executable[MAX_PATH];
+ wchar_t* pName;
+
+ memset(&pi, 0, sizeof(pi));
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.wShowWindow = SW_SHOW;
+
+ // 2nd arg of CreateProcess is in/out
+ wchar_t* msgCopy = (wchar_t*)_alloca((strlen(aMsg) + 1) * sizeof(wchar_t));
+ wcscpy(msgCopy, NS_ConvertUTF8toUTF16(aMsg).get());
+
+ if (GetModuleFileNameW(GetModuleHandleW(L"xpcom.dll"), executable, MAX_PATH) &&
+ (pName = wcsrchr(executable, '\\')) != nullptr &&
+ wcscpy(pName + 1, L"windbgdlg.exe") &&
+ CreateProcessW(executable, msgCopy, nullptr, nullptr,
+ false, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
+ nullptr, nullptr, &si, &pi)) {
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ GetExitCodeProcess(pi.hProcess, &code);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+
+ switch (code) {
+ case IDABORT:
+ //This should exit us
+ raise(SIGABRT);
+ //If we are ignored exit this way..
+ _exit(3);
+
+ case IDIGNORE:
+ return;
+ }
+ }
+
+ RealBreak();
+#elif defined(XP_MACOSX)
+ /* Note that we put this Mac OS X test above the GNUC/x86 test because the
+ * GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86
+ * impls to be the same.
+ */
+ RealBreak();
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
+ RealBreak();
+#elif defined(__arm__)
+ RealBreak();
+#elif defined(SOLARIS)
+ RealBreak();
+#else
+#warning do not know how to break on this platform
+#endif
+}
+
+nsresult
+nsDebugImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aInstancePtr)
+{
+ static const nsDebugImpl* sImpl;
+
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ if (!sImpl) {
+ sImpl = new nsDebugImpl();
+ }
+
+ return const_cast<nsDebugImpl*>(sImpl)->QueryInterface(aIID, aInstancePtr);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_ErrorAccordingToNSPR()
+{
+ PRErrorCode err = PR_GetError();
+ switch (err) {
+ case PR_OUT_OF_MEMORY_ERROR: return NS_ERROR_OUT_OF_MEMORY;
+ case PR_WOULD_BLOCK_ERROR: return NS_BASE_STREAM_WOULD_BLOCK;
+ case PR_FILE_NOT_FOUND_ERROR: return NS_ERROR_FILE_NOT_FOUND;
+ case PR_READ_ONLY_FILESYSTEM_ERROR: return NS_ERROR_FILE_READ_ONLY;
+ case PR_NOT_DIRECTORY_ERROR: return NS_ERROR_FILE_NOT_DIRECTORY;
+ case PR_IS_DIRECTORY_ERROR: return NS_ERROR_FILE_IS_DIRECTORY;
+ case PR_LOOP_ERROR: return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
+ case PR_FILE_EXISTS_ERROR: return NS_ERROR_FILE_ALREADY_EXISTS;
+ case PR_FILE_IS_LOCKED_ERROR: return NS_ERROR_FILE_IS_LOCKED;
+ case PR_FILE_TOO_BIG_ERROR: return NS_ERROR_FILE_TOO_BIG;
+ case PR_NO_DEVICE_SPACE_ERROR: return NS_ERROR_FILE_NO_DEVICE_SPACE;
+ case PR_NAME_TOO_LONG_ERROR: return NS_ERROR_FILE_NAME_TOO_LONG;
+ case PR_DIRECTORY_NOT_EMPTY_ERROR: return NS_ERROR_FILE_DIR_NOT_EMPTY;
+ case PR_NO_ACCESS_RIGHTS_ERROR: return NS_ERROR_FILE_ACCESS_DENIED;
+ default: return NS_ERROR_FAILURE;
+ }
+}
+
+void
+NS_ABORT_OOM(size_t aSize)
+{
+#if defined(MOZ_CRASHREPORTER)
+ CrashReporter::AnnotateOOMAllocationSize(aSize);
+#endif
+ MOZ_CRASH("OOM");
+}