--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -1836,27 +1836,29 @@ profiler_shutdown()
LOG("BEGIN profiler_shutdown");
MOZ_RELEASE_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(gPS);
// If the profiler is active we must get a handle to the SamplerThread before
// gPS is destroyed, in order to call Join().
SamplerThread* samplerThread = nullptr;
+ bool wasActive = false;
{
PS::AutoLock lock(gPSMutex);
// Save the profile on shutdown if requested.
if (gPS->IsActive(lock)) {
const char* filename = getenv("MOZ_PROFILER_SHUTDOWN");
if (filename) {
locked_profiler_save_profile_to_file(lock, filename);
}
samplerThread = locked_profiler_stop(lock);
+ wasActive = true;
}
set_stderr_callback(nullptr);
PS::ThreadVector& threads = gPS->Threads(lock);
while (threads.size() > 0) {
delete threads.back();
threads.pop_back();
@@ -1881,16 +1883,25 @@ profiler_shutdown()
delete tlsPseudoStack.get();
tlsPseudoStack.set(nullptr);
#ifdef MOZ_TASK_TRACER
mozilla::tasktracer::ShutdownTaskTracer();
#endif
}
+ // We call NotifyObservers() with gPSMutex unlocked. The comment in
+ // profiler_stop() explains why.
+ if (wasActive && CanNotifyObservers()) {
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
+ }
+ }
+
// We call Join() with gPSMutex unlocked. The comment in profiler_stop()
// explains why.
if (samplerThread) {
samplerThread->Join();
delete samplerThread;
}
LOG("END profiler_shutdown");
@@ -2294,83 +2305,91 @@ locked_profiler_start(PS::LockRef aLock,
if (featureMainThreadIO) {
auto interposeObserver = new mozilla::ProfilerIOInterposeObserver();
gPS->SetInterposeObserver(aLock, interposeObserver);
mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll,
interposeObserver);
}
+ LOG("END locked_profiler_start");
+}
+
+void
+profiler_start(int aEntries, double aInterval,
+ const char** aFeatures, uint32_t aFeatureCount,
+ const char** aThreadNameFilters, uint32_t aFilterCount)
+{
+ LOG("BEGIN profiler_start");
+
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ bool wasActive = false;
+
+ {
+ PS::AutoLock lock(gPSMutex);
+
+ // Initialize if necessary.
+ if (!gPS) {
+ profiler_init(nullptr);
+ }
+
+ // Reset the current state if the profiler is running.
+ if (gPS->IsActive(lock)) {
+ locked_profiler_stop(lock);
+ wasActive = true;
+ }
+
+ locked_profiler_start(lock, aEntries, aInterval, aFeatures, aFeatureCount,
+ aThreadNameFilters, aFilterCount);
+ }
+
+ // Notify observers that the profiler has been started (and possibly stopped
+ // before).
+ // This needs to be done outside of the PS lock, because observers can call
+ // back into the profiler, for example in order to capture stacks or to add
+ // markers, and doing so with the PS lock held would lead to a deadlock.
if (CanNotifyObservers()) {
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
+ if (wasActive) {
+ os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
+ }
+
nsTArray<nsCString> featuresArray;
for (size_t i = 0; i < aFeatureCount; ++i) {
featuresArray.AppendElement(aFeatures[i]);
}
nsTArray<nsCString> threadNameFiltersArray;
for (size_t i = 0; i < aFilterCount; ++i) {
threadNameFiltersArray.AppendElement(aThreadNameFilters[i]);
}
nsCOMPtr<nsIProfilerStartParams> params =
- new nsProfilerStartParams(entries, interval, featuresArray,
+ new nsProfilerStartParams(aEntries, aInterval, featuresArray,
threadNameFiltersArray);
os->NotifyObservers(params, "profiler-started", nullptr);
}
}
- LOG("END locked_profiler_start");
-}
-
-void
-profiler_start(int aEntries, double aInterval,
- const char** aFeatures, uint32_t aFeatureCount,
- const char** aThreadNameFilters, uint32_t aFilterCount)
-{
- LOG("BEGIN profiler_start");
-
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
-
- PS::AutoLock lock(gPSMutex);
-
- // Initialize if necessary.
- if (!gPS) {
- profiler_init(nullptr);
- }
-
- // Reset the current state if the profiler is running.
- if (gPS->IsActive(lock)) {
- locked_profiler_stop(lock);
- }
-
- locked_profiler_start(lock, aEntries, aInterval, aFeatures, aFeatureCount,
- aThreadNameFilters, aFilterCount);
-
LOG("END profiler_start");
}
static SamplerThread*
locked_profiler_stop(PS::LockRef aLock)
{
LOG("BEGIN locked_profiler_stop");
MOZ_RELEASE_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(gPS && gPS->IsActive(aLock));
// We clear things in roughly reverse order to their setting in
// locked_profiler_start().
- if (CanNotifyObservers()) {
- nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
- if (os)
- os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
- }
-
mozilla::IOInterposer::Unregister(mozilla::IOInterposeObserver::OpAll,
gPS->InterposeObserver(aLock));
gPS->SetInterposeObserver(aLock, nullptr);
// The Stop() call doesn't actually stop Run(); that happens in this
// function's caller, via Join(). Stop() just gives the SamplerThread a chance
// to do some cleanup with gPSMutex locked.
SamplerThread* samplerThread = gPS->SamplerThread(aLock);
@@ -2454,16 +2473,27 @@ profiler_stop()
if (!gPS->IsActive(lock)) {
LOG("END profiler_stop: inactive");
return;
}
samplerThread = locked_profiler_stop(lock);
}
+ // Notify observers that the profiler has been stopped.
+ // This needs to be done outside of the PS lock, because observers can call
+ // back into the profiler, for example to capture stacks or to add markers,
+ // and doing so with the PS lock held would lead to a deadlock.
+ if (CanNotifyObservers()) {
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
+ }
+ }
+
// We call Join() with gPSMutex unlocked. Otherwise we would get a deadlock:
// we would be waiting here with gPSMutex locked for SamplerThread::Run() to
// return so the Join() can complete, but Run() needs to lock gPSMutex to
// return.
//
// Because this call occurs with gPSMutex unlocked, it -- including the final
// iteration of Run()'s loop -- must be able detect deactivation and return
// in a way that's safe with respect to other gPSMutex-locking operations
@@ -2489,47 +2519,52 @@ profiler_is_paused()
return gPS->IsPaused(lock);
}
void
profiler_pause()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(gPS);
-
- PS::AutoLock lock(gPSMutex);
-
- if (!gPS->IsActive(lock)) {
- return;
+ {
+ PS::AutoLock lock(gPSMutex);
+
+ if (!gPS->IsActive(lock)) {
+ return;
+ }
+
+ gPS->SetIsPaused(lock, true);
}
- gPS->SetIsPaused(lock, true);
-
+ // Notify observers with gPSMutex unlocked.
if (CanNotifyObservers()) {
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->NotifyObservers(nullptr, "profiler-paused", nullptr);
}
}
}
void
profiler_resume()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(gPS);
- PS::AutoLock lock(gPSMutex);
-
- if (!gPS->IsActive(lock)) {
- return;
+ {
+ PS::AutoLock lock(gPSMutex);
+
+ if (!gPS->IsActive(lock)) {
+ return;
+ }
+
+ gPS->SetIsPaused(lock, false);
}
- gPS->SetIsPaused(lock, false);
-
+ // Notify observers with gPSMutex unlocked.
if (CanNotifyObservers()) {
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->NotifyObservers(nullptr, "profiler-resumed", nullptr);
}
}
}