platform-macos.cpp (7586B)
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/. */ 6 7 #include <unistd.h> 8 #include <sys/mman.h> 9 #include <mach/mach_init.h> 10 #include <mach-o/getsect.h> 11 12 #include <AvailabilityMacros.h> 13 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 <string.h> 29 #include <errno.h> 30 #include <math.h> 31 32 // this port is based off of v8 svn revision 9837 33 34 namespace mozilla { 35 namespace baseprofiler { 36 37 static int64_t MicrosecondsSince1970() { 38 struct timeval tv; 39 gettimeofday(&tv, NULL); 40 return int64_t(tv.tv_sec) * 1000000 + int64_t(tv.tv_usec); 41 } 42 43 void* GetStackTop(void* aGuess) { 44 pthread_t thread = pthread_self(); 45 return pthread_get_stackaddr_np(thread); 46 } 47 48 class PlatformData { 49 public: 50 explicit PlatformData(BaseProfilerThreadId aThreadId) 51 : mProfiledThread(mach_thread_self()) {} 52 53 ~PlatformData() { 54 // Deallocate Mach port for thread. 55 mach_port_deallocate(mach_task_self(), mProfiledThread); 56 } 57 58 thread_act_t ProfiledThread() { return mProfiledThread; } 59 60 private: 61 // Note: for mProfiledThread Mach primitives are used instead of pthread's 62 // because the latter doesn't provide thread manipulation primitives required. 63 // For details, consult "Mac OS X Internals" book, Section 7.3. 64 thread_act_t mProfiledThread; 65 }; 66 67 //////////////////////////////////////////////////////////////////////// 68 // BEGIN Sampler target specifics 69 70 Sampler::Sampler(PSLockRef aLock) {} 71 72 void Sampler::Disable(PSLockRef aLock) {} 73 74 template <typename Func> 75 void Sampler::SuspendAndSampleAndResumeThread( 76 PSLockRef aLock, const RegisteredThread& aRegisteredThread, 77 const TimeStamp& aNow, const Func& aProcessRegs) { 78 thread_act_t samplee_thread = 79 aRegisteredThread.GetPlatformData()->ProfiledThread(); 80 81 //----------------------------------------------------------------// 82 // Suspend the samplee thread and get its context. 83 84 // We're using thread_suspend on OS X because pthread_kill (which is what we 85 // at one time used on Linux) has less consistent performance and causes 86 // strange crashes, see bug 1166778 and bug 1166808. thread_suspend 87 // is also just a lot simpler to use. 88 89 if (KERN_SUCCESS != thread_suspend(samplee_thread)) { 90 return; 91 } 92 93 //----------------------------------------------------------------// 94 // Sample the target thread. 95 96 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 97 // 98 // The profiler's "critical section" begins here. We must be very careful 99 // what we do here, or risk deadlock. See the corresponding comment in 100 // platform-linux-android.cpp for details. 101 102 #if defined(__x86_64__) 103 thread_state_flavor_t flavor = x86_THREAD_STATE64; 104 x86_thread_state64_t state; 105 mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; 106 # if __DARWIN_UNIX03 107 # define REGISTER_FIELD(name) __r##name 108 # else 109 # define REGISTER_FIELD(name) r##name 110 # endif // __DARWIN_UNIX03 111 #elif defined(__aarch64__) 112 thread_state_flavor_t flavor = ARM_THREAD_STATE64; 113 arm_thread_state64_t state; 114 mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT; 115 # if __DARWIN_UNIX03 116 # define REGISTER_FIELD(name) __##name 117 # else 118 # define REGISTER_FIELD(name) name 119 # endif // __DARWIN_UNIX03 120 #else 121 # error "unknown architecture" 122 #endif 123 124 if (thread_get_state(samplee_thread, flavor, 125 reinterpret_cast<natural_t*>(&state), 126 &count) == KERN_SUCCESS) { 127 Registers regs; 128 #if defined(__x86_64__) 129 regs.mPC = reinterpret_cast<Address>(state.REGISTER_FIELD(ip)); 130 regs.mSP = reinterpret_cast<Address>(state.REGISTER_FIELD(sp)); 131 regs.mFP = reinterpret_cast<Address>(state.REGISTER_FIELD(bp)); 132 regs.mR10 = reinterpret_cast<Address>(state.REGISTER_FIELD(10)); 133 regs.mR12 = reinterpret_cast<Address>(state.REGISTER_FIELD(12)); 134 #elif defined(__aarch64__) 135 regs.mPC = reinterpret_cast<Address>(state.REGISTER_FIELD(pc)); 136 regs.mSP = reinterpret_cast<Address>(state.REGISTER_FIELD(sp)); 137 regs.mFP = reinterpret_cast<Address>(state.REGISTER_FIELD(fp)); 138 regs.mLR = reinterpret_cast<Address>(state.REGISTER_FIELD(lr)); 139 regs.mR11 = reinterpret_cast<Address>(state.REGISTER_FIELD(x[11])); 140 #else 141 # error "unknown architecture" 142 #endif 143 144 aProcessRegs(regs, aNow); 145 } 146 147 #undef REGISTER_FIELD 148 149 //----------------------------------------------------------------// 150 // Resume the target thread. 151 152 thread_resume(samplee_thread); 153 154 // The profiler's critical section ends here. 155 // 156 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 157 } 158 159 // END Sampler target specifics 160 //////////////////////////////////////////////////////////////////////// 161 162 //////////////////////////////////////////////////////////////////////// 163 // BEGIN SamplerThread target specifics 164 165 static void* ThreadEntry(void* aArg) { 166 auto thread = static_cast<SamplerThread*>(aArg); 167 thread->Run(); 168 return nullptr; 169 } 170 171 SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, 172 double aIntervalMilliseconds, uint32_t aFeatures) 173 : mSampler(aLock), 174 mActivityGeneration(aActivityGeneration), 175 mIntervalMicroseconds( 176 std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))), 177 mThread{nullptr} { 178 pthread_attr_t* attr_ptr = nullptr; 179 if (pthread_create(&mThread, attr_ptr, ThreadEntry, this) != 0) { 180 MOZ_CRASH("pthread_create failed"); 181 } 182 } 183 184 SamplerThread::~SamplerThread() { pthread_join(mThread, nullptr); } 185 186 void SamplerThread::SleepMicro(uint32_t aMicroseconds) { 187 usleep(aMicroseconds); 188 // FIXME: the OSX 10.12 page for usleep says "The usleep() function is 189 // obsolescent. Use nanosleep(2) instead." This implementation could be 190 // merged with the linux-android version. Also, this doesn't handle the 191 // case where the usleep call is interrupted by a signal. 192 } 193 194 void SamplerThread::Stop(PSLockRef aLock) { mSampler.Disable(aLock); } 195 196 // END SamplerThread target specifics 197 //////////////////////////////////////////////////////////////////////// 198 199 static void PlatformInit(PSLockRef aLock) {} 200 201 // clang-format off 202 #if defined(HAVE_NATIVE_UNWIND) 203 // Derive the stack pointer from the frame pointer. The 0x10 offset is 204 // 8 bytes for the previous frame pointer and 8 bytes for the return 205 // address both stored on the stack after at the beginning of the current 206 // frame. 207 # define REGISTERS_SYNC_POPULATE(regs) \ 208 regs.mSP = reinterpret_cast<Address>(__builtin_frame_address(0)) + 0x10; \ 209 _Pragma("GCC diagnostic push") \ 210 _Pragma("GCC diagnostic ignored \"-Wframe-address\"") \ 211 regs.mFP = reinterpret_cast<Address>(__builtin_frame_address(1)); \ 212 _Pragma("GCC diagnostic pop") \ 213 regs.mPC = reinterpret_cast<Address>( \ 214 __builtin_extract_return_addr(__builtin_return_address(0))); 215 #endif 216 // clang-format on 217 218 } // namespace baseprofiler 219 } // namespace mozilla