diff options
Diffstat (limited to 'browser/app/winlauncher/ErrorHandler.cpp')
-rw-r--r-- | browser/app/winlauncher/ErrorHandler.cpp | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/browser/app/winlauncher/ErrorHandler.cpp b/browser/app/winlauncher/ErrorHandler.cpp new file mode 100644 index 0000000000..7b90b9c98b --- /dev/null +++ b/browser/app/winlauncher/ErrorHandler.cpp @@ -0,0 +1,781 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "ErrorHandler.h" + +#include <utility> + +#include "mozilla/ArrayUtils.h" +#include "mozilla/CmdLineAndEnvUtils.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/JSONWriter.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "mozilla/WinTokenUtils.h" +#include "mozilla/WindowsVersion.h" +#include "mozilla/XREAppData.h" +#include "mozilla/glue/WindowsDllServices.h" +#include "mozilla/mscom/ProcessRuntime.h" +#include "nsWindowsHelpers.h" + +#if defined(MOZ_LAUNCHER_PROCESS) +# include "mozilla/LauncherRegistryInfo.h" +#endif // defined(MOZ_LAUNCHER_PROCESS) + +#include <algorithm> +#include <process.h> +#include <sstream> +#include <string> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <objbase.h> +#include <rpc.h> +#if !defined(__MINGW32__) +# include <comutil.h> +# include <iwscapi.h> +# include <wscapi.h> +#endif // !defined(__MINGW32__) +#include <tlhelp32.h> + +#if !defined(RRF_SUBKEY_WOW6464KEY) +# define RRF_SUBKEY_WOW6464KEY 0x00010000 +#endif // !defined(RRF_SUBKEY_WOW6464KEY) + +#define QUOTE_ME2(x) #x +#define QUOTE_ME(x) QUOTE_ME2(x) + +#define TELEMETRY_BASE_URL L"https://incoming.telemetry.mozilla.org/submit" +#define TELEMETRY_NAMESPACE L"/firefox-launcher-process" +#define TELEMETRY_LAUNCHER_PING_DOCTYPE L"/launcher-process-failure" +#define TELEMETRY_LAUNCHER_PING_VERSION L"/1" + +static const wchar_t kUrl[] = TELEMETRY_BASE_URL TELEMETRY_NAMESPACE + TELEMETRY_LAUNCHER_PING_DOCTYPE TELEMETRY_LAUNCHER_PING_VERSION L"/"; +static const uint32_t kGuidCharLenWithNul = 39; +static const uint32_t kGuidCharLenNoBracesNoNul = 36; +static const mozilla::StaticXREAppData* gAppData; +static bool gForceEventLog = false; + +namespace { + +struct EventSourceDeleter { + using pointer = HANDLE; + + void operator()(pointer aEvtSrc) { ::DeregisterEventSource(aEvtSrc); } +}; + +using EventLog = mozilla::UniquePtr<HANDLE, EventSourceDeleter>; + +struct SerializedEventData { + HRESULT mHr; + uint32_t mLine; + char mFile[1]; +}; + +} // anonymous namespace + +static void PostErrorToLog(const mozilla::LauncherError& aError) { + // This is very bare-bones; just enough to spit out an HRESULT to the + // Application event log. + EventLog log(::RegisterEventSourceW(nullptr, L"Firefox")); + if (!log) { + return; + } + + size_t fileLen = strlen(aError.mFile); + size_t dataLen = sizeof(HRESULT) + sizeof(uint32_t) + fileLen; + auto evtDataBuf = mozilla::MakeUnique<char[]>(dataLen); + SerializedEventData& evtData = + *reinterpret_cast<SerializedEventData*>(evtDataBuf.get()); + evtData.mHr = aError.mError.AsHResult(); + evtData.mLine = aError.mLine; + // Since this is binary data, we're not concerning ourselves with null + // terminators. + memcpy(evtData.mFile, aError.mFile, fileLen); + + ::ReportEventW(log.get(), EVENTLOG_ERROR_TYPE, 0, aError.mError.AsHResult(), + nullptr, 0, dataLen, nullptr, evtDataBuf.get()); +} + +#if defined(MOZ_TELEMETRY_REPORTING) + +namespace { + +// This JSONWriteFunc writes directly to a temp file. By creating this file +// with the FILE_ATTRIBUTE_TEMPORARY attribute, we hint to the OS that this +// file is short-lived. The OS will try to avoid flushing it to disk if at +// all possible. +class TempFileWriter final : public mozilla::JSONWriteFunc { + public: + TempFileWriter() : mFailed(false), mSuccessfulHandoff(false) { + wchar_t name[MAX_PATH + 1] = {}; + if (_wtmpnam_s(name)) { + mFailed = true; + return; + } + + mTempFileName = name; + + mTempFile.own(::CreateFileW(name, GENERIC_WRITE, FILE_SHARE_READ, nullptr, + CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, nullptr)); + if (mTempFile.get() == INVALID_HANDLE_VALUE) { + mFailed = true; + } + } + + ~TempFileWriter() { + if (mSuccessfulHandoff) { + // It is no longer our responsibility to delete the temp file if we have + // successfully handed it off to pingsender. + return; + } + + mTempFile.reset(); + ::DeleteFileW(mTempFileName.c_str()); + } + + explicit operator bool() const { return !mFailed; } + + void Write(const mozilla::Span<const char>& aStr) override { + if (mFailed) { + return; + } + + DWORD bytesWritten = 0; + if (!::WriteFile(mTempFile, aStr.data(), aStr.size(), &bytesWritten, + nullptr) || + bytesWritten != aStr.size()) { + mFailed = true; + } + } + + const std::wstring& GetFileName() const { return mTempFileName; } + + void SetSuccessfulHandoff() { mSuccessfulHandoff = true; } + + private: + bool mFailed; + bool mSuccessfulHandoff; + std::wstring mTempFileName; + nsAutoHandle mTempFile; +}; + +using SigMap = mozilla::Vector<std::wstring, 0, InfallibleAllocPolicy>; + +} // anonymous namespace + +// This is the guideline for maximum string length for telemetry intake +static const size_t kMaxStrLen = 80; + +static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr, + const size_t aStrLenExclNul) { + // Yes, this might not handle surrogate pairs correctly. Let's just let + // WideCharToMultiByte fail in that unlikely case. + size_t cvtLen = std::min(aStrLenExclNul, kMaxStrLen); + + int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, nullptr, 0, + nullptr, nullptr); + if (!numConv) { + return nullptr; + } + + // Include room for the null terminator by adding one + auto buf = mozilla::MakeUnique<char[]>(numConv + 1); + + numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, buf.get(), numConv, + nullptr, nullptr); + if (!numConv) { + return nullptr; + } + + // Add null termination. numConv does not include the terminator, so we don't + // subtract 1 when indexing into buf. + buf[numConv] = 0; + + return buf; +} + +static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr) { + return WideToUTF8(aStr, wcslen(aStr)); +} + +static mozilla::UniquePtr<char[]> WideToUTF8(const std::wstring& aStr) { + return WideToUTF8(aStr.c_str(), aStr.length()); +} + +// MinGW does not support the Windows Security Center APIs. +# if !defined(__MINGW32__) + +static mozilla::UniquePtr<char[]> WideToUTF8(const _bstr_t& aStr) { + return WideToUTF8(static_cast<const wchar_t*>(aStr), aStr.length()); +} + +namespace { + +struct ProviderKey { + WSC_SECURITY_PROVIDER mProviderType; + const char* mKey; +}; + +} // anonymous namespace + +static bool EnumWSCProductList(RefPtr<IWSCProductList>& aProdList, + mozilla::JSONWriter& aJson) { + LONG count; + HRESULT hr = aProdList->get_Count(&count); + if (FAILED(hr)) { + return false; + } + + // Unlikely, but put a bound on the max length of the output array for the + // purposes of telemetry intake. + count = std::min(count, 1000L); + + // Record the name(s) of each active registered product in this category + for (LONG index = 0; index < count; ++index) { + RefPtr<IWscProduct> product; + hr = aProdList->get_Item(index, getter_AddRefs(product)); + if (FAILED(hr)) { + return false; + } + + WSC_SECURITY_PRODUCT_STATE state; + hr = product->get_ProductState(&state); + if (FAILED(hr)) { + return false; + } + + // We only care about products that are active + if (state == WSC_SECURITY_PRODUCT_STATE_OFF || + state == WSC_SECURITY_PRODUCT_STATE_SNOOZED || + state == WSC_SECURITY_PRODUCT_STATE_EXPIRED) { + continue; + } + + _bstr_t bName; + hr = product->get_ProductName(bName.GetAddress()); + if (FAILED(hr)) { + return false; + } + + auto buf = WideToUTF8(bName); + if (!buf) { + return false; + } + + aJson.StringElement(mozilla::MakeStringSpan(buf.get())); + } + + return true; +} + +static const ProviderKey gProvKeys[] = { + {WSC_SECURITY_PROVIDER_ANTIVIRUS, "av"}, + {WSC_SECURITY_PROVIDER_ANTISPYWARE, "antispyware"}, + {WSC_SECURITY_PROVIDER_FIREWALL, "firewall"}}; + +static bool AddWscInfo(mozilla::JSONWriter& aJson) { + if (!mozilla::IsWin8OrLater()) { + // We haven't written anything yet, so we can return true here and continue + // capturing data. + return true; + } + + // We need COM for this. Using ProcessRuntime so that process-global COM + // configuration is done correctly + mozilla::mscom::ProcessRuntime mscom( + mozilla::mscom::ProcessRuntime::ProcessCategory::Launcher); + if (!mscom) { + // We haven't written anything yet, so we can return true here and continue + // capturing data. + return true; + } + + aJson.StartObjectProperty("security"); + + const CLSID clsid = __uuidof(WSCProductList); + const IID iid = __uuidof(IWSCProductList); + + for (uint32_t index = 0; index < mozilla::ArrayLength(gProvKeys); ++index) { + // NB: A separate instance of IWSCProductList is needed for each distinct + // security provider type; MSDN says that we cannot reuse the same object + // and call Initialize() to pave over the previous data. + RefPtr<IWSCProductList> prodList; + HRESULT hr = ::CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, iid, + getter_AddRefs(prodList)); + if (FAILED(hr)) { + return false; + } + + hr = prodList->Initialize(gProvKeys[index].mProviderType); + if (FAILED(hr)) { + return false; + } + + aJson.StartArrayProperty(mozilla::MakeStringSpan(gProvKeys[index].mKey)); + + if (!EnumWSCProductList(prodList, aJson)) { + return false; + } + + aJson.EndArray(); + } + + aJson.EndObject(); + + return true; +} +# endif // !defined(__MINGW32__) + +// Max array length for telemetry intake. +static const size_t kMaxArrayLen = 1000; + +static bool AddModuleInfo(const nsAutoHandle& aSnapshot, + mozilla::JSONWriter& aJson) { + if (aSnapshot.get() == INVALID_HANDLE_VALUE) { + // We haven't written anything yet, so we can return true here and continue + // capturing data. + return true; + } + + SigMap signatures; + size_t moduleCount = 0; + + MODULEENTRY32W module = {sizeof(module)}; + if (!::Module32FirstW(aSnapshot, &module)) { + // We haven't written anything yet, so we can return true here and continue + // capturing data. + return true; + } + + mozilla::glue::BasicDllServices dllServices; + + aJson.StartObjectProperty("modules"); + + // For each module, add its version number (or empty string if not present), + // followed by an optional index into the signatures array + do { + ++moduleCount; + + wchar_t leaf[_MAX_FNAME] = {}; + if (::_wsplitpath_s(module.szExePath, nullptr, 0, nullptr, 0, leaf, + mozilla::ArrayLength(leaf), nullptr, 0)) { + return false; + } + + if (_wcslwr_s(leaf, mozilla::ArrayLength(leaf))) { + return false; + } + + auto leafUtf8 = WideToUTF8(leaf); + if (!leafUtf8) { + return false; + } + + aJson.StartArrayProperty(mozilla::MakeStringSpan(leafUtf8.get())); + + std::string version; + DWORD verInfoSize = ::GetFileVersionInfoSizeW(module.szExePath, nullptr); + if (verInfoSize) { + auto verInfoBuf = mozilla::MakeUnique<BYTE[]>(verInfoSize); + + if (::GetFileVersionInfoW(module.szExePath, 0, verInfoSize, + verInfoBuf.get())) { + VS_FIXEDFILEINFO* fixedInfo = nullptr; + UINT fixedInfoLen = 0; + + if (::VerQueryValueW(verInfoBuf.get(), L"\\", + reinterpret_cast<LPVOID*>(&fixedInfo), + &fixedInfoLen)) { + std::ostringstream oss; + oss << HIWORD(fixedInfo->dwFileVersionMS) << '.' + << LOWORD(fixedInfo->dwFileVersionMS) << '.' + << HIWORD(fixedInfo->dwFileVersionLS) << '.' + << LOWORD(fixedInfo->dwFileVersionLS); + version = oss.str(); + } + } + } + + aJson.StringElement(version); + + mozilla::Maybe<ptrdiff_t> sigIndex; + auto signedBy = dllServices.GetBinaryOrgName(module.szExePath); + if (signedBy) { + std::wstring strSignedBy(signedBy.get()); + auto entry = std::find(signatures.begin(), signatures.end(), strSignedBy); + if (entry == signatures.end()) { + mozilla::Unused << signatures.append(std::move(strSignedBy)); + entry = &signatures.back(); + } + + sigIndex = mozilla::Some(entry - signatures.begin()); + } + + if (sigIndex) { + aJson.IntElement(sigIndex.value()); + } + + aJson.EndArray(); + } while (moduleCount < kMaxArrayLen && ::Module32NextW(aSnapshot, &module)); + + aJson.EndObject(); + + aJson.StartArrayProperty("signatures"); + + // Serialize each entry in the signatures array + for (auto&& itr : signatures) { + auto sigUtf8 = WideToUTF8(itr); + if (!sigUtf8) { + continue; + } + + aJson.StringElement(mozilla::MakeStringSpan(sigUtf8.get())); + } + + aJson.EndArray(); + + return true; +} + +namespace { + +struct PingThreadContext { + explicit PingThreadContext(const mozilla::LauncherError& aError, + const char* aProcessType) + : mLauncherError(aError), + mModulesSnapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)), + mProcessType(aProcessType ? aProcessType : "") {} + mozilla::LauncherError mLauncherError; + nsAutoHandle mModulesSnapshot; + std::string mProcessType; +}; + +} // anonymous namespace + +static bool PrepPing(const PingThreadContext& aContext, const std::wstring& aId, + mozilla::JSONWriter& aJson) { +# if defined(DEBUG) + const mozilla::JSONWriter::CollectionStyle style = + mozilla::JSONWriter::MultiLineStyle; +# else + const mozilla::JSONWriter::CollectionStyle style = + mozilla::JSONWriter::SingleLineStyle; +# endif // defined(DEBUG) + + aJson.Start(style); + + aJson.StringProperty("type", "launcher-process-failure"); + aJson.IntProperty("version", 1); + + auto idUtf8 = WideToUTF8(aId); + if (idUtf8) { + aJson.StringProperty("id", mozilla::MakeStringSpan(idUtf8.get())); + } + + time_t now; + time(&now); + tm gmTm; + if (!gmtime_s(&gmTm, &now)) { + char isoTimeBuf[32] = {}; + if (strftime(isoTimeBuf, mozilla::ArrayLength(isoTimeBuf), "%FT%T.000Z", + &gmTm)) { + aJson.StringProperty("creationDate", isoTimeBuf); + } + } + + aJson.StringProperty("update_channel", QUOTE_ME(MOZ_UPDATE_CHANNEL)); + + if (gAppData) { + aJson.StringProperty("build_id", + mozilla::MakeStringSpan(gAppData->buildID)); + aJson.StringProperty("build_version", + mozilla::MakeStringSpan(gAppData->version)); + } + + OSVERSIONINFOEXW osv = {sizeof(osv)}; + if (::GetVersionExW(reinterpret_cast<OSVERSIONINFOW*>(&osv))) { + std::ostringstream oss; + oss << osv.dwMajorVersion << "." << osv.dwMinorVersion << "." + << osv.dwBuildNumber; + + if (osv.dwMajorVersion == 10 && osv.dwMinorVersion == 0) { + // Get the "Update Build Revision" (UBR) value + DWORD ubrValue; + DWORD ubrValueLen = sizeof(ubrValue); + LSTATUS ubrOk = + ::RegGetValueW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + L"UBR", RRF_RT_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, + &ubrValue, &ubrValueLen); + if (ubrOk == ERROR_SUCCESS) { + oss << "." << ubrValue; + } + } + + if (oss) { + aJson.StringProperty("os_version", oss.str()); + } + + bool isServer = osv.wProductType == VER_NT_DOMAIN_CONTROLLER || + osv.wProductType == VER_NT_SERVER; + aJson.BoolProperty("server_os", isServer); + } + + WCHAR localeName[LOCALE_NAME_MAX_LENGTH] = {}; + int localeNameLen = + ::GetUserDefaultLocaleName(localeName, mozilla::ArrayLength(localeName)); + if (localeNameLen) { + auto localeNameUtf8 = WideToUTF8(localeName, localeNameLen - 1); + if (localeNameUtf8) { + aJson.StringProperty("os_locale", + mozilla::MakeStringSpan(localeNameUtf8.get())); + } + } + + SYSTEM_INFO sysInfo; + ::GetNativeSystemInfo(&sysInfo); + aJson.IntProperty("cpu_arch", sysInfo.wProcessorArchitecture); + aJson.IntProperty("num_logical_cpus", sysInfo.dwNumberOfProcessors); + + mozilla::LauncherResult<bool> isAdminWithoutUac = + mozilla::IsAdminWithoutUac(); + if (isAdminWithoutUac.isOk()) { + aJson.BoolProperty("is_admin_without_uac", isAdminWithoutUac.unwrap()); + } + + if (!aContext.mProcessType.empty()) { + aJson.StringProperty("process_type", aContext.mProcessType); + } + + MEMORYSTATUSEX memStatus = {sizeof(memStatus)}; + if (::GlobalMemoryStatusEx(&memStatus)) { + aJson.StartObjectProperty("memory"); + aJson.IntProperty("total_phys", memStatus.ullTotalPhys); + aJson.IntProperty("avail_phys", memStatus.ullAvailPhys); + aJson.IntProperty("avail_page_file", memStatus.ullAvailPageFile); + aJson.IntProperty("avail_virt", memStatus.ullAvailVirtual); + aJson.EndObject(); + } + + aJson.StringProperty("xpcom_abi", TARGET_XPCOM_ABI); + + aJson.StartObjectProperty("launcher_error", style); + + std::string srcFileLeaf(aContext.mLauncherError.mFile); + // Obtain the leaf name of the file for privacy reasons + // (In case this is somebody's local build) + auto pos = srcFileLeaf.find_last_of("/\\"); + if (pos != std::string::npos) { + srcFileLeaf = srcFileLeaf.substr(pos + 1); + } + + aJson.StringProperty("source_file", srcFileLeaf); + + aJson.IntProperty("source_line", aContext.mLauncherError.mLine); + aJson.IntProperty("hresult", aContext.mLauncherError.mError.AsHResult()); + +# if defined(NIGHTLY_BUILD) + if (aContext.mLauncherError.mDetourError.isSome()) { + static const char* kHexMap = "0123456789abcdef"; + char hexStr[sizeof(mozilla::DetourError::mOrigBytes) * 2 + 1]; + int cnt = 0; + for (uint8_t byte : aContext.mLauncherError.mDetourError->mOrigBytes) { + hexStr[cnt++] = kHexMap[(byte >> 4) & 0x0f]; + hexStr[cnt++] = kHexMap[byte & 0x0f]; + } + hexStr[cnt] = 0; + aJson.StringProperty("detour_orig_bytes", hexStr); + } +# endif // defined(NIGHTLY_BUILD) + + aJson.EndObject(); + +# if !defined(__MINGW32__) + if (!AddWscInfo(aJson)) { + return false; + } +# endif // !defined(__MINGW32__) + + if (!AddModuleInfo(aContext.mModulesSnapshot, aJson)) { + return false; + } + + aJson.End(); + + return true; +} + +static bool DoSendPing(const PingThreadContext& aContext) { + auto writeFunc = mozilla::MakeUnique<TempFileWriter>(); + if (!(*writeFunc)) { + return false; + } + + mozilla::JSONWriter json(std::move(writeFunc)); + + UUID uuid; + if (::UuidCreate(&uuid) != RPC_S_OK) { + return false; + } + + wchar_t guidBuf[kGuidCharLenWithNul] = {}; + if (::StringFromGUID2(uuid, guidBuf, kGuidCharLenWithNul) != + kGuidCharLenWithNul) { + return false; + } + + // Strip the curly braces off of the guid + std::wstring guidNoBraces(guidBuf + 1, kGuidCharLenNoBracesNoNul); + + // Populate json with the ping information + if (!PrepPing(aContext, guidNoBraces, json)) { + return false; + } + + // Obtain the name of the temp file that we have written + TempFileWriter& tempFile = *static_cast<TempFileWriter*>(json.WriteFunc()); + if (!tempFile) { + return false; + } + + const std::wstring& fileName = tempFile.GetFileName(); + + // Using the path to our executable binary, construct the path to + // pingsender.exe + mozilla::UniquePtr<wchar_t[]> exePath(mozilla::GetFullBinaryPath()); + + wchar_t drive[_MAX_DRIVE] = {}; + wchar_t dir[_MAX_DIR] = {}; + if (_wsplitpath_s(exePath.get(), drive, mozilla::ArrayLength(drive), dir, + mozilla::ArrayLength(dir), nullptr, 0, nullptr, 0)) { + return false; + } + + wchar_t pingSenderPath[MAX_PATH + 1] = {}; + if (_wmakepath_s(pingSenderPath, mozilla::ArrayLength(pingSenderPath), drive, + dir, L"pingsender", L"exe")) { + return false; + } + + // Construct the telemetry URL + wchar_t urlBuf[mozilla::ArrayLength(kUrl) + kGuidCharLenNoBracesNoNul] = {}; + if (wcscpy_s(urlBuf, kUrl)) { + return false; + } + + if (wcscat_s(urlBuf, guidNoBraces.c_str())) { + return false; + } + + // Now build the command line arguments to pingsender + wchar_t* pingSenderArgv[] = {pingSenderPath, urlBuf, + const_cast<wchar_t*>(fileName.c_str())}; + + mozilla::UniquePtr<wchar_t[]> pingSenderCmdLine(mozilla::MakeCommandLine( + mozilla::ArrayLength(pingSenderArgv), pingSenderArgv)); + + // Now start pingsender to handle the rest + PROCESS_INFORMATION pi; + + STARTUPINFOW si = {sizeof(si)}; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + if (!::CreateProcessW(pingSenderPath, pingSenderCmdLine.get(), nullptr, + nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { + return false; + } + + tempFile.SetSuccessfulHandoff(); + + nsAutoHandle proc(pi.hProcess); + nsAutoHandle thread(pi.hThread); + + return true; +} + +static unsigned __stdcall SendPingThread(void* aContext) { + mozilla::UniquePtr<PingThreadContext> context( + reinterpret_cast<PingThreadContext*>(aContext)); + + if (!DoSendPing(*context) || gForceEventLog) { + PostErrorToLog(context->mLauncherError); + } + + return 0; +} + +#endif // defined(MOZ_TELEMETRY_REPORTING) + +static bool SendPing(const mozilla::LauncherError& aError, + const char* aProcessType) { +#if defined(MOZ_TELEMETRY_REPORTING) +# if defined(MOZ_LAUNCHER_PROCESS) + mozilla::LauncherRegistryInfo regInfo; + mozilla::LauncherResult<bool> telemetryEnabled = regInfo.IsTelemetryEnabled(); + if (telemetryEnabled.isErr() || !telemetryEnabled.unwrap()) { + // Do not send anything if telemetry has been opted out + return false; + } +# endif // defined(MOZ_LAUNCHER_PROCESS) + + // We send this ping when the launcher process fails. After we start the + // SendPingThread, this thread falls back from running as the launcher process + // to running as the browser main thread. Once this happens, it will be unsafe + // to set up PoisonIOInterposer (since we have already spun up a background + // thread). + mozilla::SaveToEnv("MOZ_DISABLE_POISON_IO_INTERPOSER=1"); + + // Capture aError and our module list into context for processing on another + // thread. + auto thdParam = mozilla::MakeUnique<PingThreadContext>(aError, aProcessType); + + // The ping does a lot of file I/O. Since we want this thread to continue + // executing browser startup, we should gather that information on a + // background thread. + uintptr_t thdHandle = + _beginthreadex(nullptr, 0, &SendPingThread, thdParam.get(), + STACK_SIZE_PARAM_IS_A_RESERVATION, nullptr); + if (!thdHandle) { + return false; + } + + // We have handed off thdParam to the background thread + mozilla::Unused << thdParam.release(); + + ::CloseHandle(reinterpret_cast<HANDLE>(thdHandle)); + return true; +#else + return false; +#endif +} + +namespace mozilla { + +void HandleLauncherError(const LauncherError& aError, + const char* aProcessType) { +#if defined(MOZ_LAUNCHER_PROCESS) + LauncherRegistryInfo regInfo; + Unused << regInfo.DisableDueToFailure(); +#endif // defined(MOZ_LAUNCHER_PROCESS) + + if (SendPing(aError, aProcessType) && !gForceEventLog) { + return; + } + + PostErrorToLog(aError); +} + +void SetLauncherErrorAppData(const StaticXREAppData& aAppData) { + gAppData = &aAppData; +} + +void SetLauncherErrorForceEventLog() { gForceEventLog = true; } + +} // namespace mozilla |