/* -*- 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(¤tTime, 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); } }