Merge mozilla-central to autoland. a=merge CLOSED TREE
[gecko.git] / browser / app / winlauncher / ErrorHandler.cpp
blob5b2582284ee56ed875b0879538ddcf578a464fab
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #include "ErrorHandler.h"
9 #include <utility>
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/CmdLineAndEnvUtils.h"
13 #include "mozilla/DebugOnly.h"
14 #include "mozilla/JSONWriter.h"
15 #include "mozilla/UniquePtr.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/WinTokenUtils.h"
18 #include "mozilla/XREAppData.h"
19 #include "mozilla/glue/WindowsDllServices.h"
20 #include "mozilla/mscom/ProcessRuntime.h"
21 #include "nsWindowsHelpers.h"
23 #if defined(MOZ_LAUNCHER_PROCESS)
24 # include "mozilla/LauncherRegistryInfo.h"
25 #endif // defined(MOZ_LAUNCHER_PROCESS)
27 #include <algorithm>
28 #include <process.h>
29 #include <sstream>
30 #include <string>
31 #include <stdio.h>
32 #include <string.h>
33 #include <time.h>
35 #include <objbase.h>
36 #include <rpc.h>
37 #if !defined(__MINGW32__)
38 # include <comutil.h>
39 # include <iwscapi.h>
40 # include <wscapi.h>
41 #endif // !defined(__MINGW32__)
42 #include <tlhelp32.h>
44 #if !defined(RRF_SUBKEY_WOW6464KEY)
45 # define RRF_SUBKEY_WOW6464KEY 0x00010000
46 #endif // !defined(RRF_SUBKEY_WOW6464KEY)
48 #define QUOTE_ME2(x) #x
49 #define QUOTE_ME(x) QUOTE_ME2(x)
51 #define TELEMETRY_BASE_URL L"https://incoming.telemetry.mozilla.org/submit"
52 #define TELEMETRY_NAMESPACE L"/firefox-launcher-process"
53 #define TELEMETRY_LAUNCHER_PING_DOCTYPE L"/launcher-process-failure"
54 #define TELEMETRY_LAUNCHER_PING_VERSION L"/1"
56 static const wchar_t kUrl[] = TELEMETRY_BASE_URL TELEMETRY_NAMESPACE
57 TELEMETRY_LAUNCHER_PING_DOCTYPE TELEMETRY_LAUNCHER_PING_VERSION L"/";
58 static const uint32_t kGuidCharLenWithNul = 39;
59 static const uint32_t kGuidCharLenNoBracesNoNul = 36;
60 static const mozilla::StaticXREAppData* gAppData;
62 // Ordinarily, errors are only reported to the Windows Event Log when they are
63 // not reported upstream via telemetry (usually due either to telemetry being
64 // disabled or to network failure).
66 // If `--log-launcher-error` is given at the command line, launcher errors will
67 // always be reported to the Windows Event Log, regardless of whether or not
68 // they're sent upstream.
69 static bool gForceEventLog = false;
71 namespace {
73 constexpr wchar_t kEventSourceName[] = L"" MOZ_APP_DISPLAYNAME " Launcher";
75 struct EventSourceDeleter {
76 using pointer = HANDLE;
78 void operator()(pointer aEvtSrc) { ::DeregisterEventSource(aEvtSrc); }
81 using EventLog = mozilla::UniquePtr<HANDLE, EventSourceDeleter>;
83 struct SerializedEventData {
84 HRESULT mHr;
85 uint32_t mLine;
86 char mFile[1];
89 } // anonymous namespace
91 static void PostErrorToLog(const mozilla::LauncherError& aError) {
92 // This is very bare-bones; just enough to spit out an HRESULT to the
93 // Application event log.
94 EventLog log(::RegisterEventSourceW(nullptr, kEventSourceName));
96 if (!log) {
97 return;
100 size_t fileLen = strlen(aError.mFile);
101 size_t dataLen = sizeof(HRESULT) + sizeof(uint32_t) + fileLen;
102 auto evtDataBuf = mozilla::MakeUnique<char[]>(dataLen);
103 SerializedEventData& evtData =
104 *reinterpret_cast<SerializedEventData*>(evtDataBuf.get());
105 evtData.mHr = aError.mError.AsHResult();
106 evtData.mLine = aError.mLine;
107 // Since this is binary data, we're not concerning ourselves with null
108 // terminators.
109 memcpy(evtData.mFile, aError.mFile, fileLen);
111 ::ReportEventW(log.get(), EVENTLOG_ERROR_TYPE, 0, aError.mError.AsHResult(),
112 nullptr, 0, dataLen, nullptr, evtDataBuf.get());
115 #if defined(MOZ_TELEMETRY_REPORTING)
117 namespace {
119 // This JSONWriteFunc writes directly to a temp file. By creating this file
120 // with the FILE_ATTRIBUTE_TEMPORARY attribute, we hint to the OS that this
121 // file is short-lived. The OS will try to avoid flushing it to disk if at
122 // all possible.
123 class TempFileWriter final : public mozilla::JSONWriteFunc {
124 public:
125 TempFileWriter() : mFailed(false), mSuccessfulHandoff(false) {
126 wchar_t name[MAX_PATH + 1] = {};
127 if (_wtmpnam_s(name)) {
128 mFailed = true;
129 return;
132 mTempFileName = name;
134 mTempFile.own(::CreateFileW(name, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
135 CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, nullptr));
136 if (mTempFile.get() == INVALID_HANDLE_VALUE) {
137 mFailed = true;
141 ~TempFileWriter() {
142 if (mSuccessfulHandoff) {
143 // It is no longer our responsibility to delete the temp file if we have
144 // successfully handed it off to pingsender.
145 return;
148 mTempFile.reset();
149 ::DeleteFileW(mTempFileName.c_str());
152 explicit operator bool() const { return !mFailed; }
154 void Write(const mozilla::Span<const char>& aStr) final {
155 if (mFailed) {
156 return;
159 DWORD bytesWritten = 0;
160 if (!::WriteFile(mTempFile, aStr.data(), aStr.size(), &bytesWritten,
161 nullptr) ||
162 bytesWritten != aStr.size()) {
163 mFailed = true;
167 const std::wstring& GetFileName() const { return mTempFileName; }
169 void SetSuccessfulHandoff() { mSuccessfulHandoff = true; }
171 private:
172 bool mFailed;
173 bool mSuccessfulHandoff;
174 std::wstring mTempFileName;
175 nsAutoHandle mTempFile;
178 using SigMap = mozilla::Vector<std::wstring, 0, InfallibleAllocPolicy>;
180 } // anonymous namespace
182 // This is the guideline for maximum string length for telemetry intake
183 static const size_t kMaxStrLen = 80;
185 static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr,
186 const size_t aStrLenExclNul) {
187 // Yes, this might not handle surrogate pairs correctly. Let's just let
188 // WideCharToMultiByte fail in that unlikely case.
189 size_t cvtLen = std::min(aStrLenExclNul, kMaxStrLen);
191 int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, nullptr, 0,
192 nullptr, nullptr);
193 if (!numConv) {
194 return nullptr;
197 // Include room for the null terminator by adding one
198 auto buf = mozilla::MakeUnique<char[]>(numConv + 1);
200 numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, buf.get(), numConv,
201 nullptr, nullptr);
202 if (!numConv) {
203 return nullptr;
206 // Add null termination. numConv does not include the terminator, so we don't
207 // subtract 1 when indexing into buf.
208 buf[numConv] = 0;
210 return buf;
213 static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr) {
214 return WideToUTF8(aStr, wcslen(aStr));
217 static mozilla::UniquePtr<char[]> WideToUTF8(const std::wstring& aStr) {
218 return WideToUTF8(aStr.c_str(), aStr.length());
221 // MinGW does not support the Windows Security Center APIs.
222 # if !defined(__MINGW32__)
224 static mozilla::UniquePtr<char[]> WideToUTF8(const _bstr_t& aStr) {
225 return WideToUTF8(static_cast<const wchar_t*>(aStr), aStr.length());
228 namespace {
230 struct ProviderKey {
231 WSC_SECURITY_PROVIDER mProviderType;
232 const char* mKey;
235 } // anonymous namespace
237 static bool EnumWSCProductList(RefPtr<IWSCProductList>& aProdList,
238 mozilla::JSONWriter& aJson) {
239 LONG count;
240 HRESULT hr = aProdList->get_Count(&count);
241 if (FAILED(hr)) {
242 return false;
245 // Unlikely, but put a bound on the max length of the output array for the
246 // purposes of telemetry intake.
247 count = std::min(count, 1000L);
249 // Record the name(s) of each active registered product in this category
250 for (LONG index = 0; index < count; ++index) {
251 RefPtr<IWscProduct> product;
252 hr = aProdList->get_Item(index, getter_AddRefs(product));
253 if (FAILED(hr)) {
254 return false;
257 WSC_SECURITY_PRODUCT_STATE state;
258 hr = product->get_ProductState(&state);
259 if (FAILED(hr)) {
260 return false;
263 // We only care about products that are active
264 if (state == WSC_SECURITY_PRODUCT_STATE_OFF ||
265 state == WSC_SECURITY_PRODUCT_STATE_SNOOZED ||
266 state == WSC_SECURITY_PRODUCT_STATE_EXPIRED) {
267 continue;
270 _bstr_t bName;
271 hr = product->get_ProductName(bName.GetAddress());
272 if (FAILED(hr)) {
273 return false;
276 auto buf = WideToUTF8(bName);
277 if (!buf) {
278 return false;
281 aJson.StringElement(mozilla::MakeStringSpan(buf.get()));
284 return true;
287 static const ProviderKey gProvKeys[] = {
288 {WSC_SECURITY_PROVIDER_ANTIVIRUS, "av"},
289 {WSC_SECURITY_PROVIDER_ANTISPYWARE, "antispyware"},
290 {WSC_SECURITY_PROVIDER_FIREWALL, "firewall"}};
292 static bool AddWscInfo(mozilla::JSONWriter& aJson) {
293 // We need COM for this. Using ProcessRuntime so that process-global COM
294 // configuration is done correctly
295 mozilla::mscom::ProcessRuntime mscom(
296 mozilla::mscom::ProcessRuntime::ProcessCategory::Launcher);
297 if (!mscom) {
298 // We haven't written anything yet, so we can return true here and continue
299 // capturing data.
300 return true;
303 aJson.StartObjectProperty("security");
305 const CLSID clsid = __uuidof(WSCProductList);
306 const IID iid = __uuidof(IWSCProductList);
308 for (uint32_t index = 0; index < mozilla::ArrayLength(gProvKeys); ++index) {
309 // NB: A separate instance of IWSCProductList is needed for each distinct
310 // security provider type; MSDN says that we cannot reuse the same object
311 // and call Initialize() to pave over the previous data.
312 RefPtr<IWSCProductList> prodList;
313 HRESULT hr = ::CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, iid,
314 getter_AddRefs(prodList));
315 if (FAILED(hr)) {
316 return false;
319 hr = prodList->Initialize(gProvKeys[index].mProviderType);
320 if (FAILED(hr)) {
321 return false;
324 aJson.StartArrayProperty(mozilla::MakeStringSpan(gProvKeys[index].mKey));
326 if (!EnumWSCProductList(prodList, aJson)) {
327 return false;
330 aJson.EndArray();
333 aJson.EndObject();
335 return true;
337 # endif // !defined(__MINGW32__)
339 // Max array length for telemetry intake.
340 static const size_t kMaxArrayLen = 1000;
342 static bool AddModuleInfo(const nsAutoHandle& aSnapshot,
343 mozilla::JSONWriter& aJson) {
344 if (aSnapshot.get() == INVALID_HANDLE_VALUE) {
345 // We haven't written anything yet, so we can return true here and continue
346 // capturing data.
347 return true;
350 SigMap signatures;
351 size_t moduleCount = 0;
353 MODULEENTRY32W module = {sizeof(module)};
354 if (!::Module32FirstW(aSnapshot, &module)) {
355 // We haven't written anything yet, so we can return true here and continue
356 // capturing data.
357 return true;
360 mozilla::glue::BasicDllServices dllServices;
362 aJson.StartObjectProperty("modules");
364 // For each module, add its version number (or empty string if not present),
365 // followed by an optional index into the signatures array
366 do {
367 ++moduleCount;
369 wchar_t leaf[_MAX_FNAME] = {};
370 if (::_wsplitpath_s(module.szExePath, nullptr, 0, nullptr, 0, leaf,
371 mozilla::ArrayLength(leaf), nullptr, 0)) {
372 return false;
375 if (_wcslwr_s(leaf, mozilla::ArrayLength(leaf))) {
376 return false;
379 auto leafUtf8 = WideToUTF8(leaf);
380 if (!leafUtf8) {
381 return false;
384 aJson.StartArrayProperty(mozilla::MakeStringSpan(leafUtf8.get()));
386 std::string version;
387 DWORD verInfoSize = ::GetFileVersionInfoSizeW(module.szExePath, nullptr);
388 if (verInfoSize) {
389 auto verInfoBuf = mozilla::MakeUnique<BYTE[]>(verInfoSize);
391 if (::GetFileVersionInfoW(module.szExePath, 0, verInfoSize,
392 verInfoBuf.get())) {
393 VS_FIXEDFILEINFO* fixedInfo = nullptr;
394 UINT fixedInfoLen = 0;
396 if (::VerQueryValueW(verInfoBuf.get(), L"\\",
397 reinterpret_cast<LPVOID*>(&fixedInfo),
398 &fixedInfoLen)) {
399 std::ostringstream oss;
400 oss << HIWORD(fixedInfo->dwFileVersionMS) << '.'
401 << LOWORD(fixedInfo->dwFileVersionMS) << '.'
402 << HIWORD(fixedInfo->dwFileVersionLS) << '.'
403 << LOWORD(fixedInfo->dwFileVersionLS);
404 version = oss.str();
409 aJson.StringElement(version);
411 mozilla::Maybe<ptrdiff_t> sigIndex;
412 auto signedBy = dllServices.GetBinaryOrgName(module.szExePath);
413 if (signedBy) {
414 std::wstring strSignedBy(signedBy.get());
415 auto entry = std::find(signatures.begin(), signatures.end(), strSignedBy);
416 if (entry == signatures.end()) {
417 mozilla::Unused << signatures.append(std::move(strSignedBy));
418 entry = &signatures.back();
421 sigIndex = mozilla::Some(entry - signatures.begin());
424 if (sigIndex) {
425 aJson.IntElement(sigIndex.value());
428 aJson.EndArray();
429 } while (moduleCount < kMaxArrayLen && ::Module32NextW(aSnapshot, &module));
431 aJson.EndObject();
433 aJson.StartArrayProperty("signatures");
435 // Serialize each entry in the signatures array
436 for (auto&& itr : signatures) {
437 auto sigUtf8 = WideToUTF8(itr);
438 if (!sigUtf8) {
439 continue;
442 aJson.StringElement(mozilla::MakeStringSpan(sigUtf8.get()));
445 aJson.EndArray();
447 return true;
450 namespace {
452 struct PingThreadContext {
453 explicit PingThreadContext(const mozilla::LauncherError& aError,
454 const char* aProcessType)
455 : mLauncherError(aError),
456 mModulesSnapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)),
457 mProcessType(aProcessType ? aProcessType : "") {}
458 mozilla::LauncherError mLauncherError;
459 nsAutoHandle mModulesSnapshot;
460 std::string mProcessType;
463 } // anonymous namespace
465 static bool PrepPing(const PingThreadContext& aContext, const std::wstring& aId,
466 mozilla::JSONWriter& aJson) {
467 # if defined(DEBUG)
468 const mozilla::JSONWriter::CollectionStyle style =
469 mozilla::JSONWriter::MultiLineStyle;
470 # else
471 const mozilla::JSONWriter::CollectionStyle style =
472 mozilla::JSONWriter::SingleLineStyle;
473 # endif // defined(DEBUG)
475 aJson.Start(style);
477 aJson.StringProperty("type", "launcher-process-failure");
478 aJson.IntProperty("version", 1);
480 auto idUtf8 = WideToUTF8(aId);
481 if (idUtf8) {
482 aJson.StringProperty("id", mozilla::MakeStringSpan(idUtf8.get()));
485 time_t now;
486 time(&now);
487 tm gmTm;
488 if (!gmtime_s(&gmTm, &now)) {
489 char isoTimeBuf[32] = {};
490 if (strftime(isoTimeBuf, mozilla::ArrayLength(isoTimeBuf), "%FT%T.000Z",
491 &gmTm)) {
492 aJson.StringProperty("creationDate", isoTimeBuf);
496 aJson.StringProperty("update_channel", QUOTE_ME(MOZ_UPDATE_CHANNEL));
498 if (gAppData) {
499 aJson.StringProperty("build_id",
500 mozilla::MakeStringSpan(gAppData->buildID));
501 aJson.StringProperty("build_version",
502 mozilla::MakeStringSpan(gAppData->version));
505 OSVERSIONINFOEXW osv = {sizeof(osv)};
506 if (::GetVersionExW(reinterpret_cast<OSVERSIONINFOW*>(&osv))) {
507 std::ostringstream oss;
508 oss << osv.dwMajorVersion << "." << osv.dwMinorVersion << "."
509 << osv.dwBuildNumber;
511 if (osv.dwMajorVersion == 10 && osv.dwMinorVersion == 0) {
512 // Get the "Update Build Revision" (UBR) value
513 DWORD ubrValue;
514 DWORD ubrValueLen = sizeof(ubrValue);
515 LSTATUS ubrOk =
516 ::RegGetValueW(HKEY_LOCAL_MACHINE,
517 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
518 L"UBR", RRF_RT_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr,
519 &ubrValue, &ubrValueLen);
520 if (ubrOk == ERROR_SUCCESS) {
521 oss << "." << ubrValue;
525 if (oss) {
526 aJson.StringProperty("os_version", oss.str());
529 bool isServer = osv.wProductType == VER_NT_DOMAIN_CONTROLLER ||
530 osv.wProductType == VER_NT_SERVER;
531 aJson.BoolProperty("server_os", isServer);
534 WCHAR localeName[LOCALE_NAME_MAX_LENGTH] = {};
535 int localeNameLen =
536 ::GetUserDefaultLocaleName(localeName, mozilla::ArrayLength(localeName));
537 if (localeNameLen) {
538 auto localeNameUtf8 = WideToUTF8(localeName, localeNameLen - 1);
539 if (localeNameUtf8) {
540 aJson.StringProperty("os_locale",
541 mozilla::MakeStringSpan(localeNameUtf8.get()));
545 SYSTEM_INFO sysInfo;
546 ::GetNativeSystemInfo(&sysInfo);
547 aJson.IntProperty("cpu_arch", sysInfo.wProcessorArchitecture);
548 aJson.IntProperty("num_logical_cpus", sysInfo.dwNumberOfProcessors);
550 mozilla::LauncherResult<bool> isAdminWithoutUac =
551 mozilla::IsAdminWithoutUac();
552 if (isAdminWithoutUac.isOk()) {
553 aJson.BoolProperty("is_admin_without_uac", isAdminWithoutUac.unwrap());
556 if (!aContext.mProcessType.empty()) {
557 aJson.StringProperty("process_type", aContext.mProcessType);
560 MEMORYSTATUSEX memStatus = {sizeof(memStatus)};
561 if (::GlobalMemoryStatusEx(&memStatus)) {
562 aJson.StartObjectProperty("memory");
563 aJson.IntProperty("total_phys", memStatus.ullTotalPhys);
564 aJson.IntProperty("avail_phys", memStatus.ullAvailPhys);
565 aJson.IntProperty("avail_page_file", memStatus.ullAvailPageFile);
566 aJson.IntProperty("avail_virt", memStatus.ullAvailVirtual);
567 aJson.EndObject();
570 aJson.StringProperty("xpcom_abi", TARGET_XPCOM_ABI);
572 aJson.StartObjectProperty("launcher_error", style);
574 std::string srcFileLeaf(aContext.mLauncherError.mFile);
575 // Obtain the leaf name of the file for privacy reasons
576 // (In case this is somebody's local build)
577 auto pos = srcFileLeaf.find_last_of("/\\");
578 if (pos != std::string::npos) {
579 srcFileLeaf = srcFileLeaf.substr(pos + 1);
582 aJson.StringProperty("source_file", srcFileLeaf);
584 aJson.IntProperty("source_line", aContext.mLauncherError.mLine);
585 aJson.IntProperty("hresult", aContext.mLauncherError.mError.AsHResult());
587 # if defined(NIGHTLY_BUILD)
588 if (aContext.mLauncherError.mDetourError.isSome()) {
589 static const char* kHexMap = "0123456789abcdef";
590 char hexStr[sizeof(mozilla::DetourError::mOrigBytes) * 2 + 1];
591 int cnt = 0;
592 for (uint8_t byte : aContext.mLauncherError.mDetourError->mOrigBytes) {
593 hexStr[cnt++] = kHexMap[(byte >> 4) & 0x0f];
594 hexStr[cnt++] = kHexMap[byte & 0x0f];
596 hexStr[cnt] = 0;
597 aJson.StringProperty("detour_orig_bytes", hexStr);
599 # endif // defined(NIGHTLY_BUILD)
601 aJson.EndObject();
603 # if !defined(__MINGW32__)
604 if (!AddWscInfo(aJson)) {
605 return false;
607 # endif // !defined(__MINGW32__)
609 if (!AddModuleInfo(aContext.mModulesSnapshot, aJson)) {
610 return false;
613 aJson.End();
615 return true;
618 static bool DoSendPing(const PingThreadContext& aContext) {
619 TempFileWriter tempFile;
620 mozilla::JSONWriter json(tempFile);
622 UUID uuid;
623 if (::UuidCreate(&uuid) != RPC_S_OK) {
624 return false;
627 wchar_t guidBuf[kGuidCharLenWithNul] = {};
628 if (::StringFromGUID2(uuid, guidBuf, kGuidCharLenWithNul) !=
629 kGuidCharLenWithNul) {
630 return false;
633 // Strip the curly braces off of the guid
634 std::wstring guidNoBraces(guidBuf + 1, kGuidCharLenNoBracesNoNul);
636 // Populate json with the ping information
637 if (!PrepPing(aContext, guidNoBraces, json)) {
638 return false;
641 // Obtain the name of the temp file that we have written
642 const std::wstring& fileName = tempFile.GetFileName();
644 // Using the path to our executable binary, construct the path to
645 // pingsender.exe
646 mozilla::UniquePtr<wchar_t[]> exePath(mozilla::GetFullBinaryPath());
648 wchar_t drive[_MAX_DRIVE] = {};
649 wchar_t dir[_MAX_DIR] = {};
650 if (_wsplitpath_s(exePath.get(), drive, mozilla::ArrayLength(drive), dir,
651 mozilla::ArrayLength(dir), nullptr, 0, nullptr, 0)) {
652 return false;
655 wchar_t pingSenderPath[MAX_PATH + 1] = {};
656 if (_wmakepath_s(pingSenderPath, mozilla::ArrayLength(pingSenderPath), drive,
657 dir, L"pingsender", L"exe")) {
658 return false;
661 // Construct the telemetry URL
662 wchar_t urlBuf[mozilla::ArrayLength(kUrl) + kGuidCharLenNoBracesNoNul] = {};
663 if (wcscpy_s(urlBuf, kUrl)) {
664 return false;
667 if (wcscat_s(urlBuf, guidNoBraces.c_str())) {
668 return false;
671 // Now build the command line arguments to pingsender
672 wchar_t* pingSenderArgv[] = {pingSenderPath, urlBuf,
673 const_cast<wchar_t*>(fileName.c_str())};
675 mozilla::UniquePtr<wchar_t[]> pingSenderCmdLine(mozilla::MakeCommandLine(
676 mozilla::ArrayLength(pingSenderArgv), pingSenderArgv));
678 // Now start pingsender to handle the rest
679 PROCESS_INFORMATION pi;
681 STARTUPINFOW si = {sizeof(si)};
682 si.dwFlags = STARTF_USESHOWWINDOW;
683 si.wShowWindow = SW_HIDE;
685 if (!::CreateProcessW(pingSenderPath, pingSenderCmdLine.get(), nullptr,
686 nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
687 return false;
690 tempFile.SetSuccessfulHandoff();
692 nsAutoHandle proc(pi.hProcess);
693 nsAutoHandle thread(pi.hThread);
695 return true;
698 static unsigned __stdcall SendPingThread(void* aContext) {
699 mozilla::UniquePtr<PingThreadContext> context(
700 reinterpret_cast<PingThreadContext*>(aContext));
702 if (!DoSendPing(*context) || gForceEventLog) {
703 PostErrorToLog(context->mLauncherError);
706 return 0;
709 #endif // defined(MOZ_TELEMETRY_REPORTING)
711 static bool SendPing(const mozilla::LauncherError& aError,
712 const char* aProcessType) {
713 #if defined(MOZ_TELEMETRY_REPORTING)
714 # if defined(MOZ_LAUNCHER_PROCESS)
715 mozilla::LauncherRegistryInfo regInfo;
716 mozilla::LauncherResult<bool> telemetryEnabled = regInfo.IsTelemetryEnabled();
717 if (telemetryEnabled.isErr() || !telemetryEnabled.unwrap()) {
718 // Do not send anything if telemetry has been opted out
719 return false;
721 # endif // defined(MOZ_LAUNCHER_PROCESS)
723 // We send this ping when the launcher process fails. After we start the
724 // SendPingThread, this thread falls back from running as the launcher process
725 // to running as the browser main thread. Once this happens, it will be unsafe
726 // to set up PoisonIOInterposer (since we have already spun up a background
727 // thread).
728 mozilla::SaveToEnv("MOZ_DISABLE_POISON_IO_INTERPOSER=1");
730 // Capture aError and our module list into context for processing on another
731 // thread.
732 auto thdParam = mozilla::MakeUnique<PingThreadContext>(aError, aProcessType);
734 // The ping does a lot of file I/O. Since we want this thread to continue
735 // executing browser startup, we should gather that information on a
736 // background thread.
737 uintptr_t thdHandle =
738 _beginthreadex(nullptr, 0, &SendPingThread, thdParam.get(),
739 STACK_SIZE_PARAM_IS_A_RESERVATION, nullptr);
740 if (!thdHandle) {
741 return false;
744 // We have handed off thdParam to the background thread
745 mozilla::Unused << thdParam.release();
747 ::CloseHandle(reinterpret_cast<HANDLE>(thdHandle));
748 return true;
749 #else
750 return false;
751 #endif
754 namespace mozilla {
756 void HandleLauncherError(const LauncherError& aError,
757 const char* aProcessType) {
758 #if defined(MOZ_LAUNCHER_PROCESS)
759 LauncherRegistryInfo regInfo;
760 Unused << regInfo.DisableDueToFailure();
761 #endif // defined(MOZ_LAUNCHER_PROCESS)
763 if (!SendPing(aError, aProcessType)) {
764 // couldn't (or shouldn't) send telemetry; fall back to event log
765 PostErrorToLog(aError);
769 void SetLauncherErrorAppData(const StaticXREAppData& aAppData) {
770 gAppData = &aAppData;
773 void SetLauncherErrorForceEventLog() { gForceEventLog = true; }
775 } // namespace mozilla