michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsStatusReporterManager.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsArrayEnumerator.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: #include "nsIFile.h" michael@0: #include "nsDumpUtils.h" michael@0: #include "nsIFileStreams.h" michael@0: #include "nsPrintfCString.h" michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #define getpid _getpid michael@0: #else michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef XP_UNIX michael@0: #define DO_STATUS_REPORT 1 michael@0: #endif michael@0: michael@0: #ifdef DO_STATUS_REPORT // { michael@0: namespace { michael@0: michael@0: class DumpStatusInfoToTempDirRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: DumpStatusInfoToTempDirRunnable() michael@0: {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: nsCOMPtr mgr = michael@0: do_GetService("@mozilla.org/status-reporter-manager;1"); michael@0: mgr->DumpReports(); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: void doStatusReport(const nsCString& inputStr) michael@0: { michael@0: LOG("FifoWatcher(%s) dispatching status report runnable.", inputStr.get()); michael@0: nsRefPtr runnable = michael@0: new DumpStatusInfoToTempDirRunnable(); michael@0: NS_DispatchToMainThread(runnable); michael@0: } michael@0: michael@0: } //anonymous namespace michael@0: #endif // DO_STATUS_REPORT } michael@0: michael@0: static bool gStatusReportProgress = 0; michael@0: static int gNumReporters = 0; michael@0: michael@0: nsresult getStatus(nsACString& desc) michael@0: { michael@0: if(!gStatusReportProgress) michael@0: desc.AssignLiteral("Init"); michael@0: else { michael@0: desc.AssignLiteral("Running:\nThere are "); michael@0: desc.AppendInt(gNumReporters); michael@0: desc.AppendLiteral(" reporters"); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_STATUS_REPORTER_IMPLEMENT(StatusReporter, "StatusReporter State", getStatus) michael@0: michael@0: #define DUMP(o, s) \ michael@0: do { \ michael@0: const char* s2 = (s); \ michael@0: uint32_t dummy; \ michael@0: nsresult rv = (o)->Write((s2), strlen(s2), &dummy); \ michael@0: if (NS_WARN_IF(NS_FAILED(rv))) \ michael@0: return rv; \ michael@0: } while (0) michael@0: michael@0: static nsresult michael@0: DumpReport(nsIFileOutputStream* aOStream, const nsCString& aProcess, michael@0: const nsCString& aName, const nsCString& aDescription) michael@0: { michael@0: int pid; michael@0: if (aProcess.IsEmpty()) { michael@0: pid = getpid(); michael@0: nsPrintfCString pidStr("PID %u", pid); michael@0: DUMP(aOStream, "\n {\"Process\": \""); michael@0: DUMP(aOStream, pidStr.get()); michael@0: } else { michael@0: pid = 0; michael@0: DUMP(aOStream, "\n {\"Unknown Process\": \""); michael@0: } michael@0: michael@0: DUMP(aOStream, "\", \"Reporter name\": \""); michael@0: DUMP(aOStream, aName.get()); michael@0: michael@0: DUMP(aOStream, "\", \"Status Description\": \""); michael@0: DUMP(aOStream, aDescription.get()); michael@0: DUMP(aOStream, "\"}"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: ** nsStatusReporterManager implementation michael@0: **/ michael@0: michael@0: NS_IMPL_ISUPPORTS(nsStatusReporterManager, nsIStatusReporterManager) michael@0: michael@0: nsStatusReporterManager::nsStatusReporterManager() michael@0: { michael@0: } michael@0: michael@0: nsStatusReporterManager::~nsStatusReporterManager() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStatusReporterManager::Init() michael@0: { michael@0: RegisterReporter(new NS_STATUS_REPORTER_NAME(StatusReporter)); michael@0: gStatusReportProgress = 1; michael@0: michael@0: #ifdef DO_STATUS_REPORT michael@0: if (FifoWatcher::MaybeCreate()) { michael@0: FifoWatcher* fw = FifoWatcher::GetSingleton(); michael@0: fw->RegisterCallback(NS_LITERAL_CSTRING("status report"),doStatusReport); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStatusReporterManager::DumpReports() michael@0: { michael@0: static unsigned number = 1; michael@0: nsresult rv; michael@0: michael@0: nsCString filename("status-reports-"); michael@0: filename.AppendInt(getpid()); michael@0: filename.AppendLiteral("-"); michael@0: filename.AppendInt(number++); michael@0: filename.AppendLiteral(".json"); michael@0: michael@0: // Open a file in NS_OS_TEMP_DIR for writing. michael@0: // The file is initialized as "incomplete-status-reports-pid-number.json" in the michael@0: // begining, it will be rename as "status-reports-pid-number.json" in the end. michael@0: nsCOMPtr tmpFile; michael@0: rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") + michael@0: filename, michael@0: getter_AddRefs(tmpFile), michael@0: NS_LITERAL_CSTRING("status-reports")); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: nsCOMPtr ostream = michael@0: do_CreateInstance("@mozilla.org/network/file-output-stream;1"); michael@0: rv = ostream->Init(tmpFile, -1, -1, 0); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: //Write the reports to the file michael@0: michael@0: DUMP(ostream, " [Sysdump Report Start]: "); michael@0: michael@0: nsCOMPtr e; michael@0: bool more; michael@0: EnumerateReporters(getter_AddRefs(e)); michael@0: while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) { michael@0: nsCOMPtr supports; michael@0: e->GetNext(getter_AddRefs(supports)); michael@0: nsCOMPtr r = do_QueryInterface(supports); michael@0: michael@0: nsCString process; michael@0: rv = r->GetProcess(process); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: nsCString name; michael@0: rv = r->GetName(name); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: nsCString description; michael@0: rv = r->GetDescription(description); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = DumpReport(ostream, process, name, description); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: } michael@0: michael@0: DUMP(ostream, "\n [Sysdump Report End] "); michael@0: michael@0: rv = ostream->Close(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: // Rename the status reports file michael@0: nsCOMPtr srFinalFile; michael@0: rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(srFinalFile)); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: #ifdef ANDROID michael@0: rv = srFinalFile->AppendNative(NS_LITERAL_CSTRING("status-reports")); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: #endif michael@0: michael@0: rv = srFinalFile->AppendNative(filename); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = srFinalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: nsAutoString srActualFinalFilename; michael@0: rv = srFinalFile->GetLeafName(srActualFinalFilename); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: rv = tmpFile->MoveTo(/* directory */ nullptr, srActualFinalFilename); michael@0: michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStatusReporterManager::EnumerateReporters(nsISimpleEnumerator** result) michael@0: { michael@0: return NS_NewArrayEnumerator(result, mReporters); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStatusReporterManager::RegisterReporter(nsIStatusReporter* reporter) michael@0: { michael@0: if (mReporters.IndexOf(reporter) != -1) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: mReporters.AppendObject(reporter); michael@0: gNumReporters++; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStatusReporterManager::UnregisterReporter(nsIStatusReporter* reporter) michael@0: { michael@0: if (!mReporters.RemoveObject(reporter)) michael@0: return NS_ERROR_FAILURE; michael@0: gNumReporters--; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NS_RegisterStatusReporter (nsIStatusReporter* reporter) michael@0: { michael@0: nsCOMPtr mgr = michael@0: do_GetService("@mozilla.org/status-reporter-manager;1"); michael@0: if (mgr == nullptr) michael@0: return NS_ERROR_FAILURE; michael@0: return mgr->RegisterReporter(reporter); michael@0: } michael@0: michael@0: nsresult michael@0: NS_UnregisterStatusReporter (nsIStatusReporter* reporter) michael@0: { michael@0: nsCOMPtr mgr = michael@0: do_GetService("@mozilla.org/status-reporter-manager;1"); michael@0: if (mgr == nullptr) michael@0: return NS_ERROR_FAILURE; michael@0: return mgr->UnregisterReporter(reporter); michael@0: } michael@0: michael@0: nsresult michael@0: NS_DumpStatusReporter () michael@0: { michael@0: nsCOMPtr mgr = michael@0: do_GetService("@mozilla.org/status-reporter-manager;1"); michael@0: if (mgr == nullptr) michael@0: return NS_ERROR_FAILURE; michael@0: return mgr->DumpReports(); michael@0: }