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:
Nicholas Nethercote 2017-05-30 13:36:40 +10:00
Родитель e6fb672256
Коммит 6534e30922
2 изменённых файлов: 77 добавлений и 27 удалений

Просмотреть файл

@ -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