Source code

Revision control

Other Tools

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