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(®isteredPages[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 ? ®isteredThread->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 ®isteredThread->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