tools/profiler/platform-linux.cc

changeset 0
6474c204b198
     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 +

mercurial