Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
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>
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/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>
32 #include "nsThreadUtils.h"
34 #include "platform.h"
35 #include "TableTicker.h"
36 #include "UnwinderThread2.h" /* uwt__register_thread_for_profiling */
38 // this port is based off of v8 svn revision 9837
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 };
53 Sampler *SamplerRegistry::sampler = NULL;
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;
59 void OS::Startup() {
60 }
62 void OS::Sleep(int milliseconds) {
63 usleep(1000 * milliseconds);
64 }
66 void OS::SleepMicro(int microseconds) {
67 usleep(microseconds);
68 }
70 Thread::Thread(const char* name)
71 : stack_size_(0) {
72 set_name(name);
73 }
76 Thread::~Thread() {
77 }
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;
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 }
97 static void* ThreadEntry(void* arg) {
98 Thread* thread = reinterpret_cast<Thread*>(arg);
100 thread->thread_ = pthread_self();
101 SetThreadName(thread->name());
102 ASSERT(thread->thread_ != kNoThread);
103 thread->Run();
104 return NULL;
105 }
108 void Thread::set_name(const char* name) {
109 strncpy(name_, name, sizeof(name_));
110 name_[sizeof(name_) - 1] = '\0';
111 }
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 }
126 void Thread::Join() {
127 pthread_join(thread_, NULL);
128 }
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 }
137 ~PlatformData() {
138 // Deallocate Mach port for thread.
139 mach_port_deallocate(mach_task_self(), profiled_thread_);
140 }
142 thread_act_t profiled_thread() { return profiled_thread_; }
143 pthread_t profiled_pthread() { return profiled_pthread_; }
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 };
156 /* static */ PlatformData*
157 Sampler::AllocPlatformData(int aThreadId)
158 {
159 return new PlatformData;
160 }
162 /* static */ void
163 Sampler::FreePlatformData(PlatformData* aData)
164 {
165 delete aData;
166 }
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 }
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 }
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 }
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];
208 // This will be null if we're not interested in profiling this thread.
209 if (!info->Profile())
210 continue;
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 }
220 ThreadProfile* thread_profile = info->Profile();
222 SampleContext(SamplerRegistry::sampler, thread_profile);
223 }
224 }
225 OS::SleepMicro(intervalMicro_);
226 }
227 }
229 void SampleContext(Sampler* sampler, ThreadProfile* thread_profile) {
230 thread_act_t profiled_thread =
231 thread_profile->GetPlatformData()->profiled_thread();
233 TickSample sample_obj;
234 TickSample* sample = &sample_obj;
236 if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
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
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 }
274 int intervalMicro_;
275 //RuntimeProfilerRateLimiter rate_limiter_;
277 static SamplerThread* instance_;
279 DISALLOW_COPY_AND_ASSIGN(SamplerThread);
280 };
282 #undef REGISTER_FIELD
284 SamplerThread* SamplerThread::instance_ = NULL;
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 }
297 Sampler::~Sampler() {
298 ASSERT(!IsActive());
299 }
302 void Sampler::Start() {
303 ASSERT(!IsActive());
304 SetActive(true);
305 SamplerThread::AddActiveSampler(this);
306 }
309 void Sampler::Stop() {
310 ASSERT(IsActive());
311 SetActive(false);
312 SamplerThread::RemoveActiveSampler(this);
313 }
315 pthread_t
316 Sampler::GetProfiledThread(PlatformData* aData)
317 {
318 return aData->profiled_pthread();
319 }
321 #include <sys/syscall.h>
322 pid_t gettid()
323 {
324 return (pid_t) syscall(SYS_thread_selfid);
325 }
327 /* static */ Thread::tid_t
328 Thread::GetCurrentId()
329 {
330 return gettid();
331 }
333 bool Sampler::RegisterCurrentThread(const char* aName,
334 PseudoStack* aPseudoStack,
335 bool aIsMainThread, void* stackTop)
336 {
337 if (!Sampler::sRegisteredThreadsMutex)
338 return false;
341 mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
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 }
354 set_tls_stack_top(stackTop);
356 ThreadInfo* info = new ThreadInfo(aName, id,
357 aIsMainThread, aPseudoStack, stackTop);
359 if (sActiveSampler) {
360 sActiveSampler->RegisterThread(info);
361 }
363 sRegisteredThreads->push_back(info);
365 uwt__register_thread_for_profiling(stackTop);
366 return true;
367 }
369 void Sampler::UnregisterCurrentThread()
370 {
371 if (!Sampler::sRegisteredThreadsMutex)
372 return;
374 tlsStackTop.set(nullptr);
376 mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
378 int id = gettid();
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 }
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 }