Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome_elf / breakpad.cc
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.
4
5 // This module contains the necessary code to register the Breakpad exception
6 // handler. This implementation is based on Chrome's crash reporting code.
7
8 #include "chrome_elf/breakpad.h"
9
10 #include <sddl.h>
11
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
16
17 google_breakpad::ExceptionHandler* g_elf_breakpad = NULL;
18
19 namespace {
20
21 const wchar_t kBreakpadProductName[] = L"Chrome";
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";
26 const wchar_t kBreakpadProcessEntry[] = L"ptype";
27 const wchar_t kBreakpadChannelEntry[] = L"channel";
28
29 // The protocol for connecting to the out-of-process Breakpad crash
30 // reporter is different for x86-32 and x86-64: the message sizes
31 // are different because the message struct contains a pointer.  As
32 // a result, there are two different named pipes to connect to.  The
33 // 64-bit one is distinguished with an "-x64" suffix.
34 const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices\\";
35 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
36 const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
37
38 const wchar_t kNoErrorDialogs[] = L"noerrdialogs";
39 const wchar_t kChromeHeadless[] = L"CHROME_HEADLESS";
40
41 google_breakpad::CustomClientInfo* GetCustomInfo() {
42   base::string16 process = IsNonBrowserProcess() ? L"renderer" : L"browser";
43
44   wchar_t exe_path[MAX_PATH] = {};
45   base::string16 channel;
46   if (GetModuleFileName(NULL, exe_path, arraysize(exe_path)) &&
47       IsCanary(exe_path)) {
48     channel = L"canary";
49   }
50
51   static google_breakpad::CustomInfoEntry ver_entry(
52       kBreakpadVersionEntry, TEXT(CHROME_VERSION_STRING));
53   static google_breakpad::CustomInfoEntry prod_entry(
54       kBreakpadProdEntry, kBreakpadProductName);
55   static google_breakpad::CustomInfoEntry plat_entry(
56       kBreakpadPlatformEntry, kBreakpadPlatformWin32);
57   static google_breakpad::CustomInfoEntry proc_entry(
58       kBreakpadProcessEntry, process.c_str());
59   static google_breakpad::CustomInfoEntry channel_entry(
60       kBreakpadChannelEntry, channel.c_str());
61   static google_breakpad::CustomInfoEntry entries[] = {
62       ver_entry, prod_entry, plat_entry, proc_entry, channel_entry};
63   static google_breakpad::CustomClientInfo custom_info = {
64       entries, arraysize(entries) };
65   return &custom_info;
66 }
67
68 base::string16 GetUserSidString() {
69   // Get the current token.
70   HANDLE token = NULL;
71   base::string16 user_sid;
72   if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
73     return user_sid;
74
75   DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
76   BYTE user_bytes[sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE] = {};
77   TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes);
78
79   wchar_t* sid_string = NULL;
80   if (::GetTokenInformation(token, TokenUser, user, size, &size) &&
81       user->User.Sid &&
82       ::ConvertSidToStringSid(user->User.Sid, &sid_string)) {
83     user_sid = sid_string;
84     ::LocalFree(sid_string);
85   }
86
87   CloseHandle(token);
88   return user_sid;
89 }
90
91 bool IsHeadless() {
92   DWORD ret = ::GetEnvironmentVariable(L"CHROME_HEADLESS", NULL, 0);
93   if (ret != 0)
94     return true;
95
96   wchar_t* command_line = ::GetCommandLine();
97
98   // Note: Since this is a pure substring search rather than a check for a
99   // switch, there is a small chance that this code will match things that the
100   // Chrome code (which executes a similar check) does not. However, as long as
101   // no other switches contain the string "noerrdialogs", it should not be an
102   // issue.
103   return (command_line && wcsstr(command_line, kNoErrorDialogs));
104 }
105
106 }  // namespace
107
108 int GenerateCrashDump(EXCEPTION_POINTERS* exinfo) {
109   DWORD code = exinfo->ExceptionRecord->ExceptionCode;
110   if (code == EXCEPTION_BREAKPOINT || code == EXCEPTION_SINGLE_STEP)
111     return EXCEPTION_CONTINUE_SEARCH;
112
113   if (g_elf_breakpad != NULL)
114     g_elf_breakpad->WriteMinidumpForException(exinfo);
115   return EXCEPTION_CONTINUE_SEARCH;
116 }
117
118 void InitializeCrashReporting() {
119   wchar_t exe_path[MAX_PATH] = {};
120   if (!::GetModuleFileName(NULL, exe_path, arraysize(exe_path)))
121     return;
122
123   // Disable the message box for assertions.
124   _CrtSetReportMode(_CRT_ASSERT, 0);
125
126   // Get the alternate dump directory. We use the temp path.
127   // N.B. We don't use base::GetTempDir() here to avoid running more code then
128   //      necessary before crashes can be properly reported.
129   wchar_t temp_directory[MAX_PATH + 1] = {};
130   DWORD length = GetTempPath(MAX_PATH, temp_directory);
131   if (length == 0)
132     return;
133
134   // Minidump with stacks, PEB, TEBs and unloaded module list.
135   MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>(
136       MiniDumpWithProcessThreadData |  // Get PEB and TEB.
137       MiniDumpWithUnloadedModules |  // Get unloaded modules when available.
138       MiniDumpWithIndirectlyReferencedMemory);  // Get memory referenced by
139                                                 // stack.
140
141 #if defined(GOOGLE_CHROME_BUILD) && defined(OFFICIAL_BUILD)
142   bool is_official_chrome_build = true;
143 #else
144   bool is_official_chrome_build = false;
145 #endif
146
147   base::string16 pipe_name;
148
149   bool enabled_by_policy = false;
150   bool use_policy = ReportingIsEnforcedByPolicy(&enabled_by_policy);
151
152   if (!use_policy && IsHeadless()) {
153     pipe_name = kChromePipeName;
154   } else if (use_policy ?
155                  enabled_by_policy :
156                  (is_official_chrome_build && AreUsageStatsEnabled(exe_path))) {
157     // Build the pipe name. It can be one of:
158     // 32-bit system: \\.\pipe\GoogleCrashServices\S-1-5-18
159     // 32-bit user: \\.\pipe\GoogleCrashServices\<user SID>
160     // 64-bit system: \\.\pipe\GoogleCrashServices\S-1-5-18-x64
161     // 64-bit user: \\.\pipe\GoogleCrashServices\<user SID>-x64
162     base::string16 user_sid = IsSystemInstall(exe_path) ? kSystemPrincipalSid :
163                                                           GetUserSidString();
164     if (user_sid.empty())
165       return;
166
167     pipe_name = kGoogleUpdatePipeName;
168     pipe_name += user_sid;
169
170 #if defined(_WIN64)
171     pipe_name += L"-x64";
172 #endif
173   } else {
174     // Either this is a Chromium build, reporting is disabled by policy or the
175     // user has not given consent.
176     return;
177   }
178
179   g_elf_breakpad = new google_breakpad::ExceptionHandler(
180       temp_directory,
181       NULL,
182       NULL,
183       NULL,
184       google_breakpad::ExceptionHandler::HANDLER_ALL,
185       dump_type,
186       pipe_name.c_str(),
187       GetCustomInfo());
188
189   if (g_elf_breakpad->IsOutOfProcess()) {
190     // Tells breakpad to handle breakpoint and single step exceptions.
191     g_elf_breakpad->set_handle_debug_exceptions(true);
192   }
193 }