diff options
Diffstat (limited to 'toolkit/components/terminator/nsTerminator.cpp')
-rw-r--r-- | toolkit/components/terminator/nsTerminator.cpp | 236 |
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 |