Instruments.cpp (6232B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "Instruments.h" 6 #include "mozilla/Attributes.h" 7 8 #ifdef __APPLE__ 9 10 # include <dlfcn.h> 11 # include <CoreFoundation/CoreFoundation.h> 12 # include <unistd.h> 13 14 # define DTPerformanceLibraryPath \ 15 "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/" \ 16 "DTPerformanceSession.framework/Versions/Current/DTPerformanceSession" 17 18 extern "C" { 19 20 typedef CFTypeRef DTPerformanceSessionRef; 21 22 # define DTPerformanceSession_TimeProfiler \ 23 "com.apple.instruments.dtps.timeprofiler" 24 // DTPerformanceSession_Option_SamplingInterval is measured in microseconds 25 # define DTPerformanceSession_Option_SamplingInterval \ 26 "com.apple.instruments.dtps.option.samplinginterval" 27 28 typedef void (*dtps_errorcallback_t)(CFStringRef, CFErrorRef); 29 typedef DTPerformanceSessionRef (*DTPerformanceSessionCreateFunction)( 30 CFStringRef, CFStringRef, CFDictionaryRef, CFErrorRef*); 31 typedef bool (*DTPerformanceSessionAddInstrumentFunction)( 32 DTPerformanceSessionRef, CFStringRef, CFDictionaryRef, dtps_errorcallback_t, 33 CFErrorRef*); 34 typedef bool (*DTPerformanceSessionIsRecordingFunction)( 35 DTPerformanceSessionRef); 36 typedef bool (*DTPerformanceSessionStartFunction)(DTPerformanceSessionRef, 37 CFArrayRef, CFErrorRef*); 38 typedef bool (*DTPerformanceSessionStopFunction)(DTPerformanceSessionRef, 39 CFArrayRef, CFErrorRef*); 40 typedef bool (*DTPerformanceSessionSaveFunction)(DTPerformanceSessionRef, 41 CFStringRef, CFErrorRef*); 42 43 } // extern "C" 44 45 namespace Instruments { 46 47 static const int kSamplingInterval = 20; // microseconds 48 49 template <typename T> 50 class AutoReleased { 51 public: 52 MOZ_IMPLICIT AutoReleased(T aTypeRef) : mTypeRef(aTypeRef) {} 53 ~AutoReleased() { 54 if (mTypeRef) { 55 CFRelease(mTypeRef); 56 } 57 } 58 59 operator T() { return mTypeRef; } 60 61 private: 62 T mTypeRef; 63 }; 64 65 # define DTPERFORMANCE_SYMBOLS \ 66 SYMBOL(DTPerformanceSessionCreate) \ 67 SYMBOL(DTPerformanceSessionAddInstrument) \ 68 SYMBOL(DTPerformanceSessionIsRecording) \ 69 SYMBOL(DTPerformanceSessionStart) \ 70 SYMBOL(DTPerformanceSessionStop) \ 71 SYMBOL(DTPerformanceSessionSave) 72 73 # define SYMBOL(_sym) _sym##Function _sym = nullptr; 74 75 DTPERFORMANCE_SYMBOLS 76 77 # undef SYMBOL 78 79 void* LoadDTPerformanceLibraries(bool dontLoad) { 80 const int flags = 81 RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE | (dontLoad ? RTLD_NOLOAD : 0); 82 83 return dlopen(DTPerformanceLibraryPath, flags); 84 } 85 86 bool LoadDTPerformanceLibrary() { 87 void* DTPerformanceLibrary = LoadDTPerformanceLibraries(true); 88 if (!DTPerformanceLibrary) { 89 DTPerformanceLibrary = LoadDTPerformanceLibraries(false); 90 if (!DTPerformanceLibrary) { 91 return false; 92 } 93 } 94 95 # define SYMBOL(_sym) \ 96 _sym = \ 97 reinterpret_cast<_sym##Function>(dlsym(DTPerformanceLibrary, #_sym)); \ 98 if (!_sym) { \ 99 dlclose(DTPerformanceLibrary); \ 100 DTPerformanceLibrary = nullptr; \ 101 return false; \ 102 } 103 104 DTPERFORMANCE_SYMBOLS 105 106 # undef SYMBOL 107 108 dlclose(DTPerformanceLibrary); 109 110 return true; 111 } 112 113 static DTPerformanceSessionRef gSession; 114 115 bool Error(CFErrorRef error) { 116 if (gSession) { 117 CFErrorRef unused = nullptr; 118 DTPerformanceSessionStop(gSession, nullptr, &unused); 119 CFRelease(gSession); 120 gSession = nullptr; 121 } 122 # ifdef DEBUG 123 AutoReleased<CFDataRef> data = CFStringCreateExternalRepresentation( 124 nullptr, CFErrorCopyDescription(error), kCFStringEncodingUTF8, '?'); 125 if (data != nullptr) { 126 printf("%.*s\n\n", (int)CFDataGetLength(data), CFDataGetBytePtr(data)); 127 } 128 # endif 129 return false; 130 } 131 132 bool Start(pid_t pid) { 133 if (gSession) { 134 return false; 135 } 136 137 if (!LoadDTPerformanceLibrary()) { 138 return false; 139 } 140 141 AutoReleased<CFStringRef> process = 142 CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%d"), pid); 143 if (!process) { 144 return false; 145 } 146 CFErrorRef error = nullptr; 147 gSession = DTPerformanceSessionCreate(nullptr, process, nullptr, &error); 148 if (!gSession) { 149 return Error(error); 150 } 151 152 AutoReleased<CFNumberRef> interval = 153 CFNumberCreate(0, kCFNumberIntType, &kSamplingInterval); 154 if (!interval) { 155 return false; 156 } 157 CFStringRef keys[1] = {CFSTR(DTPerformanceSession_Option_SamplingInterval)}; 158 CFNumberRef values[1] = {interval}; 159 AutoReleased<CFDictionaryRef> options = CFDictionaryCreate( 160 kCFAllocatorDefault, (const void**)keys, (const void**)values, 1, 161 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 162 if (!options) { 163 return false; 164 } 165 166 if (!DTPerformanceSessionAddInstrument( 167 gSession, CFSTR(DTPerformanceSession_TimeProfiler), options, nullptr, 168 &error)) { 169 return Error(error); 170 } 171 172 return Resume(); 173 } 174 175 void Pause() { 176 if (gSession && DTPerformanceSessionIsRecording(gSession)) { 177 CFErrorRef error = nullptr; 178 if (!DTPerformanceSessionStop(gSession, nullptr, &error)) { 179 Error(error); 180 } 181 } 182 } 183 184 bool Resume() { 185 if (!gSession) { 186 return false; 187 } 188 189 CFErrorRef error = nullptr; 190 return DTPerformanceSessionStart(gSession, nullptr, &error) || Error(error); 191 } 192 193 void Stop(const char* profileName) { 194 Pause(); 195 196 CFErrorRef error = nullptr; 197 AutoReleased<CFStringRef> name = 198 CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%s%s"), 199 "/tmp/", profileName ? profileName : "mozilla"); 200 if (!DTPerformanceSessionSave(gSession, name, &error)) { 201 Error(error); 202 return; 203 } 204 205 CFRelease(gSession); 206 gSession = nullptr; 207 } 208 209 } // namespace Instruments 210 211 #endif /* __APPLE__ */