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/. */
9 #include <mach/mach_init.h>
10 #include <mach-o/getsect.h>
12 #include <AvailabilityMacros.h>
15 #include <semaphore.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>
24 #include <sys/resource.h>
25 #include <sys/syscall.h>
26 #include <sys/types.h>
27 #include <sys/sysctl.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
) {
54 aWriter
.StringProperty("threadCPUDelta", "\u00B5s");
58 uint64_t RunningTimes::ConvertRawToJson(uint64_t 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
) {
72 *aResult
= threadInfoData
.pth_user_time
+ threadInfoData
.pth_system_time
;
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
;
100 static RunningTimes
GetThreadRunningTimesDiff(
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
);
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
;
141 static void DiscardSuspendedThreadRunningTimes(
143 ThreadRegistration::UnlockedRWForLockedProfiler
& aThreadData
) {
145 // On macOS, suspending a thread doesn't make that thread work.
148 template <typename Func
>
149 void Sampler::SuspendAndSampleAndResumeThread(
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
)) {
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
;
181 # define REGISTER_FIELD(name) __r##name
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
;
190 # define REGISTER_FIELD(name) __##name
192 # define REGISTER_FIELD(name) name
193 # endif // __DARWIN_UNIX03
195 # error "unknown architecture"
198 if (thread_get_state(samplee_thread
, flavor
,
199 reinterpret_cast<natural_t
*>(&state
),
200 &count
) == KERN_SUCCESS
) {
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]));
215 # error "unknown architecture"
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
);
245 SamplerThread::SamplerThread(PSLockRef aLock
, uint32_t aActivityGeneration
,
246 double aIntervalMilliseconds
, uint32_t aFeatures
)
248 mActivityGeneration(aActivityGeneration
),
249 mIntervalMicroseconds(
250 std::max(1, int(floor(aIntervalMilliseconds
* 1000 + 0.5)))),
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
);
262 pthread_join(mThread
, nullptr);
264 // Just in the unlikely case some callbacks were added between the end of the
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
) {}
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
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)));