/* 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 # include # include // 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 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 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 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 interval = CFNumberCreate(0, kCFNumberIntType, &kSamplingInterval); if (!interval) { return false; } CFStringRef keys[1] = {CFSTR(DTPerformanceSession_Option_SamplingInterval)}; CFNumberRef values[1] = {interval}; AutoReleased 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 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__ */