summaryrefslogtreecommitdiffstats
path: root/toolkit/components/terminator/nsTerminator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/terminator/nsTerminator.cpp')
-rw-r--r--toolkit/components/terminator/nsTerminator.cpp236
1 files changed, 17 insertions, 219 deletions
diff --git a/toolkit/components/terminator/nsTerminator.cpp b/toolkit/components/terminator/nsTerminator.cpp
index 5a1f9693b8..6339962022 100644
--- a/toolkit/components/terminator/nsTerminator.cpp
+++ b/toolkit/components/terminator/nsTerminator.cpp
@@ -245,131 +245,9 @@ void RunWatchdog(void* arg) {
}
}
-////////////////////////////////////////////
-//
-// Writer thread
-//
-// This nspr thread is in charge of writing to disk statistics produced by the
-// watchdog thread and collected by the main thread. Note that we use a nspr
-// thread rather than usual XPCOM I/O simply because we outlive XPCOM and its
-// threads.
-//
-
-//
-// Communication between the main thread and the writer thread.
-//
-// Main thread:
-//
-// * Whenever a shutdown step has been completed, the main thread
-// obtains the number of ticks from the watchdog threads, builds
-// a string representing all the data gathered so far, places
-// this string in `gWriteData`, and wakes up the writer thread
-// using `gWriteReady`. If `gWriteData` already contained a non-null
-// pointer, this means that the writer thread is lagging behind the
-// main thread, and the main thread cleans up the memory.
-//
-// Writer thread:
-//
-// * When awake, the writer thread swaps `gWriteData` to nullptr. If
-// `gWriteData` contained data to write, the . If so, the writer
-// thread writes the data to a file named "ShutdownDuration.json.tmp",
-// then moves that file to "ShutdownDuration.json" and cleans up the
-// data. If `gWriteData` contains a nullptr, the writer goes to sleep
-// until it is awkened using `gWriteReady`.
-//
-//
-// The data written by the writer thread will be read by another
-// module upon the next restart and fed to Telemetry.
-//
-Atomic<nsCString*> gWriteData(nullptr);
-PRMonitor* gWriteReady = nullptr;
-
-void RunWriter(void* arg) {
- AUTO_PROFILER_REGISTER_THREAD("Shutdown Statistics Writer");
- NS_SetCurrentThreadName("Shutdown Statistics Writer");
-
- MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(arg);
- // Shutdown will generally complete before we have a chance to
- // deallocate. This is not a leak.
-
- // Setup destinationPath and tmpFilePath
-
- nsCString destinationPath;
- destinationPath.Adopt(static_cast<char*>(arg));
- nsAutoCString tmpFilePath;
- tmpFilePath.Append(destinationPath);
- tmpFilePath.AppendLiteral(".tmp");
-
- // Cleanup any file leftover from a previous run
- Unused << PR_Delete(tmpFilePath.get());
- Unused << PR_Delete(destinationPath.get());
-
- while (true) {
- //
- // Check whether we have received data from the main thread.
- //
- // We perform the check before waiting on `gWriteReady` as we may
- // have received data while we were busy writing.
- //
- // Also note that gWriteData may have been modified several times
- // since we last checked. That's ok, we are not losing any important
- // data (since we keep adding data), and we are not leaking memory
- // (since the main thread deallocates any data that hasn't been
- // consumed by the writer thread).
- //
- UniquePtr<nsCString> data(gWriteData.exchange(nullptr));
- if (!data) {
- // Data is not available yet.
- // Wait until the main thread provides it.
- PR_EnterMonitor(gWriteReady);
- PR_Wait(gWriteReady, PR_INTERVAL_NO_TIMEOUT);
- PR_ExitMonitor(gWriteReady);
- continue;
- }
-
- MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(data.get());
- // Shutdown may complete before we have a chance to deallocate.
- // This is not a leak.
-
- //
- // Write to a temporary file
- //
- // In case of any error, we simply give up. Since the data is
- // hardly critical, we don't want to spend too much effort
- // salvaging it.
- //
- UniquePtr<PRFileDesc, PR_CloseDelete> tmpFileDesc(PR_Open(
- tmpFilePath.get(), PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE, 00600));
-
- // Shutdown may complete before we have a chance to close the file.
- // This is not a leak.
- MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(tmpFileDesc.get());
-
- if (tmpFileDesc == nullptr) {
- break;
- }
- if (PR_Write(tmpFileDesc.get(), data->get(), data->Length()) == -1) {
- break;
- }
- tmpFileDesc.reset();
-
- //
- // Rename on top of destination file.
- //
- // This is not sufficient to guarantee that the destination file
- // will be written correctly, but, again, we don't care enough
- // about the data to make more efforts.
- //
- Unused << PR_Delete(destinationPath.get());
- if (PR_Rename(tmpFilePath.get(), destinationPath.get()) != PR_SUCCESS) {
- break;
- }
- }
-}
-
} // namespace
-NS_IMPL_ISUPPORTS(nsTerminator, nsIObserver)
+NS_IMPL_ISUPPORTS(nsTerminator, nsIObserver, nsITerminatorTest)
nsTerminator::nsTerminator() : mInitialized(false), mCurrentStep(-1) {}
@@ -379,12 +257,6 @@ void nsTerminator::Start() {
MOZ_ASSERT(!mInitialized);
StartWatchdog();
-#if !defined(NS_FREE_PERMANENT_DATA)
- // Only allow nsTerminator to write on non-leak-checked builds so we don't
- // get leak warnings on shutdown for intentional leaks (see bug 1242084).
- // This will be enabled again by bug 1255484 when 1255478 lands.
- StartWriter();
-#endif // !defined(NS_FREE_PERMANENT_DATA)
mInitialized = true;
}
@@ -470,41 +342,6 @@ void nsTerminator::StartWatchdog() {
MOZ_ASSERT(watchdogThread);
}
-// Prepare, allocate and start the writer thread. By design, it will never
-// finish, nor be deallocated. In case of error, we degrade
-// gracefully to not writing Telemetry data.
-void nsTerminator::StartWriter() {
- if (!Telemetry::CanRecordExtended()) {
- return;
- }
- nsCOMPtr<nsIFile> profLD;
- nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
- getter_AddRefs(profLD));
- if (NS_FAILED(rv)) {
- return;
- }
-
- rv = profLD->Append(u"ShutdownDuration.json"_ns);
- if (NS_FAILED(rv)) {
- return;
- }
-
- nsAutoString path;
- rv = profLD->GetPath(path);
- if (NS_FAILED(rv)) {
- return;
- }
-
- gWriteReady = PR_NewMonitor();
- MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(
- gWriteReady); // We will never deallocate this object
- PRThread* writerThread = CreateSystemThread(RunWriter, ToNewUTF8String(path));
-
- if (!writerThread) {
- return;
- }
-}
-
// This helper is here to preserve the existing crash reporting behavior
// based on observer topic names, using the shutdown phase name only for
// phases without associated topic.
@@ -531,12 +368,6 @@ void nsTerminator::AdvancePhase(mozilla::ShutdownPhase aPhase) {
}
UpdateHeartbeat(step);
-#if !defined(NS_FREE_PERMANENT_DATA)
- // Only allow nsTerminator to write on non-leak checked builds so we don't get
- // leak warnings on shutdown for intentional leaks (see bug 1242084). This
- // will be enabled again by bug 1255484 when 1255478 lands.
- UpdateTelemetry();
-#endif // !defined(NS_FREE_PERMANENT_DATA)
UpdateCrashReport(GetReadableNameForPhase(aPhase));
}
@@ -555,58 +386,25 @@ void nsTerminator::UpdateHeartbeat(int32_t aStep) {
}
}
-void nsTerminator::UpdateTelemetry() {
- if (!Telemetry::CanRecordExtended() || !gWriteReady) {
- return;
- }
-
- //
- // We need Telemetry data on the effective duration of each step,
- // to be able to tune the time-to-crash of each of both the
- // Terminator and AsyncShutdown. However, at this stage, it is too
- // late to record such data into Telemetry, so we write it to disk
- // and read it upon the next startup.
- //
-
- // Build JSON.
- UniquePtr<nsCString> telemetryData(new nsCString());
- telemetryData->AppendLiteral("{");
- size_t fields = 0;
- for (auto& shutdownStep : sShutdownSteps) {
- if (shutdownStep.mTicks < 0) {
- // Ignore this field.
- continue;
- }
- if (fields++ > 0) {
- telemetryData->AppendLiteral(", ");
- }
- telemetryData->AppendLiteral(R"(")");
- telemetryData->Append(GetReadableNameForPhase(shutdownStep.mPhase));
- telemetryData->AppendLiteral(R"(": )");
- telemetryData->AppendInt(shutdownStep.mTicks);
- }
- telemetryData->AppendLiteral("}");
-
- if (fields == 0) {
- // Nothing to write
- return;
- }
-
- //
- // Send data to the worker thread.
- //
- delete gWriteData.exchange(
- telemetryData.release()); // Clear any data that hasn't been written yet
-
- // In case the worker thread was sleeping, wake it up.
- PR_EnterMonitor(gWriteReady);
- PR_Notify(gWriteReady);
- PR_ExitMonitor(gWriteReady);
-}
-
void nsTerminator::UpdateCrashReport(const char* aTopic) {
// In case of crash, we wish to know where in shutdown we are
CrashReporter::RecordAnnotationCString(
CrashReporter::Annotation::ShutdownProgress, aTopic);
}
+
+NS_IMETHODIMP
+nsTerminator::GetTicksForShutdownPhases(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aRetval) {
+ JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+ aRetval.setObject(*obj);
+
+ for (auto& shutdownStep : sShutdownSteps) {
+ if (shutdownStep.mTicks >= 0) {
+ JS_DefineProperty(aCx, obj, GetReadableNameForPhase(shutdownStep.mPhase),
+ shutdownStep.mTicks, JSPROP_ENUMERATE);
+ }
+ }
+
+ return NS_OK;
+} // namespace mozilla
} // namespace mozilla