summaryrefslogtreecommitdiff
path: root/widget/cocoa/nsCocoaDebugUtils.mm
diff options
context:
space:
mode:
Diffstat (limited to 'widget/cocoa/nsCocoaDebugUtils.mm')
-rw-r--r--widget/cocoa/nsCocoaDebugUtils.mm284
1 files changed, 284 insertions, 0 deletions
diff --git a/widget/cocoa/nsCocoaDebugUtils.mm b/widget/cocoa/nsCocoaDebugUtils.mm
new file mode 100644
index 0000000000..35896dc401
--- /dev/null
+++ b/widget/cocoa/nsCocoaDebugUtils.mm
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsCocoaDebugUtils.h"
+
+#include <pthread.h>
+#include <libproc.h>
+#include <stdarg.h>
+#include <time.h>
+#include <execinfo.h>
+#include <asl.h>
+
+static char gProcPath[PROC_PIDPATHINFO_MAXSIZE] = {0};
+static char gBundleID[MAXPATHLEN] = {0};
+
+static void MaybeGetPathAndID()
+{
+ if (!gProcPath[0]) {
+ proc_pidpath(getpid(), gProcPath, sizeof(gProcPath));
+ }
+ if (!gBundleID[0]) {
+ // Apple's CFLog() uses "com.apple.console" (in its call to asl_open()) if
+ // it can't find the bundle id.
+ CFStringRef bundleID = NULL;
+ CFBundleRef mainBundle = CFBundleGetMainBundle();
+ if (mainBundle) {
+ bundleID = CFBundleGetIdentifier(mainBundle);
+ }
+ if (!bundleID) {
+ strcpy(gBundleID, "com.apple.console");
+ } else {
+ CFStringGetCString(bundleID, gBundleID, sizeof(gBundleID),
+ kCFStringEncodingUTF8);
+ }
+ }
+}
+
+static void GetThreadName(char* aName, size_t aSize)
+{
+ pthread_getname_np(pthread_self(), aName, aSize);
+}
+
+void
+nsCocoaDebugUtils::DebugLog(const char* aFormat, ...)
+{
+ va_list args;
+ va_start(args, aFormat);
+ CFStringRef formatCFSTR =
+ CFStringCreateWithCString(kCFAllocatorDefault, aFormat,
+ kCFStringEncodingUTF8);
+ DebugLogV(true, formatCFSTR, args);
+ CFRelease(formatCFSTR);
+ va_end(args);
+}
+
+void
+nsCocoaDebugUtils::DebugLogInt(bool aDecorate, const char* aFormat, ...)
+{
+ va_list args;
+ va_start(args, aFormat);
+ CFStringRef formatCFSTR =
+ CFStringCreateWithCString(kCFAllocatorDefault, aFormat,
+ kCFStringEncodingUTF8);
+ DebugLogV(aDecorate, formatCFSTR, args);
+ CFRelease(formatCFSTR);
+ va_end(args);
+}
+
+void
+nsCocoaDebugUtils::DebugLogV(bool aDecorate, CFStringRef aFormat,
+ va_list aArgs)
+{
+ MaybeGetPathAndID();
+
+ CFStringRef message =
+ CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL,
+ aFormat, aArgs);
+
+ int msgLength =
+ CFStringGetMaximumSizeForEncoding(CFStringGetLength(message),
+ kCFStringEncodingUTF8);
+ char* msgUTF8 = (char*) calloc(msgLength, 1);
+ CFStringGetCString(message, msgUTF8, msgLength, kCFStringEncodingUTF8);
+ CFRelease(message);
+
+ int finishedLength = msgLength + PROC_PIDPATHINFO_MAXSIZE;
+ char* finished = (char*) calloc(finishedLength, 1);
+ const time_t currentTime = time(NULL);
+ char timestamp[30] = {0};
+ ctime_r(&currentTime, timestamp);
+ if (aDecorate) {
+ char threadName[MAXPATHLEN] = {0};
+ GetThreadName(threadName, sizeof(threadName));
+ snprintf(finished, finishedLength, "(%s) %s[%u] %s[%p] %s\n",
+ timestamp, gProcPath, getpid(), threadName, pthread_self(), msgUTF8);
+ } else {
+ snprintf(finished, finishedLength, "%s\n", msgUTF8);
+ }
+ free(msgUTF8);
+
+ fputs(finished, stdout);
+
+ // Use the Apple System Log facility, as NSLog and CFLog do.
+ aslclient asl = asl_open(NULL, gBundleID, ASL_OPT_NO_DELAY);
+ aslmsg msg = asl_new(ASL_TYPE_MSG);
+ asl_set(msg, ASL_KEY_LEVEL, "4"); // kCFLogLevelWarning, used by NSLog()
+ asl_set(msg, ASL_KEY_MSG, finished);
+ asl_send(asl, msg);
+ asl_free(msg);
+ asl_close(asl);
+
+ free(finished);
+}
+
+CSTypeRef
+nsCocoaDebugUtils::sInitializer = {0};
+
+CSSymbolicatorRef
+nsCocoaDebugUtils::sSymbolicator = {0};
+
+#define STACK_MAX 256
+
+void
+nsCocoaDebugUtils::PrintStackTrace()
+{
+ void** addresses = (void**) calloc(STACK_MAX, sizeof(void*));
+ if (!addresses) {
+ return;
+ }
+
+ CSSymbolicatorRef symbolicator = GetSymbolicatorRef();
+ if (CSIsNull(symbolicator)) {
+ free(addresses);
+ return;
+ }
+
+ uint32_t count = backtrace(addresses, STACK_MAX);
+ for (uint32_t i = 0; i < count; ++i) {
+ PrintAddress(addresses[i]);
+ }
+
+ ReleaseSymbolicator();
+ free(addresses);
+}
+
+void
+nsCocoaDebugUtils::PrintAddress(void* aAddress)
+{
+ const char* ownerName = "unknown";
+ const char* addressString = "unknown + 0";
+
+ char* allocatedOwnerName = nullptr;
+ char* allocatedAddressString = nullptr;
+
+ CSSymbolOwnerRef owner = {0};
+ CSSymbolicatorRef symbolicator = GetSymbolicatorRef();
+
+ if (!CSIsNull(symbolicator)) {
+ owner =
+ CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator,
+ (unsigned long long) aAddress,
+ kCSNow);
+ }
+ if (!CSIsNull(owner)) {
+ ownerName = allocatedOwnerName = GetOwnerNameInt(aAddress, owner);
+ addressString = allocatedAddressString = GetAddressStringInt(aAddress, owner);
+ }
+ DebugLogInt(false, " (%s) %s", ownerName, addressString);
+
+ free(allocatedOwnerName);
+ free(allocatedAddressString);
+
+ ReleaseSymbolicator();
+}
+
+char*
+nsCocoaDebugUtils::GetOwnerName(void* aAddress)
+{
+ return GetOwnerNameInt(aAddress);
+}
+
+char*
+nsCocoaDebugUtils::GetOwnerNameInt(void* aAddress, CSTypeRef aOwner)
+{
+ char* retval = (char*) calloc(MAXPATHLEN, 1);
+
+ const char* ownerName = "unknown";
+
+ CSSymbolicatorRef symbolicator = GetSymbolicatorRef();
+ CSTypeRef owner = aOwner;
+
+ if (CSIsNull(owner) && !CSIsNull(symbolicator)) {
+ owner =
+ CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator,
+ (unsigned long long) aAddress,
+ kCSNow);
+ }
+
+ if (!CSIsNull(owner)) {
+ ownerName = CSSymbolOwnerGetName(owner);
+ }
+
+ snprintf(retval, MAXPATHLEN, "%s", ownerName);
+ ReleaseSymbolicator();
+
+ return retval;
+}
+
+char*
+nsCocoaDebugUtils::GetAddressString(void* aAddress)
+{
+ return GetAddressStringInt(aAddress);
+}
+
+char*
+nsCocoaDebugUtils::GetAddressStringInt(void* aAddress, CSTypeRef aOwner)
+{
+ char* retval = (char*) calloc(MAXPATHLEN, 1);
+
+ const char* addressName = "unknown";
+ unsigned long long addressOffset = 0;
+
+ CSSymbolicatorRef symbolicator = GetSymbolicatorRef();
+ CSTypeRef owner = aOwner;
+
+ if (CSIsNull(owner) && !CSIsNull(symbolicator)) {
+ owner =
+ CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator,
+ (unsigned long long) aAddress,
+ kCSNow);
+ }
+
+ if (!CSIsNull(owner)) {
+ CSSymbolRef symbol =
+ CSSymbolOwnerGetSymbolWithAddress(owner,
+ (unsigned long long) aAddress);
+ if (!CSIsNull(symbol)) {
+ addressName = CSSymbolGetName(symbol);
+ CSRange range = CSSymbolGetRange(symbol);
+ addressOffset = (unsigned long long) aAddress - range.location;
+ } else {
+ addressOffset = (unsigned long long)
+ aAddress - CSSymbolOwnerGetBaseAddress(owner);
+ }
+ }
+
+ snprintf(retval, MAXPATHLEN, "%s + 0x%llx",
+ addressName, addressOffset);
+ ReleaseSymbolicator();
+
+ return retval;
+}
+
+CSSymbolicatorRef
+nsCocoaDebugUtils::GetSymbolicatorRef()
+{
+ if (CSIsNull(sSymbolicator)) {
+ // 0x40e0000 is the value returned by
+ // uint32_t CSSymbolicatorGetFlagsForNListOnlyData(void). We don't use
+ // this method directly because it doesn't exist on OS X 10.6. Unless
+ // we limit ourselves to NList data, it will take too long to get a
+ // stack trace where Dwarf debugging info is available (about 15 seconds
+ // with Firefox). This means we won't be able to get a CSSourceInfoRef,
+ // or line number information. Oh well.
+ sSymbolicator =
+ CSSymbolicatorCreateWithPidFlagsAndNotification(getpid(),
+ 0x40e0000, 0);
+ }
+ // Retaining just after creation prevents crashes when calling symbolicator
+ // code (for example from PrintStackTrace()) as Firefox is quitting. Not
+ // sure why. Doing this may mean that we leak sSymbolicator on quitting
+ // (if we ever created it). No particular harm in that, though.
+ return CSRetain(sSymbolicator);
+}
+
+void
+nsCocoaDebugUtils::ReleaseSymbolicator()
+{
+ if (!CSIsNull(sSymbolicator)) {
+ CSRelease(sSymbolicator);
+ }
+}