tools/profiler/platform-linux.cc

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
     2 //
     3 // Redistribution and use in source and binary forms, with or without
     4 // modification, are permitted provided that the following conditions
     5 // are met:
     6 //  * Redistributions of source code must retain the above copyright
     7 //    notice, this list of conditions and the following disclaimer.
     8 //  * Redistributions in binary form must reproduce the above copyright
     9 //    notice, this list of conditions and the following disclaimer in
    10 //    the documentation and/or other materials provided with the
    11 //    distribution.
    12 //  * Neither the name of Google, Inc. nor the names of its contributors
    13 //    may be used to endorse or promote products derived from this
    14 //    software without specific prior written permission.
    15 // 
    16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    19 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    20 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    21 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    22 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
    23 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
    24 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    26 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    27 // SUCH DAMAGE.
    29 /*
    30 # vim: sw=2
    31 */
    32 #include <stdio.h>
    33 #include <math.h>
    35 #include <pthread.h>
    36 #include <semaphore.h>
    37 #include <signal.h>
    38 #include <sys/time.h>
    39 #include <sys/resource.h>
    40 #include <sys/syscall.h>
    41 #include <sys/types.h>
    42 #include <sys/prctl.h> // set name
    43 #include <stdlib.h>
    44 #include <sched.h>
    45 #ifdef ANDROID
    46 #include <android/log.h>
    47 #else
    48 #define __android_log_print(a, ...)
    49 #endif
    50 #include <ucontext.h>
    51 // Ubuntu Dapper requires memory pages to be marked as
    52 // executable. Otherwise, OS raises an exception when executing code
    53 // in that page.
    54 #include <sys/types.h>  // mmap & munmap
    55 #include <sys/mman.h>   // mmap & munmap
    56 #include <sys/stat.h>   // open
    57 #include <fcntl.h>      // open
    58 #include <unistd.h>     // sysconf
    59 #include <semaphore.h>
    60 #ifdef __GLIBC__
    61 #include <execinfo.h>   // backtrace, backtrace_symbols
    62 #endif  // def __GLIBC__
    63 #include <strings.h>    // index
    64 #include <errno.h>
    65 #include <stdarg.h>
    66 #include "platform.h"
    67 #include "GeckoProfiler.h"
    68 #include "mozilla/Mutex.h"
    69 #include "mozilla/Atomics.h"
    70 #include "ProfileEntry.h"
    71 #include "nsThreadUtils.h"
    72 #include "TableTicker.h"
    73 #include "UnwinderThread2.h"
    74 #if defined(__ARM_EABI__) && defined(MOZ_WIDGET_GONK)
    75  // Should also work on other Android and ARM Linux, but not tested there yet.
    76 #define USE_EHABI_STACKWALK
    77 #include "EHABIStackWalk.h"
    78 #endif
    80 #include <string.h>
    81 #include <stdio.h>
    82 #include <list>
    84 #ifdef MOZ_NUWA_PROCESS
    85 #include "ipc/Nuwa.h"
    86 #endif
    88 #define SIGNAL_SAVE_PROFILE SIGUSR2
    90 #if defined(__GLIBC__)
    91 // glibc doesn't implement gettid(2).
    92 #include <sys/syscall.h>
    93 pid_t gettid()
    94 {
    95   return (pid_t) syscall(SYS_gettid);
    96 }
    97 #endif
    99 /* static */ Thread::tid_t
   100 Thread::GetCurrentId()
   101 {
   102   return gettid();
   103 }
   105 #if !defined(ANDROID)
   106 // Keep track of when any of our threads calls fork(), so we can
   107 // temporarily disable signal delivery during the fork() call.  Not
   108 // doing so appears to cause a kind of race, in which signals keep
   109 // getting delivered to the thread doing fork(), which keeps causing
   110 // it to fail and be restarted; hence forward progress is delayed a
   111 // great deal.  A side effect of this is to permanently disable
   112 // sampling in the child process.  See bug 837390.
   114 // Unfortunately this is only doable on non-Android, since Bionic
   115 // doesn't have pthread_atfork.
   117 // This records the current state at the time we paused it.
   118 static bool was_paused = false;
   120 // In the parent, just before the fork, record the pausedness state,
   121 // and then pause.
   122 static void paf_prepare(void) {
   123   if (Sampler::GetActiveSampler()) {
   124     was_paused = Sampler::GetActiveSampler()->IsPaused();
   125     Sampler::GetActiveSampler()->SetPaused(true);
   126   } else {
   127     was_paused = false;
   128   }
   129 }
   131 // In the parent, just after the fork, return pausedness to the
   132 // pre-fork state.
   133 static void paf_parent(void) {
   134   if (Sampler::GetActiveSampler())
   135     Sampler::GetActiveSampler()->SetPaused(was_paused);
   136 }
   138 // Set up the fork handlers.
   139 static void* setup_atfork() {
   140   pthread_atfork(paf_prepare, paf_parent, NULL);
   141   return NULL;
   142 }
   143 #endif /* !defined(ANDROID) */
   145 struct SamplerRegistry {
   146   static void AddActiveSampler(Sampler *sampler) {
   147     ASSERT(!SamplerRegistry::sampler);
   148     SamplerRegistry::sampler = sampler;
   149   }
   150   static void RemoveActiveSampler(Sampler *sampler) {
   151     SamplerRegistry::sampler = NULL;
   152   }
   153   static Sampler *sampler;
   154 };
   156 Sampler *SamplerRegistry::sampler = NULL;
   158 static mozilla::Atomic<ThreadProfile*> sCurrentThreadProfile;
   159 static sem_t sSignalHandlingDone;
   161 static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) {
   162   Sampler::GetActiveSampler()->RequestSave();
   163 }
   165 static void SetSampleContext(TickSample* sample, void* context)
   166 {
   167   // Extracting the sample from the context is extremely machine dependent.
   168   ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
   169   mcontext_t& mcontext = ucontext->uc_mcontext;
   170 #if V8_HOST_ARCH_IA32
   171   sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
   172   sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
   173   sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
   174 #elif V8_HOST_ARCH_X64
   175   sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
   176   sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
   177   sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
   178 #elif V8_HOST_ARCH_ARM
   179 // An undefined macro evaluates to 0, so this applies to Android's Bionic also.
   180 #if !defined(ANDROID) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
   181   sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
   182   sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
   183   sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
   184 #ifdef ENABLE_ARM_LR_SAVING
   185   sample->lr = reinterpret_cast<Address>(mcontext.gregs[R14]);
   186 #endif
   187 #else
   188   sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
   189   sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
   190   sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
   191 #ifdef ENABLE_ARM_LR_SAVING
   192   sample->lr = reinterpret_cast<Address>(mcontext.arm_lr);
   193 #endif
   194 #endif
   195 #elif V8_HOST_ARCH_MIPS
   196   // Implement this on MIPS.
   197   UNIMPLEMENTED();
   198 #endif
   199 }
   201 #ifdef ANDROID
   202 #define V8_HOST_ARCH_ARM 1
   203 #define SYS_gettid __NR_gettid
   204 #define SYS_tgkill __NR_tgkill
   205 #else
   206 #define V8_HOST_ARCH_X64 1
   207 #endif
   208 static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
   209   if (!Sampler::GetActiveSampler()) {
   210     sem_post(&sSignalHandlingDone);
   211     return;
   212   }
   214   TickSample sample_obj;
   215   TickSample* sample = &sample_obj;
   216   sample->context = context;
   218 #ifdef ENABLE_SPS_LEAF_DATA
   219   // If profiling, we extract the current pc and sp.
   220   if (Sampler::GetActiveSampler()->IsProfiling()) {
   221     SetSampleContext(sample, context);
   222   }
   223 #endif
   224   sample->threadProfile = sCurrentThreadProfile;
   225   sample->timestamp = mozilla::TimeStamp::Now();
   227   Sampler::GetActiveSampler()->Tick(sample);
   229   sCurrentThreadProfile = NULL;
   230   sem_post(&sSignalHandlingDone);
   231 }
   233 // If the Nuwa process is enabled, we need to use the wrapper of tgkill() to
   234 // perform the mapping of thread ID.
   235 #ifdef MOZ_NUWA_PROCESS
   236 extern "C" MFBT_API int tgkill(pid_t tgid, pid_t tid, int signalno);
   237 #else
   238 int tgkill(pid_t tgid, pid_t tid, int signalno) {
   239   return syscall(SYS_tgkill, tgid, tid, signalno);
   240 }
   241 #endif
   243 class PlatformData : public Malloced {
   244  public:
   245   PlatformData()
   246   {}
   247 };
   249 /* static */ PlatformData*
   250 Sampler::AllocPlatformData(int aThreadId)
   251 {
   252   return new PlatformData;
   253 }
   255 /* static */ void
   256 Sampler::FreePlatformData(PlatformData* aData)
   257 {
   258   delete aData;
   259 }
   261 static void* SignalSender(void* arg) {
   262   // Taken from platform_thread_posix.cc
   263   prctl(PR_SET_NAME, "SamplerThread", 0, 0, 0);
   265 #ifdef MOZ_NUWA_PROCESS
   266   // If the Nuwa process is enabled, we need to mark and freeze the sampler
   267   // thread in the Nuwa process and have this thread recreated in the spawned
   268   // child.
   269   if(IsNuwaProcess()) {
   270     NuwaMarkCurrentThread(nullptr, nullptr);
   271     // Freeze the thread here so the spawned child will get the correct tgid
   272     // from the getpid() call below.
   273     NuwaFreezeCurrentThread();
   274   }
   275 #endif
   277   int vm_tgid_ = getpid();
   279   while (SamplerRegistry::sampler->IsActive()) {
   280     SamplerRegistry::sampler->HandleSaveRequest();
   282     if (!SamplerRegistry::sampler->IsPaused()) {
   283       mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   284       std::vector<ThreadInfo*> threads =
   285         SamplerRegistry::sampler->GetRegisteredThreads();
   287       for (uint32_t i = 0; i < threads.size(); i++) {
   288         ThreadInfo* info = threads[i];
   290         // This will be null if we're not interested in profiling this thread.
   291         if (!info->Profile())
   292           continue;
   294         PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
   295         if (sleeping == PseudoStack::SLEEPING_AGAIN) {
   296           info->Profile()->DuplicateLastSample();
   297           //XXX: This causes flushes regardless of jank-only mode
   298           info->Profile()->flush();
   299           continue;
   300         }
   302         // We use sCurrentThreadProfile the ThreadProfile for the
   303         // thread we're profiling to the signal handler
   304         sCurrentThreadProfile = info->Profile();
   306         int threadId = info->ThreadId();
   308         if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
   309           printf_stderr("profiler failed to signal tid=%d\n", threadId);
   310 #ifdef DEBUG
   311           abort();
   312 #endif
   313           continue;
   314         }
   316         // Wait for the signal handler to run before moving on to the next one
   317         sem_wait(&sSignalHandlingDone);
   318       }
   319     }
   321     // Convert ms to us and subtract 100 us to compensate delays
   322     // occuring during signal delivery.
   323     // TODO measure and confirm this.
   324     int interval = floor(SamplerRegistry::sampler->interval() * 1000 + 0.5) - 100;
   325     if (interval <= 0) {
   326       interval = 1;
   327     }
   328     OS::SleepMicro(interval);
   329   }
   330   return 0;
   331 }
   333 Sampler::Sampler(double interval, bool profiling, int entrySize)
   334     : interval_(interval),
   335       profiling_(profiling),
   336       paused_(false),
   337       active_(false),
   338       entrySize_(entrySize) {
   339 }
   341 Sampler::~Sampler() {
   342   ASSERT(!signal_sender_launched_);
   343 }
   346 void Sampler::Start() {
   347   LOG("Sampler started");
   349 #ifdef USE_EHABI_STACKWALK
   350   mozilla::EHABIStackWalkInit();
   351 #endif
   352   SamplerRegistry::AddActiveSampler(this);
   354   // Initialize signal handler communication
   355   sCurrentThreadProfile = NULL;
   356   if (sem_init(&sSignalHandlingDone, /* pshared: */ 0, /* value: */ 0) != 0) {
   357     LOG("Error initializing semaphore");
   358     return;
   359   }
   361   // Request profiling signals.
   362   LOG("Request signal");
   363   struct sigaction sa;
   364   sa.sa_sigaction = ProfilerSignalHandler;
   365   sigemptyset(&sa.sa_mask);
   366   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   367   if (sigaction(SIGPROF, &sa, &old_sigprof_signal_handler_) != 0) {
   368     LOG("Error installing signal");
   369     return;
   370   }
   372   // Request save profile signals
   373   struct sigaction sa2;
   374   sa2.sa_sigaction = ProfilerSaveSignalHandler;
   375   sigemptyset(&sa2.sa_mask);
   376   sa2.sa_flags = SA_RESTART | SA_SIGINFO;
   377   if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &old_sigsave_signal_handler_) != 0) {
   378     LOG("Error installing start signal");
   379     return;
   380   }
   381   LOG("Signal installed");
   382   signal_handler_installed_ = true;
   384   // Start a thread that sends SIGPROF signal to VM thread.
   385   // Sending the signal ourselves instead of relying on itimer provides
   386   // much better accuracy.
   387   SetActive(true);
   388   if (pthread_create(
   389         &signal_sender_thread_, NULL, SignalSender, NULL) == 0) {
   390     signal_sender_launched_ = true;
   391   }
   392   LOG("Profiler thread started");
   393 }
   396 void Sampler::Stop() {
   397   SetActive(false);
   399   // Wait for signal sender termination (it will exit after setting
   400   // active_ to false).
   401   if (signal_sender_launched_) {
   402     pthread_join(signal_sender_thread_, NULL);
   403     signal_sender_launched_ = false;
   404   }
   406   SamplerRegistry::RemoveActiveSampler(this);
   408   // Restore old signal handler
   409   if (signal_handler_installed_) {
   410     sigaction(SIGNAL_SAVE_PROFILE, &old_sigsave_signal_handler_, 0);
   411     sigaction(SIGPROF, &old_sigprof_signal_handler_, 0);
   412     signal_handler_installed_ = false;
   413   }
   414 }
   416 bool Sampler::RegisterCurrentThread(const char* aName,
   417                                     PseudoStack* aPseudoStack,
   418                                     bool aIsMainThread, void* stackTop)
   419 {
   420   if (!Sampler::sRegisteredThreadsMutex)
   421     return false;
   423   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   425   int id = gettid();
   426   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
   427     ThreadInfo* info = sRegisteredThreads->at(i);
   428     if (info->ThreadId() == id) {
   429       // Thread already registered. This means the first unregister will be
   430       // too early.
   431       ASSERT(false);
   432       return false;
   433     }
   434   }
   436   set_tls_stack_top(stackTop);
   438   ThreadInfo* info = new ThreadInfo(aName, id,
   439     aIsMainThread, aPseudoStack, stackTop);
   441   if (sActiveSampler) {
   442     sActiveSampler->RegisterThread(info);
   443   }
   445   sRegisteredThreads->push_back(info);
   447   uwt__register_thread_for_profiling(stackTop);
   448   return true;
   449 }
   451 void Sampler::UnregisterCurrentThread()
   452 {
   453   if (!Sampler::sRegisteredThreadsMutex)
   454     return;
   456   tlsStackTop.set(nullptr);
   458   mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   460   int id = gettid();
   462   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
   463     ThreadInfo* info = sRegisteredThreads->at(i);
   464     if (info->ThreadId() == id) {
   465       delete info;
   466       sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
   467       break;
   468     }
   469   }
   471   uwt__unregister_thread_for_profiling();
   472 }
   474 #ifdef ANDROID
   475 static struct sigaction old_sigstart_signal_handler;
   476 const int SIGSTART = SIGUSR2;
   478 static void freeArray(const char** array, int size) {
   479   for (int i = 0; i < size; i++) {
   480     free((void*) array[i]);
   481   }
   482 }
   484 static uint32_t readCSVArray(char* csvList, const char** buffer) {
   485   uint32_t count;
   486   char* savePtr;
   487   int newlinePos = strlen(csvList) - 1;
   488   if (csvList[newlinePos] == '\n') {
   489     csvList[newlinePos] = '\0';
   490   }
   492   char* item = strtok_r(csvList, ",", &savePtr);
   493   for (count = 0; item; item = strtok_r(NULL, ",", &savePtr)) {
   494     int length = strlen(item) + 1;  // Include \0
   495     char* newBuf = (char*) malloc(sizeof(char) * length);
   496     buffer[count] = newBuf;
   497     strncpy(newBuf, item, length);
   498     count++;
   499   }
   501   return count;
   502 }
   504 // Currently support only the env variables
   505 // reported in read_profiler_env
   506 static void ReadProfilerVars(const char* fileName, const char** features,
   507                             uint32_t* featureCount, const char** threadNames, uint32_t* threadCount) {
   508   FILE* file = fopen(fileName, "r");
   509   const int bufferSize = 1024;
   510   char line[bufferSize];
   511   char* feature;
   512   char* value;
   513   char* savePtr;
   515   if (file) {
   516     while (fgets(line, bufferSize, file) != NULL) {
   517       feature = strtok_r(line, "=", &savePtr);
   518       value = strtok_r(NULL, "", &savePtr);
   520       if (strncmp(feature, PROFILER_MODE, bufferSize) == 0) {
   521         set_profiler_mode(value);
   522       } else if (strncmp(feature, PROFILER_INTERVAL, bufferSize) == 0) {
   523         set_profiler_interval(value);
   524       } else if (strncmp(feature, PROFILER_ENTRIES, bufferSize) == 0) {
   525         set_profiler_entries(value);
   526       } else if (strncmp(feature, PROFILER_STACK, bufferSize) == 0) {
   527         set_profiler_scan(value);
   528       } else if (strncmp(feature, PROFILER_FEATURES, bufferSize) == 0) {
   529         *featureCount = readCSVArray(value, features);
   530       } else if (strncmp(feature, "threads", bufferSize) == 0) {
   531         *threadCount = readCSVArray(value, threadNames);
   532       }
   533     }
   535     fclose(file);
   536   }
   537 }
   540 static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
   541   // XXX: Everything we do here is NOT async signal safe. We risk nasty things
   542   // like deadlocks but we typically only do this once so it tends to be ok.
   543   // See bug 909403
   544   uint32_t featureCount = 0;
   545   uint32_t threadCount = 0;
   547   // Just allocate 10 features for now
   548   // FIXME: these don't really point to const chars*
   549   // So we free them later, but we don't want to change the const char**
   550   // declaration in profiler_start. Annoying but ok for now.
   551   const char* threadNames[10];
   552   const char* features[10];
   553   const char* profilerConfigFile = "/data/local/tmp/profiler.options";
   555   ReadProfilerVars(profilerConfigFile, features, &featureCount, threadNames, &threadCount);
   556   MOZ_ASSERT(featureCount < 10);
   557   MOZ_ASSERT(threadCount < 10);
   559   profiler_start(PROFILE_DEFAULT_ENTRY, 1,
   560       features, featureCount,
   561       threadNames, threadCount);
   563   freeArray(threadNames, threadCount);
   564   freeArray(features, featureCount);
   565 }
   567 void OS::Startup()
   568 {
   569   LOG("Registering start signal");
   570   struct sigaction sa;
   571   sa.sa_sigaction = StartSignalHandler;
   572   sigemptyset(&sa.sa_mask);
   573   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   574   if (sigaction(SIGSTART, &sa, &old_sigstart_signal_handler) != 0) {
   575     LOG("Error installing signal");
   576   }
   577 }
   579 #else
   581 void OS::Startup() {
   582   // Set up the fork handlers.
   583   setup_atfork();
   584 }
   586 #endif
   590 void TickSample::PopulateContext(void* aContext)
   591 {
   592   MOZ_ASSERT(aContext);
   593   ucontext_t* pContext = reinterpret_cast<ucontext_t*>(aContext);
   594   if (!getcontext(pContext)) {
   595     context = pContext;
   596     SetSampleContext(this, aContext);
   597   }
   598 }
   600 // WARNING: Works with values up to 1 second
   601 void OS::SleepMicro(int microseconds)
   602 {
   603   struct timespec ts;
   604   ts.tv_sec  = 0;
   605   ts.tv_nsec = microseconds * 1000UL;
   607   while (true) {
   608     // in the case of interrupt we keep waiting
   609     // nanosleep puts the remaining to back into ts
   610     if (!nanosleep(&ts, &ts) || errno != EINTR) {
   611       return;
   612     }
   613   }
   614 }

mercurial