| /* 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" |
| |
| #ifdef __APPLE__ |
| |
| #include "jsapi.h" |
| #include <dlfcn.h> |
| #include <CoreFoundation/CoreFoundation.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: |
| 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 = NULL; |
| |
| 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 = NULL; \ |
| return false; \ |
| } |
| |
| DTPERFORMANCE_SYMBOLS |
| |
| #undef SYMBOL |
| |
| dlclose(DTPerformanceLibrary); |
| |
| return true; |
| } |
| |
| static DTPerformanceSessionRef gSession; |
| |
| bool |
| Error(CFErrorRef error) |
| { |
| if (gSession) { |
| CFErrorRef unused = NULL; |
| DTPerformanceSessionStop(gSession, NULL, &unused); |
| CFRelease(gSession); |
| gSession = NULL; |
| } |
| #ifdef DEBUG |
| AutoReleased<CFDataRef> data = |
| CFStringCreateExternalRepresentation(NULL, CFErrorCopyDescription(error), |
| kCFStringEncodingUTF8, '?'); |
| if (data != NULL) { |
| printf("%.*s\n\n", (int)CFDataGetLength(data), CFDataGetBytePtr(data)); |
| } |
| #endif |
| return false; |
| } |
| |
| bool |
| Start() |
| { |
| if (gSession) { |
| return false; |
| } |
| |
| if (!LoadDTPerformanceLibrary()) { |
| return false; |
| } |
| |
| AutoReleased<CFStringRef> process = |
| CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), getpid()); |
| if (!process) { |
| return false; |
| } |
| CFErrorRef error = NULL; |
| gSession = DTPerformanceSessionCreate(NULL, process, NULL, &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, NULL, &error)) { |
| return Error(error); |
| } |
| |
| return Resume(); |
| } |
| |
| void |
| Pause() |
| { |
| if (gSession && DTPerformanceSessionIsRecording(gSession)) { |
| CFErrorRef error = NULL; |
| if (!DTPerformanceSessionStop(gSession, NULL, &error)) { |
| Error(error); |
| } |
| } |
| } |
| |
| bool |
| Resume() |
| { |
| if (!gSession) { |
| return false; |
| } |
| |
| CFErrorRef error = NULL; |
| return DTPerformanceSessionStart(gSession, NULL, &error) || |
| Error(error); |
| } |
| |
| void |
| Stop(const char* profileName) |
| { |
| Pause(); |
| |
| CFErrorRef error = NULL; |
| AutoReleased<CFStringRef> name = |
| CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s"), "/tmp/", |
| profileName ? profileName : "mozilla"); |
| if (!DTPerformanceSessionSave(gSession, name, &error)) { |
| Error(error); |
| return; |
| } |
| |
| CFRelease(gSession); |
| gSession = NULL; |
| } |
| |
| } // namespace Instruments |
| |
| #endif /* __APPLE__ */ |