tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

platform.cpp (128650B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 // There are three kinds of samples done by the profiler.
      8 //
      9 // - A "periodic" sample is the most complex kind. It is done in response to a
     10 //   timer while the profiler is active. It involves writing a stack trace plus
     11 //   a variety of other values (memory measurements, responsiveness
     12 //   measurements, etc.) into the main ProfileBuffer. The sampling is done from
     13 //   off-thread, and so SuspendAndSampleAndResumeThread() is used to get the
     14 //   register values.
     15 //
     16 // - A "synchronous" sample is a simpler kind. It is done in response to an API
     17 //   call (profiler_get_backtrace()). It involves writing a stack trace and
     18 //   little else into a temporary ProfileBuffer, and wrapping that up in a
     19 //   ProfilerBacktrace that can be subsequently used in a marker. The sampling
     20 //   is done on-thread, and so REGISTERS_SYNC_POPULATE() is used to get the
     21 //   register values.
     22 //
     23 // - A "backtrace" sample is the simplest kind. It is done in response to an
     24 //   API call (profiler_suspend_and_sample_thread()). It involves getting a
     25 //   stack trace via a ProfilerStackCollector; it does not write to a
     26 //   ProfileBuffer. The sampling is done from off-thread, and so uses
     27 //   SuspendAndSampleAndResumeThread() to get the register values.
     28 
     29 #include "platform.h"
     30 
     31 #include <algorithm>
     32 #include <errno.h>
     33 #include <fstream>
     34 #include <ostream>
     35 #include <set>
     36 #include <sstream>
     37 #include <string_view>
     38 
     39 // #include "memory_hooks.h"
     40 #include "mozilla/AutoProfilerLabel.h"
     41 #include "mozilla/BaseAndGeckoProfilerDetail.h"
     42 #include "mozilla/BaseProfiler.h"
     43 #include "mozilla/BaseProfilerDetail.h"
     44 #include "mozilla/BaseProfilingCategory.h"
     45 #include "mozilla/DoubleConversion.h"
     46 #include "mozilla/PodOperations.h"
     47 #include "mozilla/Printf.h"
     48 #include "mozilla/ProfileBufferChunkManagerSingle.h"
     49 #include "mozilla/ProfileBufferChunkManagerWithLocalLimit.h"
     50 #include "mozilla/ProfileChunkedBuffer.h"
     51 #include "mozilla/ProfilerBufferSize.h"
     52 #include "mozilla/Services.h"
     53 #include "mozilla/SharedLibraries.h"
     54 #include "mozilla/Span.h"
     55 #include "mozilla/StackWalk.h"
     56 #ifdef XP_WIN
     57 #  include "mozilla/StackWalkThread.h"
     58 #  include "mozilla/WindowsStackWalkInitialization.h"
     59 #endif
     60 #include "mozilla/StaticPtr.h"
     61 #include "mozilla/ThreadLocal.h"
     62 #include "mozilla/TimeStamp.h"
     63 
     64 #include "mozilla/UniquePtr.h"
     65 #include "mozilla/Vector.h"
     66 #include "prdtoa.h"
     67 #include "prtime.h"
     68 
     69 #include "PageInformation.h"
     70 #include "ProfiledThreadData.h"
     71 #include "ProfilerBacktrace.h"
     72 #include "ProfileBuffer.h"
     73 #include "RegisteredThread.h"
     74 #include "ThreadInfo.h"
     75 #include "VTuneProfiler.h"
     76 
     77 // Win32 builds always have frame pointers, so FramePointerStackWalk() always
     78 // works.
     79 #if defined(GP_PLAT_x86_windows)
     80 #  define HAVE_NATIVE_UNWIND
     81 #  define USE_FRAME_POINTER_STACK_WALK
     82 #endif
     83 
     84 // Win64 builds always omit frame pointers, so we use the slower
     85 // MozStackWalk(), which works in that case.
     86 #if defined(GP_PLAT_amd64_windows)
     87 #  define HAVE_NATIVE_UNWIND
     88 #  define USE_MOZ_STACK_WALK
     89 #endif
     90 
     91 // AArch64 Win64 doesn't seem to use frame pointers, so we use the slower
     92 // MozStackWalk().
     93 #if defined(GP_PLAT_arm64_windows)
     94 #  define HAVE_NATIVE_UNWIND
     95 #  define USE_MOZ_STACK_WALK
     96 #endif
     97 
     98 // Mac builds use FramePointerStackWalk(). Even if we build without
     99 // frame pointers, we'll still get useful stacks in system libraries
    100 // because those always have frame pointers.
    101 // We don't use MozStackWalk() on Mac.
    102 #if defined(GP_OS_darwin)
    103 #  define HAVE_NATIVE_UNWIND
    104 #  define USE_FRAME_POINTER_STACK_WALK
    105 #endif
    106 
    107 // We can only stackwalk without expensive initialization on platforms which
    108 // support FramePointerStackWalk or MozStackWalk. LUL Stackwalking requires
    109 // initializing LUL, and EHABIStackWalk requires initializing EHABI, both of
    110 // which can be expensive.
    111 #if defined(USE_FRAME_POINTER_STACK_WALK) || defined(USE_MOZ_STACK_WALK)
    112 #  define HAVE_FASTINIT_NATIVE_UNWIND
    113 #endif
    114 
    115 #ifdef MOZ_VALGRIND
    116 #  include <valgrind/memcheck.h>
    117 #else
    118 #  define VALGRIND_MAKE_MEM_DEFINED(_addr, _len) ((void)0)
    119 #endif
    120 
    121 #if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
    122 #  include <ucontext.h>
    123 #endif
    124 
    125 namespace mozilla {
    126 namespace baseprofiler {
    127 
    128 using detail::RacyFeatures;
    129 
    130 bool LogTest(int aLevelToTest) {
    131  static const int maxLevel = getenv("MOZ_BASE_PROFILER_VERBOSE_LOGGING") ? 5
    132                              : getenv("MOZ_BASE_PROFILER_DEBUG_LOGGING") ? 4
    133                              : getenv("MOZ_BASE_PROFILER_LOGGING")       ? 3
    134                                                                          : 0;
    135  return aLevelToTest <= maxLevel;
    136 }
    137 
    138 void PrintToConsole(const char* aFmt, ...) {
    139  va_list args;
    140  va_start(args, aFmt);
    141 #if defined(ANDROID)
    142  __android_log_vprint(ANDROID_LOG_INFO, "Gecko", aFmt, args);
    143 #else
    144  vfprintf(stderr, aFmt, args);
    145 #endif
    146  va_end(args);
    147 }
    148 
    149 ProfileChunkedBuffer& profiler_get_core_buffer() {
    150  // This needs its own mutex, because it is used concurrently from functions
    151  // guarded by gPSMutex as well as others without safety (e.g.,
    152  // profiler_add_marker). It is *not* used inside the critical section of the
    153  // sampler, because mutexes cannot be used there.
    154  static ProfileChunkedBuffer sProfileChunkedBuffer{
    155      ProfileChunkedBuffer::ThreadSafety::WithMutex};
    156  return sProfileChunkedBuffer;
    157 }
    158 
    159 Atomic<int, MemoryOrdering::Relaxed> gSkipSampling;
    160 
    161 constexpr static bool ValidateFeatures() {
    162  int expectedFeatureNumber = 0;
    163 
    164  // Feature numbers should start at 0 and increase by 1 each.
    165 #define CHECK_FEATURE(n_, str_, Name_, desc_) \
    166  if ((n_) != expectedFeatureNumber) {        \
    167    return false;                             \
    168  }                                           \
    169  ++expectedFeatureNumber;
    170 
    171  BASE_PROFILER_FOR_EACH_FEATURE(CHECK_FEATURE)
    172 
    173 #undef CHECK_FEATURE
    174 
    175  return true;
    176 }
    177 
    178 static_assert(ValidateFeatures(), "Feature list is invalid");
    179 
    180 // Return all features that are available on this platform.
    181 static uint32_t AvailableFeatures() {
    182  uint32_t features = 0;
    183 
    184 #define ADD_FEATURE(n_, str_, Name_, desc_) \
    185  ProfilerFeature::Set##Name_(features);
    186 
    187  // Add all the possible features.
    188  BASE_PROFILER_FOR_EACH_FEATURE(ADD_FEATURE)
    189 
    190 #undef ADD_FEATURE
    191 
    192  // Now remove features not supported on this platform/configuration.
    193  ProfilerFeature::ClearJava(features);
    194  ProfilerFeature::ClearJS(features);
    195  ProfilerFeature::ClearScreenshots(features);
    196 #if !defined(HAVE_NATIVE_UNWIND)
    197  ProfilerFeature::ClearStackWalk(features);
    198 #endif
    199 #if !defined(GP_OS_windows)
    200  ProfilerFeature::ClearNoTimerResolutionChange(features);
    201 #endif
    202 
    203  return features;
    204 }
    205 
    206 // Default features common to all contexts (even if not available).
    207 static constexpr uint32_t DefaultFeatures() {
    208  return ProfilerFeature::Java | ProfilerFeature::JS |
    209         ProfilerFeature::StackWalk | ProfilerFeature::CPUUtilization |
    210         ProfilerFeature::ProcessCPU;
    211 }
    212 
    213 // Extra default features when MOZ_PROFILER_STARTUP is set (even if not
    214 // available).
    215 static constexpr uint32_t StartupExtraDefaultFeatures() {
    216  // Enable mainthreadio by default for startup profiles as startup is heavy on
    217  // I/O operations, and main thread I/O is really important to see there.
    218  return ProfilerFeature::MainThreadIO | ProfilerFeature::IPCMessages;
    219 }
    220 
    221 // The auto-lock/unlock mutex that guards accesses to CorePS and ActivePS.
    222 // Use `PSAutoLock lock;` to take the lock until the end of the enclosing block.
    223 // External profilers may use this same lock for their own data, but as the lock
    224 // is non-recursive, *only* `f(PSLockRef, ...)` functions below should be
    225 // called, to avoid double-locking.
    226 class MOZ_RAII PSAutoLock {
    227 public:
    228  PSAutoLock() : mLock(gPSMutex) {}
    229 
    230  PSAutoLock(const PSAutoLock&) = delete;
    231  void operator=(const PSAutoLock&) = delete;
    232 
    233  [[nodiscard]] static bool IsLockedOnCurrentThread() {
    234    return gPSMutex.IsLockedOnCurrentThread();
    235  }
    236 
    237 private:
    238  static detail::BaseProfilerMutex gPSMutex;
    239  detail::BaseProfilerAutoLock mLock;
    240 };
    241 
    242 MOZ_RUNINIT detail::BaseProfilerMutex PSAutoLock::gPSMutex{
    243    "Base Profiler mutex"};
    244 
    245 // Only functions that take a PSLockRef arg can access CorePS's and ActivePS's
    246 // fields.
    247 typedef const PSAutoLock& PSLockRef;
    248 
    249 #define PS_GET(type_, name_)      \
    250  static type_ name_(PSLockRef) { \
    251    MOZ_ASSERT(sInstance);        \
    252    return sInstance->m##name_;   \
    253  }
    254 
    255 #define PS_GET_LOCKLESS(type_, name_) \
    256  static type_ name_() {              \
    257    MOZ_ASSERT(sInstance);            \
    258    return sInstance->m##name_;       \
    259  }
    260 
    261 #define PS_GET_AND_SET(type_, name_)                  \
    262  PS_GET(type_, name_)                                \
    263  static void Set##name_(PSLockRef, type_ a##name_) { \
    264    MOZ_ASSERT(sInstance);                            \
    265    sInstance->m##name_ = a##name_;                   \
    266  }
    267 
    268 // All functions in this file can run on multiple threads unless they have an
    269 // NS_IsMainThread() assertion.
    270 
    271 // This class contains the profiler's core global state, i.e. that which is
    272 // valid even when the profiler is not active. Most profile operations can't do
    273 // anything useful when this class is not instantiated, so we release-assert
    274 // its non-nullness in all such operations.
    275 //
    276 // Accesses to CorePS are guarded by gPSMutex. Getters and setters take a
    277 // PSAutoLock reference as an argument as proof that the gPSMutex is currently
    278 // locked. This makes it clear when gPSMutex is locked and helps avoid
    279 // accidental unlocked accesses to global state. There are ways to circumvent
    280 // this mechanism, but please don't do so without *very* good reason and a
    281 // detailed explanation.
    282 //
    283 // The exceptions to this rule:
    284 //
    285 // - mProcessStartTime, because it's immutable;
    286 //
    287 // - each thread's RacyRegisteredThread object is accessible without locking via
    288 //   TLSRegisteredThread::RacyRegisteredThread().
    289 class CorePS {
    290 private:
    291  CorePS() : mProcessStartTime(TimeStamp::ProcessCreation()) {}
    292 
    293  ~CorePS() {}
    294 
    295 public:
    296  static void Create(PSLockRef aLock) {
    297    MOZ_ASSERT(!sInstance);
    298    sInstance = new CorePS();
    299  }
    300 
    301  static void Destroy(PSLockRef aLock) {
    302    MOZ_ASSERT(sInstance);
    303    delete sInstance;
    304    sInstance = nullptr;
    305  }
    306 
    307  // Unlike ActivePS::Exists(), CorePS::Exists() can be called without gPSMutex
    308  // being locked. This is because CorePS is instantiated so early on the main
    309  // thread that we don't have to worry about it being racy.
    310  static bool Exists() { return !!sInstance; }
    311 
    312  static void AddSizeOf(PSLockRef, MallocSizeOf aMallocSizeOf,
    313                        size_t& aProfSize, size_t& aLulSize) {
    314    MOZ_ASSERT(sInstance);
    315 
    316    aProfSize += aMallocSizeOf(sInstance);
    317 
    318    for (auto& registeredThread : sInstance->mRegisteredThreads) {
    319      aProfSize += registeredThread->SizeOfIncludingThis(aMallocSizeOf);
    320    }
    321 
    322    for (auto& registeredPage : sInstance->mRegisteredPages) {
    323      aProfSize += registeredPage->SizeOfIncludingThis(aMallocSizeOf);
    324    }
    325 
    326    // Measurement of the following things may be added later if DMD finds it
    327    // is worthwhile:
    328    // - CorePS::mRegisteredThreads itself (its elements' children are
    329    // measured above)
    330    // - CorePS::mRegisteredPages itself (its elements' children are
    331    // measured above)
    332    // - CorePS::mInterposeObserver
    333  }
    334 
    335  // No PSLockRef is needed for this field because it's immutable.
    336  PS_GET_LOCKLESS(const TimeStamp&, ProcessStartTime)
    337 
    338  PS_GET(const Vector<UniquePtr<RegisteredThread>>&, RegisteredThreads)
    339 
    340  static void AppendRegisteredThread(
    341      PSLockRef, UniquePtr<RegisteredThread>&& aRegisteredThread) {
    342    MOZ_ASSERT(sInstance);
    343    MOZ_RELEASE_ASSERT(
    344        sInstance->mRegisteredThreads.append(std::move(aRegisteredThread)));
    345  }
    346 
    347  static void RemoveRegisteredThread(PSLockRef,
    348                                     RegisteredThread* aRegisteredThread) {
    349    MOZ_ASSERT(sInstance);
    350    // Remove aRegisteredThread from mRegisteredThreads.
    351    for (UniquePtr<RegisteredThread>& rt : sInstance->mRegisteredThreads) {
    352      if (rt.get() == aRegisteredThread) {
    353        sInstance->mRegisteredThreads.erase(&rt);
    354        return;
    355      }
    356    }
    357  }
    358 
    359  PS_GET(Vector<RefPtr<PageInformation>>&, RegisteredPages)
    360 
    361  static void AppendRegisteredPage(PSLockRef,
    362                                   RefPtr<PageInformation>&& aRegisteredPage) {
    363    MOZ_ASSERT(sInstance);
    364    struct RegisteredPageComparator {
    365      PageInformation* aA;
    366      bool operator()(PageInformation* aB) const { return aA->Equals(aB); }
    367    };
    368 
    369    auto foundPageIter = std::find_if(
    370        sInstance->mRegisteredPages.begin(), sInstance->mRegisteredPages.end(),
    371        RegisteredPageComparator{aRegisteredPage.get()});
    372 
    373    if (foundPageIter != sInstance->mRegisteredPages.end()) {
    374      if ((*foundPageIter)->Url() == "about:blank") {
    375        // When a BrowsingContext is loaded, the first url loaded in it will be
    376        // about:blank, and if the principal matches, the first document loaded
    377        // in it will share an inner window. That's why we should delete the
    378        // intermittent about:blank if they share the inner window.
    379        sInstance->mRegisteredPages.erase(foundPageIter);
    380      } else {
    381        // Do not register the same page again.
    382        return;
    383      }
    384    }
    385    MOZ_RELEASE_ASSERT(
    386        sInstance->mRegisteredPages.append(std::move(aRegisteredPage)));
    387  }
    388 
    389  static void RemoveRegisteredPage(PSLockRef,
    390                                   uint64_t aRegisteredInnerWindowID) {
    391    MOZ_ASSERT(sInstance);
    392    // Remove RegisteredPage from mRegisteredPages by given inner window ID.
    393    sInstance->mRegisteredPages.eraseIf([&](const RefPtr<PageInformation>& rd) {
    394      return rd->InnerWindowID() == aRegisteredInnerWindowID;
    395    });
    396  }
    397 
    398  static void ClearRegisteredPages(PSLockRef) {
    399    MOZ_ASSERT(sInstance);
    400    sInstance->mRegisteredPages.clear();
    401  }
    402 
    403  PS_GET(const Vector<BaseProfilerCount*>&, Counters)
    404 
    405  static void AppendCounter(PSLockRef, BaseProfilerCount* aCounter) {
    406    MOZ_ASSERT(sInstance);
    407    // we don't own the counter; they may be stored in static objects
    408    MOZ_RELEASE_ASSERT(sInstance->mCounters.append(aCounter));
    409  }
    410 
    411  static void RemoveCounter(PSLockRef, BaseProfilerCount* aCounter) {
    412    // we may be called to remove a counter after the profiler is stopped or
    413    // late in shutdown.
    414    if (sInstance) {
    415      auto* counter = std::find(sInstance->mCounters.begin(),
    416                                sInstance->mCounters.end(), aCounter);
    417      MOZ_RELEASE_ASSERT(counter != sInstance->mCounters.end());
    418      sInstance->mCounters.erase(counter);
    419    }
    420  }
    421 
    422  PS_GET_AND_SET(const std::string&, ProcessName)
    423  PS_GET_AND_SET(const std::string&, ETLDplus1)
    424 
    425 private:
    426  // The singleton instance
    427  static CorePS* sInstance;
    428 
    429  // The time that the process started.
    430  const TimeStamp mProcessStartTime;
    431 
    432  // Info on all the registered threads.
    433  // ThreadIds in mRegisteredThreads are unique.
    434  Vector<UniquePtr<RegisteredThread>> mRegisteredThreads;
    435 
    436  // Info on all the registered pages.
    437  // InnerWindowIDs in mRegisteredPages are unique.
    438  Vector<RefPtr<PageInformation>> mRegisteredPages;
    439 
    440  // Non-owning pointers to all active counters
    441  Vector<BaseProfilerCount*> mCounters;
    442 
    443  // Process name, provided by child process initialization code.
    444  std::string mProcessName;
    445  // Private name, provided by child process initialization code (eTLD+1 in
    446  // fission)
    447  std::string mETLDplus1;
    448 };
    449 
    450 CorePS* CorePS::sInstance = nullptr;
    451 
    452 class SamplerThread;
    453 
    454 static SamplerThread* NewSamplerThread(PSLockRef aLock, uint32_t aGeneration,
    455                                       double aInterval, uint32_t aFeatures);
    456 
    457 struct LiveProfiledThreadData {
    458  RegisteredThread* mRegisteredThread;
    459  UniquePtr<ProfiledThreadData> mProfiledThreadData;
    460 };
    461 
    462 // The buffer size is provided as a number of "entries", this is their size in
    463 // bytes.
    464 constexpr static uint32_t scBytesPerEntry = 8;
    465 
    466 // This class contains the profiler's global state that is valid only when the
    467 // profiler is active. When not instantiated, the profiler is inactive.
    468 //
    469 // Accesses to ActivePS are guarded by gPSMutex, in much the same fashion as
    470 // CorePS.
    471 //
    472 class ActivePS {
    473 private:
    474  constexpr static uint32_t ChunkSizeForEntries(uint32_t aEntries) {
    475    return uint32_t(std::min(size_t(ClampToAllowedEntries(aEntries)) *
    476                                 scBytesPerEntry / scMinimumNumberOfChunks,
    477                             size_t(scMaximumChunkSize)));
    478  }
    479 
    480  static uint32_t AdjustFeatures(uint32_t aFeatures, uint32_t aFilterCount) {
    481    // Filter out any features unavailable in this platform/configuration.
    482    aFeatures &= AvailableFeatures();
    483 
    484    // Some features imply others.
    485    if (aFeatures & ProfilerFeature::FileIOAll) {
    486      aFeatures |= ProfilerFeature::MainThreadIO | ProfilerFeature::FileIO;
    487    } else if (aFeatures & ProfilerFeature::FileIO) {
    488      aFeatures |= ProfilerFeature::MainThreadIO;
    489    }
    490 
    491    return aFeatures;
    492  }
    493 
    494  ActivePS(PSLockRef aLock, const TimeStamp& aProfilingStartTime,
    495           PowerOfTwo32 aCapacity, double aInterval, uint32_t aFeatures,
    496           const char** aFilters, uint32_t aFilterCount,
    497           const Maybe<double>& aDuration)
    498      : mProfilingStartTime(aProfilingStartTime),
    499        mGeneration(sNextGeneration++),
    500        mCapacity(aCapacity),
    501        mDuration(aDuration),
    502        mInterval(aInterval),
    503        mFeatures(AdjustFeatures(aFeatures, aFilterCount)),
    504        mProfileBufferChunkManager(
    505            MakeUnique<ProfileBufferChunkManagerWithLocalLimit>(
    506                size_t(ClampToAllowedEntries(aCapacity.Value())) *
    507                    scBytesPerEntry,
    508                ChunkSizeForEntries(aCapacity.Value()))),
    509        mProfileBuffer([this]() -> ProfileChunkedBuffer& {
    510          ProfileChunkedBuffer& buffer = profiler_get_core_buffer();
    511          buffer.SetChunkManager(*mProfileBufferChunkManager);
    512          return buffer;
    513        }()),
    514        // The new sampler thread doesn't start sampling immediately because the
    515        // main loop within Run() is blocked until this function's caller
    516        // unlocks gPSMutex.
    517        mSamplerThread(
    518            NewSamplerThread(aLock, mGeneration, aInterval, aFeatures)),
    519        mIsPaused(false),
    520        mIsSamplingPaused(false) {
    521    // Deep copy and lower-case aFilters.
    522    MOZ_ALWAYS_TRUE(mFilters.resize(aFilterCount));
    523    MOZ_ALWAYS_TRUE(mFiltersLowered.resize(aFilterCount));
    524    for (uint32_t i = 0; i < aFilterCount; ++i) {
    525      mFilters[i] = aFilters[i];
    526      mFiltersLowered[i].reserve(mFilters[i].size());
    527      std::transform(mFilters[i].cbegin(), mFilters[i].cend(),
    528                     std::back_inserter(mFiltersLowered[i]), ::tolower);
    529    }
    530  }
    531 
    532  ~ActivePS() {
    533    if (mProfileBufferChunkManager) {
    534      // We still control the chunk manager, remove it from the core buffer.
    535      profiler_get_core_buffer().ResetChunkManager();
    536    }
    537  }
    538 
    539  bool ThreadSelected(const char* aThreadName) {
    540    if (mFiltersLowered.empty()) {
    541      return true;
    542    }
    543 
    544    std::string name = aThreadName;
    545    std::transform(name.begin(), name.end(), name.begin(), ::tolower);
    546 
    547    for (const auto& filter : mFiltersLowered) {
    548      if (filter == "*") {
    549        return true;
    550      }
    551 
    552      // Crude, non UTF-8 compatible, case insensitive substring search
    553      if (name.find(filter) != std::string::npos) {
    554        return true;
    555      }
    556 
    557      // If the filter is "pid:<my pid>", profile all threads.
    558      if (mozilla::profiler::detail::FilterHasPid(filter.c_str())) {
    559        return true;
    560      }
    561    }
    562 
    563    return false;
    564  }
    565 
    566 public:
    567  static void Create(PSLockRef aLock, const TimeStamp& aProfilingStartTime,
    568                     PowerOfTwo32 aCapacity, double aInterval,
    569                     uint32_t aFeatures, const char** aFilters,
    570                     uint32_t aFilterCount, const Maybe<double>& aDuration) {
    571    MOZ_ASSERT(!sInstance);
    572    sInstance = new ActivePS(aLock, aProfilingStartTime, aCapacity, aInterval,
    573                             aFeatures, aFilters, aFilterCount, aDuration);
    574  }
    575 
    576  [[nodiscard]] static SamplerThread* Destroy(PSLockRef aLock) {
    577    MOZ_ASSERT(sInstance);
    578    auto samplerThread = sInstance->mSamplerThread;
    579    delete sInstance;
    580    sInstance = nullptr;
    581 
    582    return samplerThread;
    583  }
    584 
    585  static bool Exists(PSLockRef) { return !!sInstance; }
    586 
    587  static bool Equals(PSLockRef, PowerOfTwo32 aCapacity,
    588                     const Maybe<double>& aDuration, double aInterval,
    589                     uint32_t aFeatures, const char** aFilters,
    590                     uint32_t aFilterCount) {
    591    MOZ_ASSERT(sInstance);
    592    if (sInstance->mCapacity != aCapacity ||
    593        sInstance->mDuration != aDuration ||
    594        sInstance->mInterval != aInterval ||
    595        sInstance->mFeatures != aFeatures ||
    596        sInstance->mFilters.length() != aFilterCount) {
    597      return false;
    598    }
    599 
    600    for (uint32_t i = 0; i < sInstance->mFilters.length(); ++i) {
    601      if (strcmp(sInstance->mFilters[i].c_str(), aFilters[i]) != 0) {
    602        return false;
    603      }
    604    }
    605    return true;
    606  }
    607 
    608  static size_t SizeOf(PSLockRef, MallocSizeOf aMallocSizeOf) {
    609    MOZ_ASSERT(sInstance);
    610 
    611    size_t n = aMallocSizeOf(sInstance);
    612 
    613    n += sInstance->mProfileBuffer.SizeOfExcludingThis(aMallocSizeOf);
    614 
    615    // Measurement of the following members may be added later if DMD finds it
    616    // is worthwhile:
    617    // - mLiveProfiledThreads (both the array itself, and the contents)
    618    // - mDeadProfiledThreads (both the array itself, and the contents)
    619    //
    620 
    621    return n;
    622  }
    623 
    624  static UniquePtr<ProfileBufferChunkManagerWithLocalLimit>
    625  ExtractBaseProfilerChunkManager(PSLockRef) {
    626    MOZ_ASSERT(sInstance);
    627    return std::move(sInstance->mProfileBufferChunkManager);
    628  }
    629 
    630  static bool ShouldProfileThread(PSLockRef aLock, ThreadInfo* aInfo) {
    631    MOZ_ASSERT(sInstance);
    632    return sInstance->ThreadSelected(aInfo->Name());
    633  }
    634 
    635  PS_GET_LOCKLESS(TimeStamp, ProfilingStartTime)
    636 
    637  PS_GET(uint32_t, Generation)
    638 
    639  PS_GET(PowerOfTwo32, Capacity)
    640 
    641  PS_GET(Maybe<double>, Duration)
    642 
    643  PS_GET(double, Interval)
    644 
    645  PS_GET(uint32_t, Features)
    646 
    647 #define PS_GET_FEATURE(n_, str_, Name_, desc_)                \
    648  static bool Feature##Name_(PSLockRef) {                     \
    649    MOZ_ASSERT(sInstance);                                    \
    650    return ProfilerFeature::Has##Name_(sInstance->mFeatures); \
    651  }
    652 
    653  BASE_PROFILER_FOR_EACH_FEATURE(PS_GET_FEATURE)
    654 
    655 #undef PS_GET_FEATURE
    656 
    657  PS_GET(const Vector<std::string>&, Filters)
    658  PS_GET(const Vector<std::string>&, FiltersLowered)
    659 
    660  static void FulfillChunkRequests(PSLockRef) {
    661    MOZ_ASSERT(sInstance);
    662    if (sInstance->mProfileBufferChunkManager) {
    663      sInstance->mProfileBufferChunkManager->FulfillChunkRequests();
    664    }
    665  }
    666 
    667  static ProfileBuffer& Buffer(PSLockRef) {
    668    MOZ_ASSERT(sInstance);
    669    return sInstance->mProfileBuffer;
    670  }
    671 
    672  static const Vector<LiveProfiledThreadData>& LiveProfiledThreads(PSLockRef) {
    673    MOZ_ASSERT(sInstance);
    674    return sInstance->mLiveProfiledThreads;
    675  }
    676 
    677  // Returns an array containing (RegisteredThread*, ProfiledThreadData*) pairs
    678  // for all threads that should be included in a profile, both for threads
    679  // that are still registered, and for threads that have been unregistered but
    680  // still have data in the buffer.
    681  // For threads that have already been unregistered, the RegisteredThread
    682  // pointer will be null.
    683  // The returned array is sorted by thread register time.
    684  // Do not hold on to the return value across thread registration or profiler
    685  // restarts.
    686  static Vector<std::pair<RegisteredThread*, ProfiledThreadData*>>
    687  ProfiledThreads(PSLockRef) {
    688    MOZ_ASSERT(sInstance);
    689    Vector<std::pair<RegisteredThread*, ProfiledThreadData*>> array;
    690    MOZ_RELEASE_ASSERT(
    691        array.initCapacity(sInstance->mLiveProfiledThreads.length() +
    692                           sInstance->mDeadProfiledThreads.length()));
    693    for (auto& t : sInstance->mLiveProfiledThreads) {
    694      MOZ_RELEASE_ASSERT(array.append(
    695          std::make_pair(t.mRegisteredThread, t.mProfiledThreadData.get())));
    696    }
    697    for (auto& t : sInstance->mDeadProfiledThreads) {
    698      MOZ_RELEASE_ASSERT(
    699          array.append(std::make_pair((RegisteredThread*)nullptr, t.get())));
    700    }
    701 
    702    std::sort(array.begin(), array.end(),
    703              [](const std::pair<RegisteredThread*, ProfiledThreadData*>& a,
    704                 const std::pair<RegisteredThread*, ProfiledThreadData*>& b) {
    705                return a.second->Info()->RegisterTime() <
    706                       b.second->Info()->RegisterTime();
    707              });
    708    return array;
    709  }
    710 
    711  static Vector<RefPtr<PageInformation>> ProfiledPages(PSLockRef aLock) {
    712    MOZ_ASSERT(sInstance);
    713    Vector<RefPtr<PageInformation>> array;
    714    for (auto& d : CorePS::RegisteredPages(aLock)) {
    715      MOZ_RELEASE_ASSERT(array.append(d));
    716    }
    717    for (auto& d : sInstance->mDeadProfiledPages) {
    718      MOZ_RELEASE_ASSERT(array.append(d));
    719    }
    720    // We don't need to sort the pages like threads since we won't show them
    721    // as a list.
    722    return array;
    723  }
    724 
    725  // Do a linear search through mLiveProfiledThreads to find the
    726  // ProfiledThreadData object for a RegisteredThread.
    727  static ProfiledThreadData* GetProfiledThreadData(
    728      PSLockRef, RegisteredThread* aRegisteredThread) {
    729    MOZ_ASSERT(sInstance);
    730    for (const LiveProfiledThreadData& thread :
    731         sInstance->mLiveProfiledThreads) {
    732      if (thread.mRegisteredThread == aRegisteredThread) {
    733        return thread.mProfiledThreadData.get();
    734      }
    735    }
    736    return nullptr;
    737  }
    738 
    739  static ProfiledThreadData* AddLiveProfiledThread(
    740      PSLockRef, RegisteredThread* aRegisteredThread,
    741      UniquePtr<ProfiledThreadData>&& aProfiledThreadData) {
    742    MOZ_ASSERT(sInstance);
    743    MOZ_RELEASE_ASSERT(
    744        sInstance->mLiveProfiledThreads.append(LiveProfiledThreadData{
    745            aRegisteredThread, std::move(aProfiledThreadData)}));
    746 
    747    // Return a weak pointer to the ProfiledThreadData object.
    748    return sInstance->mLiveProfiledThreads.back().mProfiledThreadData.get();
    749  }
    750 
    751  static void UnregisterThread(PSLockRef aLockRef,
    752                               RegisteredThread* aRegisteredThread) {
    753    MOZ_ASSERT(sInstance);
    754 
    755    DiscardExpiredDeadProfiledThreads(aLockRef);
    756 
    757    // Find the right entry in the mLiveProfiledThreads array and remove the
    758    // element, moving the ProfiledThreadData object for the thread into the
    759    // mDeadProfiledThreads array.
    760    // The thread's RegisteredThread object gets destroyed here.
    761    for (size_t i = 0; i < sInstance->mLiveProfiledThreads.length(); i++) {
    762      LiveProfiledThreadData& thread = sInstance->mLiveProfiledThreads[i];
    763      if (thread.mRegisteredThread == aRegisteredThread) {
    764        thread.mProfiledThreadData->NotifyUnregistered(
    765            sInstance->mProfileBuffer.BufferRangeEnd());
    766        MOZ_RELEASE_ASSERT(sInstance->mDeadProfiledThreads.append(
    767            std::move(thread.mProfiledThreadData)));
    768        sInstance->mLiveProfiledThreads.erase(
    769            &sInstance->mLiveProfiledThreads[i]);
    770        return;
    771      }
    772    }
    773  }
    774 
    775  PS_GET_AND_SET(bool, IsPaused)
    776 
    777  // True if sampling is paused (though generic `SetIsPaused()` or specific
    778  // `SetIsSamplingPaused()`).
    779  static bool IsSamplingPaused(PSLockRef lock) {
    780    MOZ_ASSERT(sInstance);
    781    return IsPaused(lock) || sInstance->mIsSamplingPaused;
    782  }
    783 
    784  static void SetIsSamplingPaused(PSLockRef, bool aIsSamplingPaused) {
    785    MOZ_ASSERT(sInstance);
    786    sInstance->mIsSamplingPaused = aIsSamplingPaused;
    787  }
    788 
    789  static void DiscardExpiredDeadProfiledThreads(PSLockRef) {
    790    MOZ_ASSERT(sInstance);
    791    uint64_t bufferRangeStart = sInstance->mProfileBuffer.BufferRangeStart();
    792    // Discard any dead threads that were unregistered before bufferRangeStart.
    793    sInstance->mDeadProfiledThreads.eraseIf(
    794        [bufferRangeStart](
    795            const UniquePtr<ProfiledThreadData>& aProfiledThreadData) {
    796          Maybe<uint64_t> bufferPosition =
    797              aProfiledThreadData->BufferPositionWhenUnregistered();
    798          MOZ_RELEASE_ASSERT(bufferPosition,
    799                             "should have unregistered this thread");
    800          return *bufferPosition < bufferRangeStart;
    801        });
    802  }
    803 
    804  static void UnregisterPage(PSLockRef aLock,
    805                             uint64_t aRegisteredInnerWindowID) {
    806    MOZ_ASSERT(sInstance);
    807    auto& registeredPages = CorePS::RegisteredPages(aLock);
    808    for (size_t i = 0; i < registeredPages.length(); i++) {
    809      RefPtr<PageInformation>& page = registeredPages[i];
    810      if (page->InnerWindowID() == aRegisteredInnerWindowID) {
    811        page->NotifyUnregistered(sInstance->mProfileBuffer.BufferRangeEnd());
    812        MOZ_RELEASE_ASSERT(
    813            sInstance->mDeadProfiledPages.append(std::move(page)));
    814        registeredPages.erase(&registeredPages[i--]);
    815      }
    816    }
    817  }
    818 
    819  static void DiscardExpiredPages(PSLockRef) {
    820    MOZ_ASSERT(sInstance);
    821    uint64_t bufferRangeStart = sInstance->mProfileBuffer.BufferRangeStart();
    822    // Discard any dead pages that were unregistered before
    823    // bufferRangeStart.
    824    sInstance->mDeadProfiledPages.eraseIf(
    825        [bufferRangeStart](const RefPtr<PageInformation>& aProfiledPage) {
    826          Maybe<uint64_t> bufferPosition =
    827              aProfiledPage->BufferPositionWhenUnregistered();
    828          MOZ_RELEASE_ASSERT(bufferPosition,
    829                             "should have unregistered this page");
    830          return *bufferPosition < bufferRangeStart;
    831        });
    832  }
    833 
    834  static void ClearUnregisteredPages(PSLockRef) {
    835    MOZ_ASSERT(sInstance);
    836    sInstance->mDeadProfiledPages.clear();
    837  }
    838 
    839  static void ClearExpiredExitProfiles(PSLockRef) {
    840    MOZ_ASSERT(sInstance);
    841    uint64_t bufferRangeStart = sInstance->mProfileBuffer.BufferRangeStart();
    842    // Discard exit profiles that were gathered before our buffer RangeStart.
    843    sInstance->mExitProfiles.eraseIf(
    844        [bufferRangeStart](const ExitProfile& aExitProfile) {
    845          return aExitProfile.mBufferPositionAtGatherTime < bufferRangeStart;
    846        });
    847  }
    848 
    849  static void AddExitProfile(PSLockRef aLock, const std::string& aExitProfile) {
    850    MOZ_ASSERT(sInstance);
    851 
    852    ClearExpiredExitProfiles(aLock);
    853 
    854    MOZ_RELEASE_ASSERT(sInstance->mExitProfiles.append(
    855        ExitProfile{aExitProfile, sInstance->mProfileBuffer.BufferRangeEnd()}));
    856  }
    857 
    858  static Vector<std::string> MoveExitProfiles(PSLockRef aLock) {
    859    MOZ_ASSERT(sInstance);
    860 
    861    ClearExpiredExitProfiles(aLock);
    862 
    863    Vector<std::string> profiles;
    864    MOZ_RELEASE_ASSERT(
    865        profiles.initCapacity(sInstance->mExitProfiles.length()));
    866    for (auto& profile : sInstance->mExitProfiles) {
    867      MOZ_RELEASE_ASSERT(profiles.append(std::move(profile.mJSON)));
    868    }
    869    sInstance->mExitProfiles.clear();
    870    return profiles;
    871  }
    872 
    873 private:
    874  // The singleton instance.
    875  static ActivePS* sInstance;
    876 
    877  const TimeStamp mProfilingStartTime;
    878 
    879  // We need to track activity generations. If we didn't we could have the
    880  // following scenario.
    881  //
    882  // - profiler_stop() locks gPSMutex, de-instantiates ActivePS, unlocks
    883  //   gPSMutex, deletes the SamplerThread (which does a join).
    884  //
    885  // - profiler_start() runs on a different thread, locks gPSMutex,
    886  //   re-instantiates ActivePS, unlocks gPSMutex -- all before the join
    887  //   completes.
    888  //
    889  // - SamplerThread::Run() locks gPSMutex, sees that ActivePS is instantiated,
    890  //   and continues as if the start/stop pair didn't occur. Also
    891  //   profiler_stop() is stuck, unable to finish.
    892  //
    893  // By checking ActivePS *and* the generation, we can avoid this scenario.
    894  // sNextGeneration is used to track the next generation number; it is static
    895  // because it must persist across different ActivePS instantiations.
    896  const uint32_t mGeneration;
    897  static uint32_t sNextGeneration;
    898 
    899  // The maximum number of 8-byte entries in mProfileBuffer.
    900  const PowerOfTwo32 mCapacity;
    901 
    902  // The maximum duration of entries in mProfileBuffer, in seconds.
    903  const Maybe<double> mDuration;
    904 
    905  // The interval between samples, measured in milliseconds.
    906  const double mInterval;
    907 
    908  // The profile features that are enabled.
    909  const uint32_t mFeatures;
    910 
    911  // Substrings of names of threads we want to profile.
    912  Vector<std::string> mFilters;
    913  Vector<std::string> mFiltersLowered;
    914 
    915  // The chunk manager used by `mProfileBuffer` below.
    916  // May become null if it gets transferred to the Gecko Profiler.
    917  UniquePtr<ProfileBufferChunkManagerWithLocalLimit> mProfileBufferChunkManager;
    918 
    919  // The buffer into which all samples are recorded.
    920  ProfileBuffer mProfileBuffer;
    921 
    922  // ProfiledThreadData objects for any threads that were profiled at any point
    923  // during this run of the profiler:
    924  //  - mLiveProfiledThreads contains all threads that are still registered, and
    925  //  - mDeadProfiledThreads contains all threads that have already been
    926  //    unregistered but for which there is still data in the profile buffer.
    927  Vector<LiveProfiledThreadData> mLiveProfiledThreads;
    928  Vector<UniquePtr<ProfiledThreadData>> mDeadProfiledThreads;
    929 
    930  // Info on all the dead pages.
    931  // Registered pages are being moved to this array after unregistration.
    932  // We are keeping them in case we need them in the profile data.
    933  // We are removing them when we ensure that we won't need them anymore.
    934  Vector<RefPtr<PageInformation>> mDeadProfiledPages;
    935 
    936  // The current sampler thread. This class is not responsible for destroying
    937  // the SamplerThread object; the Destroy() method returns it so the caller
    938  // can destroy it.
    939  SamplerThread* const mSamplerThread;
    940 
    941  // Is the profiler fully paused?
    942  bool mIsPaused;
    943 
    944  // Is the profiler periodic sampling paused?
    945  bool mIsSamplingPaused;
    946 
    947  struct ExitProfile {
    948    std::string mJSON;
    949    uint64_t mBufferPositionAtGatherTime;
    950  };
    951  Vector<ExitProfile> mExitProfiles;
    952 };
    953 
    954 ActivePS* ActivePS::sInstance = nullptr;
    955 uint32_t ActivePS::sNextGeneration = 0;
    956 
    957 #undef PS_GET
    958 #undef PS_GET_LOCKLESS
    959 #undef PS_GET_AND_SET
    960 
    961 namespace detail {
    962 
    963 TimeStamp GetProfilingStartTime() {
    964  if (!CorePS::Exists()) {
    965    return {};
    966  }
    967  PSAutoLock lock;
    968  if (!ActivePS::Exists(lock)) {
    969    return {};
    970  }
    971  return ActivePS::ProfilingStartTime();
    972 }
    973 
    974 [[nodiscard]] MFBT_API UniquePtr<ProfileBufferChunkManagerWithLocalLimit>
    975 ExtractBaseProfilerChunkManager() {
    976  PSAutoLock lock;
    977  if (MOZ_UNLIKELY(!ActivePS::Exists(lock))) {
    978    return nullptr;
    979  }
    980  return ActivePS::ExtractBaseProfilerChunkManager(lock);
    981 }
    982 
    983 }  // namespace detail
    984 
    985 MFBT_DATA Atomic<uint32_t, MemoryOrdering::Relaxed>
    986    RacyFeatures::sActiveAndFeatures(0);
    987 
    988 /* static */
    989 void RacyFeatures::SetActive(uint32_t aFeatures) {
    990  sActiveAndFeatures = Active | aFeatures;
    991 }
    992 
    993 /* static */
    994 void RacyFeatures::SetInactive() { sActiveAndFeatures = 0; }
    995 
    996 /* static */
    997 bool RacyFeatures::IsActive() { return uint32_t(sActiveAndFeatures) & Active; }
    998 
    999 /* static */
   1000 void RacyFeatures::SetPaused() { sActiveAndFeatures |= Paused; }
   1001 
   1002 /* static */
   1003 void RacyFeatures::SetUnpaused() { sActiveAndFeatures &= ~Paused; }
   1004 
   1005 /* static */
   1006 void RacyFeatures::SetSamplingPaused() { sActiveAndFeatures |= SamplingPaused; }
   1007 
   1008 /* static */
   1009 void RacyFeatures::SetSamplingUnpaused() {
   1010  sActiveAndFeatures &= ~SamplingPaused;
   1011 }
   1012 
   1013 /* static */
   1014 bool RacyFeatures::IsActiveWithFeature(uint32_t aFeature) {
   1015  uint32_t af = sActiveAndFeatures;  // copy it first
   1016  return (af & Active) && (af & aFeature);
   1017 }
   1018 
   1019 /* static */
   1020 bool RacyFeatures::IsActiveWithoutFeature(uint32_t aFeature) {
   1021  uint32_t af = sActiveAndFeatures;  // copy it first
   1022  return (af & Active) && !(af & aFeature);
   1023 }
   1024 
   1025 /* static */
   1026 bool RacyFeatures::IsActiveAndUnpaused() {
   1027  uint32_t af = sActiveAndFeatures;  // copy it first
   1028  return (af & Active) && !(af & Paused);
   1029 }
   1030 
   1031 /* static */
   1032 bool RacyFeatures::IsActiveAndSamplingUnpaused() {
   1033  uint32_t af = sActiveAndFeatures;  // copy it first
   1034  return (af & Active) && !(af & (Paused | SamplingPaused));
   1035 }
   1036 
   1037 // Each live thread has a RegisteredThread, and we store a reference to it in
   1038 // TLS. This class encapsulates that TLS.
   1039 class TLSRegisteredThread {
   1040 public:
   1041  static bool Init(PSLockRef) {
   1042    bool ok1 = sRegisteredThread.init();
   1043    bool ok2 = AutoProfilerLabel::sProfilingStack.init();
   1044    return ok1 && ok2;
   1045  }
   1046 
   1047  // Get the entire RegisteredThread. Accesses are guarded by gPSMutex.
   1048  static class RegisteredThread* RegisteredThread(PSLockRef) {
   1049    return sRegisteredThread.get();
   1050  }
   1051 
   1052  // Get only the RacyRegisteredThread. Accesses are not guarded by gPSMutex.
   1053  static class RacyRegisteredThread* RacyRegisteredThread() {
   1054    class RegisteredThread* registeredThread = sRegisteredThread.get();
   1055    return registeredThread ? &registeredThread->RacyRegisteredThread()
   1056                            : nullptr;
   1057  }
   1058 
   1059  // Get only the ProfilingStack. Accesses are not guarded by gPSMutex.
   1060  // RacyRegisteredThread() can also be used to get the ProfilingStack, but that
   1061  // is marginally slower because it requires an extra pointer indirection.
   1062  static ProfilingStack* Stack() {
   1063    return AutoProfilerLabel::sProfilingStack.get();
   1064  }
   1065 
   1066  static void SetRegisteredThread(PSLockRef,
   1067                                  class RegisteredThread* aRegisteredThread) {
   1068    sRegisteredThread.set(aRegisteredThread);
   1069    AutoProfilerLabel::sProfilingStack.set(
   1070        aRegisteredThread
   1071            ? &aRegisteredThread->RacyRegisteredThread().ProfilingStack()
   1072            : nullptr);
   1073  }
   1074 
   1075 private:
   1076  // This is a non-owning reference to the RegisteredThread;
   1077  // CorePS::mRegisteredThreads is the owning reference. On thread
   1078  // deregistration, this reference is cleared and the RegisteredThread is
   1079  // destroyed.
   1080  static MOZ_THREAD_LOCAL(class RegisteredThread*) sRegisteredThread;
   1081 };
   1082 
   1083 MOZ_THREAD_LOCAL(RegisteredThread*) TLSRegisteredThread::sRegisteredThread;
   1084 
   1085 /* static */
   1086 ProfilingStack* AutoProfilerLabel::GetProfilingStack() {
   1087  return sProfilingStack.get();
   1088 }
   1089 
   1090 // Although you can access a thread's ProfilingStack via
   1091 // TLSRegisteredThread::sRegisteredThread, we also have a second TLS pointer
   1092 // directly to the ProfilingStack. Here's why.
   1093 //
   1094 // - We need to be able to push to and pop from the ProfilingStack in
   1095 //   AutoProfilerLabel.
   1096 //
   1097 // - The class functions are hot and must be defined in BaseProfiler.h so they
   1098 //   can be inlined.
   1099 //
   1100 // - We don't want to expose TLSRegisteredThread (and RegisteredThread) in
   1101 //   BaseProfiler.h.
   1102 //
   1103 // This second pointer isn't ideal, but does provide a way to satisfy those
   1104 // constraints. TLSRegisteredThread is responsible for updating it.
   1105 MOZ_THREAD_LOCAL(ProfilingStack*) AutoProfilerLabel::sProfilingStack;
   1106 
   1107 namespace detail {
   1108 
   1109 [[nodiscard]] MFBT_API TimeStamp GetThreadRegistrationTime() {
   1110  if (!CorePS::Exists()) {
   1111    return {};
   1112  }
   1113 
   1114  PSAutoLock lock;
   1115 
   1116  RegisteredThread* registeredThread =
   1117      TLSRegisteredThread::RegisteredThread(lock);
   1118  if (!registeredThread) {
   1119    return {};
   1120  }
   1121 
   1122  return registeredThread->Info()->RegisterTime();
   1123 }
   1124 
   1125 }  // namespace detail
   1126 
   1127 // The name of the main thread.
   1128 static const char* const kMainThreadName = "GeckoMain";
   1129 
   1130 ////////////////////////////////////////////////////////////////////////
   1131 // BEGIN sampling/unwinding code
   1132 
   1133 // Additional registers that have to be saved when thread is paused.
   1134 #if defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android) || \
   1135    defined(GP_ARCH_x86)
   1136 #  define UNWINDING_REGS_HAVE_ECX_EDX
   1137 #elif defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_amd64_android) || \
   1138    defined(GP_PLAT_amd64_freebsd) || defined(GP_ARCH_amd64) ||         \
   1139    defined(__x86_64__)
   1140 #  define UNWINDING_REGS_HAVE_R10_R12
   1141 #elif defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android)
   1142 #  define UNWINDING_REGS_HAVE_LR_R7
   1143 #elif defined(GP_PLAT_arm64_linux) || defined(GP_PLAT_arm64_android) || \
   1144    defined(GP_PLAT_arm64_freebsd) || defined(GP_ARCH_arm64) ||         \
   1145    defined(__aarch64__)
   1146 #  define UNWINDING_REGS_HAVE_LR_R11
   1147 #endif
   1148 
   1149 // The registers used for stack unwinding and a few other sampling purposes.
   1150 // The ctor does nothing; users are responsible for filling in the fields.
   1151 class Registers {
   1152 public:
   1153  Registers()
   1154      : mPC{nullptr},
   1155        mSP{nullptr},
   1156        mFP{nullptr}
   1157 #if defined(UNWINDING_REGS_HAVE_ECX_EDX)
   1158        ,
   1159        mEcx{nullptr},
   1160        mEdx{nullptr}
   1161 #elif defined(UNWINDING_REGS_HAVE_R10_R12)
   1162        ,
   1163        mR10{nullptr},
   1164        mR12{nullptr}
   1165 #elif defined(UNWINDING_REGS_HAVE_LR_R7)
   1166        ,
   1167        mLR{nullptr},
   1168        mR7{nullptr}
   1169 #elif defined(UNWINDING_REGS_HAVE_LR_R11)
   1170        ,
   1171        mLR{nullptr},
   1172        mR11{nullptr}
   1173 #endif
   1174  {
   1175  }
   1176 
   1177  void Clear() { memset(this, 0, sizeof(*this)); }
   1178 
   1179  // These fields are filled in by
   1180  // Sampler::SuspendAndSampleAndResumeThread() for periodic and backtrace
   1181  // samples, and by REGISTERS_SYNC_POPULATE for synchronous samples.
   1182  Address mPC;  // Instruction pointer.
   1183  Address mSP;  // Stack pointer.
   1184  Address mFP;  // Frame pointer.
   1185 #if defined(UNWINDING_REGS_HAVE_ECX_EDX)
   1186  Address mEcx;  // Temp for return address.
   1187  Address mEdx;  // Temp for frame pointer.
   1188 #elif defined(UNWINDING_REGS_HAVE_R10_R12)
   1189  Address mR10;  // Temp for return address.
   1190  Address mR12;  // Temp for frame pointer.
   1191 #elif defined(UNWINDING_REGS_HAVE_LR_R7)
   1192  Address mLR;  // ARM link register, or temp for return address.
   1193  Address mR7;  // Temp for frame pointer.
   1194 #elif defined(UNWINDING_REGS_HAVE_LR_R11)
   1195  Address mLR;   // ARM link register, or temp for return address.
   1196  Address mR11;  // Temp for frame pointer.
   1197 #endif
   1198 
   1199 #if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
   1200  // This contains all the registers, which means it duplicates the four fields
   1201  // above. This is ok.
   1202  ucontext_t* mContext;  // The context from the signal handler.
   1203 #endif
   1204 };
   1205 
   1206 // Setting MAX_NATIVE_FRAMES too high risks the unwinder wasting a lot of time
   1207 // looping on corrupted stacks.
   1208 static const size_t MAX_NATIVE_FRAMES = 1024;
   1209 
   1210 struct NativeStack {
   1211  void* mPCs[MAX_NATIVE_FRAMES];
   1212  void* mSPs[MAX_NATIVE_FRAMES];
   1213  size_t mCount;  // Number of frames filled.
   1214 
   1215  NativeStack() : mPCs(), mSPs(), mCount(0) {}
   1216 };
   1217 
   1218 // Merges the profiling stack and native stack, outputting the details to
   1219 // aCollector.
   1220 static void MergeStacks(bool aIsSynchronous,
   1221                        const RegisteredThread& aRegisteredThread,
   1222                        const NativeStack& aNativeStack,
   1223                        ProfilerStackCollector& aCollector) {
   1224  // WARNING: this function runs within the profiler's "critical section".
   1225  // WARNING: this function might be called while the profiler is inactive, and
   1226  //          cannot rely on ActivePS.
   1227 
   1228  const ProfilingStack& profilingStack =
   1229      aRegisteredThread.RacyRegisteredThread().ProfilingStack();
   1230  const ProfilingStackFrame* profilingStackFrames = profilingStack.frames;
   1231  uint32_t profilingStackFrameCount = profilingStack.stackSize();
   1232 
   1233  Maybe<uint64_t> samplePosInBuffer;
   1234  if (!aIsSynchronous) {
   1235    // aCollector.SamplePositionInBuffer() will return Nothing() when
   1236    // profiler_suspend_and_sample_thread is called from the background hang
   1237    // reporter.
   1238    samplePosInBuffer = aCollector.SamplePositionInBuffer();
   1239  }
   1240  // While the profiling stack array is ordered oldest-to-youngest, the JS and
   1241  // native arrays are ordered youngest-to-oldest. We must add frames to aInfo
   1242  // oldest-to-youngest. Thus, iterate over the profiling stack forwards and JS
   1243  // and native arrays backwards. Note: this means the terminating condition
   1244  // jsIndex and nativeIndex is being < 0.
   1245  uint32_t profilingStackIndex = 0;
   1246  int32_t nativeIndex = aNativeStack.mCount - 1;
   1247 
   1248  uint8_t* lastLabelFrameStackAddr = nullptr;
   1249 
   1250  // Iterate as long as there is at least one frame remaining.
   1251  while (profilingStackIndex != profilingStackFrameCount || nativeIndex >= 0) {
   1252    // There are 1 to 3 frames available. Find and add the oldest.
   1253    uint8_t* profilingStackAddr = nullptr;
   1254    uint8_t* nativeStackAddr = nullptr;
   1255 
   1256    if (profilingStackIndex != profilingStackFrameCount) {
   1257      const ProfilingStackFrame& profilingStackFrame =
   1258          profilingStackFrames[profilingStackIndex];
   1259 
   1260      if (profilingStackFrame.isLabelFrame() ||
   1261          profilingStackFrame.isSpMarkerFrame()) {
   1262        lastLabelFrameStackAddr = (uint8_t*)profilingStackFrame.stackAddress();
   1263      }
   1264 
   1265      // Skip any JS_OSR frames. Such frames are used when the JS interpreter
   1266      // enters a jit frame on a loop edge (via on-stack-replacement, or OSR).
   1267      // To avoid both the profiling stack frame and jit frame being recorded
   1268      // (and showing up twice), the interpreter marks the interpreter
   1269      // profiling stack frame as JS_OSR to ensure that it doesn't get counted.
   1270      if (profilingStackFrame.isOSRFrame()) {
   1271        profilingStackIndex++;
   1272        continue;
   1273      }
   1274 
   1275      MOZ_ASSERT(lastLabelFrameStackAddr);
   1276      profilingStackAddr = lastLabelFrameStackAddr;
   1277    }
   1278 
   1279    if (nativeIndex >= 0) {
   1280      nativeStackAddr = (uint8_t*)aNativeStack.mSPs[nativeIndex];
   1281    }
   1282 
   1283    // If there's a native stack frame which has the same SP as a profiling
   1284    // stack frame, pretend we didn't see the native stack frame.  Ditto for a
   1285    // native stack frame which has the same SP as a JS stack frame.  In effect
   1286    // this means profiling stack frames or JS frames trump conflicting native
   1287    // frames.
   1288    if (nativeStackAddr && (profilingStackAddr == nativeStackAddr)) {
   1289      nativeStackAddr = nullptr;
   1290      nativeIndex--;
   1291      MOZ_ASSERT(profilingStackAddr);
   1292    }
   1293 
   1294    // Sanity checks.
   1295    MOZ_ASSERT_IF(profilingStackAddr, profilingStackAddr != nativeStackAddr);
   1296    MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != profilingStackAddr);
   1297 
   1298    // Check to see if profiling stack frame is top-most.
   1299    if (profilingStackAddr > nativeStackAddr) {
   1300      MOZ_ASSERT(profilingStackIndex < profilingStackFrameCount);
   1301      const ProfilingStackFrame& profilingStackFrame =
   1302          profilingStackFrames[profilingStackIndex];
   1303 
   1304      // Sp marker frames are just annotations and should not be recorded in
   1305      // the profile.
   1306      if (!profilingStackFrame.isSpMarkerFrame()) {
   1307        if (aIsSynchronous && profilingStackFrame.categoryPair() ==
   1308                                  ProfilingCategoryPair::PROFILER) {
   1309          // For stacks captured synchronously (ie. marker stacks), stop
   1310          // walking the stack as soon as we enter the profiler category,
   1311          // to avoid showing profiler internal code in marker stacks.
   1312          return;
   1313        }
   1314        aCollector.CollectProfilingStackFrame(profilingStackFrame);
   1315      }
   1316      profilingStackIndex++;
   1317      continue;
   1318    }
   1319 
   1320    // If we reach here, there must be a native stack frame and it must be the
   1321    // greatest frame.
   1322    if (nativeStackAddr) {
   1323      MOZ_ASSERT(nativeIndex >= 0);
   1324      void* addr = (void*)aNativeStack.mPCs[nativeIndex];
   1325      aCollector.CollectNativeLeafAddr(addr);
   1326    }
   1327    if (nativeIndex >= 0) {
   1328      nativeIndex--;
   1329    }
   1330  }
   1331 }
   1332 
   1333 #if defined(GP_OS_windows) && defined(USE_MOZ_STACK_WALK)
   1334 static HANDLE GetThreadHandle(PlatformData* aData);
   1335 #endif
   1336 
   1337 #if defined(USE_FRAME_POINTER_STACK_WALK) || defined(USE_MOZ_STACK_WALK)
   1338 static void StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP,
   1339                              void* aClosure) {
   1340  NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
   1341  MOZ_ASSERT(nativeStack->mCount < MAX_NATIVE_FRAMES);
   1342  nativeStack->mSPs[nativeStack->mCount] = aSP;
   1343  nativeStack->mPCs[nativeStack->mCount] = aPC;
   1344  nativeStack->mCount++;
   1345 }
   1346 #endif
   1347 
   1348 #if defined(USE_FRAME_POINTER_STACK_WALK)
   1349 static void DoFramePointerBacktrace(PSLockRef aLock,
   1350                                    const RegisteredThread& aRegisteredThread,
   1351                                    const Registers& aRegs,
   1352                                    NativeStack& aNativeStack) {
   1353  // WARNING: this function runs within the profiler's "critical section".
   1354  // WARNING: this function might be called while the profiler is inactive, and
   1355  //          cannot rely on ActivePS.
   1356 
   1357  // Start with the current function. We use 0 as the frame number here because
   1358  // the FramePointerStackWalk() call below will use 1..N. This is a bit weird
   1359  // but it doesn't matter because StackWalkCallback() doesn't use the frame
   1360  // number argument.
   1361  StackWalkCallback(/* frameNum */ 0, aRegs.mPC, aRegs.mSP, &aNativeStack);
   1362 
   1363  uint32_t maxFrames = uint32_t(MAX_NATIVE_FRAMES - aNativeStack.mCount);
   1364 
   1365  const void* stackEnd = aRegisteredThread.StackTop();
   1366  if (aRegs.mFP >= aRegs.mSP && aRegs.mFP <= stackEnd) {
   1367    FramePointerStackWalk(StackWalkCallback, maxFrames, &aNativeStack,
   1368                          reinterpret_cast<void**>(aRegs.mFP),
   1369                          const_cast<void*>(stackEnd));
   1370  }
   1371 }
   1372 #endif
   1373 
   1374 #if defined(USE_MOZ_STACK_WALK)
   1375 static void DoMozStackWalkBacktrace(PSLockRef aLock,
   1376                                    const RegisteredThread& aRegisteredThread,
   1377                                    const Registers& aRegs,
   1378                                    NativeStack& aNativeStack) {
   1379  // WARNING: this function runs within the profiler's "critical section".
   1380  // WARNING: this function might be called while the profiler is inactive, and
   1381  //          cannot rely on ActivePS.
   1382 
   1383  // Start with the current function. We use 0 as the frame number here because
   1384  // the MozStackWalkThread() call below will use 1..N. This is a bit weird but
   1385  // it doesn't matter because StackWalkCallback() doesn't use the frame number
   1386  // argument.
   1387  StackWalkCallback(/* frameNum */ 0, aRegs.mPC, aRegs.mSP, &aNativeStack);
   1388 
   1389  uint32_t maxFrames = uint32_t(MAX_NATIVE_FRAMES - aNativeStack.mCount);
   1390 
   1391  HANDLE thread = GetThreadHandle(aRegisteredThread.GetPlatformData());
   1392  MOZ_ASSERT(thread);
   1393  MozStackWalkThread(StackWalkCallback, maxFrames, &aNativeStack, thread,
   1394                     /* context */ nullptr);
   1395 }
   1396 #endif
   1397 
   1398 #ifdef USE_EHABI_STACKWALK
   1399 static void DoEHABIBacktrace(PSLockRef aLock,
   1400                             const RegisteredThread& aRegisteredThread,
   1401                             const Registers& aRegs,
   1402                             NativeStack& aNativeStack) {
   1403  // WARNING: this function runs within the profiler's "critical section".
   1404  // WARNING: this function might be called while the profiler is inactive, and
   1405  //          cannot rely on ActivePS.
   1406 
   1407  aNativeStack.mCount =
   1408      EHABIStackWalk(aRegs.mContext->uc_mcontext,
   1409                     const_cast<void*>(aRegisteredThread.StackTop()),
   1410                     aNativeStack.mSPs, aNativeStack.mPCs, MAX_NATIVE_FRAMES);
   1411 }
   1412 #endif
   1413 
   1414 #ifdef HAVE_NATIVE_UNWIND
   1415 static void DoNativeBacktrace(PSLockRef aLock,
   1416                              const RegisteredThread& aRegisteredThread,
   1417                              const Registers& aRegs,
   1418                              NativeStack& aNativeStack) {
   1419  // This method determines which stackwalker is used for periodic and
   1420  // synchronous samples. (Backtrace samples are treated differently, see
   1421  // profiler_suspend_and_sample_thread() for details). The only part of the
   1422  // ordering that matters is that LUL must precede FRAME_POINTER, because on
   1423  // Linux they can both be present.
   1424 #  if defined(USE_EHABI_STACKWALK)
   1425  DoEHABIBacktrace(aLock, aRegisteredThread, aRegs, aNativeStack);
   1426 #  elif defined(USE_FRAME_POINTER_STACK_WALK)
   1427  DoFramePointerBacktrace(aLock, aRegisteredThread, aRegs, aNativeStack);
   1428 #  elif defined(USE_MOZ_STACK_WALK)
   1429  DoMozStackWalkBacktrace(aLock, aRegisteredThread, aRegs, aNativeStack);
   1430 #  else
   1431 #    error "Invalid configuration"
   1432 #  endif
   1433 }
   1434 #endif
   1435 
   1436 // Writes some components shared by periodic and synchronous profiles to
   1437 // ActivePS's ProfileBuffer. (This should only be called from DoSyncSample()
   1438 // and DoPeriodicSample().)
   1439 //
   1440 // The grammar for entry sequences is in a comment above
   1441 // ProfileBuffer::StreamSamplesToJSON.
   1442 static inline void DoSharedSample(
   1443    PSLockRef aLock, bool aIsSynchronous, RegisteredThread& aRegisteredThread,
   1444    const Registers& aRegs, uint64_t aSamplePos, uint64_t aBufferRangeStart,
   1445    ProfileBuffer& aBuffer,
   1446    StackCaptureOptions aCaptureOptions = StackCaptureOptions::Full) {
   1447  // WARNING: this function runs within the profiler's "critical section".
   1448 
   1449  MOZ_ASSERT(!aBuffer.IsThreadSafe(),
   1450             "Mutexes cannot be used inside this critical section");
   1451 
   1452  MOZ_RELEASE_ASSERT(ActivePS::Exists(aLock));
   1453 
   1454  ProfileBufferCollector collector(aBuffer, aSamplePos, aBufferRangeStart);
   1455  NativeStack nativeStack;
   1456 #if defined(HAVE_NATIVE_UNWIND)
   1457  if (ActivePS::FeatureStackWalk(aLock) &&
   1458      aCaptureOptions == StackCaptureOptions::Full) {
   1459    DoNativeBacktrace(aLock, aRegisteredThread, aRegs, nativeStack);
   1460 
   1461    MergeStacks(aIsSynchronous, aRegisteredThread, nativeStack, collector);
   1462  } else
   1463 #endif
   1464  {
   1465    MergeStacks(aIsSynchronous, aRegisteredThread, nativeStack, collector);
   1466 
   1467    // We can't walk the whole native stack, but we can record the top frame.
   1468    if (aCaptureOptions == StackCaptureOptions::Full) {
   1469      aBuffer.AddEntry(ProfileBufferEntry::NativeLeafAddr((void*)aRegs.mPC));
   1470    }
   1471  }
   1472 }
   1473 
   1474 // Writes the components of a synchronous sample to the given ProfileBuffer.
   1475 static void DoSyncSample(PSLockRef aLock, RegisteredThread& aRegisteredThread,
   1476                         const TimeStamp& aNow, const Registers& aRegs,
   1477                         ProfileBuffer& aBuffer,
   1478                         StackCaptureOptions aCaptureOptions) {
   1479  // WARNING: this function runs within the profiler's "critical section".
   1480 
   1481  MOZ_ASSERT(aCaptureOptions != StackCaptureOptions::NoStack,
   1482             "DoSyncSample should not be called when no capture is needed");
   1483 
   1484  const uint64_t bufferRangeStart = aBuffer.BufferRangeStart();
   1485 
   1486  const uint64_t samplePos =
   1487      aBuffer.AddThreadIdEntry(aRegisteredThread.Info()->ThreadId());
   1488 
   1489  TimeDuration delta = aNow - CorePS::ProcessStartTime();
   1490  aBuffer.AddEntry(ProfileBufferEntry::Time(delta.ToMilliseconds()));
   1491 
   1492  DoSharedSample(aLock, /* aIsSynchronous = */ true, aRegisteredThread, aRegs,
   1493                 samplePos, bufferRangeStart, aBuffer, aCaptureOptions);
   1494 }
   1495 
   1496 // Writes the components of a periodic sample to ActivePS's ProfileBuffer.
   1497 // The ThreadId entry is already written in the main ProfileBuffer, its location
   1498 // is `aSamplePos`, we can write the rest to `aBuffer` (which may be different).
   1499 static void DoPeriodicSample(PSLockRef aLock,
   1500                             RegisteredThread& aRegisteredThread,
   1501                             ProfiledThreadData& aProfiledThreadData,
   1502                             const Registers& aRegs, uint64_t aSamplePos,
   1503                             uint64_t aBufferRangeStart,
   1504                             ProfileBuffer& aBuffer) {
   1505  // WARNING: this function runs within the profiler's "critical section".
   1506 
   1507  DoSharedSample(aLock, /* aIsSynchronous = */ false, aRegisteredThread, aRegs,
   1508                 aSamplePos, aBufferRangeStart, aBuffer);
   1509 }
   1510 
   1511 #undef UNWINDING_REGS_HAVE_ECX_EDX
   1512 #undef UNWINDING_REGS_HAVE_R10_R12
   1513 #undef UNWINDING_REGS_HAVE_LR_R7
   1514 #undef UNWINDING_REGS_HAVE_LR_R11
   1515 
   1516 // END sampling/unwinding code
   1517 ////////////////////////////////////////////////////////////////////////
   1518 
   1519 ////////////////////////////////////////////////////////////////////////
   1520 // BEGIN saving/streaming code
   1521 
   1522 const static uint64_t kJS_MAX_SAFE_UINTEGER = +9007199254740991ULL;
   1523 
   1524 static int64_t SafeJSInteger(uint64_t aValue) {
   1525  return aValue <= kJS_MAX_SAFE_UINTEGER ? int64_t(aValue) : -1;
   1526 }
   1527 
   1528 static void AddSharedLibraryInfoToStream(JSONWriter& aWriter,
   1529                                         const SharedLibrary& aLib) {
   1530  aWriter.StartObjectElement();
   1531  aWriter.IntProperty("start", SafeJSInteger(aLib.GetStart()));
   1532  aWriter.IntProperty("end", SafeJSInteger(aLib.GetEnd()));
   1533  aWriter.IntProperty("offset", SafeJSInteger(aLib.GetOffset()));
   1534  aWriter.StringProperty("name", aLib.GetModuleName());
   1535  aWriter.StringProperty("path", aLib.GetModulePath());
   1536  aWriter.StringProperty("debugName", aLib.GetDebugName());
   1537  aWriter.StringProperty("debugPath", aLib.GetDebugPath());
   1538  aWriter.StringProperty("breakpadId", aLib.GetBreakpadId());
   1539  aWriter.StringProperty("codeId", aLib.GetCodeId());
   1540  aWriter.StringProperty("arch", aLib.GetArch());
   1541  aWriter.EndObject();
   1542 }
   1543 
   1544 void AppendSharedLibraries(JSONWriter& aWriter) {
   1545  SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
   1546  info.SortByAddress();
   1547  for (size_t i = 0; i < info.GetSize(); i++) {
   1548    AddSharedLibraryInfoToStream(aWriter, info.GetEntry(i));
   1549  }
   1550 }
   1551 
   1552 static void StreamCategories(SpliceableJSONWriter& aWriter) {
   1553  // Same order as ProfilingCategory. Format:
   1554  // [
   1555  //   {
   1556  //     name: "Idle",
   1557  //     color: "transparent",
   1558  //     subcategories: ["Other"],
   1559  //   },
   1560  //   {
   1561  //     name: "Other",
   1562  //     color: "grey",
   1563  //     subcategories: [
   1564  //       "JSM loading",
   1565  //       "Subprocess launching",
   1566  //       "DLL loading"
   1567  //     ]
   1568  //   },
   1569  //   ...
   1570  // ]
   1571  for (const auto& categoryInfo : GetProfilingCategoryList()) {
   1572    aWriter.Start();
   1573    aWriter.StringProperty("name", MakeStringSpan(categoryInfo.mName));
   1574    aWriter.StringProperty("color", MakeStringSpan(categoryInfo.mColor));
   1575    aWriter.StartArrayProperty("subcategories");
   1576    for (const auto& subcategoryName : categoryInfo.mSubcategoryNames) {
   1577      aWriter.StringElement(MakeStringSpan(subcategoryName));
   1578    }
   1579    aWriter.EndArray();
   1580    aWriter.EndObject();
   1581  }
   1582 }
   1583 
   1584 static void StreamMarkerSchema(SpliceableJSONWriter& aWriter) {
   1585  // Get an array view with all registered marker-type-specific functions.
   1586  base_profiler_markers_detail::Streaming::LockedMarkerTypeFunctionsList
   1587      markerTypeFunctionsArray;
   1588  // List of streamed marker names, this is used to spot duplicates.
   1589  std::set<std::string> names;
   1590  // Stream the display schema for each different one. (Duplications may come
   1591  // from the same code potentially living in different libraries.)
   1592  for (const auto& markerTypeFunctions : markerTypeFunctionsArray) {
   1593    auto name = markerTypeFunctions.mMarkerTypeNameFunction();
   1594    // std::set.insert(T&&) returns a pair, its `second` is true if the element
   1595    // was actually inserted (i.e., it was not there yet.)
   1596    const bool didInsert =
   1597        names.insert(std::string(name.data(), name.size())).second;
   1598    if (didInsert) {
   1599      markerTypeFunctions.mMarkerSchemaFunction().Stream(aWriter, name);
   1600    }
   1601  }
   1602 }
   1603 
   1604 static int64_t MicrosecondsSince1970();
   1605 
   1606 static void MaybeWriteRawStartTimeValue(SpliceableJSONWriter& aWriter,
   1607                                        const TimeStamp& aStartTime) {
   1608 #ifdef XP_LINUX
   1609  aWriter.DoubleProperty(
   1610      "startTimeAsClockMonotonicNanosecondsSinceBoot",
   1611      static_cast<double>(aStartTime.RawClockMonotonicNanosecondsSinceBoot()));
   1612 #endif
   1613 
   1614 #ifdef XP_DARWIN
   1615  aWriter.DoubleProperty(
   1616      "startTimeAsMachAbsoluteTimeNanoseconds",
   1617      static_cast<double>(aStartTime.RawMachAbsoluteTimeNanoseconds()));
   1618 #endif
   1619 
   1620 #ifdef XP_WIN
   1621  uint64_t startTimeQPC = aStartTime.RawQueryPerformanceCounterValue();
   1622  aWriter.DoubleProperty("startTimeAsQueryPerformanceCounterValue",
   1623                         static_cast<double>(startTimeQPC));
   1624 #endif
   1625 }
   1626 
   1627 static void StreamMetaJSCustomObject(PSLockRef aLock,
   1628                                     SpliceableJSONWriter& aWriter,
   1629                                     bool aIsShuttingDown) {
   1630  MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock));
   1631 
   1632  aWriter.IntProperty("version", GECKO_PROFILER_FORMAT_VERSION);
   1633 
   1634  // The "startTime" field holds the number of milliseconds since midnight
   1635  // January 1, 1970 GMT (the "Unix epoch"). This grotty code computes (Now -
   1636  // (Now - ProcessStartTime)) to convert CorePS::ProcessStartTime() into that
   1637  // form. Note: This start time, and the platform-specific "raw start time",
   1638  // are the only absolute time values in the profile! All other timestamps are
   1639  // relative to this startTime.
   1640  TimeStamp startTime = CorePS::ProcessStartTime();
   1641  TimeStamp now = TimeStamp::Now();
   1642  double millisecondsSinceUnixEpoch =
   1643      static_cast<double>(MicrosecondsSince1970()) / 1000.0;
   1644  double millisecondsSinceStartTime = (now - startTime).ToMilliseconds();
   1645  double millisecondsBetweenUnixEpochAndStartTime =
   1646      millisecondsSinceUnixEpoch - millisecondsSinceStartTime;
   1647  aWriter.DoubleProperty("startTime", millisecondsBetweenUnixEpochAndStartTime);
   1648 
   1649  MaybeWriteRawStartTimeValue(aWriter, startTime);
   1650 
   1651  aWriter.DoubleProperty("profilingStartTime", (ActivePS::ProfilingStartTime() -
   1652                                                CorePS::ProcessStartTime())
   1653                                                   .ToMilliseconds());
   1654 
   1655  if (const TimeStamp contentEarliestTime =
   1656          ActivePS::Buffer(aLock)
   1657              .UnderlyingChunkedBuffer()
   1658              .GetEarliestChunkStartTimeStamp();
   1659      !contentEarliestTime.IsNull()) {
   1660    aWriter.DoubleProperty(
   1661        "contentEarliestTime",
   1662        (contentEarliestTime - CorePS::ProcessStartTime()).ToMilliseconds());
   1663  } else {
   1664    aWriter.NullProperty("contentEarliestTime");
   1665  }
   1666 
   1667  const double profilingEndTime = profiler_time();
   1668  aWriter.DoubleProperty("profilingEndTime", profilingEndTime);
   1669 
   1670  if (aIsShuttingDown) {
   1671    aWriter.DoubleProperty("shutdownTime", profilingEndTime);
   1672  } else {
   1673    aWriter.NullProperty("shutdownTime");
   1674  }
   1675 
   1676  aWriter.StartArrayProperty("categories");
   1677  StreamCategories(aWriter);
   1678  aWriter.EndArray();
   1679 
   1680  aWriter.StartArrayProperty("markerSchema");
   1681  StreamMarkerSchema(aWriter);
   1682  aWriter.EndArray();
   1683 
   1684  if (!profiler_is_main_thread()) {
   1685    // Leave the rest of the properties out if we're not on the main thread.
   1686    // At the moment, the only case in which this function is called on a
   1687    // background thread is if we're in a content process and are going to
   1688    // send this profile to the parent process. In that case, the parent
   1689    // process profile's "meta" object already has the rest of the properties,
   1690    // and the parent process profile is dumped on that process's main thread.
   1691    return;
   1692  }
   1693 
   1694  aWriter.DoubleProperty("interval", ActivePS::Interval(aLock));
   1695  aWriter.IntProperty("stackwalk", ActivePS::FeatureStackWalk(aLock));
   1696 
   1697 #ifdef DEBUG
   1698  aWriter.IntProperty("debug", 1);
   1699 #else
   1700  aWriter.IntProperty("debug", 0);
   1701 #endif
   1702 
   1703  aWriter.IntProperty("gcpoison", 0);
   1704 
   1705  aWriter.IntProperty("asyncstack", 0);
   1706 
   1707  aWriter.IntProperty("processType", 0);
   1708 }
   1709 
   1710 static void StreamPages(PSLockRef aLock, SpliceableJSONWriter& aWriter) {
   1711  MOZ_RELEASE_ASSERT(CorePS::Exists());
   1712  ActivePS::DiscardExpiredPages(aLock);
   1713  for (const auto& page : ActivePS::ProfiledPages(aLock)) {
   1714    page->StreamJSON(aWriter);
   1715  }
   1716 }
   1717 
   1718 static void locked_profiler_stream_json_for_this_process(
   1719    PSLockRef aLock, SpliceableJSONWriter& aWriter, double aSinceTime,
   1720    bool aIsShuttingDown, bool aOnlyThreads = false) {
   1721  LOG("locked_profiler_stream_json_for_this_process");
   1722 
   1723  MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock));
   1724 
   1725  AUTO_PROFILER_STATS(base_locked_profiler_stream_json_for_this_process);
   1726 
   1727  const double collectionStartMs = profiler_time();
   1728 
   1729  ProfileBuffer& buffer = ActivePS::Buffer(aLock);
   1730 
   1731  // If there is a set "Window length", discard older data.
   1732  Maybe<double> durationS = ActivePS::Duration(aLock);
   1733  if (durationS.isSome()) {
   1734    const double durationStartMs = collectionStartMs - *durationS * 1000;
   1735    buffer.DiscardSamplesBeforeTime(durationStartMs);
   1736  }
   1737 
   1738  if (!aOnlyThreads) {
   1739    // Put shared library info
   1740    aWriter.StartArrayProperty("libs");
   1741    AppendSharedLibraries(aWriter);
   1742    aWriter.EndArray();
   1743 
   1744    // Put meta data
   1745    aWriter.StartObjectProperty("meta");
   1746    {
   1747      StreamMetaJSCustomObject(aLock, aWriter, aIsShuttingDown);
   1748    }
   1749    aWriter.EndObject();
   1750 
   1751    // Put page data
   1752    aWriter.StartArrayProperty("pages");
   1753    {
   1754      StreamPages(aLock, aWriter);
   1755    }
   1756    aWriter.EndArray();
   1757 
   1758    buffer.StreamProfilerOverheadToJSON(aWriter, CorePS::ProcessStartTime(),
   1759                                        aSinceTime);
   1760    buffer.StreamCountersToJSON(aWriter, CorePS::ProcessStartTime(),
   1761                                aSinceTime);
   1762 
   1763    // Lists the samples for each thread profile
   1764    aWriter.StartArrayProperty("threads");
   1765  }
   1766 
   1767  // if aOnlyThreads is true, the only output will be the threads array items.
   1768  {
   1769    ActivePS::DiscardExpiredDeadProfiledThreads(aLock);
   1770    Vector<std::pair<RegisteredThread*, ProfiledThreadData*>> threads =
   1771        ActivePS::ProfiledThreads(aLock);
   1772    for (auto& thread : threads) {
   1773      ProfiledThreadData* profiledThreadData = thread.second;
   1774      profiledThreadData->StreamJSON(
   1775          buffer, aWriter, CorePS::ProcessName(aLock), CorePS::ETLDplus1(aLock),
   1776          CorePS::ProcessStartTime(), aSinceTime);
   1777    }
   1778  }
   1779 
   1780  if (!aOnlyThreads) {
   1781    aWriter.EndArray();
   1782 
   1783    aWriter.StartArrayProperty("pausedRanges");
   1784    {
   1785      buffer.StreamPausedRangesToJSON(aWriter, aSinceTime);
   1786    }
   1787    aWriter.EndArray();
   1788  }
   1789 
   1790  const double collectionEndMs = profiler_time();
   1791 
   1792  // Record timestamps for the collection into the buffer, so that consumers
   1793  // know why we didn't collect any samples for its duration.
   1794  // We put these entries into the buffer after we've collected the profile,
   1795  // so they'll be visible for the *next* profile collection (if they haven't
   1796  // been overwritten due to buffer wraparound by then).
   1797  buffer.AddEntry(ProfileBufferEntry::CollectionStart(collectionStartMs));
   1798  buffer.AddEntry(ProfileBufferEntry::CollectionEnd(collectionEndMs));
   1799 }
   1800 
   1801 bool profiler_stream_json_for_this_process(SpliceableJSONWriter& aWriter,
   1802                                           double aSinceTime,
   1803                                           bool aIsShuttingDown,
   1804                                           bool aOnlyThreads) {
   1805  LOG("profiler_stream_json_for_this_process");
   1806 
   1807  MOZ_RELEASE_ASSERT(CorePS::Exists());
   1808 
   1809  PSAutoLock lock;
   1810 
   1811  if (!ActivePS::Exists(lock)) {
   1812    return false;
   1813  }
   1814 
   1815  locked_profiler_stream_json_for_this_process(lock, aWriter, aSinceTime,
   1816                                               aIsShuttingDown, aOnlyThreads);
   1817  return true;
   1818 }
   1819 
   1820 // END saving/streaming code
   1821 ////////////////////////////////////////////////////////////////////////
   1822 
   1823 static char FeatureCategory(uint32_t aFeature) {
   1824  if (aFeature & DefaultFeatures()) {
   1825    if (aFeature & AvailableFeatures()) {
   1826      return 'D';
   1827    }
   1828    return 'd';
   1829  }
   1830 
   1831  if (aFeature & StartupExtraDefaultFeatures()) {
   1832    if (aFeature & AvailableFeatures()) {
   1833      return 'S';
   1834    }
   1835    return 's';
   1836  }
   1837 
   1838  if (aFeature & AvailableFeatures()) {
   1839    return '-';
   1840  }
   1841  return 'x';
   1842 }
   1843 
   1844 static void PrintUsage() {
   1845  PrintToConsole(
   1846      "\n"
   1847      "Profiler environment variable usage:\n"
   1848      "\n"
   1849      "  MOZ_BASE_PROFILER_HELP\n"
   1850      "  If set to any value, prints this message.\n"
   1851      "  (Only BaseProfiler features are known here; Use MOZ_PROFILER_HELP\n"
   1852      "  for Gecko Profiler help, with more features).\n"
   1853      "\n"
   1854      "  MOZ_BASE_PROFILER_{,DEBUG_,VERBOSE}LOGGING\n"
   1855      "  Enables BaseProfiler logging to stdout. The levels of logging\n"
   1856      "  available are MOZ_BASE_PROFILER_LOGGING' (least verbose),\n"
   1857      "  '..._DEBUG_LOGGING', '..._VERBOSE_LOGGING' (most verbose)\n"
   1858      "\n"
   1859      "  MOZ_PROFILER_STARTUP\n"
   1860      "  If set to any value other than '' or '0'/'N'/'n', starts the\n"
   1861      "  profiler immediately on start-up.\n"
   1862      "  Useful if you want profile code that runs very early.\n"
   1863      "\n"
   1864      "  MOZ_PROFILER_STARTUP_ENTRIES=<%u..%u>\n"
   1865      "  If MOZ_PROFILER_STARTUP is set, specifies the number of entries\n"
   1866      "  per process in the profiler's circular buffer when the profiler is\n"
   1867      "  first started.\n"
   1868      "  If unset, the platform default is used:\n"
   1869      "  %u entries per process, or %u when MOZ_PROFILER_STARTUP is set.\n"
   1870      "  (%u bytes per entry -> %u or %u total bytes per process)\n"
   1871      "  Optional units in bytes: KB, KiB, MB, MiB, GB, GiB\n"
   1872      "\n"
   1873      "  MOZ_PROFILER_STARTUP_DURATION=<1..>\n"
   1874      "  If MOZ_PROFILER_STARTUP is set, specifies the maximum life time\n"
   1875      "  of entries in the the profiler's circular buffer when the profiler\n"
   1876      "  is first started, in seconds.\n"
   1877      "  If unset, the life time of the entries will only be restricted by\n"
   1878      "  MOZ_PROFILER_STARTUP_ENTRIES (or its default value), and no\n"
   1879      "  additional time duration restriction will be applied.\n"
   1880      "\n"
   1881      "  MOZ_PROFILER_STARTUP_INTERVAL=<1..1000>\n"
   1882      "  If MOZ_PROFILER_STARTUP is set, specifies the sample interval,\n"
   1883      "  measured in milliseconds, when the profiler is first started.\n"
   1884      "  If unset, the platform default is used.\n"
   1885      "\n"
   1886      "  MOZ_PROFILER_STARTUP_FEATURES_BITFIELD=<Number>\n"
   1887      "  If MOZ_PROFILER_STARTUP is set, specifies the profiling\n"
   1888      "  features, as the integer value of the features bitfield.\n"
   1889      "  If unset, the value from MOZ_PROFILER_STARTUP_FEATURES is used.\n"
   1890      "\n"
   1891      "  MOZ_PROFILER_STARTUP_FEATURES=<Features>\n"
   1892      "  If MOZ_PROFILER_STARTUP is set, specifies the profiling\n"
   1893      "  features, as a comma-separated list of strings.\n"
   1894      "  Ignored if MOZ_PROFILER_STARTUP_FEATURES_BITFIELD is set.\n"
   1895      "  If unset, the platform default is used.\n"
   1896      "\n"
   1897      "    Features: (x=unavailable, D/d=default/unavailable,\n"
   1898      "               S/s=MOZ_PROFILER_STARTUP extra "
   1899      "default/unavailable)\n",
   1900      unsigned(scMinimumBufferEntries), unsigned(scMaximumBufferEntries),
   1901      unsigned(BASE_PROFILER_DEFAULT_ENTRIES.Value()),
   1902      unsigned(BASE_PROFILER_DEFAULT_STARTUP_ENTRIES.Value()),
   1903      unsigned(scBytesPerEntry),
   1904      unsigned(BASE_PROFILER_DEFAULT_ENTRIES.Value() * scBytesPerEntry),
   1905      unsigned(BASE_PROFILER_DEFAULT_STARTUP_ENTRIES.Value() *
   1906               scBytesPerEntry));
   1907 
   1908 #define PRINT_FEATURE(n_, str_, Name_, desc_)             \
   1909  PrintToConsole("    %c %7u: \"%s\" (%s)\n",             \
   1910                 FeatureCategory(ProfilerFeature::Name_), \
   1911                 ProfilerFeature::Name_, str_, desc_);
   1912 
   1913  BASE_PROFILER_FOR_EACH_FEATURE(PRINT_FEATURE)
   1914 
   1915 #undef PRINT_FEATURE
   1916 
   1917  PrintToConsole(
   1918      "    -          \"default\" (All above D+S defaults)\n"
   1919      "\n"
   1920      "  MOZ_PROFILER_STARTUP_FILTERS=<Filters>\n"
   1921      "  If MOZ_PROFILER_STARTUP is set, specifies the thread filters, as "
   1922      "a\n"
   1923      "  comma-separated list of strings. A given thread will be sampled if\n"
   1924      "  any of the filters is a case-insensitive substring of the thread\n"
   1925      "  name. If unset, a default is used.\n"
   1926      "\n"
   1927      "  MOZ_PROFILER_SHUTDOWN\n"
   1928      "  If set, the profiler saves a profile to the named file on shutdown.\n"
   1929      "\n"
   1930      "  MOZ_PROFILER_SYMBOLICATE\n"
   1931      "  If set, the profiler will pre-symbolicate profiles.\n"
   1932      "  *Note* This will add a significant pause when gathering data, and\n"
   1933      "  is intended mainly for local development.\n"
   1934      "\n"
   1935      "  MOZ_PROFILER_LUL_TEST\n"
   1936      "  If set to any value, runs LUL unit tests at startup.\n"
   1937      "\n"
   1938      "  This platform %s native unwinding.\n"
   1939      "\n",
   1940 #if defined(HAVE_NATIVE_UNWIND)
   1941      "supports"
   1942 #else
   1943      "does not support"
   1944 #endif
   1945  );
   1946 }
   1947 
   1948 ////////////////////////////////////////////////////////////////////////
   1949 // BEGIN Sampler
   1950 
   1951 #if defined(GP_OS_linux) || defined(GP_OS_android)
   1952 struct SigHandlerCoordinator;
   1953 #endif
   1954 
   1955 // Sampler performs setup and teardown of the state required to sample with the
   1956 // profiler. Sampler may exist when ActivePS is not present.
   1957 //
   1958 // SuspendAndSampleAndResumeThread must only be called from a single thread,
   1959 // and must not sample the thread it is being called from. A separate Sampler
   1960 // instance must be used for each thread which wants to capture samples.
   1961 
   1962 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
   1963 //
   1964 // With the exception of SamplerThread, all Sampler objects must be Disable-d
   1965 // before releasing the lock which was used to create them. This avoids races
   1966 // on linux with the SIGPROF signal handler.
   1967 
   1968 class Sampler {
   1969 public:
   1970  // Sets up the profiler such that it can begin sampling.
   1971  explicit Sampler(PSLockRef aLock);
   1972 
   1973  // Disable the sampler, restoring it to its previous state. This must be
   1974  // called once, and only once, before the Sampler is destroyed.
   1975  void Disable(PSLockRef aLock);
   1976 
   1977  // This method suspends and resumes the samplee thread. It calls the passed-in
   1978  // function-like object aProcessRegs (passing it a populated |const
   1979  // Registers&| arg) while the samplee thread is suspended.
   1980  //
   1981  // Func must be a function-like object of type `void()`.
   1982  template <typename Func>
   1983  void SuspendAndSampleAndResumeThread(
   1984      PSLockRef aLock, const RegisteredThread& aRegisteredThread,
   1985      const TimeStamp& aNow, const Func& aProcessRegs);
   1986 
   1987 private:
   1988 #if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
   1989  // Used to restore the SIGPROF handler when ours is removed.
   1990  struct sigaction mOldSigprofHandler;
   1991 
   1992  // This process' ID. Needed as an argument for tgkill in
   1993  // SuspendAndSampleAndResumeThread.
   1994  BaseProfilerProcessId mMyPid;
   1995 
   1996  // The sampler thread's ID.  Used to assert that it is not sampling itself,
   1997  // which would lead to deadlock.
   1998  BaseProfilerThreadId mSamplerTid;
   1999 
   2000 public:
   2001  // This is the one-and-only variable used to communicate between the sampler
   2002  // thread and the samplee thread's signal handler. It's static because the
   2003  // samplee thread's signal handler is static.
   2004  static struct SigHandlerCoordinator* sSigHandlerCoordinator;
   2005 #endif
   2006 };
   2007 
   2008 // END Sampler
   2009 ////////////////////////////////////////////////////////////////////////
   2010 
   2011 ////////////////////////////////////////////////////////////////////////
   2012 // BEGIN SamplerThread
   2013 
   2014 // The sampler thread controls sampling and runs whenever the profiler is
   2015 // active. It periodically runs through all registered threads, finds those
   2016 // that should be sampled, then pauses and samples them.
   2017 
   2018 class SamplerThread {
   2019 public:
   2020  // Creates a sampler thread, but doesn't start it.
   2021  SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration,
   2022                double aIntervalMilliseconds, uint32_t aFeatures);
   2023  ~SamplerThread();
   2024 
   2025  // This runs on (is!) the sampler thread.
   2026  void Run();
   2027 
   2028  // This runs on the main thread.
   2029  void Stop(PSLockRef aLock);
   2030 
   2031 private:
   2032  // This suspends the calling thread for the given number of microseconds.
   2033  // Best effort timing.
   2034  void SleepMicro(uint32_t aMicroseconds);
   2035 
   2036  // The sampler used to suspend and sample threads.
   2037  Sampler mSampler;
   2038 
   2039  // The activity generation, for detecting when the sampler thread must stop.
   2040  const uint32_t mActivityGeneration;
   2041 
   2042  // The interval between samples, measured in microseconds.
   2043  const int mIntervalMicroseconds;
   2044 
   2045  // The OS-specific handle for the sampler thread.
   2046 #if defined(GP_OS_windows)
   2047  HANDLE mThread;
   2048 #elif defined(GP_OS_darwin) || defined(GP_OS_linux) || \
   2049    defined(GP_OS_android) || defined(GP_OS_freebsd)
   2050  pthread_t mThread;
   2051 #endif
   2052 
   2053 #if defined(GP_OS_windows)
   2054  bool mNoTimerResolutionChange = true;
   2055 #endif
   2056 
   2057  SamplerThread(const SamplerThread&) = delete;
   2058  void operator=(const SamplerThread&) = delete;
   2059 };
   2060 
   2061 // This function is required because we need to create a SamplerThread within
   2062 // ActivePS's constructor, but SamplerThread is defined after ActivePS. It
   2063 // could probably be removed by moving some code around.
   2064 static SamplerThread* NewSamplerThread(PSLockRef aLock, uint32_t aGeneration,
   2065                                       double aInterval, uint32_t aFeatures) {
   2066  return new SamplerThread(aLock, aGeneration, aInterval, aFeatures);
   2067 }
   2068 
   2069 // This function is the sampler thread.  This implementation is used for all
   2070 // targets.
   2071 void SamplerThread::Run() {
   2072  // TODO: If possible, name this thread later on, after NSPR becomes available.
   2073  // PR_SetCurrentThreadName("SamplerThread");
   2074 
   2075  // Features won't change during this SamplerThread's lifetime, so we can read
   2076  // them once and store them locally.
   2077  const uint32_t features = []() -> uint32_t {
   2078    PSAutoLock lock;
   2079    if (!ActivePS::Exists(lock)) {
   2080      // If there is no active profiler, it doesn't matter what we return,
   2081      // because this thread will exit before any feature is used.
   2082      return 0;
   2083    }
   2084    return ActivePS::Features(lock);
   2085  }();
   2086 
   2087  // Not *no*-stack-sampling means we do want stack sampling.
   2088  const bool stackSampling = !ProfilerFeature::HasNoStackSampling(features);
   2089 
   2090  // Use local ProfileBuffer to capture the stack.
   2091  // (This is to avoid touching the CorePS::CoreBuffer lock while
   2092  // a thread is suspended, because that thread could be working with
   2093  // the CorePS::CoreBuffer as well.)
   2094  ProfileBufferChunkManagerSingle localChunkManager(
   2095      ProfileBufferChunkManager::scExpectedMaximumStackSize);
   2096  ProfileChunkedBuffer localBuffer(
   2097      ProfileChunkedBuffer::ThreadSafety::WithoutMutex, localChunkManager);
   2098  ProfileBuffer localProfileBuffer(localBuffer);
   2099 
   2100  // Will be kept between collections, to know what each collection does.
   2101  auto previousState = localBuffer.GetState();
   2102 
   2103  // This will be positive if we are running behind schedule (sampling less
   2104  // frequently than desired) and negative if we are ahead of schedule.
   2105  TimeDuration lastSleepOvershoot = 0;
   2106  TimeStamp sampleStart = TimeStamp::Now();
   2107 
   2108  while (true) {
   2109    // This scope is for |lock|. It ends before we sleep below.
   2110    {
   2111      PSAutoLock lock;
   2112      TimeStamp lockAcquired = TimeStamp::Now();
   2113 
   2114      if (!ActivePS::Exists(lock)) {
   2115        return;
   2116      }
   2117 
   2118      // At this point profiler_stop() might have been called, and
   2119      // profiler_start() might have been called on another thread. If this
   2120      // happens the generation won't match.
   2121      if (ActivePS::Generation(lock) != mActivityGeneration) {
   2122        return;
   2123      }
   2124 
   2125      ActivePS::ClearExpiredExitProfiles(lock);
   2126 
   2127      TimeStamp expiredMarkersCleaned = TimeStamp::Now();
   2128 
   2129      if (int(gSkipSampling) <= 0 && !ActivePS::IsSamplingPaused(lock)) {
   2130        TimeDuration delta = sampleStart - CorePS::ProcessStartTime();
   2131        ProfileBuffer& buffer = ActivePS::Buffer(lock);
   2132 
   2133        // handle per-process generic counters
   2134        const Vector<BaseProfilerCount*>& counters = CorePS::Counters(lock);
   2135        for (auto& counter : counters) {
   2136          // create Buffer entries for each counter
   2137          buffer.AddEntry(ProfileBufferEntry::CounterId(counter));
   2138          buffer.AddEntry(ProfileBufferEntry::Time(delta.ToMilliseconds()));
   2139          int64_t count;
   2140          uint64_t number;
   2141          counter->Sample(count, number);
   2142          buffer.AddEntry(ProfileBufferEntry::Count(count));
   2143          if (number) {
   2144            buffer.AddEntry(ProfileBufferEntry::Number(number));
   2145          }
   2146        }
   2147        TimeStamp countersSampled = TimeStamp::Now();
   2148 
   2149        if (stackSampling) {
   2150          const Vector<LiveProfiledThreadData>& liveThreads =
   2151              ActivePS::LiveProfiledThreads(lock);
   2152 
   2153          for (auto& thread : liveThreads) {
   2154            RegisteredThread* registeredThread = thread.mRegisteredThread;
   2155            ProfiledThreadData* profiledThreadData =
   2156                thread.mProfiledThreadData.get();
   2157            RefPtr<ThreadInfo> info = registeredThread->Info();
   2158 
   2159            // If the thread is asleep and has been sampled before in the same
   2160            // sleep episode, find and copy the previous sample, as that's
   2161            // cheaper than taking a new sample.
   2162            if (registeredThread->RacyRegisteredThread()
   2163                    .CanDuplicateLastSampleDueToSleep()) {
   2164              bool dup_ok = ActivePS::Buffer(lock).DuplicateLastSample(
   2165                  info->ThreadId(), CorePS::ProcessStartTime(),
   2166                  profiledThreadData->LastSample());
   2167              if (dup_ok) {
   2168                continue;
   2169              }
   2170            }
   2171 
   2172            AUTO_PROFILER_STATS(base_SamplerThread_Run_DoPeriodicSample);
   2173 
   2174            TimeStamp now = TimeStamp::Now();
   2175 
   2176            // Record the global profiler buffer's range start now, before
   2177            // adding the first entry for this thread's sample.
   2178            const uint64_t bufferRangeStart = buffer.BufferRangeStart();
   2179 
   2180            // Add the thread ID now, so we know its position in the main
   2181            // buffer, which is used by some JS data. (DoPeriodicSample only
   2182            // knows about the temporary local buffer.)
   2183            const uint64_t samplePos =
   2184                buffer.AddThreadIdEntry(registeredThread->Info()->ThreadId());
   2185            profiledThreadData->LastSample() = Some(samplePos);
   2186 
   2187            // Also add the time, so it's always there after the thread ID, as
   2188            // expected by the parser. (Other stack data is optional.)
   2189            TimeDuration delta = now - CorePS::ProcessStartTime();
   2190            buffer.AddEntry(ProfileBufferEntry::Time(delta.ToMilliseconds()));
   2191 
   2192            mSampler.SuspendAndSampleAndResumeThread(
   2193                lock, *registeredThread, now,
   2194                [&](const Registers& aRegs, const TimeStamp& aNow) {
   2195                  DoPeriodicSample(lock, *registeredThread, *profiledThreadData,
   2196                                   aRegs, samplePos, bufferRangeStart,
   2197                                   localProfileBuffer);
   2198                });
   2199 
   2200            // If data is complete, copy it into the global buffer.
   2201            auto state = localBuffer.GetState();
   2202            if (state.mClearedBlockCount != previousState.mClearedBlockCount) {
   2203              LOG("Stack sample too big for local storage, needed %u bytes",
   2204                  unsigned(state.mRangeEnd - previousState.mRangeEnd));
   2205            } else if (state.mRangeEnd - previousState.mRangeEnd >=
   2206                       *profiler_get_core_buffer().BufferLength()) {
   2207              LOG("Stack sample too big for profiler storage, needed %u bytes",
   2208                  unsigned(state.mRangeEnd - previousState.mRangeEnd));
   2209            } else {
   2210              profiler_get_core_buffer().AppendContents(localBuffer);
   2211            }
   2212 
   2213            // Clean up for the next run.
   2214            localBuffer.Clear();
   2215            previousState = localBuffer.GetState();
   2216          }
   2217        }
   2218 
   2219 #if defined(USE_LUL_STACKWALK)
   2220        // The LUL unwind object accumulates frame statistics. Periodically we
   2221        // should poke it to give it a chance to print those statistics.  This
   2222        // involves doing I/O (fprintf, __android_log_print, etc.) and so
   2223        // can't safely be done from the critical section inside
   2224        // SuspendAndSampleAndResumeThread, which is why it is done here.
   2225        lul::LUL* lul = CorePS::Lul(lock);
   2226        if (lul) {
   2227          lul->MaybeShowStats();
   2228        }
   2229 #endif
   2230        TimeStamp threadsSampled = TimeStamp::Now();
   2231 
   2232        {
   2233          AUTO_PROFILER_STATS(Sampler_FulfillChunkRequests);
   2234          ActivePS::FulfillChunkRequests(lock);
   2235        }
   2236 
   2237        buffer.CollectOverheadStats(delta, lockAcquired - sampleStart,
   2238                                    expiredMarkersCleaned - lockAcquired,
   2239                                    countersSampled - expiredMarkersCleaned,
   2240                                    threadsSampled - countersSampled);
   2241      }
   2242    }
   2243    // gPSMutex is not held after this point.
   2244 
   2245    // Calculate how long a sleep to request.  After the sleep, measure how
   2246    // long we actually slept and take the difference into account when
   2247    // calculating the sleep interval for the next iteration.  This is an
   2248    // attempt to keep "to schedule" in the presence of inaccuracy of the
   2249    // actual sleep intervals.
   2250    TimeStamp targetSleepEndTime =
   2251        sampleStart + TimeDuration::FromMicroseconds(mIntervalMicroseconds);
   2252    TimeStamp beforeSleep = TimeStamp::Now();
   2253    TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep;
   2254    double sleepTime = std::max(
   2255        0.0, (targetSleepDuration - lastSleepOvershoot).ToMicroseconds());
   2256    SleepMicro(static_cast<uint32_t>(sleepTime));
   2257    sampleStart = TimeStamp::Now();
   2258    lastSleepOvershoot =
   2259        sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime));
   2260  }
   2261 }
   2262 
   2263 // Temporary closing namespaces from enclosing platform.cpp.
   2264 }  // namespace baseprofiler
   2265 }  // namespace mozilla
   2266 
   2267 // We #include these files directly because it means those files can use
   2268 // declarations from this file trivially.  These provide target-specific
   2269 // implementations of all SamplerThread methods except Run().
   2270 #if defined(GP_OS_windows)
   2271 #  include "platform-win32.cpp"
   2272 #elif defined(GP_OS_darwin)
   2273 #  include "platform-macos.cpp"
   2274 #elif defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
   2275 #  include "platform-linux-android.cpp"
   2276 #else
   2277 #  error "bad platform"
   2278 #endif
   2279 
   2280 namespace mozilla {
   2281 namespace baseprofiler {
   2282 
   2283 UniquePlatformData AllocPlatformData(BaseProfilerThreadId aThreadId) {
   2284  return UniquePlatformData(new PlatformData(aThreadId));
   2285 }
   2286 
   2287 void PlatformDataDestructor::operator()(PlatformData* aData) { delete aData; }
   2288 
   2289 // END SamplerThread
   2290 ////////////////////////////////////////////////////////////////////////
   2291 
   2292 ////////////////////////////////////////////////////////////////////////
   2293 // BEGIN externally visible functions
   2294 
   2295 static uint32_t ParseFeature(const char* aFeature, bool aIsStartup) {
   2296  if (strcmp(aFeature, "default") == 0) {
   2297    return (aIsStartup ? (DefaultFeatures() | StartupExtraDefaultFeatures())
   2298                       : DefaultFeatures()) &
   2299           AvailableFeatures();
   2300  }
   2301 
   2302 #define PARSE_FEATURE_BIT(n_, str_, Name_, desc_) \
   2303  if (strcmp(aFeature, str_) == 0) {              \
   2304    return ProfilerFeature::Name_;                \
   2305  }
   2306 
   2307  BASE_PROFILER_FOR_EACH_FEATURE(PARSE_FEATURE_BIT)
   2308 
   2309 #undef PARSE_FEATURE_BIT
   2310 
   2311  PrintToConsole("\nUnrecognized feature \"%s\".\n\n", aFeature);
   2312  // Since we may have an old feature we don't implement anymore, don't exit.
   2313  PrintUsage();
   2314  return 0;
   2315 }
   2316 
   2317 uint32_t ParseFeaturesFromStringArray(const char** aFeatures,
   2318                                      uint32_t aFeatureCount,
   2319                                      bool aIsStartup /* = false */) {
   2320  uint32_t features = 0;
   2321  for (size_t i = 0; i < aFeatureCount; i++) {
   2322    features |= ParseFeature(aFeatures[i], aIsStartup);
   2323  }
   2324  return features;
   2325 }
   2326 
   2327 // Find the RegisteredThread for the current thread. This should only be called
   2328 // in places where TLSRegisteredThread can't be used.
   2329 static RegisteredThread* FindCurrentThreadRegisteredThread(PSLockRef aLock) {
   2330  BaseProfilerThreadId id = profiler_current_thread_id();
   2331  const Vector<UniquePtr<RegisteredThread>>& registeredThreads =
   2332      CorePS::RegisteredThreads(aLock);
   2333  for (auto& registeredThread : registeredThreads) {
   2334    if (registeredThread->Info()->ThreadId() == id) {
   2335      return registeredThread.get();
   2336    }
   2337  }
   2338 
   2339  return nullptr;
   2340 }
   2341 
   2342 static ProfilingStack* locked_register_thread(PSLockRef aLock,
   2343                                              const char* aName,
   2344                                              void* aStackTop) {
   2345  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2346 
   2347  MOZ_ASSERT(!FindCurrentThreadRegisteredThread(aLock));
   2348 
   2349  VTUNE_REGISTER_THREAD(aName);
   2350 
   2351  if (!TLSRegisteredThread::Init(aLock)) {
   2352    return nullptr;
   2353  }
   2354 
   2355  RefPtr<ThreadInfo> info = new ThreadInfo(aName, profiler_current_thread_id(),
   2356                                           profiler_is_main_thread());
   2357  UniquePtr<RegisteredThread> registeredThread =
   2358      MakeUnique<RegisteredThread>(info, aStackTop);
   2359 
   2360  TLSRegisteredThread::SetRegisteredThread(aLock, registeredThread.get());
   2361 
   2362  if (ActivePS::Exists(aLock) && ActivePS::ShouldProfileThread(aLock, info)) {
   2363    registeredThread->RacyRegisteredThread().SetIsBeingProfiled(true);
   2364    ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
   2365                                    MakeUnique<ProfiledThreadData>(info));
   2366  }
   2367 
   2368  ProfilingStack* profilingStack =
   2369      &registeredThread->RacyRegisteredThread().ProfilingStack();
   2370 
   2371  CorePS::AppendRegisteredThread(aLock, std::move(registeredThread));
   2372 
   2373  return profilingStack;
   2374 }
   2375 
   2376 static void locked_profiler_start(PSLockRef aLock, PowerOfTwo32 aCapacity,
   2377                                  double aInterval, uint32_t aFeatures,
   2378                                  const char** aFilters, uint32_t aFilterCount,
   2379                                  const Maybe<double>& aDuration);
   2380 
   2381 static Vector<const char*> SplitAtCommas(const char* aString,
   2382                                         UniquePtr<char[]>& aStorage) {
   2383  size_t len = strlen(aString);
   2384  aStorage = MakeUnique<char[]>(len + 1);
   2385  PodCopy(aStorage.get(), aString, len + 1);
   2386 
   2387  // Iterate over all characters in aStorage and split at commas, by
   2388  // overwriting commas with the null char.
   2389  Vector<const char*> array;
   2390  size_t currentElementStart = 0;
   2391  for (size_t i = 0; i <= len; i++) {
   2392    if (aStorage[i] == ',') {
   2393      aStorage[i] = '\0';
   2394    }
   2395    if (aStorage[i] == '\0') {
   2396      // Only add non-empty elements, otherwise ParseFeatures would later
   2397      // complain about unrecognized features.
   2398      if (currentElementStart != i) {
   2399        MOZ_RELEASE_ASSERT(array.append(&aStorage[currentElementStart]));
   2400      }
   2401      currentElementStart = i + 1;
   2402    }
   2403  }
   2404  return array;
   2405 }
   2406 
   2407 static const char* get_size_suffix(const char* str) {
   2408  const char* ptr = str;
   2409 
   2410  while (isdigit(*ptr)) {
   2411    ptr++;
   2412  }
   2413 
   2414  return ptr;
   2415 }
   2416 
   2417 void profiler_init(void* aStackTop) {
   2418  LOG("profiler_init");
   2419 
   2420  profiler_init_main_thread_id();
   2421 
   2422  Flow::Init();
   2423 
   2424  VTUNE_INIT();
   2425 
   2426  MOZ_RELEASE_ASSERT(!CorePS::Exists());
   2427 
   2428  if (getenv("MOZ_BASE_PROFILER_HELP")) {
   2429    PrintUsage();
   2430    exit(0);
   2431  }
   2432 
   2433  SharedLibraryInfo::Initialize();
   2434 
   2435  uint32_t features = DefaultFeatures() & AvailableFeatures();
   2436 
   2437  UniquePtr<char[]> filterStorage;
   2438 
   2439  Vector<const char*> filters;
   2440  MOZ_RELEASE_ASSERT(filters.append(kMainThreadName));
   2441 
   2442  PowerOfTwo32 capacity = BASE_PROFILER_DEFAULT_ENTRIES;
   2443  Maybe<double> duration = Nothing();
   2444  double interval = BASE_PROFILER_DEFAULT_INTERVAL;
   2445 
   2446  {
   2447    PSAutoLock lock;
   2448 
   2449    // We've passed the possible failure point. Instantiate CorePS, which
   2450    // indicates that the profiler has initialized successfully.
   2451    CorePS::Create(lock);
   2452 
   2453    (void)locked_register_thread(lock, kMainThreadName, aStackTop);
   2454 
   2455    // Platform-specific initialization.
   2456    PlatformInit(lock);
   2457 
   2458    // (Linux-only) We could create CorePS::mLul and read unwind info into it
   2459    // at this point. That would match the lifetime implied by destruction of
   2460    // it in profiler_shutdown() just below. However, that gives a big delay on
   2461    // startup, even if no profiling is actually to be done. So, instead, it is
   2462    // created on demand at the first call to PlatformStart().
   2463 
   2464    const char* startupEnv = getenv("MOZ_PROFILER_STARTUP");
   2465    if (!startupEnv || startupEnv[0] == '\0' ||
   2466        ((startupEnv[0] == '0' || startupEnv[0] == 'N' ||
   2467          startupEnv[0] == 'n') &&
   2468         startupEnv[1] == '\0')) {
   2469      return;
   2470    }
   2471 
   2472    // Hidden option to stop Base Profiler, mostly due to Talos intermittents,
   2473    // see https://bugzilla.mozilla.org/show_bug.cgi?id=1638851#c3
   2474    // TODO: Investigate root cause and remove this in bugs 1648324 and 1648325.
   2475    if (getenv("MOZ_PROFILER_STARTUP_NO_BASE")) {
   2476      return;
   2477    }
   2478 
   2479    LOG("- MOZ_PROFILER_STARTUP is set");
   2480 
   2481    // Startup default capacity may be different.
   2482    capacity = BASE_PROFILER_DEFAULT_STARTUP_ENTRIES;
   2483 
   2484    const char* startupCapacity = getenv("MOZ_PROFILER_STARTUP_ENTRIES");
   2485    if (startupCapacity && startupCapacity[0] != '\0') {
   2486      errno = 0;
   2487      long capacityLong = strtol(startupCapacity, nullptr, 10);
   2488      std::string_view sizeSuffix = get_size_suffix(startupCapacity);
   2489 
   2490      if (sizeSuffix == "KB") {
   2491        capacityLong *= 1000 / scBytesPerEntry;
   2492      } else if (sizeSuffix == "KiB") {
   2493        capacityLong *= 1024 / scBytesPerEntry;
   2494      } else if (sizeSuffix == "MB") {
   2495        capacityLong *= (1000 * 1000) / scBytesPerEntry;
   2496      } else if (sizeSuffix == "MiB") {
   2497        capacityLong *= (1024 * 1024) / scBytesPerEntry;
   2498      } else if (sizeSuffix == "GB") {
   2499        capacityLong *= (1000 * 1000 * 1000) / scBytesPerEntry;
   2500      } else if (sizeSuffix == "GiB") {
   2501        capacityLong *= (1024 * 1024 * 1024) / scBytesPerEntry;
   2502      } else if (!sizeSuffix.empty()) {
   2503        PrintToConsole(
   2504            "- MOZ_PROFILER_STARTUP_ENTRIES unit must be one of the "
   2505            "following: KB, KiB, MB, MiB, GB, GiB");
   2506        PrintUsage();
   2507        exit(1);
   2508      }
   2509 
   2510      // `long` could be 32 or 64 bits, so we force a 64-bit comparison with
   2511      // the maximum 32-bit signed number (as more than that is clamped down to
   2512      // 2^31 anyway).
   2513      if (errno == 0 && capacityLong > 0 &&
   2514          static_cast<uint64_t>(capacityLong) <=
   2515              static_cast<uint64_t>(INT32_MAX)) {
   2516        capacity = PowerOfTwo32(
   2517            ClampToAllowedEntries(static_cast<uint32_t>(capacityLong)));
   2518        LOG("- MOZ_PROFILER_STARTUP_ENTRIES = %u", unsigned(capacity.Value()));
   2519      } else {
   2520        PrintToConsole("- MOZ_PROFILER_STARTUP_ENTRIES not a valid integer: %s",
   2521                       startupCapacity);
   2522        PrintUsage();
   2523        exit(1);
   2524      }
   2525    }
   2526 
   2527    const char* startupDuration = getenv("MOZ_PROFILER_STARTUP_DURATION");
   2528    if (startupDuration && startupDuration[0] != '\0') {
   2529      // The duration is a floating point number. Use StringToDouble rather than
   2530      // strtod, so that "." is used as the decimal separator regardless of OS
   2531      // locale.
   2532      auto durationVal = StringToDouble(std::string(startupDuration));
   2533      if (durationVal && *durationVal >= 0.0) {
   2534        if (*durationVal > 0.0) {
   2535          duration = Some(*durationVal);
   2536        }
   2537        LOG("- MOZ_PROFILER_STARTUP_DURATION = %f", *durationVal);
   2538      } else {
   2539        PrintToConsole("- MOZ_PROFILER_STARTUP_DURATION not a valid float: %s",
   2540                       startupDuration);
   2541        PrintUsage();
   2542        exit(1);
   2543      }
   2544    }
   2545 
   2546    const char* startupInterval = getenv("MOZ_PROFILER_STARTUP_INTERVAL");
   2547    if (startupInterval && startupInterval[0] != '\0') {
   2548      // The interval is a floating point number. Use StringToDouble rather than
   2549      // strtod, so that "." is used as the decimal separator regardless of OS
   2550      // locale.
   2551      auto intervalValue = StringToDouble(MakeStringSpan(startupInterval));
   2552      if (intervalValue && *intervalValue > 0.0 && *intervalValue <= 1000.0) {
   2553        interval = *intervalValue;
   2554        LOG("- MOZ_PROFILER_STARTUP_INTERVAL = %f", interval);
   2555      } else {
   2556        PrintToConsole("- MOZ_PROFILER_STARTUP_INTERVAL not a valid float: %s",
   2557                       startupInterval);
   2558        PrintUsage();
   2559        exit(1);
   2560      }
   2561    }
   2562 
   2563    features |= StartupExtraDefaultFeatures() & AvailableFeatures();
   2564 
   2565    const char* startupFeaturesBitfield =
   2566        getenv("MOZ_PROFILER_STARTUP_FEATURES_BITFIELD");
   2567    if (startupFeaturesBitfield && startupFeaturesBitfield[0] != '\0') {
   2568      errno = 0;
   2569      features = strtol(startupFeaturesBitfield, nullptr, 10);
   2570      if (errno == 0) {
   2571        LOG("- MOZ_PROFILER_STARTUP_FEATURES_BITFIELD = %d", features);
   2572      } else {
   2573        PrintToConsole(
   2574            "- MOZ_PROFILER_STARTUP_FEATURES_BITFIELD not a valid integer: %s",
   2575            startupFeaturesBitfield);
   2576        PrintUsage();
   2577        exit(1);
   2578      }
   2579    } else {
   2580      const char* startupFeatures = getenv("MOZ_PROFILER_STARTUP_FEATURES");
   2581      if (startupFeatures) {
   2582        // Interpret startupFeatures as a list of feature strings, separated by
   2583        // commas.
   2584        UniquePtr<char[]> featureStringStorage;
   2585        Vector<const char*> featureStringArray =
   2586            SplitAtCommas(startupFeatures, featureStringStorage);
   2587        features = ParseFeaturesFromStringArray(featureStringArray.begin(),
   2588                                                featureStringArray.length(),
   2589                                                /* aIsStartup */ true);
   2590        LOG("- MOZ_PROFILER_STARTUP_FEATURES = %d", features);
   2591      }
   2592    }
   2593 
   2594    const char* startupFilters = getenv("MOZ_PROFILER_STARTUP_FILTERS");
   2595    if (startupFilters && startupFilters[0] != '\0') {
   2596      filters = SplitAtCommas(startupFilters, filterStorage);
   2597      LOG("- MOZ_PROFILER_STARTUP_FILTERS = %s", startupFilters);
   2598 
   2599      if (mozilla::profiler::detail::FiltersExcludePid(filters)) {
   2600        LOG(" -> This process is excluded and won't be profiled");
   2601        return;
   2602      }
   2603    }
   2604 
   2605    locked_profiler_start(lock, capacity, interval, features, filters.begin(),
   2606                          filters.length(), duration);
   2607  }
   2608 
   2609  // TODO: Install memory counter if it is possible from mozglue.
   2610  // #if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY)
   2611  //   // start counting memory allocations (outside of lock because this may
   2612  //   call
   2613  //   // profiler_add_sampled_counter which would attempt to take the lock.)
   2614  //   mozilla::profiler::install_memory_counter(true);
   2615  // #endif
   2616 }
   2617 
   2618 static void locked_profiler_save_profile_to_file(PSLockRef aLock,
   2619                                                 const char* aFilename,
   2620                                                 bool aIsShuttingDown);
   2621 
   2622 static SamplerThread* locked_profiler_stop(PSLockRef aLock);
   2623 
   2624 void profiler_shutdown() {
   2625  LOG("profiler_shutdown");
   2626 
   2627  VTUNE_SHUTDOWN();
   2628 
   2629  MOZ_RELEASE_ASSERT(profiler_is_main_thread());
   2630  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2631 
   2632  // If the profiler is active we must get a handle to the SamplerThread before
   2633  // ActivePS is destroyed, in order to delete it.
   2634  SamplerThread* samplerThread = nullptr;
   2635  {
   2636    PSAutoLock lock;
   2637 
   2638    // Save the profile on shutdown if requested.
   2639    if (ActivePS::Exists(lock)) {
   2640      const char* filename = getenv("MOZ_PROFILER_SHUTDOWN");
   2641      if (filename && filename[0] != '\0') {
   2642        locked_profiler_save_profile_to_file(lock, filename,
   2643                                             /* aIsShuttingDown */ true);
   2644      }
   2645 
   2646      samplerThread = locked_profiler_stop(lock);
   2647    }
   2648 
   2649    CorePS::Destroy(lock);
   2650 
   2651    // We just destroyed CorePS and the ThreadInfos it contains, so we can
   2652    // clear this thread's TLSRegisteredThread.
   2653    TLSRegisteredThread::SetRegisteredThread(lock, nullptr);
   2654  }
   2655 
   2656  // We do these operations with gPSMutex unlocked. The comments in
   2657  // profiler_stop() explain why.
   2658  if (samplerThread) {
   2659    delete samplerThread;
   2660  }
   2661 }
   2662 
   2663 static bool WriteProfileToJSONWriter(SpliceableChunkedJSONWriter& aWriter,
   2664                                     double aSinceTime, bool aIsShuttingDown,
   2665                                     bool aOnlyThreads = false) {
   2666  LOG("WriteProfileToJSONWriter");
   2667 
   2668  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2669 
   2670  if (!aOnlyThreads) {
   2671    aWriter.Start();
   2672    {
   2673      if (!profiler_stream_json_for_this_process(
   2674              aWriter, aSinceTime, aIsShuttingDown, aOnlyThreads)) {
   2675        return false;
   2676      }
   2677 
   2678      // Don't include profiles from other processes because this is a
   2679      // synchronous function.
   2680      aWriter.StartArrayProperty("processes");
   2681      aWriter.EndArray();
   2682    }
   2683    aWriter.End();
   2684  } else {
   2685    aWriter.StartBareList();
   2686    if (!profiler_stream_json_for_this_process(aWriter, aSinceTime,
   2687                                               aIsShuttingDown, aOnlyThreads)) {
   2688      return false;
   2689    }
   2690    aWriter.EndBareList();
   2691  }
   2692  return true;
   2693 }
   2694 
   2695 void profiler_set_process_name(const std::string& aProcessName,
   2696                               const std::string* aETLDplus1) {
   2697  LOG("profiler_set_process_name(\"%s\", \"%s\")", aProcessName.c_str(),
   2698      aETLDplus1 ? aETLDplus1->c_str() : "<none>");
   2699  PSAutoLock lock;
   2700  CorePS::SetProcessName(lock, aProcessName);
   2701  if (aETLDplus1) {
   2702    CorePS::SetETLDplus1(lock, *aETLDplus1);
   2703  }
   2704 }
   2705 
   2706 UniquePtr<char[]> profiler_get_profile(double aSinceTime, bool aIsShuttingDown,
   2707                                       bool aOnlyThreads) {
   2708  LOG("profiler_get_profile");
   2709 
   2710  SpliceableChunkedJSONWriter b{FailureLatchInfallibleSource::Singleton()};
   2711  if (!WriteProfileToJSONWriter(b, aSinceTime, aIsShuttingDown, aOnlyThreads)) {
   2712    return nullptr;
   2713  }
   2714  return b.ChunkedWriteFunc().CopyData();
   2715 }
   2716 
   2717 void profiler_get_start_params(int* aCapacity, Maybe<double>* aDuration,
   2718                               double* aInterval, uint32_t* aFeatures,
   2719                               Vector<const char*>* aFilters) {
   2720  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2721 
   2722  if (!aCapacity || !aDuration || !aInterval || !aFeatures || !aFilters) {
   2723    return;
   2724  }
   2725 
   2726  PSAutoLock lock;
   2727 
   2728  if (!ActivePS::Exists(lock)) {
   2729    *aCapacity = 0;
   2730    *aDuration = Nothing();
   2731    *aInterval = 0;
   2732    *aFeatures = 0;
   2733    aFilters->clear();
   2734    return;
   2735  }
   2736 
   2737  *aCapacity = ActivePS::Capacity(lock).Value();
   2738  *aDuration = ActivePS::Duration(lock);
   2739  *aInterval = ActivePS::Interval(lock);
   2740  *aFeatures = ActivePS::Features(lock);
   2741 
   2742  const Vector<std::string>& filters = ActivePS::Filters(lock);
   2743  MOZ_ALWAYS_TRUE(aFilters->resize(filters.length()));
   2744  for (uint32_t i = 0; i < filters.length(); ++i) {
   2745    (*aFilters)[i] = filters[i].c_str();
   2746  }
   2747 }
   2748 
   2749 void GetProfilerEnvVarsForChildProcess(
   2750    std::function<void(const char* key, const char* value)>&& aSetEnv) {
   2751  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2752 
   2753  PSAutoLock lock;
   2754 
   2755  if (!ActivePS::Exists(lock)) {
   2756    aSetEnv("MOZ_PROFILER_STARTUP", "");
   2757    return;
   2758  }
   2759 
   2760  aSetEnv("MOZ_PROFILER_STARTUP", "1");
   2761  auto capacityString =
   2762      Smprintf("%u", unsigned(ActivePS::Capacity(lock).Value()));
   2763  aSetEnv("MOZ_PROFILER_STARTUP_ENTRIES", capacityString.get());
   2764 
   2765  // Use AppendFloat instead of Smprintf with %f because the decimal
   2766  // separator used by %f is locale-dependent. But the string we produce needs
   2767  // to be parseable by strtod, which only accepts the period character as a
   2768  // decimal separator. AppendFloat always uses the period character.
   2769  std::string intervalString = std::to_string(ActivePS::Interval(lock));
   2770  aSetEnv("MOZ_PROFILER_STARTUP_INTERVAL", intervalString.c_str());
   2771 
   2772  auto featuresString = Smprintf("%d", ActivePS::Features(lock));
   2773  aSetEnv("MOZ_PROFILER_STARTUP_FEATURES_BITFIELD", featuresString.get());
   2774 
   2775  std::string filtersString;
   2776  const Vector<std::string>& filters = ActivePS::Filters(lock);
   2777  for (uint32_t i = 0; i < filters.length(); ++i) {
   2778    filtersString += filters[i];
   2779    if (i != filters.length() - 1) {
   2780      filtersString += ",";
   2781    }
   2782  }
   2783  aSetEnv("MOZ_PROFILER_STARTUP_FILTERS", filtersString.c_str());
   2784 }
   2785 
   2786 void profiler_received_exit_profile(const std::string& aExitProfile) {
   2787  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2788  PSAutoLock lock;
   2789  if (!ActivePS::Exists(lock)) {
   2790    return;
   2791  }
   2792  ActivePS::AddExitProfile(lock, aExitProfile);
   2793 }
   2794 
   2795 Vector<std::string> profiler_move_exit_profiles() {
   2796  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2797  PSAutoLock lock;
   2798  Vector<std::string> profiles;
   2799  if (ActivePS::Exists(lock)) {
   2800    profiles = ActivePS::MoveExitProfiles(lock);
   2801  }
   2802  return profiles;
   2803 }
   2804 
   2805 static void locked_profiler_save_profile_to_file(PSLockRef aLock,
   2806                                                 const char* aFilename,
   2807                                                 bool aIsShuttingDown = false) {
   2808  LOG("locked_profiler_save_profile_to_file(%s)", aFilename);
   2809 
   2810  MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock));
   2811 
   2812  std::ofstream stream;
   2813  stream.open(aFilename);
   2814  if (stream.is_open()) {
   2815    OStreamJSONWriteFunc jw(stream);
   2816    SpliceableJSONWriter w(jw, FailureLatchInfallibleSource::Singleton());
   2817    w.Start();
   2818    {
   2819      locked_profiler_stream_json_for_this_process(aLock, w, /* sinceTime */ 0,
   2820                                                   aIsShuttingDown);
   2821 
   2822      w.StartArrayProperty("processes");
   2823      Vector<std::string> exitProfiles = ActivePS::MoveExitProfiles(aLock);
   2824      for (auto& exitProfile : exitProfiles) {
   2825        if (!exitProfile.empty()) {
   2826          w.Splice(exitProfile);
   2827        }
   2828      }
   2829      w.EndArray();
   2830    }
   2831    w.End();
   2832 
   2833    stream.close();
   2834  }
   2835 }
   2836 
   2837 void baseprofiler_save_profile_to_file(const char* aFilename) {
   2838  LOG("baseprofiler_save_profile_to_file(%s)", aFilename);
   2839 
   2840  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2841 
   2842  PSAutoLock lock;
   2843 
   2844  if (!ActivePS::Exists(lock)) {
   2845    return;
   2846  }
   2847 
   2848  locked_profiler_save_profile_to_file(lock, aFilename);
   2849 }
   2850 
   2851 uint32_t profiler_get_available_features() {
   2852  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2853  return AvailableFeatures();
   2854 }
   2855 
   2856 Maybe<ProfilerBufferInfo> profiler_get_buffer_info() {
   2857  MOZ_RELEASE_ASSERT(CorePS::Exists());
   2858 
   2859  PSAutoLock lock;
   2860 
   2861  if (!ActivePS::Exists(lock)) {
   2862    return Nothing();
   2863  }
   2864 
   2865  return Some(ActivePS::Buffer(lock).GetProfilerBufferInfo());
   2866 }
   2867 
   2868 // This basically duplicates AutoProfilerLabel's constructor.
   2869 static void* MozGlueBaseLabelEnter(const char* aLabel,
   2870                                   const char* aDynamicString, void* aSp) {
   2871  ProfilingStack* profilingStack = AutoProfilerLabel::sProfilingStack.get();
   2872  if (profilingStack) {
   2873    profilingStack->pushLabelFrame(aLabel, aDynamicString, aSp,
   2874                                   ProfilingCategoryPair::OTHER);
   2875  }
   2876  return profilingStack;
   2877 }
   2878 
   2879 // This basically duplicates AutoProfilerLabel's destructor.
   2880 static void MozGlueBaseLabelExit(void* sProfilingStack) {
   2881  if (sProfilingStack) {
   2882    reinterpret_cast<ProfilingStack*>(sProfilingStack)->pop();
   2883  }
   2884 }
   2885 
   2886 static void locked_profiler_start(PSLockRef aLock, PowerOfTwo32 aCapacity,
   2887                                  double aInterval, uint32_t aFeatures,
   2888                                  const char** aFilters, uint32_t aFilterCount,
   2889                                  const Maybe<double>& aDuration) {
   2890  const TimeStamp profilingStartTime = TimeStamp::Now();
   2891 
   2892  if (LOG_TEST) {
   2893    LOG("locked_profiler_start");
   2894    LOG("- capacity  = %d", int(aCapacity.Value()));
   2895    LOG("- duration  = %.2f", aDuration ? *aDuration : -1);
   2896    LOG("- interval = %.2f", aInterval);
   2897 
   2898 #define LOG_FEATURE(n_, str_, Name_, desc_)     \
   2899  if (ProfilerFeature::Has##Name_(aFeatures)) { \
   2900    LOG("- feature  = %s", str_);               \
   2901  }
   2902 
   2903    BASE_PROFILER_FOR_EACH_FEATURE(LOG_FEATURE)
   2904 
   2905 #undef LOG_FEATURE
   2906 
   2907    for (uint32_t i = 0; i < aFilterCount; i++) {
   2908      LOG("- threads  = %s", aFilters[i]);
   2909    }
   2910  }
   2911 
   2912  MOZ_RELEASE_ASSERT(CorePS::Exists() && !ActivePS::Exists(aLock));
   2913 
   2914  mozilla::base_profiler_markers_detail::EnsureBufferForMainThreadAddMarker();
   2915 
   2916 #if defined(GP_PLAT_amd64_windows) || defined(GP_PLAT_arm64_windows)
   2917  mozilla::WindowsStackWalkInitialization();
   2918 #endif
   2919 
   2920  // Fall back to the default values if the passed-in values are unreasonable.
   2921  // We want to be able to store at least one full stack.
   2922  // TODO: Review magic numbers.
   2923  PowerOfTwo32 capacity =
   2924      (aCapacity.Value() >=
   2925       ProfileBufferChunkManager::scExpectedMaximumStackSize / scBytesPerEntry)
   2926          ? aCapacity
   2927          : BASE_PROFILER_DEFAULT_ENTRIES;
   2928  Maybe<double> duration = aDuration;
   2929 
   2930  if (aDuration && *aDuration <= 0) {
   2931    duration = Nothing();
   2932  }
   2933  double interval = aInterval > 0 ? aInterval : BASE_PROFILER_DEFAULT_INTERVAL;
   2934 
   2935  ActivePS::Create(aLock, profilingStartTime, capacity, interval, aFeatures,
   2936                   aFilters, aFilterCount, duration);
   2937 
   2938  // Set up profiling for each registered thread, if appropriate.
   2939  const Vector<UniquePtr<RegisteredThread>>& registeredThreads =
   2940      CorePS::RegisteredThreads(aLock);
   2941  for (auto& registeredThread : registeredThreads) {
   2942    RefPtr<ThreadInfo> info = registeredThread->Info();
   2943 
   2944    if (ActivePS::ShouldProfileThread(aLock, info)) {
   2945      registeredThread->RacyRegisteredThread().SetIsBeingProfiled(true);
   2946      ActivePS::AddLiveProfiledThread(aLock, registeredThread.get(),
   2947                                      MakeUnique<ProfiledThreadData>(info));
   2948      registeredThread->RacyRegisteredThread().ReinitializeOnResume();
   2949    }
   2950  }
   2951 
   2952  // Setup support for pushing/popping labels in mozglue.
   2953  RegisterProfilerLabelEnterExit(MozGlueBaseLabelEnter, MozGlueBaseLabelExit);
   2954 
   2955  // At the very end, set up RacyFeatures.
   2956  RacyFeatures::SetActive(ActivePS::Features(aLock));
   2957 }
   2958 
   2959 void profiler_start(PowerOfTwo32 aCapacity, double aInterval,
   2960                    uint32_t aFeatures, const char** aFilters,
   2961                    uint32_t aFilterCount, const Maybe<double>& aDuration) {
   2962  LOG("profiler_start");
   2963 
   2964  SamplerThread* samplerThread = nullptr;
   2965  {
   2966    PSAutoLock lock;
   2967 
   2968    // Initialize if necessary.
   2969    if (!CorePS::Exists()) {
   2970      profiler_init(nullptr);
   2971    }
   2972 
   2973    // Reset the current state if the profiler is running.
   2974    if (ActivePS::Exists(lock)) {
   2975      samplerThread = locked_profiler_stop(lock);
   2976    }
   2977 
   2978    locked_profiler_start(lock, aCapacity, aInterval, aFeatures, aFilters,
   2979                          aFilterCount, aDuration);
   2980  }
   2981 
   2982  // TODO: Install memory counter if it is possible from mozglue.
   2983  // #if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY)
   2984  //   // start counting memory allocations (outside of lock because this may
   2985  //   call
   2986  //   // profiler_add_sampled_counter which would attempt to take the lock.)
   2987  //   mozilla::profiler::install_memory_counter(true);
   2988  // #endif
   2989 
   2990  // We do these operations with gPSMutex unlocked. The comments in
   2991  // profiler_stop() explain why.
   2992  if (samplerThread) {
   2993    delete samplerThread;
   2994  }
   2995 }
   2996 
   2997 void profiler_ensure_started(PowerOfTwo32 aCapacity, double aInterval,
   2998                             uint32_t aFeatures, const char** aFilters,
   2999                             uint32_t aFilterCount,
   3000                             const Maybe<double>& aDuration) {
   3001  LOG("profiler_ensure_started");
   3002 
   3003  // bool startedProfiler = false; (See TODO below)
   3004  SamplerThread* samplerThread = nullptr;
   3005  {
   3006    PSAutoLock lock;
   3007 
   3008    // Initialize if necessary.
   3009    if (!CorePS::Exists()) {
   3010      profiler_init(nullptr);
   3011    }
   3012 
   3013    if (ActivePS::Exists(lock)) {
   3014      // The profiler is active.
   3015      if (!ActivePS::Equals(lock, aCapacity, aDuration, aInterval, aFeatures,
   3016                            aFilters, aFilterCount)) {
   3017        // Stop and restart with different settings.
   3018        samplerThread = locked_profiler_stop(lock);
   3019        locked_profiler_start(lock, aCapacity, aInterval, aFeatures, aFilters,
   3020                              aFilterCount, aDuration);
   3021        // startedProfiler = true; (See TODO below)
   3022      }
   3023    } else {
   3024      // The profiler is stopped.
   3025      locked_profiler_start(lock, aCapacity, aInterval, aFeatures, aFilters,
   3026                            aFilterCount, aDuration);
   3027      // startedProfiler = true; (See TODO below)
   3028    }
   3029  }
   3030 
   3031  // TODO: Install memory counter if it is possible from mozglue.
   3032  // #if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY)
   3033  //   // start counting memory allocations (outside of lock because this may
   3034  //   // call profiler_add_sampled_counter which would attempt to take the
   3035  //   // lock.)
   3036  //   mozilla::profiler::install_memory_counter(true);
   3037  // #endif
   3038 
   3039  // We do these operations with gPSMutex unlocked. The comments in
   3040  // profiler_stop() explain why.
   3041  if (samplerThread) {
   3042    delete samplerThread;
   3043  }
   3044 }
   3045 
   3046 [[nodiscard]] static SamplerThread* locked_profiler_stop(PSLockRef aLock) {
   3047  LOG("locked_profiler_stop");
   3048 
   3049  MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock));
   3050 
   3051  // At the very start, clear RacyFeatures.
   3052  RacyFeatures::SetInactive();
   3053 
   3054  // TODO: Uninstall memory counter if it is possible from mozglue.
   3055  // #if defined(MOZ_REPLACE_MALLOC) && defined(MOZ_PROFILER_MEMORY)
   3056  //   mozilla::profiler::install_memory_counter(false);
   3057  // #endif
   3058 
   3059  // Remove support for pushing/popping labels in mozglue.
   3060  RegisterProfilerLabelEnterExit(nullptr, nullptr);
   3061 
   3062  // Stop sampling live threads.
   3063  const Vector<LiveProfiledThreadData>& liveProfiledThreads =
   3064      ActivePS::LiveProfiledThreads(aLock);
   3065  for (auto& thread : liveProfiledThreads) {
   3066    RegisteredThread* registeredThread = thread.mRegisteredThread;
   3067    registeredThread->RacyRegisteredThread().SetIsBeingProfiled(false);
   3068  }
   3069 
   3070  // The Stop() call doesn't actually stop Run(); that happens in this
   3071  // function's caller when the sampler thread is destroyed. Stop() just gives
   3072  // the SamplerThread a chance to do some cleanup with gPSMutex locked.
   3073  SamplerThread* samplerThread = ActivePS::Destroy(aLock);
   3074  samplerThread->Stop(aLock);
   3075 
   3076  mozilla::base_profiler_markers_detail::ReleaseBufferForMainThreadAddMarker();
   3077 
   3078  return samplerThread;
   3079 }
   3080 
   3081 void profiler_stop() {
   3082  LOG("profiler_stop");
   3083 
   3084  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3085 
   3086  SamplerThread* samplerThread;
   3087  {
   3088    PSAutoLock lock;
   3089 
   3090    if (!ActivePS::Exists(lock)) {
   3091      return;
   3092    }
   3093 
   3094    samplerThread = locked_profiler_stop(lock);
   3095  }
   3096 
   3097  // We delete with gPSMutex unlocked. Otherwise we would get a deadlock: we
   3098  // would be waiting here with gPSMutex locked for SamplerThread::Run() to
   3099  // return so the join operation within the destructor can complete, but Run()
   3100  // needs to lock gPSMutex to return.
   3101  //
   3102  // Because this call occurs with gPSMutex unlocked, it -- including the final
   3103  // iteration of Run()'s loop -- must be able detect deactivation and return
   3104  // in a way that's safe with respect to other gPSMutex-locking operations
   3105  // that may have occurred in the meantime.
   3106  delete samplerThread;
   3107 }
   3108 
   3109 bool profiler_is_paused() {
   3110  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3111 
   3112  PSAutoLock lock;
   3113 
   3114  if (!ActivePS::Exists(lock)) {
   3115    return false;
   3116  }
   3117 
   3118  return ActivePS::IsPaused(lock);
   3119 }
   3120 
   3121 void profiler_pause() {
   3122  LOG("profiler_pause");
   3123 
   3124  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3125 
   3126  {
   3127    PSAutoLock lock;
   3128 
   3129    if (!ActivePS::Exists(lock)) {
   3130      return;
   3131    }
   3132 
   3133    RacyFeatures::SetPaused();
   3134    ActivePS::SetIsPaused(lock, true);
   3135    ActivePS::Buffer(lock).AddEntry(ProfileBufferEntry::Pause(profiler_time()));
   3136  }
   3137 }
   3138 
   3139 void profiler_resume() {
   3140  LOG("profiler_resume");
   3141 
   3142  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3143 
   3144  {
   3145    PSAutoLock lock;
   3146 
   3147    if (!ActivePS::Exists(lock)) {
   3148      return;
   3149    }
   3150 
   3151    ActivePS::Buffer(lock).AddEntry(
   3152        ProfileBufferEntry::Resume(profiler_time()));
   3153    ActivePS::SetIsPaused(lock, false);
   3154    RacyFeatures::SetUnpaused();
   3155  }
   3156 }
   3157 
   3158 bool profiler_is_sampling_paused() {
   3159  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3160 
   3161  PSAutoLock lock;
   3162 
   3163  if (!ActivePS::Exists(lock)) {
   3164    return false;
   3165  }
   3166 
   3167  return ActivePS::IsSamplingPaused(lock);
   3168 }
   3169 
   3170 void profiler_pause_sampling() {
   3171  LOG("profiler_pause_sampling");
   3172 
   3173  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3174 
   3175  {
   3176    PSAutoLock lock;
   3177 
   3178    if (!ActivePS::Exists(lock)) {
   3179      return;
   3180    }
   3181 
   3182    RacyFeatures::SetSamplingPaused();
   3183    ActivePS::SetIsSamplingPaused(lock, true);
   3184    ActivePS::Buffer(lock).AddEntry(
   3185        ProfileBufferEntry::PauseSampling(profiler_time()));
   3186  }
   3187 }
   3188 
   3189 void profiler_resume_sampling() {
   3190  LOG("profiler_resume_sampling");
   3191 
   3192  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3193 
   3194  {
   3195    PSAutoLock lock;
   3196 
   3197    if (!ActivePS::Exists(lock)) {
   3198      return;
   3199    }
   3200 
   3201    ActivePS::Buffer(lock).AddEntry(
   3202        ProfileBufferEntry::ResumeSampling(profiler_time()));
   3203    ActivePS::SetIsSamplingPaused(lock, false);
   3204    RacyFeatures::SetSamplingUnpaused();
   3205  }
   3206 }
   3207 
   3208 bool profiler_feature_active(uint32_t aFeature) {
   3209  // This function runs both on and off the main thread.
   3210 
   3211  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3212 
   3213  // This function is hot enough that we use RacyFeatures, not ActivePS.
   3214  return RacyFeatures::IsActiveWithFeature(aFeature);
   3215 }
   3216 
   3217 bool profiler_active_without_feature(uint32_t aFeature) {
   3218  // This function runs both on and off the main thread.
   3219 
   3220  // This function is hot enough that we use RacyFeatures, not ActivePS.
   3221  return RacyFeatures::IsActiveWithoutFeature(aFeature);
   3222 }
   3223 
   3224 void profiler_add_sampled_counter(BaseProfilerCount* aCounter) {
   3225  DEBUG_LOG("profiler_add_sampled_counter(%s)", aCounter->mLabel);
   3226  PSAutoLock lock;
   3227  CorePS::AppendCounter(lock, aCounter);
   3228 }
   3229 
   3230 void profiler_remove_sampled_counter(BaseProfilerCount* aCounter) {
   3231  DEBUG_LOG("profiler_remove_sampled_counter(%s)", aCounter->mLabel);
   3232  PSAutoLock lock;
   3233  // Note: we don't enforce a final sample, though we could do so if the
   3234  // profiler was active
   3235  CorePS::RemoveCounter(lock, aCounter);
   3236 }
   3237 
   3238 ProfilingStack* profiler_register_thread(const char* aName,
   3239                                         void* aGuessStackTop) {
   3240  DEBUG_LOG("profiler_register_thread(%s)", aName);
   3241 
   3242  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3243 
   3244  PSAutoLock lock;
   3245 
   3246  if (RegisteredThread* thread = FindCurrentThreadRegisteredThread(lock);
   3247      thread) {
   3248    LOG("profiler_register_thread(%s) - thread %" PRIu64
   3249        " already registered as %s",
   3250        aName, uint64_t(profiler_current_thread_id().ToNumber()),
   3251        thread->Info()->Name());
   3252    // TODO: Use new name. This is currently not possible because the
   3253    // RegisteredThread's ThreadInfo cannot be changed.
   3254    // In the meantime, we record a marker that could be used in the frontend.
   3255    std::string text("Thread ");
   3256    text += std::to_string(profiler_current_thread_id().ToNumber());
   3257    text += " \"";
   3258    text += thread->Info()->Name();
   3259    text += "\" attempted to re-register as \"";
   3260    text += aName;
   3261    text += "\"";
   3262    BASE_PROFILER_MARKER_TEXT("profiler_register_thread again", OTHER_Profiling,
   3263                              MarkerThreadId::MainThread(), text);
   3264 
   3265    return &thread->RacyRegisteredThread().ProfilingStack();
   3266  }
   3267 
   3268  void* stackTop = GetStackTop(aGuessStackTop);
   3269  return locked_register_thread(lock, aName, stackTop);
   3270 }
   3271 
   3272 void profiler_unregister_thread() {
   3273  if (!CorePS::Exists()) {
   3274    // This function can be called after the main thread has already shut down.
   3275    return;
   3276  }
   3277 
   3278  PSAutoLock lock;
   3279 
   3280  RegisteredThread* registeredThread = FindCurrentThreadRegisteredThread(lock);
   3281  MOZ_RELEASE_ASSERT(registeredThread ==
   3282                     TLSRegisteredThread::RegisteredThread(lock));
   3283  if (registeredThread) {
   3284    RefPtr<ThreadInfo> info = registeredThread->Info();
   3285 
   3286    DEBUG_LOG("profiler_unregister_thread: %s", info->Name());
   3287 
   3288    if (ActivePS::Exists(lock)) {
   3289      ActivePS::UnregisterThread(lock, registeredThread);
   3290    }
   3291 
   3292    // Clear the pointer to the RegisteredThread object that we're about to
   3293    // destroy.
   3294    TLSRegisteredThread::SetRegisteredThread(lock, nullptr);
   3295 
   3296    // Remove the thread from the list of registered threads. This deletes the
   3297    // registeredThread object.
   3298    CorePS::RemoveRegisteredThread(lock, registeredThread);
   3299  } else {
   3300    LOG("profiler_unregister_thread() - thread %" PRIu64
   3301        " already unregistered",
   3302        uint64_t(profiler_current_thread_id().ToNumber()));
   3303    // We cannot record a marker on this thread because it was already
   3304    // unregistered. Send it to the main thread (unless this *is* already the
   3305    // main thread, which has been unregistered); this may be useful to catch
   3306    // mismatched register/unregister pairs in Firefox.
   3307    if (BaseProfilerThreadId tid = profiler_current_thread_id();
   3308        tid != profiler_main_thread_id()) {
   3309      BASE_PROFILER_MARKER_TEXT(
   3310          "profiler_unregister_thread again", OTHER_Profiling,
   3311          MarkerThreadId::MainThread(),
   3312          std::to_string(profiler_current_thread_id().ToNumber()));
   3313    }
   3314    // There are two ways FindCurrentThreadRegisteredThread() might have failed.
   3315    //
   3316    // - TLSRegisteredThread::Init() failed in locked_register_thread().
   3317    //
   3318    // - We've already called profiler_unregister_thread() for this thread.
   3319    //   (Whether or not it should, this does happen in practice.)
   3320    //
   3321    // Either way, TLSRegisteredThread should be empty.
   3322    MOZ_RELEASE_ASSERT(!TLSRegisteredThread::RegisteredThread(lock));
   3323  }
   3324 }
   3325 
   3326 void profiler_register_page(uint64_t aTabID, uint64_t aInnerWindowID,
   3327                            const std::string& aUrl,
   3328                            uint64_t aEmbedderInnerWindowID) {
   3329  DEBUG_LOG("profiler_register_page(%" PRIu64 ", %" PRIu64 ", %s, %" PRIu64 ")",
   3330            aTabID, aInnerWindowID, aUrl.c_str(), aEmbedderInnerWindowID);
   3331 
   3332  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3333 
   3334  PSAutoLock lock;
   3335 
   3336  // When a Browsing context is first loaded, the first url loaded in it will be
   3337  // about:blank. Because of that, this call keeps the first non-about:blank
   3338  // registration of window and discards the previous one.
   3339  RefPtr<PageInformation> pageInfo =
   3340      new PageInformation(aTabID, aInnerWindowID, aUrl, aEmbedderInnerWindowID);
   3341  CorePS::AppendRegisteredPage(lock, std::move(pageInfo));
   3342 
   3343  // After appending the given page to CorePS, look for the expired
   3344  // pages and remove them if there are any.
   3345  if (ActivePS::Exists(lock)) {
   3346    ActivePS::DiscardExpiredPages(lock);
   3347  }
   3348 }
   3349 
   3350 void profiler_unregister_page(uint64_t aRegisteredInnerWindowID) {
   3351  if (!CorePS::Exists()) {
   3352    // This function can be called after the main thread has already shut down.
   3353    return;
   3354  }
   3355 
   3356  PSAutoLock lock;
   3357 
   3358  // During unregistration, if the profiler is active, we have to keep the
   3359  // page information since there may be some markers associated with the given
   3360  // page. But if profiler is not active. we have no reason to keep the
   3361  // page information here because there can't be any marker associated with it.
   3362  if (ActivePS::Exists(lock)) {
   3363    ActivePS::UnregisterPage(lock, aRegisteredInnerWindowID);
   3364  } else {
   3365    CorePS::RemoveRegisteredPage(lock, aRegisteredInnerWindowID);
   3366  }
   3367 }
   3368 
   3369 void profiler_clear_all_pages() {
   3370  if (!CorePS::Exists()) {
   3371    // This function can be called after the main thread has already shut down.
   3372    return;
   3373  }
   3374 
   3375  {
   3376    PSAutoLock lock;
   3377    CorePS::ClearRegisteredPages(lock);
   3378    if (ActivePS::Exists(lock)) {
   3379      ActivePS::ClearUnregisteredPages(lock);
   3380    }
   3381  }
   3382 }
   3383 
   3384 void profiler_thread_sleep() {
   3385  // This function runs both on and off the main thread.
   3386 
   3387  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3388 
   3389  RacyRegisteredThread* racyRegisteredThread =
   3390      TLSRegisteredThread::RacyRegisteredThread();
   3391  if (!racyRegisteredThread) {
   3392    return;
   3393  }
   3394 
   3395  racyRegisteredThread->SetSleeping();
   3396 }
   3397 
   3398 void profiler_thread_wake() {
   3399  // This function runs both on and off the main thread.
   3400 
   3401  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3402 
   3403  RacyRegisteredThread* racyRegisteredThread =
   3404      TLSRegisteredThread::RacyRegisteredThread();
   3405  if (!racyRegisteredThread) {
   3406    return;
   3407  }
   3408 
   3409  racyRegisteredThread->SetAwake();
   3410 }
   3411 
   3412 bool detail::IsThreadBeingProfiled() {
   3413  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3414 
   3415  const RacyRegisteredThread* racyRegisteredThread =
   3416      TLSRegisteredThread::RacyRegisteredThread();
   3417  return racyRegisteredThread && racyRegisteredThread->IsBeingProfiled();
   3418 }
   3419 
   3420 bool profiler_thread_is_sleeping() {
   3421  MOZ_RELEASE_ASSERT(profiler_is_main_thread());
   3422  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3423 
   3424  RacyRegisteredThread* racyRegisteredThread =
   3425      TLSRegisteredThread::RacyRegisteredThread();
   3426  if (!racyRegisteredThread) {
   3427    return false;
   3428  }
   3429  return racyRegisteredThread->IsSleeping();
   3430 }
   3431 
   3432 double profiler_time() {
   3433  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3434 
   3435  TimeDuration delta = TimeStamp::Now() - CorePS::ProcessStartTime();
   3436  return delta.ToMilliseconds();
   3437 }
   3438 
   3439 bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer,
   3440                                     StackCaptureOptions aCaptureOptions) {
   3441  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3442 
   3443  PSAutoLock lock;
   3444 
   3445  if (!ActivePS::Exists(lock) ||
   3446      aCaptureOptions == StackCaptureOptions::NoStack) {
   3447    return false;
   3448  }
   3449 
   3450  RegisteredThread* registeredThread =
   3451      TLSRegisteredThread::RegisteredThread(lock);
   3452  if (!registeredThread) {
   3453    MOZ_ASSERT(registeredThread);
   3454    return false;
   3455  }
   3456 
   3457  ProfileBuffer profileBuffer(aChunkedBuffer);
   3458 
   3459  Registers regs;
   3460 #if defined(HAVE_NATIVE_UNWIND)
   3461  REGISTERS_SYNC_POPULATE(regs);
   3462 #else
   3463  regs.Clear();
   3464 #endif
   3465 
   3466  DoSyncSample(lock, *registeredThread, TimeStamp::Now(), regs, profileBuffer,
   3467               aCaptureOptions);
   3468 
   3469  return true;
   3470 }
   3471 
   3472 UniquePtr<ProfileChunkedBuffer> profiler_capture_backtrace() {
   3473  MOZ_RELEASE_ASSERT(CorePS::Exists());
   3474  AUTO_BASE_PROFILER_LABEL("baseprofiler::profiler_capture_backtrace",
   3475                           PROFILER);
   3476 
   3477  // Quick is-active check before allocating a buffer.
   3478  // If NoMarkerStacks is set, we don't want to capture a backtrace.
   3479  if (!profiler_active_without_feature(ProfilerFeature::NoMarkerStacks)) {
   3480    return nullptr;
   3481  }
   3482 
   3483  auto buffer = MakeUnique<ProfileChunkedBuffer>(
   3484      ProfileChunkedBuffer::ThreadSafety::WithoutMutex,
   3485      MakeUnique<ProfileBufferChunkManagerSingle>(
   3486          ProfileBufferChunkManager::scExpectedMaximumStackSize));
   3487 
   3488  if (!profiler_capture_backtrace_into(*buffer, StackCaptureOptions::Full)) {
   3489    return nullptr;
   3490  }
   3491 
   3492  return buffer;
   3493 }
   3494 
   3495 UniqueProfilerBacktrace profiler_get_backtrace() {
   3496  UniquePtr<ProfileChunkedBuffer> buffer = profiler_capture_backtrace();
   3497 
   3498  if (!buffer) {
   3499    return nullptr;
   3500  }
   3501 
   3502  return UniqueProfilerBacktrace(
   3503      new ProfilerBacktrace("SyncProfile", std::move(buffer)));
   3504 }
   3505 
   3506 void ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {
   3507  delete aBacktrace;
   3508 }
   3509 
   3510 bool profiler_is_locked_on_current_thread() {
   3511  // This function is used to help users avoid calling `profiler_...` functions
   3512  // when the profiler may already have a lock in place, which would prevent a
   3513  // 2nd recursive lock (resulting in a crash or a never-ending wait).
   3514  // So we must return `true` for any of:
   3515  // - The main profiler mutex, used by most functions, and/or
   3516  // - The buffer mutex, used directly in some functions without locking the
   3517  //   main mutex, e.g., marker-related functions.
   3518  return PSAutoLock::IsLockedOnCurrentThread() ||
   3519         profiler_get_core_buffer().IsThreadSafeAndLockedOnCurrentThread();
   3520 }
   3521 
   3522 // This is a simplified version of profiler_add_marker that can be easily passed
   3523 // into the JS engine.
   3524 void profiler_add_js_marker(const char* aMarkerName, const char* aMarkerText) {
   3525  BASE_PROFILER_MARKER_TEXT(
   3526      ProfilerString8View::WrapNullTerminatedString(aMarkerName), JS, {},
   3527      ProfilerString8View::WrapNullTerminatedString(aMarkerText));
   3528 }
   3529 
   3530 // NOTE: aCollector's methods will be called while the target thread is paused.
   3531 // Doing things in those methods like allocating -- which may try to claim
   3532 // locks -- is a surefire way to deadlock.
   3533 void profiler_suspend_and_sample_thread(BaseProfilerThreadId aThreadId,
   3534                                        uint32_t aFeatures,
   3535                                        ProfilerStackCollector& aCollector,
   3536                                        bool aSampleNative /* = true */) {
   3537  const bool isSynchronous = [&aThreadId]() {
   3538    const BaseProfilerThreadId currentThreadId = profiler_current_thread_id();
   3539    if (!aThreadId.IsSpecified()) {
   3540      aThreadId = currentThreadId;
   3541      return true;
   3542    }
   3543    return aThreadId == currentThreadId;
   3544  }();
   3545 
   3546  // Lock the profiler mutex
   3547  PSAutoLock lock;
   3548 
   3549  const Vector<UniquePtr<RegisteredThread>>& registeredThreads =
   3550      CorePS::RegisteredThreads(lock);
   3551  for (auto& thread : registeredThreads) {
   3552    RefPtr<ThreadInfo> info = thread->Info();
   3553    RegisteredThread& registeredThread = *thread.get();
   3554 
   3555    if (info->ThreadId() == aThreadId) {
   3556      if (info->IsMainThread()) {
   3557        aCollector.SetIsMainThread();
   3558      }
   3559 
   3560      // Allocate the space for the native stack
   3561      NativeStack nativeStack;
   3562 
   3563      auto collectStack = [&](const Registers& aRegs, const TimeStamp& aNow) {
   3564      // The target thread is now suspended. Collect a native
   3565      // backtrace, and call the callback.
   3566 #if defined(HAVE_FASTINIT_NATIVE_UNWIND)
   3567        if (aSampleNative) {
   3568          // We can only use FramePointerStackWalk or MozStackWalk from
   3569          // suspend_and_sample_thread as other stackwalking methods may not be
   3570          // initialized.
   3571 #  if defined(USE_FRAME_POINTER_STACK_WALK)
   3572          DoFramePointerBacktrace(lock, registeredThread, aRegs, nativeStack);
   3573 #  elif defined(USE_MOZ_STACK_WALK)
   3574          DoMozStackWalkBacktrace(lock, registeredThread, aRegs, nativeStack);
   3575 #  else
   3576 #    error "Invalid configuration"
   3577 #  endif
   3578 
   3579          MergeStacks(isSynchronous, registeredThread, nativeStack, aCollector);
   3580        } else
   3581 #endif
   3582        {
   3583          MergeStacks(isSynchronous, registeredThread, nativeStack, aCollector);
   3584 
   3585          aCollector.CollectNativeLeafAddr((void*)aRegs.mPC);
   3586        }
   3587      };
   3588 
   3589      if (isSynchronous) {
   3590        // Sampling the current thread, do NOT suspend it!
   3591        Registers regs;
   3592 #if defined(HAVE_NATIVE_UNWIND)
   3593        REGISTERS_SYNC_POPULATE(regs);
   3594 #else
   3595        regs.Clear();
   3596 #endif
   3597        collectStack(regs, TimeStamp::Now());
   3598      } else {
   3599        // Suspend, sample, and then resume the target thread.
   3600        Sampler sampler(lock);
   3601        TimeStamp now = TimeStamp::Now();
   3602        sampler.SuspendAndSampleAndResumeThread(lock, registeredThread, now,
   3603                                                collectStack);
   3604 
   3605        // NOTE: Make sure to disable the sampler before it is destroyed, in
   3606        // case the profiler is running at the same time.
   3607        sampler.Disable(lock);
   3608      }
   3609      break;
   3610    }
   3611  }
   3612 }
   3613 
   3614 // END externally visible functions
   3615 ////////////////////////////////////////////////////////////////////////
   3616 
   3617 }  // namespace baseprofiler
   3618 }  // namespace mozilla