Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / tools / profiler / core / platform-macos.cpp
blobad0b0699f37206f6e0f902a9abb16354d3c945ec
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <unistd.h>
8 #include <sys/mman.h>
9 #include <mach/mach_init.h>
10 #include <mach-o/getsect.h>
12 #include <AvailabilityMacros.h>
14 #include <pthread.h>
15 #include <semaphore.h>
16 #include <signal.h>
17 #include <libkern/OSAtomic.h>
18 #include <mach/mach.h>
19 #include <mach/semaphore.h>
20 #include <mach/task.h>
21 #include <mach/thread_act.h>
22 #include <mach/vm_statistics.h>
23 #include <sys/time.h>
24 #include <sys/resource.h>
25 #include <sys/syscall.h>
26 #include <sys/types.h>
27 #include <sys/sysctl.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <math.h>
34 // this port is based off of v8 svn revision 9837
36 mozilla::profiler::PlatformData::PlatformData(ProfilerThreadId aThreadId)
37 : mProfiledThread(mach_thread_self()) {}
39 mozilla::profiler::PlatformData::~PlatformData() {
40 // Deallocate Mach port for thread.
41 mach_port_deallocate(mach_task_self(), mProfiledThread);
44 ////////////////////////////////////////////////////////////////////////
45 // BEGIN Sampler target specifics
47 Sampler::Sampler(PSLockRef aLock) {}
49 void Sampler::Disable(PSLockRef aLock) {}
51 static void StreamMetaPlatformSampleUnits(PSLockRef aLock,
52 SpliceableJSONWriter& aWriter) {
53 // Microseconds.
54 aWriter.StringProperty("threadCPUDelta", "\u00B5s");
57 /* static */
58 uint64_t RunningTimes::ConvertRawToJson(uint64_t aRawValue) {
59 return aRawValue;
62 namespace mozilla::profiler {
63 bool GetCpuTimeSinceThreadStartInNs(
64 uint64_t* aResult, const mozilla::profiler::PlatformData& aPlatformData) {
65 thread_extended_info_data_t threadInfoData;
66 mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT;
67 if (thread_info(aPlatformData.ProfiledThread(), THREAD_EXTENDED_INFO,
68 (thread_info_t)&threadInfoData, &count) != KERN_SUCCESS) {
69 return false;
72 *aResult = threadInfoData.pth_user_time + threadInfoData.pth_system_time;
73 return true;
75 } // namespace mozilla::profiler
77 static RunningTimes GetProcessRunningTimesDiff(
78 PSLockRef aLock, RunningTimes& aPreviousRunningTimesToBeUpdated) {
79 AUTO_PROFILER_STATS(GetProcessRunningTimes);
81 RunningTimes newRunningTimes;
83 AUTO_PROFILER_STATS(GetProcessRunningTimes_task_info);
85 task_power_info_data_t task_power_info;
86 mach_msg_type_number_t count = TASK_POWER_INFO_COUNT;
87 if (task_info(mach_task_self(), TASK_POWER_INFO,
88 (task_info_t)&task_power_info, &count) == KERN_SUCCESS) {
89 newRunningTimes.SetThreadCPUDelta(task_power_info.total_user +
90 task_power_info.total_system);
92 newRunningTimes.SetPostMeasurementTimeStamp(TimeStamp::Now());
95 const RunningTimes diff = newRunningTimes - aPreviousRunningTimesToBeUpdated;
96 aPreviousRunningTimesToBeUpdated = newRunningTimes;
97 return diff;
100 static RunningTimes GetThreadRunningTimesDiff(
101 PSLockRef aLock,
102 ThreadRegistration::UnlockedRWForLockedProfiler& aThreadData) {
103 AUTO_PROFILER_STATS(GetRunningTimes);
105 const mozilla::profiler::PlatformData& platformData =
106 aThreadData.PlatformDataCRef();
108 const RunningTimes newRunningTimes = GetRunningTimesWithTightTimestamp(
109 [&platformData](RunningTimes& aRunningTimes) {
110 AUTO_PROFILER_STATS(GetRunningTimes_thread_info);
111 thread_basic_info_data_t threadBasicInfo;
112 mach_msg_type_number_t basicCount = THREAD_BASIC_INFO_COUNT;
113 if (thread_info(platformData.ProfiledThread(), THREAD_BASIC_INFO,
114 reinterpret_cast<thread_info_t>(&threadBasicInfo),
115 &basicCount) == KERN_SUCCESS &&
116 basicCount == THREAD_BASIC_INFO_COUNT) {
117 uint64_t userTimeUs =
118 uint64_t(threadBasicInfo.user_time.seconds) *
119 uint64_t(USEC_PER_SEC) +
120 uint64_t(threadBasicInfo.user_time.microseconds);
121 uint64_t systemTimeUs =
122 uint64_t(threadBasicInfo.system_time.seconds) *
123 uint64_t(USEC_PER_SEC) +
124 uint64_t(threadBasicInfo.system_time.microseconds);
125 aRunningTimes.ResetThreadCPUDelta(userTimeUs + systemTimeUs);
126 } else {
127 aRunningTimes.ClearThreadCPUDelta();
131 ProfiledThreadData* profiledThreadData =
132 aThreadData.GetProfiledThreadData(aLock);
133 MOZ_ASSERT(profiledThreadData);
134 RunningTimes& previousRunningTimes =
135 profiledThreadData->PreviousThreadRunningTimesRef();
136 const RunningTimes diff = newRunningTimes - previousRunningTimes;
137 previousRunningTimes = newRunningTimes;
138 return diff;
141 static void DiscardSuspendedThreadRunningTimes(
142 PSLockRef aLock,
143 ThreadRegistration::UnlockedRWForLockedProfiler& aThreadData) {
144 // Nothing to do!
145 // On macOS, suspending a thread doesn't make that thread work.
148 template <typename Func>
149 void Sampler::SuspendAndSampleAndResumeThread(
150 PSLockRef aLock,
151 const ThreadRegistration::UnlockedReaderAndAtomicRWOnThread& aThreadData,
152 const TimeStamp& aNow, const Func& aProcessRegs) {
153 thread_act_t samplee_thread = aThreadData.PlatformDataCRef().ProfiledThread();
155 //----------------------------------------------------------------//
156 // Suspend the samplee thread and get its context.
158 // We're using thread_suspend on OS X because pthread_kill (which is what we
159 // at one time used on Linux) has less consistent performance and causes
160 // strange crashes, see bug 1166778 and bug 1166808. thread_suspend
161 // is also just a lot simpler to use.
163 if (KERN_SUCCESS != thread_suspend(samplee_thread)) {
164 return;
167 //----------------------------------------------------------------//
168 // Sample the target thread.
170 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
172 // The profiler's "critical section" begins here. We must be very careful
173 // what we do here, or risk deadlock. See the corresponding comment in
174 // platform-linux-android.cpp for details.
176 #if defined(__x86_64__)
177 thread_state_flavor_t flavor = x86_THREAD_STATE64;
178 x86_thread_state64_t state;
179 mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
180 # if __DARWIN_UNIX03
181 # define REGISTER_FIELD(name) __r##name
182 # else
183 # define REGISTER_FIELD(name) r##name
184 # endif // __DARWIN_UNIX03
185 #elif defined(__aarch64__)
186 thread_state_flavor_t flavor = ARM_THREAD_STATE64;
187 arm_thread_state64_t state;
188 mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT;
189 # if __DARWIN_UNIX03
190 # define REGISTER_FIELD(name) __##name
191 # else
192 # define REGISTER_FIELD(name) name
193 # endif // __DARWIN_UNIX03
194 #else
195 # error "unknown architecture"
196 #endif
198 if (thread_get_state(samplee_thread, flavor,
199 reinterpret_cast<natural_t*>(&state),
200 &count) == KERN_SUCCESS) {
201 Registers regs;
202 #if defined(__x86_64__)
203 regs.mPC = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
204 regs.mSP = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
205 regs.mFP = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
206 regs.mR10 = reinterpret_cast<Address>(state.REGISTER_FIELD(10));
207 regs.mR12 = reinterpret_cast<Address>(state.REGISTER_FIELD(12));
208 #elif defined(__aarch64__)
209 regs.mPC = reinterpret_cast<Address>(state.REGISTER_FIELD(pc));
210 regs.mSP = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
211 regs.mFP = reinterpret_cast<Address>(state.REGISTER_FIELD(fp));
212 regs.mLR = reinterpret_cast<Address>(state.REGISTER_FIELD(lr));
213 regs.mR11 = reinterpret_cast<Address>(state.REGISTER_FIELD(x[11]));
214 #else
215 # error "unknown architecture"
216 #endif
218 aProcessRegs(regs, aNow);
221 #undef REGISTER_FIELD
223 //----------------------------------------------------------------//
224 // Resume the target thread.
226 thread_resume(samplee_thread);
228 // The profiler's critical section ends here.
230 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
233 // END Sampler target specifics
234 ////////////////////////////////////////////////////////////////////////
236 ////////////////////////////////////////////////////////////////////////
237 // BEGIN SamplerThread target specifics
239 static void* ThreadEntry(void* aArg) {
240 auto thread = static_cast<SamplerThread*>(aArg);
241 thread->Run();
242 return nullptr;
245 SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration,
246 double aIntervalMilliseconds, uint32_t aFeatures)
247 : mSampler(aLock),
248 mActivityGeneration(aActivityGeneration),
249 mIntervalMicroseconds(
250 std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))),
251 mThread{nullptr} {
252 pthread_attr_t* attr_ptr = nullptr;
253 if (pthread_create(&mThread, attr_ptr, ThreadEntry, this) != 0) {
254 MOZ_CRASH("pthread_create failed");
258 SamplerThread::~SamplerThread() {
259 if (pthread_equal(mThread, pthread_self())) {
260 pthread_detach(mThread);
261 } else {
262 pthread_join(mThread, nullptr);
264 // Just in the unlikely case some callbacks were added between the end of the
265 // thread and now.
266 InvokePostSamplingCallbacks(std::move(mPostSamplingCallbackList),
267 SamplingState::JustStopped);
270 void SamplerThread::SleepMicro(uint32_t aMicroseconds) {
271 usleep(aMicroseconds);
272 // FIXME: the OSX 10.12 page for usleep says "The usleep() function is
273 // obsolescent. Use nanosleep(2) instead." This implementation could be
274 // merged with the linux-android version. Also, this doesn't handle the
275 // case where the usleep call is interrupted by a signal.
278 void SamplerThread::Stop(PSLockRef aLock) { mSampler.Disable(aLock); }
280 // END SamplerThread target specifics
281 ////////////////////////////////////////////////////////////////////////
283 static void PlatformInit(PSLockRef aLock) {}
285 // clang-format off
286 #if defined(HAVE_NATIVE_UNWIND)
287 // Derive the stack pointer from the frame pointer. The 0x10 offset is
288 // 8 bytes for the previous frame pointer and 8 bytes for the return
289 // address both stored on the stack after at the beginning of the current
290 // frame.
291 # define REGISTERS_SYNC_POPULATE(regs) \
292 regs.mSP = reinterpret_cast<Address>(__builtin_frame_address(0)) + 0x10; \
293 _Pragma("GCC diagnostic push") \
294 _Pragma("GCC diagnostic ignored \"-Wframe-address\"") \
295 regs.mFP = reinterpret_cast<Address>(__builtin_frame_address(1)); \
296 _Pragma("GCC diagnostic pop") \
297 regs.mPC = reinterpret_cast<Address>( \
298 __builtin_extract_return_addr(__builtin_return_address(0)));
299 #endif
300 // clang-format on