/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include "platform.h" #include "PlatformMacros.h" #include "mozilla/ArrayUtils.h" #include "mozilla/UniquePtr.h" #include "GeckoProfiler.h" #ifndef SPS_STANDALONE #include "ProfilerIOInterposeObserver.h" #include "mozilla/StaticPtr.h" #endif #include "mozilla/ThreadLocal.h" #include "mozilla/TimeStamp.h" #include "PseudoStack.h" #include "GeckoSampler.h" #ifndef SPS_STANDALONE #include "nsIObserverService.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "nsXULAppAPI.h" #include "nsProfilerStartParams.h" #include "mozilla/Services.h" #include "nsThreadUtils.h" #endif #include "ProfilerMarkers.h" #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) #include "AndroidBridge.h" #endif #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) #include "GeneratedJNINatives.h" #endif #ifndef SPS_STANDALONE #if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux) # define USE_LUL_STACKWALK # include "lul/LulMain.h" # include "lul/platform-linux-lul.h" #endif #endif #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) class GeckoJavaSampler : public widget::GeckoJavaSampler::Natives { private: GeckoJavaSampler(); public: static double GetProfilerTime() { if (!profiler_is_active()) { return 0.0; } return profiler_time(); }; }; #endif mozilla::ThreadLocal tlsPseudoStack; mozilla::ThreadLocal tlsTicker; mozilla::ThreadLocal tlsStackTop; // We need to track whether we've been initialized otherwise // we end up using tlsStack without initializing it. // Because tlsStack is totally opaque to us we can't reuse // it as the flag itself. bool stack_key_initialized; mozilla::TimeStamp sLastTracerEvent; // is raced on mozilla::TimeStamp sStartTime; int sFrameNumber = 0; int sLastFrameNumber = 0; int sInitCount = 0; // Each init must have a matched shutdown. static bool sIsProfiling = false; // is raced on static bool sIsGPUProfiling = false; // is raced on static bool sIsLayersDump = false; // is raced on static bool sIsDisplayListDump = false; // is raced on static bool sIsRestyleProfiling = false; // is raced on // Environment variables to control the profiler const char* PROFILER_HELP = "MOZ_PROFILER_HELP"; const char* PROFILER_INTERVAL = "MOZ_PROFILER_INTERVAL"; const char* PROFILER_ENTRIES = "MOZ_PROFILER_ENTRIES"; const char* PROFILER_STACK = "MOZ_PROFILER_STACK_SCAN"; const char* PROFILER_FEATURES = "MOZ_PROFILING_FEATURES"; /* we don't need to worry about overflow because we only treat the * case of them being the same as special. i.e. we only run into * a problem if 2^32 events happen between samples that we need * to know are associated with different events */ // Values harvested from env vars, that control the profiler. static int sUnwindInterval; /* in milliseconds */ static int sUnwindStackScan; /* max # of dubious frames allowed */ static int sProfileEntries; /* how many entries do we store? */ std::vector* Sampler::sRegisteredThreads = nullptr; mozilla::UniquePtr< ::Mutex> Sampler::sRegisteredThreadsMutex; GeckoSampler* Sampler::sActiveSampler; #ifndef SPS_STANDALONE static mozilla::StaticAutoPtr sInterposeObserver; #endif // The name that identifies the gecko thread for calls to // profiler_register_thread. static const char * gGeckoThreadName = "GeckoMain"; void Sampler::Startup() { sRegisteredThreads = new std::vector(); sRegisteredThreadsMutex = OS::CreateMutex("sRegisteredThreads mutex"); // We could create the sLUL object and read unwind info into it at // this point. That would match the lifetime implied by destruction // of it in Sampler::Shutdown just below. However, that gives a big // delay on startup, even if no profiling is actually to be done. // So, instead, sLUL is created on demand at the first call to // Sampler::Start. } void Sampler::Shutdown() { while (sRegisteredThreads->size() > 0) { delete sRegisteredThreads->back(); sRegisteredThreads->pop_back(); } sRegisteredThreadsMutex = nullptr; delete sRegisteredThreads; // UnregisterThread can be called after shutdown in XPCShell. Thus // we need to point to null to ignore such a call after shutdown. sRegisteredThreadsMutex = nullptr; sRegisteredThreads = nullptr; #if defined(USE_LUL_STACKWALK) // Delete the sLUL object, if it actually got created. if (sLUL) { delete sLUL; sLUL = nullptr; } #endif } StackOwningThreadInfo::StackOwningThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop) : ThreadInfo(aName, aThreadId, aIsMainThread, aPseudoStack, aStackTop) { aPseudoStack->ref(); } StackOwningThreadInfo::~StackOwningThreadInfo() { PseudoStack* stack = Stack(); if (stack) { stack->deref(); } } void StackOwningThreadInfo::SetPendingDelete() { PseudoStack* stack = Stack(); if (stack) { stack->deref(); } ThreadInfo::SetPendingDelete(); } ProfilerMarker::ProfilerMarker(const char* aMarkerName, ProfilerMarkerPayload* aPayload, double aTime) : mMarkerName(strdup(aMarkerName)) , mPayload(aPayload) , mTime(aTime) { } ProfilerMarker::~ProfilerMarker() { free(mMarkerName); delete mPayload; } void ProfilerMarker::SetGeneration(uint32_t aGenID) { mGenID = aGenID; } double ProfilerMarker::GetTime() const { return mTime; } void ProfilerMarker::StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks) const { // Schema: // [name, time, data] aWriter.StartArrayElement(); { aUniqueStacks.mUniqueStrings.WriteElement(aWriter, GetMarkerName()); aWriter.DoubleElement(mTime); // TODO: Store the callsite for this marker if available: // if have location data // b.NameValue(marker, "location", ...); if (mPayload) { aWriter.StartObjectElement(); { mPayload->StreamPayload(aWriter, aUniqueStacks); } aWriter.EndObject(); } } aWriter.EndArray(); } /* Has MOZ_PROFILER_VERBOSE been set? */ // Verbosity control for the profiler. The aim is to check env var // MOZ_PROFILER_VERBOSE only once. However, we may need to temporarily // override that so as to print the profiler's help message. That's // what moz_profiler_set_verbosity is for. enum class ProfilerVerbosity : int8_t { UNCHECKED, NOTVERBOSE, VERBOSE }; // Raced on, potentially static ProfilerVerbosity profiler_verbosity = ProfilerVerbosity::UNCHECKED; bool moz_profiler_verbose() { if (profiler_verbosity == ProfilerVerbosity::UNCHECKED) { if (getenv("MOZ_PROFILER_VERBOSE") != nullptr) profiler_verbosity = ProfilerVerbosity::VERBOSE; else profiler_verbosity = ProfilerVerbosity::NOTVERBOSE; } return profiler_verbosity == ProfilerVerbosity::VERBOSE; } void moz_profiler_set_verbosity(ProfilerVerbosity pv) { MOZ_ASSERT(pv == ProfilerVerbosity::UNCHECKED || pv == ProfilerVerbosity::VERBOSE); profiler_verbosity = pv; } bool set_profiler_interval(const char* interval) { if (interval) { errno = 0; long int n = strtol(interval, (char**)nullptr, 10); if (errno == 0 && n >= 1 && n <= 1000) { sUnwindInterval = n; return true; } return false; } return true; } bool set_profiler_entries(const char* entries) { if (entries) { errno = 0; long int n = strtol(entries, (char**)nullptr, 10); if (errno == 0 && n > 0) { sProfileEntries = n; return true; } return false; } return true; } bool set_profiler_scan(const char* scanCount) { if (scanCount) { errno = 0; long int n = strtol(scanCount, (char**)nullptr, 10); if (errno == 0 && n >= 0 && n <= 100) { sUnwindStackScan = n; return true; } return false; } return true; } bool is_native_unwinding_avail() { # if defined(HAVE_NATIVE_UNWIND) return true; #else return false; #endif } // Read env vars at startup, so as to set: // sUnwindInterval, sProfileEntries, sUnwindStackScan. void read_profiler_env_vars() { /* Set defaults */ sUnwindInterval = 0; /* We'll have to look elsewhere */ sProfileEntries = 0; const char* interval = getenv(PROFILER_INTERVAL); const char* entries = getenv(PROFILER_ENTRIES); const char* scanCount = getenv(PROFILER_STACK); if (getenv(PROFILER_HELP)) { // Enable verbose output moz_profiler_set_verbosity(ProfilerVerbosity::VERBOSE); profiler_usage(); // Now force the next enquiry of moz_profiler_verbose to re-query // env var MOZ_PROFILER_VERBOSE. moz_profiler_set_verbosity(ProfilerVerbosity::UNCHECKED); } if (!set_profiler_interval(interval) || !set_profiler_entries(entries) || !set_profiler_scan(scanCount)) { profiler_usage(); } else { LOG( "SPS:"); LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")", (int)sUnwindInterval); LOGF("SPS: Entry store size = %d (zero means \"platform default\")", (int)sProfileEntries); LOGF("SPS: UnwindStackScan = %d (max dubious frames per unwind).", (int)sUnwindStackScan); LOG( "SPS:"); } } void profiler_usage() { LOG( "SPS: "); LOG( "SPS: Environment variable usage:"); LOG( "SPS: "); LOG( "SPS: MOZ_PROFILER_HELP"); LOG( "SPS: If set to any value, prints this message."); LOG( "SPS: "); LOG( "SPS: MOZ_PROFILER_INTERVAL= (milliseconds, 1 to 1000)"); LOG( "SPS: If unset, platform default is used."); LOG( "SPS: "); LOG( "SPS: MOZ_PROFILER_ENTRIES= (count, minimum of 1)"); LOG( "SPS: If unset, platform default is used."); LOG( "SPS: "); LOG( "SPS: MOZ_PROFILER_VERBOSE"); LOG( "SPS: If set to any value, increases verbosity (recommended)."); LOG( "SPS: "); LOG( "SPS: MOZ_PROFILER_STACK_SCAN= (default is zero)"); LOG( "SPS: The number of dubious (stack-scanned) frames allowed"); LOG( "SPS: "); LOG( "SPS: MOZ_PROFILER_LUL_TEST"); LOG( "SPS: If set to any value, runs LUL unit tests at startup of"); LOG( "SPS: the unwinder thread, and prints a short summary of results."); LOG( "SPS: "); LOGF("SPS: This platform %s native unwinding.", is_native_unwinding_avail() ? "supports" : "does not support"); LOG( "SPS: "); /* Re-set defaults */ sUnwindInterval = 0; /* We'll have to look elsewhere */ sProfileEntries = 0; sUnwindStackScan = 0; LOG( "SPS:"); LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")", (int)sUnwindInterval); LOGF("SPS: Entry store size = %d (zero means \"platform default\")", (int)sProfileEntries); LOGF("SPS: UnwindStackScan = %d (max dubious frames per unwind).", (int)sUnwindStackScan); LOG( "SPS:"); return; } void set_tls_stack_top(void* stackTop) { // Round |stackTop| up to the end of the containing page. We may // as well do this -- there's no danger of a fault, and we might // get a few more base-of-the-stack frames as a result. This // assumes that no target has a page size smaller than 4096. uintptr_t stackTopR = (uintptr_t)stackTop; if (stackTop) { stackTopR = (stackTopR & ~(uintptr_t)4095) + (uintptr_t)4095; } tlsStackTop.set((void*)stackTopR); } bool is_main_thread_name(const char* aName) { if (!aName) { return false; } return strcmp(aName, gGeckoThreadName) == 0; } #ifndef SPS_STANDALONE #ifdef HAVE_VA_COPY #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar) #elif defined(HAVE_VA_LIST_AS_ARRAY) #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0] #else #define VARARGS_ASSIGN(foo, bar) (foo) = (bar) #endif void mozilla_sampler_log(const char *fmt, va_list args) { if (profiler_is_active()) { // nsAutoCString AppendPrintf would be nicer but // this is mozilla external code char buf[2048]; va_list argsCpy; VARARGS_ASSIGN(argsCpy, args); int required = vsnprintf(buf, sizeof(buf), fmt, argsCpy); va_end(argsCpy); if (required < 0) { return; // silently drop for now } else if (required < 2048) { profiler_tracing("log", buf, TRACING_EVENT); } else { char* heapBuf = new char[required+1]; va_list argsCpy; VARARGS_ASSIGN(argsCpy, args); vsnprintf(heapBuf, required+1, fmt, argsCpy); va_end(argsCpy); // EVENT_BACKTRACE could be used to get a source // for all log events. This could be a runtime // flag later. profiler_tracing("log", heapBuf, TRACING_EVENT); delete[] heapBuf; } } } #endif //////////////////////////////////////////////////////////////////////// // BEGIN externally visible functions void mozilla_sampler_init(void* stackTop) { // 10.4Fx does not support the Sampler; it doesn't grok PPC or 10.4. return; sInitCount++; if (stack_key_initialized) return; #ifdef SPS_STANDALONE mozilla::TimeStamp::Startup(); #endif LOG("BEGIN mozilla_sampler_init"); if (!tlsPseudoStack.init() || !tlsTicker.init() || !tlsStackTop.init()) { LOG("Failed to init."); return; } bool ignore; sStartTime = mozilla::TimeStamp::ProcessCreation(ignore); stack_key_initialized = true; Sampler::Startup(); PseudoStack *stack = PseudoStack::create(); tlsPseudoStack.set(stack); bool isMainThread = true; Sampler::RegisterCurrentThread(isMainThread ? gGeckoThreadName : "Application Thread", stack, isMainThread, stackTop); // Read interval settings from MOZ_PROFILER_INTERVAL and stack-scan // threshhold from MOZ_PROFILER_STACK_SCAN. read_profiler_env_vars(); // platform specific initialization OS::Startup(); #ifndef SPS_STANDALONE set_stderr_callback(mozilla_sampler_log); #endif #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) if (mozilla::jni::IsAvailable()) { GeckoJavaSampler::Init(); } #endif // We can't open pref so we use an environment variable // to know if we should trigger the profiler on startup // NOTE: Default const char *val = getenv("MOZ_PROFILER_STARTUP"); if (!val || !*val) { return; } const char* features[] = {"js" , "leaf" , "threads" #if defined(XP_WIN) || defined(XP_MACOSX) \ || (defined(SPS_ARCH_arm) && defined(linux)) \ || defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux) , "stackwalk" #endif #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) , "java" #endif }; const char* threadFilters[] = { "GeckoMain", "Compositor" }; profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL, features, MOZ_ARRAY_LENGTH(features), threadFilters, MOZ_ARRAY_LENGTH(threadFilters)); LOG("END mozilla_sampler_init"); } void mozilla_sampler_shutdown() { // 10.4Fx does not support the Sampler; it doesn't grok PPC or 10.4. return; sInitCount--; if (sInitCount > 0) return; // Save the profile on shutdown if requested. GeckoSampler *t = tlsTicker.get(); if (t) { const char *val = getenv("MOZ_PROFILER_SHUTDOWN"); if (val) { std::ofstream stream; stream.open(val); if (stream.is_open()) { t->ToStreamAsJSON(stream); stream.close(); } } } profiler_stop(); #ifndef SPS_STANDALONE set_stderr_callback(nullptr); #endif Sampler::Shutdown(); #ifdef SPS_STANDALONE mozilla::TimeStamp::Shutdown(); #endif PseudoStack *stack = tlsPseudoStack.get(); stack->deref(); tlsPseudoStack.set(nullptr); } void mozilla_sampler_save() { GeckoSampler *t = tlsTicker.get(); if (!t) { return; } t->RequestSave(); // We're on the main thread already so we don't // have to wait to handle the save request. t->HandleSaveRequest(); } mozilla::UniquePtr mozilla_sampler_get_profile(double aSinceTime) { return nullptr; // 10.4Fx GeckoSampler *t = tlsTicker.get(); if (!t) { return nullptr; } return t->ToJSON(aSinceTime); } #ifndef SPS_STANDALONE JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, double aSinceTime) { return nullptr; // 10.4Fx GeckoSampler *t = tlsTicker.get(); if (!t) { return nullptr; } return t->ToJSObject(aCx, aSinceTime); } void mozilla_sampler_get_profile_data_async(double aSinceTime, mozilla::dom::Promise* aPromise) { GeckoSampler *t = tlsTicker.get(); if (NS_WARN_IF(!t)) { return; } t->ToJSObjectAsync(aSinceTime, aPromise); } void mozilla_sampler_get_profiler_start_params(int* aEntrySize, double* aInterval, mozilla::Vector* aFilters, mozilla::Vector* aFeatures) { if (NS_WARN_IF(!aEntrySize) || NS_WARN_IF(!aInterval) || NS_WARN_IF(!aFilters) || NS_WARN_IF(!aFeatures)) { return; } GeckoSampler *t = tlsTicker.get(); if (NS_WARN_IF(!t)) { return; } *aEntrySize = t->EntrySize(); *aInterval = t->interval(); const ThreadNameFilterList& threadNameFilterList = t->ThreadNameFilters(); MOZ_ALWAYS_TRUE(aFilters->resize(threadNameFilterList.length())); for (uint32_t i = 0; i < threadNameFilterList.length(); ++i) { (*aFilters)[i] = threadNameFilterList[i].c_str(); } const FeatureList& featureList = t->Features(); MOZ_ALWAYS_TRUE(aFeatures->resize(featureList.length())); for (size_t i = 0; i < featureList.length(); ++i) { (*aFeatures)[i] = featureList[i].c_str(); } } void mozilla_sampler_get_gatherer(nsISupports** aRetVal) { if (!aRetVal) { return; } if (NS_WARN_IF(!profiler_is_active())) { *aRetVal = nullptr; return; } GeckoSampler *t = tlsTicker.get(); if (NS_WARN_IF(!t)) { *aRetVal = nullptr; return; } t->GetGatherer(aRetVal); } #endif void mozilla_sampler_save_profile_to_file(const char* aFilename) { GeckoSampler *t = tlsTicker.get(); if (!t) { return; } std::ofstream stream; stream.open(aFilename); if (stream.is_open()) { t->ToStreamAsJSON(stream); stream.close(); LOGF("Saved to %s", aFilename); } else { LOG("Fail to open profile log file."); } } const char** mozilla_sampler_get_features() { return nullptr; // 10.4Fx static const char* features[] = { #if defined(MOZ_PROFILING) && defined(HAVE_NATIVE_UNWIND) // Walk the C++ stack. "stackwalk", #endif #if defined(ENABLE_SPS_LEAF_DATA) // Include the C++ leaf node if not stackwalking. DevTools // profiler doesn't want the native addresses. "leaf", #endif #if !defined(SPS_OS_windows) // Use a seperate thread of walking the stack. "unwinder", #endif "java", // Only record samples during periods of bad responsiveness "jank", // Tell the JS engine to emmit pseudostack entries in the // pro/epilogue. "js", // GPU Profiling (may not be supported by the GL) "gpu", // Profile the registered secondary threads. "threads", // Do not include user-identifiable information "privacy", // Dump the layer tree with the textures. "layersdump", // Dump the display list with the textures. "displaylistdump", // Add main thread I/O to the profile "mainthreadio", // Add RSS collection "memory", #ifdef MOZ_TASK_TRACER // Start profiling with feature TaskTracer. "tasktracer", #endif #if defined(XP_WIN) // Add power collection "power", #endif nullptr }; return features; } void mozilla_sampler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration) { *aCurrentPosition = 0; *aTotalSize = 0; *aGeneration = 0; if (!stack_key_initialized) return; GeckoSampler *t = tlsTicker.get(); if (!t) return; t->GetBufferInfo(aCurrentPosition, aTotalSize, aGeneration); } // Values are only honored on the first start void mozilla_sampler_start(int aProfileEntries, double aInterval, const char** aFeatures, uint32_t aFeatureCount, const char** aThreadNameFilters, uint32_t aFilterCount) { return; // 10.4Fx LOG("BEGIN mozilla_sampler_start"); if (!stack_key_initialized) profiler_init(nullptr); /* If the sampling interval was set using env vars, use that in preference to anything else. */ if (sUnwindInterval > 0) aInterval = sUnwindInterval; /* If the entry count was set using env vars, use that, too: */ if (sProfileEntries > 0) aProfileEntries = sProfileEntries; // Reset the current state if the profiler is running profiler_stop(); GeckoSampler* t; t = new GeckoSampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL, aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount); tlsTicker.set(t); t->Start(); if (t->ProfileJS() || t->InPrivacyMode()) { ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); std::vector threads = t->GetRegisteredThreads(); for (uint32_t i = 0; i < threads.size(); i++) { ThreadInfo* info = threads[i]; if (info->IsPendingDelete()) { continue; } ThreadProfile* thread_profile = info->Profile(); if (!thread_profile) { continue; } thread_profile->GetPseudoStack()->reinitializeOnResume(); #ifndef SPS_STANDALONE if (t->ProfileJS()) { thread_profile->GetPseudoStack()->enableJSSampling(); } if (t->InPrivacyMode()) { thread_profile->GetPseudoStack()->mPrivacyMode = true; } #endif } } #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) if (t->ProfileJava()) { int javaInterval = aInterval; // Java sampling doesn't accuratly keep up with 1ms sampling if (javaInterval < 10) { aInterval = 10; } mozilla::widget::GeckoJavaSampler::StartJavaProfiling(javaInterval, 1000); } #endif #ifndef SPS_STANDALONE if (t->AddMainThreadIO()) { if (!sInterposeObserver) { // Lazily create IO interposer observer sInterposeObserver = new mozilla::ProfilerIOInterposeObserver(); } mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll, sInterposeObserver); } #endif sIsProfiling = true; #ifndef SPS_STANDALONE sIsGPUProfiling = t->ProfileGPU(); sIsLayersDump = t->LayersDump(); sIsDisplayListDump = t->DisplayListDump(); sIsRestyleProfiling = t->ProfileRestyle(); if (Sampler::CanNotifyObservers()) { nsCOMPtr os = mozilla::services::GetObserverService(); if (os) { nsTArray featuresArray; nsTArray threadNameFiltersArray; for (size_t i = 0; i < aFeatureCount; ++i) { featuresArray.AppendElement(aFeatures[i]); } for (size_t i = 0; i < aFilterCount; ++i) { threadNameFiltersArray.AppendElement(aThreadNameFilters[i]); } nsCOMPtr params = new nsProfilerStartParams(aProfileEntries, aInterval, featuresArray, threadNameFiltersArray); os->NotifyObservers(params, "profiler-started", nullptr); } } #endif LOG("END mozilla_sampler_start"); } void mozilla_sampler_stop() { return; // 10.4Fx LOG("BEGIN mozilla_sampler_stop"); if (!stack_key_initialized) return; GeckoSampler *t = tlsTicker.get(); if (!t) { LOG("END mozilla_sampler_stop-early"); return; } bool disableJS = t->ProfileJS(); t->Stop(); delete t; tlsTicker.set(nullptr); #ifndef SPS_STANDALONE if (disableJS) { PseudoStack *stack = tlsPseudoStack.get(); ASSERT(stack != nullptr); stack->disableJSSampling(); } mozilla::IOInterposer::Unregister(mozilla::IOInterposeObserver::OpAll, sInterposeObserver); sInterposeObserver = nullptr; #endif sIsProfiling = false; #ifndef SPS_STANDALONE sIsGPUProfiling = false; sIsLayersDump = false; sIsDisplayListDump = false; sIsRestyleProfiling = false; if (Sampler::CanNotifyObservers()) { nsCOMPtr os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-stopped", nullptr); } #endif LOG("END mozilla_sampler_stop"); } bool mozilla_sampler_is_paused() { if (Sampler::GetActiveSampler()) { return Sampler::GetActiveSampler()->IsPaused(); } else { return false; } } void mozilla_sampler_pause() { if (Sampler::GetActiveSampler()) { Sampler::GetActiveSampler()->SetPaused(true); #ifndef SPS_STANDALONE if (Sampler::CanNotifyObservers()) { nsCOMPtr os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-paused", nullptr); } #endif } } void mozilla_sampler_resume() { if (Sampler::GetActiveSampler()) { Sampler::GetActiveSampler()->SetPaused(false); #ifndef SPS_STANDALONE if (Sampler::CanNotifyObservers()) { nsCOMPtr os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-resumed", nullptr); } #endif } } bool mozilla_sampler_feature_active(const char* aName) { if (!profiler_is_active()) { return false; } if (strcmp(aName, "gpu") == 0) { return sIsGPUProfiling; } if (strcmp(aName, "layersdump") == 0) { return sIsLayersDump; } if (strcmp(aName, "displaylistdump") == 0) { return sIsDisplayListDump; } if (strcmp(aName, "restyle") == 0) { return sIsRestyleProfiling; } return false; } bool mozilla_sampler_is_active() { // We never run the sampler. return false; return sIsProfiling; } void mozilla_sampler_responsiveness(const mozilla::TimeStamp& aTime) { sLastTracerEvent = aTime; } void mozilla_sampler_frame_number(int frameNumber) { sFrameNumber = frameNumber; } void mozilla_sampler_lock() { return; profiler_stop(); #ifndef SPS_STANDALONE nsCOMPtr os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-locked", nullptr); #endif } void mozilla_sampler_unlock() { return; #ifndef SPS_STANDALONE nsCOMPtr os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-unlocked", nullptr); #endif } bool mozilla_sampler_register_thread(const char* aName, void* aGuessStackTop) { return false; if (sInitCount == 0) { return false; } #if defined(MOZ_WIDGET_GONK) && !defined(MOZ_PROFILING) // The only way to profile secondary threads on b2g // is to build with profiling OR have the profiler // running on startup. if (!profiler_is_active()) { return false; } #endif MOZ_ASSERT(tlsPseudoStack.get() == nullptr); PseudoStack* stack = PseudoStack::create(); tlsPseudoStack.set(stack); bool isMainThread = is_main_thread_name(aName); void* stackTop = GetStackTop(aGuessStackTop); return Sampler::RegisterCurrentThread(aName, stack, isMainThread, stackTop); } void mozilla_sampler_unregister_thread() { return; // 10.4Fx // Don't check sInitCount count here -- we may be unregistering the // thread after the sampler was shut down. if (!stack_key_initialized) { return; } PseudoStack *stack = tlsPseudoStack.get(); if (!stack) { return; } stack->deref(); tlsPseudoStack.set(nullptr); Sampler::UnregisterCurrentThread(); } void mozilla_sampler_sleep_start() { if (sInitCount == 0) { return; } PseudoStack *stack = tlsPseudoStack.get(); if (stack == nullptr) { return; } stack->setSleeping(1); } void mozilla_sampler_sleep_end() { if (sInitCount == 0) { return; } PseudoStack *stack = tlsPseudoStack.get(); if (stack == nullptr) { return; } stack->setSleeping(0); } double mozilla_sampler_time(const mozilla::TimeStamp& aTime) { mozilla::TimeDuration delta = aTime - sStartTime; return delta.ToMilliseconds(); } double mozilla_sampler_time() { return mozilla_sampler_time(mozilla::TimeStamp::Now()); } ProfilerBacktrace* mozilla_sampler_get_backtrace() { if (!stack_key_initialized) return nullptr; // Don't capture a stack if we're not profiling if (!profiler_is_active()) { return nullptr; } // Don't capture a stack if we don't want to include personal information if (profiler_in_privacy_mode()) { return nullptr; } GeckoSampler* t = tlsTicker.get(); if (!t) { return nullptr; } return new ProfilerBacktrace(t->GetBacktrace()); } void mozilla_sampler_free_backtrace(ProfilerBacktrace* aBacktrace) { delete aBacktrace; } // Fill the output buffer with the following pattern: // "Lable 1" "\0" "Label 2" "\0" ... "Label N" "\0" "\0" // TODO: use the unwinder instead of pseudo stack. void mozilla_sampler_get_backtrace_noalloc(char *output, size_t outputSize) { MOZ_ASSERT(outputSize >= 2); char *bound = output + outputSize - 2; output[0] = output[1] = '\0'; PseudoStack *pseudoStack = tlsPseudoStack.get(); if (!pseudoStack) { return; } volatile StackEntry *pseudoFrames = pseudoStack->mStack; uint32_t pseudoCount = pseudoStack->stackSize(); for (uint32_t i = 0; i < pseudoCount; i++) { size_t len = strlen(pseudoFrames[i].label()); if (output + len >= bound) break; strcpy(output, pseudoFrames[i].label()); output += len; *output++ = '\0'; *output = '\0'; } } void mozilla_sampler_tracing(const char* aCategory, const char* aInfo, TracingMetadata aMetaData) { mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData)); } void mozilla_sampler_tracing(const char* aCategory, const char* aInfo, ProfilerBacktrace* aCause, TracingMetadata aMetaData) { mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData, aCause)); } void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload) { // Note that aPayload may be allocated by the caller, so we need to make sure // that we free it at some point. mozilla::UniquePtr payload(aPayload); if (!stack_key_initialized) return; // Don't insert a marker if we're not profiling to avoid // the heap copy (malloc). if (!profiler_is_active()) { return; } // Don't add a marker if we don't want to include personal information if (profiler_in_privacy_mode()) { return; } PseudoStack *stack = tlsPseudoStack.get(); if (!stack) { return; } mozilla::TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull()) ? aPayload->GetStartTime() : mozilla::TimeStamp::Now(); mozilla::TimeDuration delta = origin - sStartTime; stack->addMarker(aMarker, payload.release(), delta.ToMilliseconds()); } #ifndef SPS_STANDALONE #include "mozilla/Mutex.h" class GeckoMutex : public ::Mutex { public: explicit GeckoMutex(const char* aDesc) : mMutex(aDesc) {} virtual ~GeckoMutex() {} virtual int Lock() { mMutex.Lock(); return 0; } virtual int Unlock() { mMutex.Unlock(); return 0; } private: mozilla::Mutex mMutex; }; mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) { return mozilla::MakeUnique(aDesc); } #else // Otherwise use c++11 Mutex #include class OSXMutex : public ::Mutex { public: OSXMutex(const char* aDesc) : mMutex() {} virtual ~OSXMutex() {} virtual int Lock() { mMutex.lock(); return 0; } virtual int Unlock() { mMutex.unlock(); return 0; } private: std::mutex mMutex; }; mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) { return mozilla::MakeUnique(aDesc); } #endif // END externally visible functions ////////////////////////////////////////////////////////////////////////