1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/profiler/platform-linux.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,615 @@ 1.4 +// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. 1.5 +// 1.6 +// Redistribution and use in source and binary forms, with or without 1.7 +// modification, are permitted provided that the following conditions 1.8 +// are met: 1.9 +// * Redistributions of source code must retain the above copyright 1.10 +// notice, this list of conditions and the following disclaimer. 1.11 +// * Redistributions in binary form must reproduce the above copyright 1.12 +// notice, this list of conditions and the following disclaimer in 1.13 +// the documentation and/or other materials provided with the 1.14 +// distribution. 1.15 +// * Neither the name of Google, Inc. nor the names of its contributors 1.16 +// may be used to endorse or promote products derived from this 1.17 +// software without specific prior written permission. 1.18 +// 1.19 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.20 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.21 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 1.22 +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 1.23 +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1.24 +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1.25 +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 1.26 +// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 1.27 +// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 1.28 +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 1.29 +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1.30 +// SUCH DAMAGE. 1.31 + 1.32 +/* 1.33 +# vim: sw=2 1.34 +*/ 1.35 +#include <stdio.h> 1.36 +#include <math.h> 1.37 + 1.38 +#include <pthread.h> 1.39 +#include <semaphore.h> 1.40 +#include <signal.h> 1.41 +#include <sys/time.h> 1.42 +#include <sys/resource.h> 1.43 +#include <sys/syscall.h> 1.44 +#include <sys/types.h> 1.45 +#include <sys/prctl.h> // set name 1.46 +#include <stdlib.h> 1.47 +#include <sched.h> 1.48 +#ifdef ANDROID 1.49 +#include <android/log.h> 1.50 +#else 1.51 +#define __android_log_print(a, ...) 1.52 +#endif 1.53 +#include <ucontext.h> 1.54 +// Ubuntu Dapper requires memory pages to be marked as 1.55 +// executable. Otherwise, OS raises an exception when executing code 1.56 +// in that page. 1.57 +#include <sys/types.h> // mmap & munmap 1.58 +#include <sys/mman.h> // mmap & munmap 1.59 +#include <sys/stat.h> // open 1.60 +#include <fcntl.h> // open 1.61 +#include <unistd.h> // sysconf 1.62 +#include <semaphore.h> 1.63 +#ifdef __GLIBC__ 1.64 +#include <execinfo.h> // backtrace, backtrace_symbols 1.65 +#endif // def __GLIBC__ 1.66 +#include <strings.h> // index 1.67 +#include <errno.h> 1.68 +#include <stdarg.h> 1.69 +#include "platform.h" 1.70 +#include "GeckoProfiler.h" 1.71 +#include "mozilla/Mutex.h" 1.72 +#include "mozilla/Atomics.h" 1.73 +#include "ProfileEntry.h" 1.74 +#include "nsThreadUtils.h" 1.75 +#include "TableTicker.h" 1.76 +#include "UnwinderThread2.h" 1.77 +#if defined(__ARM_EABI__) && defined(MOZ_WIDGET_GONK) 1.78 + // Should also work on other Android and ARM Linux, but not tested there yet. 1.79 +#define USE_EHABI_STACKWALK 1.80 +#include "EHABIStackWalk.h" 1.81 +#endif 1.82 + 1.83 +#include <string.h> 1.84 +#include <stdio.h> 1.85 +#include <list> 1.86 + 1.87 +#ifdef MOZ_NUWA_PROCESS 1.88 +#include "ipc/Nuwa.h" 1.89 +#endif 1.90 + 1.91 +#define SIGNAL_SAVE_PROFILE SIGUSR2 1.92 + 1.93 +#if defined(__GLIBC__) 1.94 +// glibc doesn't implement gettid(2). 1.95 +#include <sys/syscall.h> 1.96 +pid_t gettid() 1.97 +{ 1.98 + return (pid_t) syscall(SYS_gettid); 1.99 +} 1.100 +#endif 1.101 + 1.102 +/* static */ Thread::tid_t 1.103 +Thread::GetCurrentId() 1.104 +{ 1.105 + return gettid(); 1.106 +} 1.107 + 1.108 +#if !defined(ANDROID) 1.109 +// Keep track of when any of our threads calls fork(), so we can 1.110 +// temporarily disable signal delivery during the fork() call. Not 1.111 +// doing so appears to cause a kind of race, in which signals keep 1.112 +// getting delivered to the thread doing fork(), which keeps causing 1.113 +// it to fail and be restarted; hence forward progress is delayed a 1.114 +// great deal. A side effect of this is to permanently disable 1.115 +// sampling in the child process. See bug 837390. 1.116 + 1.117 +// Unfortunately this is only doable on non-Android, since Bionic 1.118 +// doesn't have pthread_atfork. 1.119 + 1.120 +// This records the current state at the time we paused it. 1.121 +static bool was_paused = false; 1.122 + 1.123 +// In the parent, just before the fork, record the pausedness state, 1.124 +// and then pause. 1.125 +static void paf_prepare(void) { 1.126 + if (Sampler::GetActiveSampler()) { 1.127 + was_paused = Sampler::GetActiveSampler()->IsPaused(); 1.128 + Sampler::GetActiveSampler()->SetPaused(true); 1.129 + } else { 1.130 + was_paused = false; 1.131 + } 1.132 +} 1.133 + 1.134 +// In the parent, just after the fork, return pausedness to the 1.135 +// pre-fork state. 1.136 +static void paf_parent(void) { 1.137 + if (Sampler::GetActiveSampler()) 1.138 + Sampler::GetActiveSampler()->SetPaused(was_paused); 1.139 +} 1.140 + 1.141 +// Set up the fork handlers. 1.142 +static void* setup_atfork() { 1.143 + pthread_atfork(paf_prepare, paf_parent, NULL); 1.144 + return NULL; 1.145 +} 1.146 +#endif /* !defined(ANDROID) */ 1.147 + 1.148 +struct SamplerRegistry { 1.149 + static void AddActiveSampler(Sampler *sampler) { 1.150 + ASSERT(!SamplerRegistry::sampler); 1.151 + SamplerRegistry::sampler = sampler; 1.152 + } 1.153 + static void RemoveActiveSampler(Sampler *sampler) { 1.154 + SamplerRegistry::sampler = NULL; 1.155 + } 1.156 + static Sampler *sampler; 1.157 +}; 1.158 + 1.159 +Sampler *SamplerRegistry::sampler = NULL; 1.160 + 1.161 +static mozilla::Atomic<ThreadProfile*> sCurrentThreadProfile; 1.162 +static sem_t sSignalHandlingDone; 1.163 + 1.164 +static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) { 1.165 + Sampler::GetActiveSampler()->RequestSave(); 1.166 +} 1.167 + 1.168 +static void SetSampleContext(TickSample* sample, void* context) 1.169 +{ 1.170 + // Extracting the sample from the context is extremely machine dependent. 1.171 + ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); 1.172 + mcontext_t& mcontext = ucontext->uc_mcontext; 1.173 +#if V8_HOST_ARCH_IA32 1.174 + sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]); 1.175 + sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]); 1.176 + sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]); 1.177 +#elif V8_HOST_ARCH_X64 1.178 + sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]); 1.179 + sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]); 1.180 + sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]); 1.181 +#elif V8_HOST_ARCH_ARM 1.182 +// An undefined macro evaluates to 0, so this applies to Android's Bionic also. 1.183 +#if !defined(ANDROID) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) 1.184 + sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]); 1.185 + sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]); 1.186 + sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]); 1.187 +#ifdef ENABLE_ARM_LR_SAVING 1.188 + sample->lr = reinterpret_cast<Address>(mcontext.gregs[R14]); 1.189 +#endif 1.190 +#else 1.191 + sample->pc = reinterpret_cast<Address>(mcontext.arm_pc); 1.192 + sample->sp = reinterpret_cast<Address>(mcontext.arm_sp); 1.193 + sample->fp = reinterpret_cast<Address>(mcontext.arm_fp); 1.194 +#ifdef ENABLE_ARM_LR_SAVING 1.195 + sample->lr = reinterpret_cast<Address>(mcontext.arm_lr); 1.196 +#endif 1.197 +#endif 1.198 +#elif V8_HOST_ARCH_MIPS 1.199 + // Implement this on MIPS. 1.200 + UNIMPLEMENTED(); 1.201 +#endif 1.202 +} 1.203 + 1.204 +#ifdef ANDROID 1.205 +#define V8_HOST_ARCH_ARM 1 1.206 +#define SYS_gettid __NR_gettid 1.207 +#define SYS_tgkill __NR_tgkill 1.208 +#else 1.209 +#define V8_HOST_ARCH_X64 1 1.210 +#endif 1.211 +static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { 1.212 + if (!Sampler::GetActiveSampler()) { 1.213 + sem_post(&sSignalHandlingDone); 1.214 + return; 1.215 + } 1.216 + 1.217 + TickSample sample_obj; 1.218 + TickSample* sample = &sample_obj; 1.219 + sample->context = context; 1.220 + 1.221 +#ifdef ENABLE_SPS_LEAF_DATA 1.222 + // If profiling, we extract the current pc and sp. 1.223 + if (Sampler::GetActiveSampler()->IsProfiling()) { 1.224 + SetSampleContext(sample, context); 1.225 + } 1.226 +#endif 1.227 + sample->threadProfile = sCurrentThreadProfile; 1.228 + sample->timestamp = mozilla::TimeStamp::Now(); 1.229 + 1.230 + Sampler::GetActiveSampler()->Tick(sample); 1.231 + 1.232 + sCurrentThreadProfile = NULL; 1.233 + sem_post(&sSignalHandlingDone); 1.234 +} 1.235 + 1.236 +// If the Nuwa process is enabled, we need to use the wrapper of tgkill() to 1.237 +// perform the mapping of thread ID. 1.238 +#ifdef MOZ_NUWA_PROCESS 1.239 +extern "C" MFBT_API int tgkill(pid_t tgid, pid_t tid, int signalno); 1.240 +#else 1.241 +int tgkill(pid_t tgid, pid_t tid, int signalno) { 1.242 + return syscall(SYS_tgkill, tgid, tid, signalno); 1.243 +} 1.244 +#endif 1.245 + 1.246 +class PlatformData : public Malloced { 1.247 + public: 1.248 + PlatformData() 1.249 + {} 1.250 +}; 1.251 + 1.252 +/* static */ PlatformData* 1.253 +Sampler::AllocPlatformData(int aThreadId) 1.254 +{ 1.255 + return new PlatformData; 1.256 +} 1.257 + 1.258 +/* static */ void 1.259 +Sampler::FreePlatformData(PlatformData* aData) 1.260 +{ 1.261 + delete aData; 1.262 +} 1.263 + 1.264 +static void* SignalSender(void* arg) { 1.265 + // Taken from platform_thread_posix.cc 1.266 + prctl(PR_SET_NAME, "SamplerThread", 0, 0, 0); 1.267 + 1.268 +#ifdef MOZ_NUWA_PROCESS 1.269 + // If the Nuwa process is enabled, we need to mark and freeze the sampler 1.270 + // thread in the Nuwa process and have this thread recreated in the spawned 1.271 + // child. 1.272 + if(IsNuwaProcess()) { 1.273 + NuwaMarkCurrentThread(nullptr, nullptr); 1.274 + // Freeze the thread here so the spawned child will get the correct tgid 1.275 + // from the getpid() call below. 1.276 + NuwaFreezeCurrentThread(); 1.277 + } 1.278 +#endif 1.279 + 1.280 + int vm_tgid_ = getpid(); 1.281 + 1.282 + while (SamplerRegistry::sampler->IsActive()) { 1.283 + SamplerRegistry::sampler->HandleSaveRequest(); 1.284 + 1.285 + if (!SamplerRegistry::sampler->IsPaused()) { 1.286 + mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); 1.287 + std::vector<ThreadInfo*> threads = 1.288 + SamplerRegistry::sampler->GetRegisteredThreads(); 1.289 + 1.290 + for (uint32_t i = 0; i < threads.size(); i++) { 1.291 + ThreadInfo* info = threads[i]; 1.292 + 1.293 + // This will be null if we're not interested in profiling this thread. 1.294 + if (!info->Profile()) 1.295 + continue; 1.296 + 1.297 + PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); 1.298 + if (sleeping == PseudoStack::SLEEPING_AGAIN) { 1.299 + info->Profile()->DuplicateLastSample(); 1.300 + //XXX: This causes flushes regardless of jank-only mode 1.301 + info->Profile()->flush(); 1.302 + continue; 1.303 + } 1.304 + 1.305 + // We use sCurrentThreadProfile the ThreadProfile for the 1.306 + // thread we're profiling to the signal handler 1.307 + sCurrentThreadProfile = info->Profile(); 1.308 + 1.309 + int threadId = info->ThreadId(); 1.310 + 1.311 + if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) { 1.312 + printf_stderr("profiler failed to signal tid=%d\n", threadId); 1.313 +#ifdef DEBUG 1.314 + abort(); 1.315 +#endif 1.316 + continue; 1.317 + } 1.318 + 1.319 + // Wait for the signal handler to run before moving on to the next one 1.320 + sem_wait(&sSignalHandlingDone); 1.321 + } 1.322 + } 1.323 + 1.324 + // Convert ms to us and subtract 100 us to compensate delays 1.325 + // occuring during signal delivery. 1.326 + // TODO measure and confirm this. 1.327 + int interval = floor(SamplerRegistry::sampler->interval() * 1000 + 0.5) - 100; 1.328 + if (interval <= 0) { 1.329 + interval = 1; 1.330 + } 1.331 + OS::SleepMicro(interval); 1.332 + } 1.333 + return 0; 1.334 +} 1.335 + 1.336 +Sampler::Sampler(double interval, bool profiling, int entrySize) 1.337 + : interval_(interval), 1.338 + profiling_(profiling), 1.339 + paused_(false), 1.340 + active_(false), 1.341 + entrySize_(entrySize) { 1.342 +} 1.343 + 1.344 +Sampler::~Sampler() { 1.345 + ASSERT(!signal_sender_launched_); 1.346 +} 1.347 + 1.348 + 1.349 +void Sampler::Start() { 1.350 + LOG("Sampler started"); 1.351 + 1.352 +#ifdef USE_EHABI_STACKWALK 1.353 + mozilla::EHABIStackWalkInit(); 1.354 +#endif 1.355 + SamplerRegistry::AddActiveSampler(this); 1.356 + 1.357 + // Initialize signal handler communication 1.358 + sCurrentThreadProfile = NULL; 1.359 + if (sem_init(&sSignalHandlingDone, /* pshared: */ 0, /* value: */ 0) != 0) { 1.360 + LOG("Error initializing semaphore"); 1.361 + return; 1.362 + } 1.363 + 1.364 + // Request profiling signals. 1.365 + LOG("Request signal"); 1.366 + struct sigaction sa; 1.367 + sa.sa_sigaction = ProfilerSignalHandler; 1.368 + sigemptyset(&sa.sa_mask); 1.369 + sa.sa_flags = SA_RESTART | SA_SIGINFO; 1.370 + if (sigaction(SIGPROF, &sa, &old_sigprof_signal_handler_) != 0) { 1.371 + LOG("Error installing signal"); 1.372 + return; 1.373 + } 1.374 + 1.375 + // Request save profile signals 1.376 + struct sigaction sa2; 1.377 + sa2.sa_sigaction = ProfilerSaveSignalHandler; 1.378 + sigemptyset(&sa2.sa_mask); 1.379 + sa2.sa_flags = SA_RESTART | SA_SIGINFO; 1.380 + if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &old_sigsave_signal_handler_) != 0) { 1.381 + LOG("Error installing start signal"); 1.382 + return; 1.383 + } 1.384 + LOG("Signal installed"); 1.385 + signal_handler_installed_ = true; 1.386 + 1.387 + // Start a thread that sends SIGPROF signal to VM thread. 1.388 + // Sending the signal ourselves instead of relying on itimer provides 1.389 + // much better accuracy. 1.390 + SetActive(true); 1.391 + if (pthread_create( 1.392 + &signal_sender_thread_, NULL, SignalSender, NULL) == 0) { 1.393 + signal_sender_launched_ = true; 1.394 + } 1.395 + LOG("Profiler thread started"); 1.396 +} 1.397 + 1.398 + 1.399 +void Sampler::Stop() { 1.400 + SetActive(false); 1.401 + 1.402 + // Wait for signal sender termination (it will exit after setting 1.403 + // active_ to false). 1.404 + if (signal_sender_launched_) { 1.405 + pthread_join(signal_sender_thread_, NULL); 1.406 + signal_sender_launched_ = false; 1.407 + } 1.408 + 1.409 + SamplerRegistry::RemoveActiveSampler(this); 1.410 + 1.411 + // Restore old signal handler 1.412 + if (signal_handler_installed_) { 1.413 + sigaction(SIGNAL_SAVE_PROFILE, &old_sigsave_signal_handler_, 0); 1.414 + sigaction(SIGPROF, &old_sigprof_signal_handler_, 0); 1.415 + signal_handler_installed_ = false; 1.416 + } 1.417 +} 1.418 + 1.419 +bool Sampler::RegisterCurrentThread(const char* aName, 1.420 + PseudoStack* aPseudoStack, 1.421 + bool aIsMainThread, void* stackTop) 1.422 +{ 1.423 + if (!Sampler::sRegisteredThreadsMutex) 1.424 + return false; 1.425 + 1.426 + mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); 1.427 + 1.428 + int id = gettid(); 1.429 + for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { 1.430 + ThreadInfo* info = sRegisteredThreads->at(i); 1.431 + if (info->ThreadId() == id) { 1.432 + // Thread already registered. This means the first unregister will be 1.433 + // too early. 1.434 + ASSERT(false); 1.435 + return false; 1.436 + } 1.437 + } 1.438 + 1.439 + set_tls_stack_top(stackTop); 1.440 + 1.441 + ThreadInfo* info = new ThreadInfo(aName, id, 1.442 + aIsMainThread, aPseudoStack, stackTop); 1.443 + 1.444 + if (sActiveSampler) { 1.445 + sActiveSampler->RegisterThread(info); 1.446 + } 1.447 + 1.448 + sRegisteredThreads->push_back(info); 1.449 + 1.450 + uwt__register_thread_for_profiling(stackTop); 1.451 + return true; 1.452 +} 1.453 + 1.454 +void Sampler::UnregisterCurrentThread() 1.455 +{ 1.456 + if (!Sampler::sRegisteredThreadsMutex) 1.457 + return; 1.458 + 1.459 + tlsStackTop.set(nullptr); 1.460 + 1.461 + mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); 1.462 + 1.463 + int id = gettid(); 1.464 + 1.465 + for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { 1.466 + ThreadInfo* info = sRegisteredThreads->at(i); 1.467 + if (info->ThreadId() == id) { 1.468 + delete info; 1.469 + sRegisteredThreads->erase(sRegisteredThreads->begin() + i); 1.470 + break; 1.471 + } 1.472 + } 1.473 + 1.474 + uwt__unregister_thread_for_profiling(); 1.475 +} 1.476 + 1.477 +#ifdef ANDROID 1.478 +static struct sigaction old_sigstart_signal_handler; 1.479 +const int SIGSTART = SIGUSR2; 1.480 + 1.481 +static void freeArray(const char** array, int size) { 1.482 + for (int i = 0; i < size; i++) { 1.483 + free((void*) array[i]); 1.484 + } 1.485 +} 1.486 + 1.487 +static uint32_t readCSVArray(char* csvList, const char** buffer) { 1.488 + uint32_t count; 1.489 + char* savePtr; 1.490 + int newlinePos = strlen(csvList) - 1; 1.491 + if (csvList[newlinePos] == '\n') { 1.492 + csvList[newlinePos] = '\0'; 1.493 + } 1.494 + 1.495 + char* item = strtok_r(csvList, ",", &savePtr); 1.496 + for (count = 0; item; item = strtok_r(NULL, ",", &savePtr)) { 1.497 + int length = strlen(item) + 1; // Include \0 1.498 + char* newBuf = (char*) malloc(sizeof(char) * length); 1.499 + buffer[count] = newBuf; 1.500 + strncpy(newBuf, item, length); 1.501 + count++; 1.502 + } 1.503 + 1.504 + return count; 1.505 +} 1.506 + 1.507 +// Currently support only the env variables 1.508 +// reported in read_profiler_env 1.509 +static void ReadProfilerVars(const char* fileName, const char** features, 1.510 + uint32_t* featureCount, const char** threadNames, uint32_t* threadCount) { 1.511 + FILE* file = fopen(fileName, "r"); 1.512 + const int bufferSize = 1024; 1.513 + char line[bufferSize]; 1.514 + char* feature; 1.515 + char* value; 1.516 + char* savePtr; 1.517 + 1.518 + if (file) { 1.519 + while (fgets(line, bufferSize, file) != NULL) { 1.520 + feature = strtok_r(line, "=", &savePtr); 1.521 + value = strtok_r(NULL, "", &savePtr); 1.522 + 1.523 + if (strncmp(feature, PROFILER_MODE, bufferSize) == 0) { 1.524 + set_profiler_mode(value); 1.525 + } else if (strncmp(feature, PROFILER_INTERVAL, bufferSize) == 0) { 1.526 + set_profiler_interval(value); 1.527 + } else if (strncmp(feature, PROFILER_ENTRIES, bufferSize) == 0) { 1.528 + set_profiler_entries(value); 1.529 + } else if (strncmp(feature, PROFILER_STACK, bufferSize) == 0) { 1.530 + set_profiler_scan(value); 1.531 + } else if (strncmp(feature, PROFILER_FEATURES, bufferSize) == 0) { 1.532 + *featureCount = readCSVArray(value, features); 1.533 + } else if (strncmp(feature, "threads", bufferSize) == 0) { 1.534 + *threadCount = readCSVArray(value, threadNames); 1.535 + } 1.536 + } 1.537 + 1.538 + fclose(file); 1.539 + } 1.540 +} 1.541 + 1.542 + 1.543 +static void StartSignalHandler(int signal, siginfo_t* info, void* context) { 1.544 + // XXX: Everything we do here is NOT async signal safe. We risk nasty things 1.545 + // like deadlocks but we typically only do this once so it tends to be ok. 1.546 + // See bug 909403 1.547 + uint32_t featureCount = 0; 1.548 + uint32_t threadCount = 0; 1.549 + 1.550 + // Just allocate 10 features for now 1.551 + // FIXME: these don't really point to const chars* 1.552 + // So we free them later, but we don't want to change the const char** 1.553 + // declaration in profiler_start. Annoying but ok for now. 1.554 + const char* threadNames[10]; 1.555 + const char* features[10]; 1.556 + const char* profilerConfigFile = "/data/local/tmp/profiler.options"; 1.557 + 1.558 + ReadProfilerVars(profilerConfigFile, features, &featureCount, threadNames, &threadCount); 1.559 + MOZ_ASSERT(featureCount < 10); 1.560 + MOZ_ASSERT(threadCount < 10); 1.561 + 1.562 + profiler_start(PROFILE_DEFAULT_ENTRY, 1, 1.563 + features, featureCount, 1.564 + threadNames, threadCount); 1.565 + 1.566 + freeArray(threadNames, threadCount); 1.567 + freeArray(features, featureCount); 1.568 +} 1.569 + 1.570 +void OS::Startup() 1.571 +{ 1.572 + LOG("Registering start signal"); 1.573 + struct sigaction sa; 1.574 + sa.sa_sigaction = StartSignalHandler; 1.575 + sigemptyset(&sa.sa_mask); 1.576 + sa.sa_flags = SA_RESTART | SA_SIGINFO; 1.577 + if (sigaction(SIGSTART, &sa, &old_sigstart_signal_handler) != 0) { 1.578 + LOG("Error installing signal"); 1.579 + } 1.580 +} 1.581 + 1.582 +#else 1.583 + 1.584 +void OS::Startup() { 1.585 + // Set up the fork handlers. 1.586 + setup_atfork(); 1.587 +} 1.588 + 1.589 +#endif 1.590 + 1.591 + 1.592 + 1.593 +void TickSample::PopulateContext(void* aContext) 1.594 +{ 1.595 + MOZ_ASSERT(aContext); 1.596 + ucontext_t* pContext = reinterpret_cast<ucontext_t*>(aContext); 1.597 + if (!getcontext(pContext)) { 1.598 + context = pContext; 1.599 + SetSampleContext(this, aContext); 1.600 + } 1.601 +} 1.602 + 1.603 +// WARNING: Works with values up to 1 second 1.604 +void OS::SleepMicro(int microseconds) 1.605 +{ 1.606 + struct timespec ts; 1.607 + ts.tv_sec = 0; 1.608 + ts.tv_nsec = microseconds * 1000UL; 1.609 + 1.610 + while (true) { 1.611 + // in the case of interrupt we keep waiting 1.612 + // nanosleep puts the remaining to back into ts 1.613 + if (!nanosleep(&ts, &ts) || errno != EINTR) { 1.614 + return; 1.615 + } 1.616 + } 1.617 +} 1.618 +