2015-04-30 22:41:21 +03:00
|
|
|
/* -*- 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) {
|
2015-05-21 09:02:59 +03:00
|
|
|
const char* ownerName = "unknown";
|
|
|
|
const char* addressString = "unknown + 0";
|
|
|
|
|
|
|
|
char* allocatedOwnerName = nullptr;
|
|
|
|
char* allocatedAddressString = nullptr;
|
2015-04-30 22:41:21 +03:00
|
|
|
|
|
|
|
CSSymbolOwnerRef owner = {0};
|
|
|
|
CSSymbolicatorRef symbolicator = GetSymbolicatorRef();
|
|
|
|
|
|
|
|
if (!CSIsNull(symbolicator)) {
|
|
|
|
owner = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator,
|
|
|
|
(unsigned long long)aAddress, kCSNow);
|
|
|
|
}
|
|
|
|
if (!CSIsNull(owner)) {
|
2015-05-21 09:02:59 +03:00
|
|
|
ownerName = allocatedOwnerName = GetOwnerNameInt(aAddress, owner);
|
|
|
|
addressString = allocatedAddressString = GetAddressStringInt(aAddress, owner);
|
2015-04-30 22:41:21 +03:00
|
|
|
}
|
|
|
|
DebugLogInt(false, " (%s) %s", ownerName, addressString);
|
|
|
|
|
2015-05-21 09:02:59 +03:00
|
|
|
free(allocatedOwnerName);
|
|
|
|
free(allocatedAddressString);
|
|
|
|
|
2015-04-30 22:41:21 +03:00
|
|
|
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;
|
2015-09-21 22:20:35 +03:00
|
|
|
} else {
|
|
|
|
addressOffset = (unsigned long long)aAddress - CSSymbolOwnerGetBaseAddress(owner);
|
2015-04-30 22:41:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|