|
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 |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include <dlfcn.h> |
|
6 #include <unistd.h> |
|
7 #include <sys/mman.h> |
|
8 #include <mach/mach_init.h> |
|
9 #include <mach-o/dyld.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/vm_statistics.h> |
|
22 #include <sys/time.h> |
|
23 #include <sys/resource.h> |
|
24 #include <sys/types.h> |
|
25 #include <sys/sysctl.h> |
|
26 #include <stdarg.h> |
|
27 #include <stdlib.h> |
|
28 #include <string.h> |
|
29 #include <errno.h> |
|
30 #include <math.h> |
|
31 |
|
32 #include "nsThreadUtils.h" |
|
33 |
|
34 #include "platform.h" |
|
35 #include "TableTicker.h" |
|
36 #include "UnwinderThread2.h" /* uwt__register_thread_for_profiling */ |
|
37 |
|
38 // this port is based off of v8 svn revision 9837 |
|
39 |
|
40 // XXX: this is a very stubbed out implementation |
|
41 // that only supports a single Sampler |
|
42 struct SamplerRegistry { |
|
43 static void AddActiveSampler(Sampler *sampler) { |
|
44 ASSERT(!SamplerRegistry::sampler); |
|
45 SamplerRegistry::sampler = sampler; |
|
46 } |
|
47 static void RemoveActiveSampler(Sampler *sampler) { |
|
48 SamplerRegistry::sampler = NULL; |
|
49 } |
|
50 static Sampler *sampler; |
|
51 }; |
|
52 |
|
53 Sampler *SamplerRegistry::sampler = NULL; |
|
54 |
|
55 // 0 is never a valid thread id on MacOSX since a ptread_t is |
|
56 // a pointer. |
|
57 static const pthread_t kNoThread = (pthread_t) 0; |
|
58 |
|
59 void OS::Startup() { |
|
60 } |
|
61 |
|
62 void OS::Sleep(int milliseconds) { |
|
63 usleep(1000 * milliseconds); |
|
64 } |
|
65 |
|
66 void OS::SleepMicro(int microseconds) { |
|
67 usleep(microseconds); |
|
68 } |
|
69 |
|
70 Thread::Thread(const char* name) |
|
71 : stack_size_(0) { |
|
72 set_name(name); |
|
73 } |
|
74 |
|
75 |
|
76 Thread::~Thread() { |
|
77 } |
|
78 |
|
79 |
|
80 static void SetThreadName(const char* name) { |
|
81 // pthread_setname_np is only available in 10.6 or later, so test |
|
82 // for it at runtime. |
|
83 int (*dynamic_pthread_setname_np)(const char*); |
|
84 *reinterpret_cast<void**>(&dynamic_pthread_setname_np) = |
|
85 dlsym(RTLD_DEFAULT, "pthread_setname_np"); |
|
86 if (!dynamic_pthread_setname_np) |
|
87 return; |
|
88 |
|
89 // Mac OS X does not expose the length limit of the name, so hardcode it. |
|
90 static const int kMaxNameLength = 63; |
|
91 USE(kMaxNameLength); |
|
92 ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength); |
|
93 dynamic_pthread_setname_np(name); |
|
94 } |
|
95 |
|
96 |
|
97 static void* ThreadEntry(void* arg) { |
|
98 Thread* thread = reinterpret_cast<Thread*>(arg); |
|
99 |
|
100 thread->thread_ = pthread_self(); |
|
101 SetThreadName(thread->name()); |
|
102 ASSERT(thread->thread_ != kNoThread); |
|
103 thread->Run(); |
|
104 return NULL; |
|
105 } |
|
106 |
|
107 |
|
108 void Thread::set_name(const char* name) { |
|
109 strncpy(name_, name, sizeof(name_)); |
|
110 name_[sizeof(name_) - 1] = '\0'; |
|
111 } |
|
112 |
|
113 |
|
114 void Thread::Start() { |
|
115 pthread_attr_t* attr_ptr = NULL; |
|
116 pthread_attr_t attr; |
|
117 if (stack_size_ > 0) { |
|
118 pthread_attr_init(&attr); |
|
119 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_)); |
|
120 attr_ptr = &attr; |
|
121 } |
|
122 pthread_create(&thread_, attr_ptr, ThreadEntry, this); |
|
123 ASSERT(thread_ != kNoThread); |
|
124 } |
|
125 |
|
126 void Thread::Join() { |
|
127 pthread_join(thread_, NULL); |
|
128 } |
|
129 |
|
130 class PlatformData : public Malloced { |
|
131 public: |
|
132 PlatformData() : profiled_thread_(mach_thread_self()) |
|
133 { |
|
134 profiled_pthread_ = pthread_from_mach_thread_np(profiled_thread_); |
|
135 } |
|
136 |
|
137 ~PlatformData() { |
|
138 // Deallocate Mach port for thread. |
|
139 mach_port_deallocate(mach_task_self(), profiled_thread_); |
|
140 } |
|
141 |
|
142 thread_act_t profiled_thread() { return profiled_thread_; } |
|
143 pthread_t profiled_pthread() { return profiled_pthread_; } |
|
144 |
|
145 private: |
|
146 // Note: for profiled_thread_ Mach primitives are used instead of PThread's |
|
147 // because the latter doesn't provide thread manipulation primitives required. |
|
148 // For details, consult "Mac OS X Internals" book, Section 7.3. |
|
149 thread_act_t profiled_thread_; |
|
150 // we also store the pthread because Mach threads have no concept of stack |
|
151 // and we want to be able to get the stack size when we need to unwind the |
|
152 // stack using frame pointers. |
|
153 pthread_t profiled_pthread_; |
|
154 }; |
|
155 |
|
156 /* static */ PlatformData* |
|
157 Sampler::AllocPlatformData(int aThreadId) |
|
158 { |
|
159 return new PlatformData; |
|
160 } |
|
161 |
|
162 /* static */ void |
|
163 Sampler::FreePlatformData(PlatformData* aData) |
|
164 { |
|
165 delete aData; |
|
166 } |
|
167 |
|
168 class SamplerThread : public Thread { |
|
169 public: |
|
170 explicit SamplerThread(double interval) |
|
171 : Thread("SamplerThread") |
|
172 , intervalMicro_(floor(interval * 1000 + 0.5)) |
|
173 { |
|
174 if (intervalMicro_ <= 0) { |
|
175 intervalMicro_ = 1; |
|
176 } |
|
177 } |
|
178 |
|
179 static void AddActiveSampler(Sampler* sampler) { |
|
180 mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); |
|
181 SamplerRegistry::AddActiveSampler(sampler); |
|
182 if (instance_ == NULL) { |
|
183 instance_ = new SamplerThread(sampler->interval()); |
|
184 instance_->Start(); |
|
185 } |
|
186 } |
|
187 |
|
188 static void RemoveActiveSampler(Sampler* sampler) { |
|
189 mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); |
|
190 instance_->Join(); |
|
191 //XXX: unlike v8 we need to remove the active sampler after doing the Join |
|
192 // because we drop the sampler immediately |
|
193 SamplerRegistry::RemoveActiveSampler(sampler); |
|
194 delete instance_; |
|
195 instance_ = NULL; |
|
196 } |
|
197 |
|
198 // Implement Thread::Run(). |
|
199 virtual void Run() { |
|
200 while (SamplerRegistry::sampler->IsActive()) { |
|
201 if (!SamplerRegistry::sampler->IsPaused()) { |
|
202 mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); |
|
203 std::vector<ThreadInfo*> threads = |
|
204 SamplerRegistry::sampler->GetRegisteredThreads(); |
|
205 for (uint32_t i = 0; i < threads.size(); i++) { |
|
206 ThreadInfo* info = threads[i]; |
|
207 |
|
208 // This will be null if we're not interested in profiling this thread. |
|
209 if (!info->Profile()) |
|
210 continue; |
|
211 |
|
212 PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); |
|
213 if (sleeping == PseudoStack::SLEEPING_AGAIN) { |
|
214 info->Profile()->DuplicateLastSample(); |
|
215 //XXX: This causes flushes regardless of jank-only mode |
|
216 info->Profile()->flush(); |
|
217 continue; |
|
218 } |
|
219 |
|
220 ThreadProfile* thread_profile = info->Profile(); |
|
221 |
|
222 SampleContext(SamplerRegistry::sampler, thread_profile); |
|
223 } |
|
224 } |
|
225 OS::SleepMicro(intervalMicro_); |
|
226 } |
|
227 } |
|
228 |
|
229 void SampleContext(Sampler* sampler, ThreadProfile* thread_profile) { |
|
230 thread_act_t profiled_thread = |
|
231 thread_profile->GetPlatformData()->profiled_thread(); |
|
232 |
|
233 TickSample sample_obj; |
|
234 TickSample* sample = &sample_obj; |
|
235 |
|
236 if (KERN_SUCCESS != thread_suspend(profiled_thread)) return; |
|
237 |
|
238 #if V8_HOST_ARCH_X64 |
|
239 thread_state_flavor_t flavor = x86_THREAD_STATE64; |
|
240 x86_thread_state64_t state; |
|
241 mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; |
|
242 #if __DARWIN_UNIX03 |
|
243 #define REGISTER_FIELD(name) __r ## name |
|
244 #else |
|
245 #define REGISTER_FIELD(name) r ## name |
|
246 #endif // __DARWIN_UNIX03 |
|
247 #elif V8_HOST_ARCH_IA32 |
|
248 thread_state_flavor_t flavor = i386_THREAD_STATE; |
|
249 i386_thread_state_t state; |
|
250 mach_msg_type_number_t count = i386_THREAD_STATE_COUNT; |
|
251 #if __DARWIN_UNIX03 |
|
252 #define REGISTER_FIELD(name) __e ## name |
|
253 #else |
|
254 #define REGISTER_FIELD(name) e ## name |
|
255 #endif // __DARWIN_UNIX03 |
|
256 #else |
|
257 #error Unsupported Mac OS X host architecture. |
|
258 #endif // V8_HOST_ARCH |
|
259 |
|
260 if (thread_get_state(profiled_thread, |
|
261 flavor, |
|
262 reinterpret_cast<natural_t*>(&state), |
|
263 &count) == KERN_SUCCESS) { |
|
264 sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip)); |
|
265 sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp)); |
|
266 sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp)); |
|
267 sample->timestamp = mozilla::TimeStamp::Now(); |
|
268 sample->threadProfile = thread_profile; |
|
269 sampler->Tick(sample); |
|
270 } |
|
271 thread_resume(profiled_thread); |
|
272 } |
|
273 |
|
274 int intervalMicro_; |
|
275 //RuntimeProfilerRateLimiter rate_limiter_; |
|
276 |
|
277 static SamplerThread* instance_; |
|
278 |
|
279 DISALLOW_COPY_AND_ASSIGN(SamplerThread); |
|
280 }; |
|
281 |
|
282 #undef REGISTER_FIELD |
|
283 |
|
284 SamplerThread* SamplerThread::instance_ = NULL; |
|
285 |
|
286 Sampler::Sampler(double interval, bool profiling, int entrySize) |
|
287 : // isolate_(isolate), |
|
288 interval_(interval), |
|
289 profiling_(profiling), |
|
290 paused_(false), |
|
291 active_(false), |
|
292 entrySize_(entrySize) /*, |
|
293 samples_taken_(0)*/ { |
|
294 } |
|
295 |
|
296 |
|
297 Sampler::~Sampler() { |
|
298 ASSERT(!IsActive()); |
|
299 } |
|
300 |
|
301 |
|
302 void Sampler::Start() { |
|
303 ASSERT(!IsActive()); |
|
304 SetActive(true); |
|
305 SamplerThread::AddActiveSampler(this); |
|
306 } |
|
307 |
|
308 |
|
309 void Sampler::Stop() { |
|
310 ASSERT(IsActive()); |
|
311 SetActive(false); |
|
312 SamplerThread::RemoveActiveSampler(this); |
|
313 } |
|
314 |
|
315 pthread_t |
|
316 Sampler::GetProfiledThread(PlatformData* aData) |
|
317 { |
|
318 return aData->profiled_pthread(); |
|
319 } |
|
320 |
|
321 #include <sys/syscall.h> |
|
322 pid_t gettid() |
|
323 { |
|
324 return (pid_t) syscall(SYS_thread_selfid); |
|
325 } |
|
326 |
|
327 /* static */ Thread::tid_t |
|
328 Thread::GetCurrentId() |
|
329 { |
|
330 return gettid(); |
|
331 } |
|
332 |
|
333 bool Sampler::RegisterCurrentThread(const char* aName, |
|
334 PseudoStack* aPseudoStack, |
|
335 bool aIsMainThread, void* stackTop) |
|
336 { |
|
337 if (!Sampler::sRegisteredThreadsMutex) |
|
338 return false; |
|
339 |
|
340 |
|
341 mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); |
|
342 |
|
343 int id = gettid(); |
|
344 for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { |
|
345 ThreadInfo* info = sRegisteredThreads->at(i); |
|
346 if (info->ThreadId() == id) { |
|
347 // Thread already registered. This means the first unregister will be |
|
348 // too early. |
|
349 ASSERT(false); |
|
350 return false; |
|
351 } |
|
352 } |
|
353 |
|
354 set_tls_stack_top(stackTop); |
|
355 |
|
356 ThreadInfo* info = new ThreadInfo(aName, id, |
|
357 aIsMainThread, aPseudoStack, stackTop); |
|
358 |
|
359 if (sActiveSampler) { |
|
360 sActiveSampler->RegisterThread(info); |
|
361 } |
|
362 |
|
363 sRegisteredThreads->push_back(info); |
|
364 |
|
365 uwt__register_thread_for_profiling(stackTop); |
|
366 return true; |
|
367 } |
|
368 |
|
369 void Sampler::UnregisterCurrentThread() |
|
370 { |
|
371 if (!Sampler::sRegisteredThreadsMutex) |
|
372 return; |
|
373 |
|
374 tlsStackTop.set(nullptr); |
|
375 |
|
376 mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); |
|
377 |
|
378 int id = gettid(); |
|
379 |
|
380 for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { |
|
381 ThreadInfo* info = sRegisteredThreads->at(i); |
|
382 if (info->ThreadId() == id) { |
|
383 delete info; |
|
384 sRegisteredThreads->erase(sRegisteredThreads->begin() + i); |
|
385 break; |
|
386 } |
|
387 } |
|
388 } |
|
389 |
|
390 void TickSample::PopulateContext(void* aContext) |
|
391 { |
|
392 // Note that this asm changes if PopulateContext's parameter list is altered |
|
393 #if defined(SPS_PLAT_amd64_darwin) |
|
394 asm ( |
|
395 // Compute caller's %rsp by adding to %rbp: |
|
396 // 8 bytes for previous %rbp, 8 bytes for return address |
|
397 "leaq 0x10(%%rbp), %0\n\t" |
|
398 // Dereference %rbp to get previous %rbp |
|
399 "movq (%%rbp), %1\n\t" |
|
400 : |
|
401 "=r"(sp), |
|
402 "=r"(fp) |
|
403 ); |
|
404 #elif defined(SPS_PLAT_x86_darwin) |
|
405 asm ( |
|
406 // Compute caller's %esp by adding to %ebp: |
|
407 // 4 bytes for aContext + 4 bytes for return address + |
|
408 // 4 bytes for previous %ebp |
|
409 "leal 0xc(%%ebp), %0\n\t" |
|
410 // Dereference %ebp to get previous %ebp |
|
411 "movl (%%ebp), %1\n\t" |
|
412 : |
|
413 "=r"(sp), |
|
414 "=r"(fp) |
|
415 ); |
|
416 #else |
|
417 # error "Unsupported architecture" |
|
418 #endif |
|
419 pc = reinterpret_cast<Address>(__builtin_extract_return_addr( |
|
420 __builtin_return_address(0))); |
|
421 } |
|
422 |