summaryrefslogtreecommitdiffstats
path: root/tools/profiler/core/platform-linux.cc
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /tools/profiler/core/platform-linux.cc
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'tools/profiler/core/platform-linux.cc')
-rw-r--r--tools/profiler/core/platform-linux.cc715
1 files changed, 715 insertions, 0 deletions
diff --git a/tools/profiler/core/platform-linux.cc b/tools/profiler/core/platform-linux.cc
new file mode 100644
index 000000000..160873c9d
--- /dev/null
+++ b/tools/profiler/core/platform-linux.cc
@@ -0,0 +1,715 @@
+// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in
+// the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google, Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this
+// software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+/*
+# vim: sw=2
+*/
+#include <stdio.h>
+#include <math.h>
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/prctl.h> // set name
+#include <stdlib.h>
+#include <sched.h>
+#ifdef ANDROID
+#include <android/log.h>
+#else
+#define __android_log_print(a, ...)
+#endif
+#include <ucontext.h>
+// Ubuntu Dapper requires memory pages to be marked as
+// executable. Otherwise, OS raises an exception when executing code
+// in that page.
+#include <sys/types.h> // mmap & munmap
+#include <sys/mman.h> // mmap & munmap
+#include <sys/stat.h> // open
+#include <fcntl.h> // open
+#include <unistd.h> // sysconf
+#include <semaphore.h>
+#ifdef __GLIBC__
+#include <execinfo.h> // backtrace, backtrace_symbols
+#endif // def __GLIBC__
+#include <strings.h> // index
+#include <errno.h>
+#include <stdarg.h>
+#include "prenv.h"
+#include "platform.h"
+#include "GeckoProfiler.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/LinuxSignal.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/DebugOnly.h"
+#include "ProfileEntry.h"
+#include "nsThreadUtils.h"
+#include "GeckoSampler.h"
+#include "ThreadResponsiveness.h"
+
+#if defined(__ARM_EABI__) && defined(ANDROID)
+ // Should also work on other Android and ARM Linux, but not tested there yet.
+# define USE_EHABI_STACKWALK
+# include "EHABIStackWalk.h"
+#elif defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
+# define USE_LUL_STACKWALK
+# include "lul/LulMain.h"
+# include "lul/platform-linux-lul.h"
+#endif
+
+// Memory profile
+#include "nsMemoryReporterManager.h"
+
+#include <string.h>
+#include <list>
+
+#define SIGNAL_SAVE_PROFILE SIGUSR2
+
+using namespace mozilla;
+
+#if defined(USE_LUL_STACKWALK)
+// A singleton instance of the library. It is initialised at first
+// use. Currently only the main thread can call Sampler::Start, so
+// there is no need for a mechanism to ensure that it is only
+// created once in a multi-thread-use situation.
+lul::LUL* sLUL = nullptr;
+
+// This is the sLUL initialization routine.
+static void sLUL_initialization_routine(void)
+{
+ MOZ_ASSERT(!sLUL);
+ MOZ_ASSERT(gettid() == getpid()); /* "this is the main thread" */
+ sLUL = new lul::LUL(logging_sink_for_LUL);
+ // Read all the unwind info currently available.
+ read_procmaps(sLUL);
+}
+#endif
+
+/* static */ Thread::tid_t
+Thread::GetCurrentId()
+{
+ return gettid();
+}
+
+#if !defined(ANDROID)
+// Keep track of when any of our threads calls fork(), so we can
+// temporarily disable signal delivery during the fork() call. Not
+// doing so appears to cause a kind of race, in which signals keep
+// getting delivered to the thread doing fork(), which keeps causing
+// it to fail and be restarted; hence forward progress is delayed a
+// great deal. A side effect of this is to permanently disable
+// sampling in the child process. See bug 837390.
+
+// Unfortunately this is only doable on non-Android, since Bionic
+// doesn't have pthread_atfork.
+
+// This records the current state at the time we paused it.
+static bool was_paused = false;
+
+// In the parent, just before the fork, record the pausedness state,
+// and then pause.
+static void paf_prepare(void) {
+ if (Sampler::GetActiveSampler()) {
+ was_paused = Sampler::GetActiveSampler()->IsPaused();
+ Sampler::GetActiveSampler()->SetPaused(true);
+ } else {
+ was_paused = false;
+ }
+}
+
+// In the parent, just after the fork, return pausedness to the
+// pre-fork state.
+static void paf_parent(void) {
+ if (Sampler::GetActiveSampler())
+ Sampler::GetActiveSampler()->SetPaused(was_paused);
+}
+
+// Set up the fork handlers.
+static void* setup_atfork() {
+ pthread_atfork(paf_prepare, paf_parent, NULL);
+ return NULL;
+}
+#endif /* !defined(ANDROID) */
+
+struct SamplerRegistry {
+ static void AddActiveSampler(Sampler *sampler) {
+ ASSERT(!SamplerRegistry::sampler);
+ SamplerRegistry::sampler = sampler;
+ }
+ static void RemoveActiveSampler(Sampler *sampler) {
+ SamplerRegistry::sampler = NULL;
+ }
+ static Sampler *sampler;
+};
+
+Sampler *SamplerRegistry::sampler = NULL;
+
+static mozilla::Atomic<ThreadProfile*> sCurrentThreadProfile;
+static sem_t sSignalHandlingDone;
+
+static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) {
+ Sampler::GetActiveSampler()->RequestSave();
+}
+
+static void SetSampleContext(TickSample* sample, void* context)
+{
+ // Extracting the sample from the context is extremely machine dependent.
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
+ mcontext_t& mcontext = ucontext->uc_mcontext;
+#if V8_HOST_ARCH_IA32
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
+#elif V8_HOST_ARCH_X64
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
+#elif V8_HOST_ARCH_ARM
+// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
+#if !defined(ANDROID) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
+#ifdef ENABLE_ARM_LR_SAVING
+ sample->lr = reinterpret_cast<Address>(mcontext.gregs[R14]);
+#endif
+#else
+ sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
+ sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
+ sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
+#ifdef ENABLE_ARM_LR_SAVING
+ sample->lr = reinterpret_cast<Address>(mcontext.arm_lr);
+#endif
+#endif
+#elif V8_HOST_ARCH_MIPS
+ // Implement this on MIPS.
+ UNIMPLEMENTED();
+#endif
+}
+
+#ifdef ANDROID
+#define V8_HOST_ARCH_ARM 1
+#define SYS_gettid __NR_gettid
+#define SYS_tgkill __NR_tgkill
+#else
+#define V8_HOST_ARCH_X64 1
+#endif
+
+namespace {
+
+void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
+ // Avoid TSan warning about clobbering errno.
+ int savedErrno = errno;
+
+ if (!Sampler::GetActiveSampler()) {
+ sem_post(&sSignalHandlingDone);
+ errno = savedErrno;
+ return;
+ }
+
+ TickSample sample_obj;
+ TickSample* sample = &sample_obj;
+ sample->context = context;
+
+ // If profiling, we extract the current pc and sp.
+ if (Sampler::GetActiveSampler()->IsProfiling()) {
+ SetSampleContext(sample, context);
+ }
+ sample->threadProfile = sCurrentThreadProfile;
+ sample->timestamp = mozilla::TimeStamp::Now();
+ sample->rssMemory = sample->threadProfile->mRssMemory;
+ sample->ussMemory = sample->threadProfile->mUssMemory;
+
+ Sampler::GetActiveSampler()->Tick(sample);
+
+ sCurrentThreadProfile = NULL;
+ sem_post(&sSignalHandlingDone);
+ errno = savedErrno;
+}
+
+} // namespace
+
+static void ProfilerSignalThread(ThreadProfile *profile,
+ bool isFirstProfiledThread)
+{
+ if (isFirstProfiledThread && Sampler::GetActiveSampler()->ProfileMemory()) {
+ profile->mRssMemory = nsMemoryReporterManager::ResidentFast();
+ profile->mUssMemory = nsMemoryReporterManager::ResidentUnique();
+ } else {
+ profile->mRssMemory = 0;
+ profile->mUssMemory = 0;
+ }
+}
+
+int tgkill(pid_t tgid, pid_t tid, int signalno) {
+ return syscall(SYS_tgkill, tgid, tid, signalno);
+}
+
+class PlatformData {
+ public:
+ PlatformData()
+ {
+ MOZ_COUNT_CTOR(PlatformData);
+ }
+
+ ~PlatformData()
+ {
+ MOZ_COUNT_DTOR(PlatformData);
+ }
+};
+
+/* static */ PlatformData*
+Sampler::AllocPlatformData(int aThreadId)
+{
+ return new PlatformData;
+}
+
+/* static */ void
+Sampler::FreePlatformData(PlatformData* aData)
+{
+ delete aData;
+}
+
+static void* SignalSender(void* arg) {
+ // Taken from platform_thread_posix.cc
+ prctl(PR_SET_NAME, "SamplerThread", 0, 0, 0);
+
+ int vm_tgid_ = getpid();
+ DebugOnly<int> my_tid = gettid();
+
+ unsigned int nSignalsSent = 0;
+
+ TimeDuration lastSleepOverhead = 0;
+ TimeStamp sampleStart = TimeStamp::Now();
+ while (SamplerRegistry::sampler->IsActive()) {
+
+ SamplerRegistry::sampler->HandleSaveRequest();
+ SamplerRegistry::sampler->DeleteExpiredMarkers();
+
+ if (!SamplerRegistry::sampler->IsPaused()) {
+ ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+ std::vector<ThreadInfo*> threads =
+ SamplerRegistry::sampler->GetRegisteredThreads();
+
+ bool isFirstProfiledThread = true;
+ for (uint32_t i = 0; i < threads.size(); i++) {
+ ThreadInfo* info = threads[i];
+
+ // This will be null if we're not interested in profiling this thread.
+ if (!info->Profile() || info->IsPendingDelete())
+ continue;
+
+ PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
+ if (sleeping == PseudoStack::SLEEPING_AGAIN) {
+ info->Profile()->DuplicateLastSample();
+ continue;
+ }
+
+ info->Profile()->GetThreadResponsiveness()->Update();
+
+ // We use sCurrentThreadProfile the ThreadProfile for the
+ // thread we're profiling to the signal handler
+ sCurrentThreadProfile = info->Profile();
+
+ int threadId = info->ThreadId();
+ MOZ_ASSERT(threadId != my_tid);
+
+ // Profile from the signal sender for information which is not signal
+ // safe, and will have low variation between the emission of the signal
+ // and the signal handler catch.
+ ProfilerSignalThread(sCurrentThreadProfile, isFirstProfiledThread);
+
+ // Profile from the signal handler for information which is signal safe
+ // and needs to be precise too, such as the stack of the interrupted
+ // thread.
+ if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
+ printf_stderr("profiler failed to signal tid=%d\n", threadId);
+#ifdef DEBUG
+ abort();
+#else
+ continue;
+#endif
+ }
+
+ // Wait for the signal handler to run before moving on to the next one
+ sem_wait(&sSignalHandlingDone);
+ isFirstProfiledThread = false;
+
+ // The LUL unwind object accumulates frame statistics.
+ // Periodically we should poke it to give it a chance to print
+ // those statistics. This involves doing I/O (fprintf,
+ // __android_log_print, etc) and so can't safely be done from
+ // the unwinder threads, which is why it is done here.
+ if ((++nSignalsSent & 0xF) == 0) {
+# if defined(USE_LUL_STACKWALK)
+ sLUL->MaybeShowStats();
+# endif
+ }
+ }
+ }
+
+ TimeStamp targetSleepEndTime = sampleStart + TimeDuration::FromMicroseconds(SamplerRegistry::sampler->interval() * 1000);
+ TimeStamp beforeSleep = TimeStamp::Now();
+ TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep;
+ double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds());
+ OS::SleepMicro(sleepTime);
+ sampleStart = TimeStamp::Now();
+ lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime));
+ }
+ return 0;
+}
+
+Sampler::Sampler(double interval, bool profiling, int entrySize)
+ : interval_(interval),
+ profiling_(profiling),
+ paused_(false),
+ active_(false),
+ entrySize_(entrySize) {
+ MOZ_COUNT_CTOR(Sampler);
+}
+
+Sampler::~Sampler() {
+ MOZ_COUNT_DTOR(Sampler);
+ ASSERT(!signal_sender_launched_);
+}
+
+
+void Sampler::Start() {
+ LOG("Sampler started");
+
+#if defined(USE_EHABI_STACKWALK)
+ mozilla::EHABIStackWalkInit();
+#elif defined(USE_LUL_STACKWALK)
+ // NOTE: this isn't thread-safe. But we expect Sampler::Start to be
+ // called only from the main thread, so this is OK in general.
+ if (!sLUL) {
+ sLUL_initialization_routine();
+ }
+#endif
+
+ SamplerRegistry::AddActiveSampler(this);
+
+ // Initialize signal handler communication
+ sCurrentThreadProfile = NULL;
+ if (sem_init(&sSignalHandlingDone, /* pshared: */ 0, /* value: */ 0) != 0) {
+ LOG("Error initializing semaphore");
+ return;
+ }
+
+ // Request profiling signals.
+ LOG("Request signal");
+ struct sigaction sa;
+ sa.sa_sigaction = MOZ_SIGNAL_TRAMPOLINE(ProfilerSignalHandler);
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ if (sigaction(SIGPROF, &sa, &old_sigprof_signal_handler_) != 0) {
+ LOG("Error installing signal");
+ return;
+ }
+
+ // Request save profile signals
+ struct sigaction sa2;
+ sa2.sa_sigaction = ProfilerSaveSignalHandler;
+ sigemptyset(&sa2.sa_mask);
+ sa2.sa_flags = SA_RESTART | SA_SIGINFO;
+ if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &old_sigsave_signal_handler_) != 0) {
+ LOG("Error installing start signal");
+ return;
+ }
+ LOG("Signal installed");
+ signal_handler_installed_ = true;
+
+#if defined(USE_LUL_STACKWALK)
+ // Switch into unwind mode. After this point, we can't add or
+ // remove any unwind info to/from this LUL instance. The only thing
+ // we can do with it is Unwind() calls.
+ sLUL->EnableUnwinding();
+
+ // Has a test been requested?
+ if (PR_GetEnv("MOZ_PROFILER_LUL_TEST")) {
+ int nTests = 0, nTestsPassed = 0;
+ RunLulUnitTests(&nTests, &nTestsPassed, sLUL);
+ }
+#endif
+
+ // Start a thread that sends SIGPROF signal to VM thread.
+ // Sending the signal ourselves instead of relying on itimer provides
+ // much better accuracy.
+ SetActive(true);
+ if (pthread_create(
+ &signal_sender_thread_, NULL, SignalSender, NULL) == 0) {
+ signal_sender_launched_ = true;
+ }
+ LOG("Profiler thread started");
+}
+
+
+void Sampler::Stop() {
+ SetActive(false);
+
+ // Wait for signal sender termination (it will exit after setting
+ // active_ to false).
+ if (signal_sender_launched_) {
+ pthread_join(signal_sender_thread_, NULL);
+ signal_sender_launched_ = false;
+ }
+
+ SamplerRegistry::RemoveActiveSampler(this);
+
+ // Restore old signal handler
+ if (signal_handler_installed_) {
+ sigaction(SIGNAL_SAVE_PROFILE, &old_sigsave_signal_handler_, 0);
+ sigaction(SIGPROF, &old_sigprof_signal_handler_, 0);
+ signal_handler_installed_ = false;
+ }
+}
+
+bool Sampler::RegisterCurrentThread(const char* aName,
+ PseudoStack* aPseudoStack,
+ bool aIsMainThread, void* stackTop)
+{
+ if (!Sampler::sRegisteredThreadsMutex)
+ return false;
+
+ ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+
+ int id = gettid();
+ for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
+ ThreadInfo* info = sRegisteredThreads->at(i);
+ if (info->ThreadId() == id && !info->IsPendingDelete()) {
+ // Thread already registered. This means the first unregister will be
+ // too early.
+ ASSERT(false);
+ return false;
+ }
+ }
+
+ set_tls_stack_top(stackTop);
+
+ ThreadInfo* info = new StackOwningThreadInfo(aName, id,
+ aIsMainThread, aPseudoStack, stackTop);
+
+ if (sActiveSampler) {
+ sActiveSampler->RegisterThread(info);
+ }
+
+ sRegisteredThreads->push_back(info);
+
+ return true;
+}
+
+void Sampler::UnregisterCurrentThread()
+{
+ if (!Sampler::sRegisteredThreadsMutex)
+ return;
+
+ tlsStackTop.set(nullptr);
+
+ ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+
+ int id = gettid();
+
+ for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
+ ThreadInfo* info = sRegisteredThreads->at(i);
+ if (info->ThreadId() == id && !info->IsPendingDelete()) {
+ if (profiler_is_active()) {
+ // We still want to show the results of this thread if you
+ // save the profile shortly after a thread is terminated.
+ // For now we will defer the delete to profile stop.
+ info->SetPendingDelete();
+ break;
+ } else {
+ delete info;
+ sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
+ break;
+ }
+ }
+ }
+}
+
+#ifdef ANDROID
+static struct sigaction old_sigstart_signal_handler;
+const int SIGSTART = SIGUSR2;
+
+static void freeArray(const char** array, int size) {
+ for (int i = 0; i < size; i++) {
+ free((void*) array[i]);
+ }
+}
+
+static uint32_t readCSVArray(char* csvList, const char** buffer) {
+ uint32_t count;
+ char* savePtr;
+ int newlinePos = strlen(csvList) - 1;
+ if (csvList[newlinePos] == '\n') {
+ csvList[newlinePos] = '\0';
+ }
+
+ char* item = strtok_r(csvList, ",", &savePtr);
+ for (count = 0; item; item = strtok_r(NULL, ",", &savePtr)) {
+ int length = strlen(item) + 1; // Include \0
+ char* newBuf = (char*) malloc(sizeof(char) * length);
+ buffer[count] = newBuf;
+ strncpy(newBuf, item, length);
+ count++;
+ }
+
+ return count;
+}
+
+// Currently support only the env variables
+// reported in read_profiler_env
+static void ReadProfilerVars(const char* fileName, const char** features,
+ uint32_t* featureCount, const char** threadNames, uint32_t* threadCount) {
+ FILE* file = fopen(fileName, "r");
+ const int bufferSize = 1024;
+ char line[bufferSize];
+ char* feature;
+ char* value;
+ char* savePtr;
+
+ if (file) {
+ while (fgets(line, bufferSize, file) != NULL) {
+ feature = strtok_r(line, "=", &savePtr);
+ value = strtok_r(NULL, "", &savePtr);
+
+ if (strncmp(feature, PROFILER_INTERVAL, bufferSize) == 0) {
+ set_profiler_interval(value);
+ } else if (strncmp(feature, PROFILER_ENTRIES, bufferSize) == 0) {
+ set_profiler_entries(value);
+ } else if (strncmp(feature, PROFILER_STACK, bufferSize) == 0) {
+ set_profiler_scan(value);
+ } else if (strncmp(feature, PROFILER_FEATURES, bufferSize) == 0) {
+ *featureCount = readCSVArray(value, features);
+ } else if (strncmp(feature, "threads", bufferSize) == 0) {
+ *threadCount = readCSVArray(value, threadNames);
+ }
+ }
+
+ fclose(file);
+ }
+}
+
+static void DoStartTask() {
+ uint32_t featureCount = 0;
+ uint32_t threadCount = 0;
+
+ // Just allocate 10 features for now
+ // FIXME: these don't really point to const chars*
+ // So we free them later, but we don't want to change the const char**
+ // declaration in profiler_start. Annoying but ok for now.
+ const char* threadNames[10];
+ const char* features[10];
+ const char* profilerConfigFile = "/data/local/tmp/profiler.options";
+
+ ReadProfilerVars(profilerConfigFile, features, &featureCount, threadNames, &threadCount);
+ MOZ_ASSERT(featureCount < 10);
+ MOZ_ASSERT(threadCount < 10);
+
+ profiler_start(PROFILE_DEFAULT_ENTRY, 1,
+ features, featureCount,
+ threadNames, threadCount);
+
+ freeArray(threadNames, threadCount);
+ freeArray(features, featureCount);
+}
+
+static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
+ class StartTask : public Runnable {
+ public:
+ NS_IMETHOD Run() override {
+ DoStartTask();
+ return NS_OK;
+ }
+ };
+ // XXX: technically NS_DispatchToMainThread is NOT async signal safe. We risk
+ // nasty things like deadlocks, but the probability is very low and we
+ // typically only do this once so it tends to be ok. See bug 909403.
+ NS_DispatchToMainThread(new StartTask());
+}
+
+void OS::Startup()
+{
+ LOG("Registering start signal");
+ struct sigaction sa;
+ sa.sa_sigaction = StartSignalHandler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ if (sigaction(SIGSTART, &sa, &old_sigstart_signal_handler) != 0) {
+ LOG("Error installing signal");
+ }
+}
+
+#else
+
+void OS::Startup() {
+ // Set up the fork handlers.
+ setup_atfork();
+}
+
+#endif
+
+
+
+void TickSample::PopulateContext(void* aContext)
+{
+ MOZ_ASSERT(aContext);
+ ucontext_t* pContext = reinterpret_cast<ucontext_t*>(aContext);
+ if (!getcontext(pContext)) {
+ context = pContext;
+ SetSampleContext(this, aContext);
+ }
+}
+
+void OS::SleepMicro(int microseconds)
+{
+ if (MOZ_UNLIKELY(microseconds >= 1000000)) {
+ // Use usleep for larger intervals, because the nanosleep
+ // code below only supports intervals < 1 second.
+ MOZ_ALWAYS_TRUE(!::usleep(microseconds));
+ return;
+ }
+
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = microseconds * 1000UL;
+
+ int rv = ::nanosleep(&ts, &ts);
+
+ while (rv != 0 && errno == EINTR) {
+ // Keep waiting in case of interrupt.
+ // nanosleep puts the remaining time back into ts.
+ rv = ::nanosleep(&ts, &ts);
+ }
+
+ MOZ_ASSERT(!rv, "nanosleep call failed");
+}