diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/devtools/Instruments.cpp | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/js/src/devtools/Instruments.cpp b/js/src/devtools/Instruments.cpp new file mode 100644 index 0000000000..39fbe882b8 --- /dev/null +++ b/js/src/devtools/Instruments.cpp @@ -0,0 +1,223 @@ +/* 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__ */ |