Source code

Revision control

Copy as Markdown

Other Tools

/* 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 "Instruments.h"
#include "mozilla/Attributes.h"
#ifdef __APPLE__
# include <dlfcn.h>
# include <CoreFoundation/CoreFoundation.h>
# include <unistd.h>
// There are now 2 paths to the DTPerformanceSession framework. We try to load
// the one contained in /Applications/Xcode.app first, falling back to the one
// contained in /Library/Developer/4.0/Instruments.
# define DTPerformanceLibraryPath \
"/Applications/Xcode.app/Contents/Developer/Library/Frameworks/" \
"DTPerformanceSession.framework/Versions/Current/DTPerformanceSession"
# define OldDTPerformanceLibraryPath \
"/Library/Developer/4.0/Instruments/Frameworks/" \
"DTPerformanceSession.framework/Versions/Current/DTPerformanceSession"
extern "C" {
typedef CFTypeRef DTPerformanceSessionRef;
# define DTPerformanceSession_TimeProfiler \
"com.apple.instruments.dtps.timeprofiler"
// DTPerformanceSession_Option_SamplingInterval is measured in microseconds
# define DTPerformanceSession_Option_SamplingInterval \
"com.apple.instruments.dtps.option.samplinginterval"
typedef void (*dtps_errorcallback_t)(CFStringRef, CFErrorRef);
typedef DTPerformanceSessionRef (*DTPerformanceSessionCreateFunction)(
CFStringRef, CFStringRef, CFDictionaryRef, CFErrorRef*);
typedef bool (*DTPerformanceSessionAddInstrumentFunction)(
DTPerformanceSessionRef, CFStringRef, CFDictionaryRef, dtps_errorcallback_t,
CFErrorRef*);
typedef bool (*DTPerformanceSessionIsRecordingFunction)(
DTPerformanceSessionRef);
typedef bool (*DTPerformanceSessionStartFunction)(DTPerformanceSessionRef,
CFArrayRef, CFErrorRef*);
typedef bool (*DTPerformanceSessionStopFunction)(DTPerformanceSessionRef,
CFArrayRef, CFErrorRef*);
typedef bool (*DTPerformanceSessionSaveFunction)(DTPerformanceSessionRef,
CFStringRef, CFErrorRef*);
} // extern "C"
namespace Instruments {
static const int kSamplingInterval = 20; // microseconds
template <typename T>
class AutoReleased {
public:
MOZ_IMPLICIT AutoReleased(T aTypeRef) : mTypeRef(aTypeRef) {}
~AutoReleased() {
if (mTypeRef) {
CFRelease(mTypeRef);
}
}
operator T() { return mTypeRef; }
private:
T mTypeRef;
};
# define DTPERFORMANCE_SYMBOLS \
SYMBOL(DTPerformanceSessionCreate) \
SYMBOL(DTPerformanceSessionAddInstrument) \
SYMBOL(DTPerformanceSessionIsRecording) \
SYMBOL(DTPerformanceSessionStart) \
SYMBOL(DTPerformanceSessionStop) \
SYMBOL(DTPerformanceSessionSave)
# define SYMBOL(_sym) _sym##Function _sym = nullptr;
DTPERFORMANCE_SYMBOLS
# undef SYMBOL
void* LoadDTPerformanceLibraries(bool dontLoad) {
int flags = RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE;
if (dontLoad) {
flags |= RTLD_NOLOAD;
}
void* DTPerformanceLibrary = dlopen(DTPerformanceLibraryPath, flags);
if (!DTPerformanceLibrary) {
DTPerformanceLibrary = dlopen(OldDTPerformanceLibraryPath, flags);
}
return DTPerformanceLibrary;
}
bool LoadDTPerformanceLibrary() {
void* DTPerformanceLibrary = LoadDTPerformanceLibraries(true);
if (!DTPerformanceLibrary) {
DTPerformanceLibrary = LoadDTPerformanceLibraries(false);
if (!DTPerformanceLibrary) {
return false;
}
}
# define SYMBOL(_sym) \
_sym = \
reinterpret_cast<_sym##Function>(dlsym(DTPerformanceLibrary, #_sym)); \
if (!_sym) { \
dlclose(DTPerformanceLibrary); \
DTPerformanceLibrary = nullptr; \
return false; \
}
DTPERFORMANCE_SYMBOLS
# undef SYMBOL
dlclose(DTPerformanceLibrary);
return true;
}
static DTPerformanceSessionRef gSession;
bool Error(CFErrorRef error) {
if (gSession) {
CFErrorRef unused = nullptr;
DTPerformanceSessionStop(gSession, nullptr, &unused);
CFRelease(gSession);
gSession = nullptr;
}
# ifdef DEBUG
AutoReleased<CFDataRef> data = CFStringCreateExternalRepresentation(
nullptr, CFErrorCopyDescription(error), kCFStringEncodingUTF8, '?');
if (data != nullptr) {
printf("%.*s\n\n", (int)CFDataGetLength(data), CFDataGetBytePtr(data));
}
# endif
return false;
}
bool Start(pid_t pid) {
if (gSession) {
return false;
}
if (!LoadDTPerformanceLibrary()) {
return false;
}
AutoReleased<CFStringRef> process =
CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%d"), pid);
if (!process) {
return false;
}
CFErrorRef error = nullptr;
gSession = DTPerformanceSessionCreate(nullptr, process, nullptr, &error);
if (!gSession) {
return Error(error);
}
AutoReleased<CFNumberRef> interval =
CFNumberCreate(0, kCFNumberIntType, &kSamplingInterval);
if (!interval) {
return false;
}
CFStringRef keys[1] = {CFSTR(DTPerformanceSession_Option_SamplingInterval)};
CFNumberRef values[1] = {interval};
AutoReleased<CFDictionaryRef> options = CFDictionaryCreate(
kCFAllocatorDefault, (const void**)keys, (const void**)values, 1,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!options) {
return false;
}
if (!DTPerformanceSessionAddInstrument(
gSession, CFSTR(DTPerformanceSession_TimeProfiler), options, nullptr,
&error)) {
return Error(error);
}
return Resume();
}
void Pause() {
if (gSession && DTPerformanceSessionIsRecording(gSession)) {
CFErrorRef error = nullptr;
if (!DTPerformanceSessionStop(gSession, nullptr, &error)) {
Error(error);
}
}
}
bool Resume() {
if (!gSession) {
return false;
}
CFErrorRef error = nullptr;
return DTPerformanceSessionStart(gSession, nullptr, &error) || Error(error);
}
void Stop(const char* profileName) {
Pause();
CFErrorRef error = nullptr;
AutoReleased<CFStringRef> name =
CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%s%s"),
"/tmp/", profileName ? profileName : "mozilla");
if (!DTPerformanceSessionSave(gSession, name, &error)) {
Error(error);
return;
}
CFRelease(gSession);
gSession = nullptr;
}
} // namespace Instruments
#endif /* __APPLE__ */