gecko-dev/widget/cocoa/nsCocoaDebugUtils.mm

237 строки
7.6 KiB
Plaintext

/* -*- 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);
}
}