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