summaryrefslogtreecommitdiffstats
path: root/js/src/devtools/Instruments.cpp
blob: 39fbe882b843c8c3c32b48454e2d965e13d1415a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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__ */