зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1347274 (part 3) - Make some hot profiler functions lockless. r=mstange.
The functions are profiler_is_active(), profiler_feature_active(), profiler_tracing(), profiler_log(), profiler_add_marker(). This is achieved by adding RacyFeatures, which duplicates the features stored in ActivePS in a way that can be accessed locklessly. There's a chance of imprecision, but that doesn't matter for the way these functions are used; the worst that can happen is we might very occasionally miss adding a marker, or add one that we shouldn't. --HG-- extra : rebase_source : 278ebad8d643670e3f2e92c8eeec13b19f94b8ce
This commit is contained in:
Родитель
e6fb672256
Коммит
6534e30922
|
@ -479,6 +479,52 @@ uint32_t ActivePS::sNextGeneration = 0;
|
|||
// The mutex that guards accesses to CorePS and ActivePS.
|
||||
static PSMutex gPSMutex;
|
||||
|
||||
// The preferred way to check profiler activeness and features is via
|
||||
// ActivePS(). However, that requires locking gPSMutex. There are some hot
|
||||
// operations where absolute precision isn't required, so we duplicate the
|
||||
// activeness/feature state in a lock-free manner in this class.
|
||||
class RacyFeatures
|
||||
{
|
||||
public:
|
||||
static void SetActive(uint32_t aFeatures)
|
||||
{
|
||||
sActiveAndFeatures = Active | aFeatures;
|
||||
}
|
||||
|
||||
static void SetInactive() { sActiveAndFeatures = 0; }
|
||||
|
||||
static bool IsActive() { return uint32_t(sActiveAndFeatures) & Active; }
|
||||
|
||||
static bool IsActiveWithFeature(uint32_t aFeature)
|
||||
{
|
||||
uint32_t af = sActiveAndFeatures; // copy it first
|
||||
return (af & Active) && (af & aFeature);
|
||||
}
|
||||
|
||||
static bool IsActiveWithoutPrivacy()
|
||||
{
|
||||
uint32_t af = sActiveAndFeatures; // copy it first
|
||||
return (af & Active) && !(af & ProfilerFeature::Privacy);
|
||||
}
|
||||
|
||||
private:
|
||||
static const uint32_t Active = 1u << 31;
|
||||
|
||||
// Ensure Active doesn't overlap with any of the feature bits.
|
||||
#define NO_OVERLAP(n_, str_, Name_) \
|
||||
static_assert(ProfilerFeature::Name_ != Active, "bad Active value");
|
||||
|
||||
PROFILER_FOR_EACH_FEATURE(NO_OVERLAP);
|
||||
|
||||
#undef NO_OVERLAP
|
||||
|
||||
// We combine the active bit with the feature bits so they can be read or
|
||||
// written in a single atomic operation.
|
||||
static Atomic<uint32_t> sActiveAndFeatures;
|
||||
};
|
||||
|
||||
Atomic<uint32_t> RacyFeatures::sActiveAndFeatures(0);
|
||||
|
||||
// Each live thread has a ThreadInfo, and we store a reference to it in TLS.
|
||||
// This class encapsulates that TLS.
|
||||
class TLSInfo
|
||||
|
@ -2411,6 +2457,9 @@ locked_profiler_start(PSLockRef aLock, int aEntries, double aInterval,
|
|||
mozilla::java::GeckoJavaSampler::Start(javaInterval, 1000);
|
||||
}
|
||||
#endif
|
||||
|
||||
// At the very end, set up RacyFeatures.
|
||||
RacyFeatures::SetActive(ActivePS::Features(aLock));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2457,6 +2506,9 @@ locked_profiler_stop(PSLockRef aLock)
|
|||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock));
|
||||
|
||||
// At the very start, clear RacyFeatures.
|
||||
RacyFeatures::SetInactive();
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
if (ActivePS::FeatureTaskTracer(aLock)) {
|
||||
mozilla::tasktracer::StopLogging();
|
||||
|
@ -2599,13 +2651,8 @@ profiler_feature_active(uint32_t aFeature)
|
|||
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
if (!ActivePS::Exists(lock)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!(ActivePS::Features(lock) & aFeature);
|
||||
// This function is hot enough that we use RacyFeatures, not ActivePS.
|
||||
return RacyFeatures::IsActiveWithFeature(aFeature);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2615,9 +2662,8 @@ profiler_is_active()
|
|||
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
return ActivePS::Exists(lock);
|
||||
// This function is hot enough that we use RacyFeatures, notActivePS.
|
||||
return RacyFeatures::IsActive();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2857,14 +2903,19 @@ profiler_get_backtrace_noalloc(char *output, size_t outputSize)
|
|||
}
|
||||
|
||||
static void
|
||||
locked_profiler_add_marker(PSLockRef aLock, const char* aMarkerName,
|
||||
ProfilerMarkerPayload* aPayload)
|
||||
racy_profiler_add_marker(const char* aMarkerName,
|
||||
ProfilerMarkerPayload* aPayload)
|
||||
{
|
||||
// This function runs both on and off the main thread.
|
||||
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
MOZ_RELEASE_ASSERT(ActivePS::Exists(aLock) &&
|
||||
!ActivePS::FeaturePrivacy(aLock));
|
||||
|
||||
// We don't assert that RacyFeatures::IsActiveWithoutPrivacy() is true here,
|
||||
// because it's possible that the result has changed since we tested it in
|
||||
// the caller.
|
||||
//
|
||||
// Because of this imprecision it's possible to miss a marker or record one
|
||||
// we shouldn't. Either way is not a big deal.
|
||||
|
||||
// aPayload must be freed if we return early.
|
||||
mozilla::UniquePtr<ProfilerMarkerPayload> payload(aPayload);
|
||||
|
@ -2889,16 +2940,15 @@ profiler_add_marker(const char* aMarkerName, ProfilerMarkerPayload* aPayload)
|
|||
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
// aPayload must be freed if we return early.
|
||||
mozilla::UniquePtr<ProfilerMarkerPayload> payload(aPayload);
|
||||
|
||||
if (!ActivePS::Exists(lock) || ActivePS::FeaturePrivacy(lock)) {
|
||||
// This function is hot enough that we use RacyFeatures, notActivePS.
|
||||
if (RacyFeatures::IsActiveWithoutPrivacy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
locked_profiler_add_marker(lock, aMarkerName, payload.release());
|
||||
racy_profiler_add_marker(aMarkerName, payload.release());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2909,14 +2959,13 @@ profiler_tracing(const char* aCategory, const char* aMarkerName,
|
|||
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
if (!ActivePS::Exists(lock) || ActivePS::FeaturePrivacy(lock)) {
|
||||
// This function is hot enough that we use RacyFeatures, notActivePS.
|
||||
if (RacyFeatures::IsActiveWithoutPrivacy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto payload = new ProfilerMarkerTracing(aCategory, aKind);
|
||||
locked_profiler_add_marker(lock, aMarkerName, payload);
|
||||
racy_profiler_add_marker(aMarkerName, payload);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2927,15 +2976,14 @@ profiler_tracing(const char* aCategory, const char* aMarkerName,
|
|||
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
if (!ActivePS::Exists(lock) || ActivePS::FeaturePrivacy(lock)) {
|
||||
// This function is hot enough that we use RacyFeatures, notActivePS.
|
||||
if (RacyFeatures::IsActiveWithoutPrivacy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto payload =
|
||||
new ProfilerMarkerTracing(aCategory, aKind, mozilla::Move(aCause));
|
||||
locked_profiler_add_marker(lock, aMarkerName, payload);
|
||||
racy_profiler_add_marker(aMarkerName, payload);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -290,7 +290,9 @@ ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {}
|
|||
PROFILER_FUNC(bool profiler_is_active(), false)
|
||||
|
||||
// Check if a profiler feature (specified via the ProfilerFeature type) is
|
||||
// active. Returns false if the profiler is inactive.
|
||||
// active. Returns false if the profiler is inactive. Note: the return value
|
||||
// can become immediately out-of-date, much like the return value of
|
||||
// profiler_is_active().
|
||||
PROFILER_FUNC(bool profiler_feature_active(uint32_t aFeature), false)
|
||||
|
||||
// Get the profile encoded as a JSON string. A no-op (returning nullptr) if the
|
||||
|
|
Загрузка…
Ссылка в новой задаче