1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // This module contains the necessary code to register the Breakpad exception
6 // handler. This implementation is based on Chrome's crash reporting code.
8 #include "chrome_elf/breakpad.h"
12 #include "base/macros.h"
13 #include "breakpad/src/client/windows/handler/exception_handler.h"
14 #include "chrome_elf/chrome_elf_util.h"
15 #include "version.h" // NOLINT
17 google_breakpad::ExceptionHandler
* g_elf_breakpad
= NULL
;
21 const wchar_t kBreakpadProductName
[] = L
"ChromeElf";
22 const wchar_t kBreakpadVersionEntry
[] = L
"ver";
23 const wchar_t kBreakpadProdEntry
[] = L
"prod";
24 const wchar_t kBreakpadPlatformEntry
[] = L
"plat";
25 const wchar_t kBreakpadPlatformWin32
[] = L
"Win32";
27 // The protocol for connecting to the out-of-process Breakpad crash
28 // reporter is different for x86-32 and x86-64: the message sizes
29 // are different because the message struct contains a pointer. As
30 // a result, there are two different named pipes to connect to. The
31 // 64-bit one is distinguished with an "-x64" suffix.
32 const wchar_t kChromePipeName
[] = L
"\\\\.\\pipe\\ChromeCrashServices\\";
33 const wchar_t kGoogleUpdatePipeName
[] = L
"\\\\.\\pipe\\GoogleCrashServices\\";
34 const wchar_t kSystemPrincipalSid
[] = L
"S-1-5-18";
36 const wchar_t kNoErrorDialogs
[] = L
"noerrdialogs";
37 const wchar_t kChromeHeadless
[] = L
"CHROME_HEADLESS";
39 google_breakpad::CustomClientInfo
* GetCustomInfo() {
40 static google_breakpad::CustomInfoEntry
ver_entry(
41 kBreakpadVersionEntry
, TEXT(CHROME_VERSION_STRING
));
42 static google_breakpad::CustomInfoEntry
prod_entry(
43 kBreakpadProdEntry
, kBreakpadProductName
);
44 static google_breakpad::CustomInfoEntry
plat_entry(
45 kBreakpadPlatformEntry
, kBreakpadPlatformWin32
);
46 static google_breakpad::CustomInfoEntry entries
[] = {
47 ver_entry
, prod_entry
, plat_entry
};
48 static google_breakpad::CustomClientInfo custom_info
= {
49 entries
, arraysize(entries
) };
53 base::string16
GetUserSidString() {
54 // Get the current token.
56 base::string16 user_sid
;
57 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY
, &token
))
60 DWORD size
= sizeof(TOKEN_USER
) + SECURITY_MAX_SID_SIZE
;
61 BYTE user_bytes
[sizeof(TOKEN_USER
) + SECURITY_MAX_SID_SIZE
] = {};
62 TOKEN_USER
* user
= reinterpret_cast<TOKEN_USER
*>(user_bytes
);
64 wchar_t* sid_string
= NULL
;
65 if (::GetTokenInformation(token
, TokenUser
, user
, size
, &size
) &&
67 ::ConvertSidToStringSid(user
->User
.Sid
, &sid_string
)) {
68 user_sid
= sid_string
;
69 ::LocalFree(sid_string
);
77 DWORD ret
= ::GetEnvironmentVariable(L
"CHROME_HEADLESS", NULL
, 0);
81 wchar_t* command_line
= ::GetCommandLine();
83 // Note: Since this is a pure substring search rather than a check for a
84 // switch, there is a small chance that this code will match things that the
85 // Chrome code (which executes a similar check) does not. However, as long as
86 // no other switches contain the string "noerrdialogs", it should not be an
88 return (command_line
&& wcsstr(command_line
, kNoErrorDialogs
));
93 int GenerateCrashDump(EXCEPTION_POINTERS
* exinfo
) {
94 DWORD code
= exinfo
->ExceptionRecord
->ExceptionCode
;
95 if (code
== EXCEPTION_BREAKPOINT
|| code
== EXCEPTION_SINGLE_STEP
)
96 return EXCEPTION_CONTINUE_SEARCH
;
98 if (g_elf_breakpad
!= NULL
)
99 g_elf_breakpad
->WriteMinidumpForException(exinfo
);
100 return EXCEPTION_CONTINUE_SEARCH
;
103 void InitializeCrashReporting() {
104 wchar_t exe_path
[MAX_PATH
] = {};
105 if(!::GetModuleFileName(NULL
, exe_path
, arraysize(exe_path
)))
108 // Disable the message box for assertions.
109 _CrtSetReportMode(_CRT_ASSERT
, 0);
111 // Get the alternate dump directory. We use the temp path.
112 // N.B. We don't use base::GetTempDir() here to avoid running more code then
113 // necessary before crashes can be properly reported.
114 wchar_t temp_directory
[MAX_PATH
+ 1] = {};
115 DWORD length
= GetTempPath(MAX_PATH
, temp_directory
);
119 // Minidump with stacks, PEB, TEBs and unloaded module list.
120 MINIDUMP_TYPE dump_type
= static_cast<MINIDUMP_TYPE
>(
121 MiniDumpWithProcessThreadData
| // Get PEB and TEB.
122 MiniDumpWithUnloadedModules
| // Get unloaded modules when available.
123 MiniDumpWithIndirectlyReferencedMemory
); // Get memory referenced by
126 // Convert #define to a variable so that we can use if() rather than
127 // #if below and so at least compile-test the Chrome code in
129 #if defined(GOOGLE_CHROME_BUILD)
130 bool is_chrome_build
= true;
132 bool is_chrome_build
= false;
135 base::string16 pipe_name
;
137 bool enabled_by_policy
= false;
138 bool use_policy
= ReportingIsEnforcedByPolicy(&enabled_by_policy
);
140 if (!use_policy
&& IsHeadless()) {
141 pipe_name
= kChromePipeName
;
142 } else if (use_policy
? enabled_by_policy
:
143 (is_chrome_build
&& AreUsageStatsEnabled(exe_path
))) {
144 // Build the pipe name. It can be one of:
145 // 32-bit system: \\.\pipe\GoogleCrashServices\S-1-5-18
146 // 32-bit user: \\.\pipe\GoogleCrashServices\<user SID>
147 // 64-bit system: \\.\pipe\GoogleCrashServices\S-1-5-18-x64
148 // 64-bit user: \\.\pipe\GoogleCrashServices\<user SID>-x64
149 base::string16 user_sid
= IsSystemInstall(exe_path
) ? kSystemPrincipalSid
:
151 if (user_sid
.empty())
154 pipe_name
= kGoogleUpdatePipeName
;
155 pipe_name
+= user_sid
;
158 pipe_name
+= L
"-x64";
161 // Either this is a Chromium build, reporting is disabled by policy or the
162 // user has not given consent.
166 g_elf_breakpad
= new google_breakpad::ExceptionHandler(
171 google_breakpad::ExceptionHandler::HANDLER_ALL
,
176 if (g_elf_breakpad
->IsOutOfProcess()) {
177 // Tells breakpad to handle breakpoint and single step exceptions.
178 g_elf_breakpad
->set_handle_debug_exceptions(true);