Inline NetLog IPv6 reachability events.
[chromium-blink-merge.git] / chrome / chrome_watcher / chrome_watcher_main.cc
blobc6db791a41d1aa7ffd881bda449346557b5e95cc
1 // Copyright (c) 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 #include <windows.h>
7 #include "base/at_exit.h"
8 #include "base/bind.h"
9 #include "base/bind_helpers.h"
10 #include "base/command_line.h"
11 #include "base/file_version_info.h"
12 #include "base/files/file_path.h"
13 #include "base/logging_win.h"
14 #include "base/macros.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/message_loop/message_loop_proxy.h"
18 #include "base/process/process.h"
19 #include "base/run_loop.h"
20 #include "base/sequenced_task_runner.h"
21 #include "base/strings/string16.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_piece.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/synchronization/waitable_event.h"
26 #include "base/template_util.h"
27 #include "base/threading/thread.h"
28 #include "base/time/time.h"
29 #include "base/win/scoped_handle.h"
30 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
31 #include "chrome/installer/util/util_constants.h"
32 #include "components/browser_watcher/endsession_watcher_window_win.h"
33 #include "components/browser_watcher/exit_code_watcher_win.h"
34 #include "components/browser_watcher/exit_funnel_win.h"
35 #include "components/browser_watcher/window_hang_monitor_win.h"
37 #ifdef KASKO
38 #include "syzygy/kasko/api/reporter.h"
39 #endif
41 namespace {
43 // Use the same log facility as Chrome for convenience.
44 // {7FE69228-633E-4f06-80C1-527FEA23E3A7}
45 const GUID kChromeWatcherTraceProviderName = {
46 0x7fe69228, 0x633e, 0x4f06,
47 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
49 // The amount of time we wait around for a WM_ENDSESSION or a process exit.
50 const int kDelayTimeSeconds = 30;
52 // Takes care of monitoring a browser. This class watches for a browser's exit
53 // code, as well as listening for WM_ENDSESSION messages. Events are recorded in
54 // an exit funnel, for reporting the next time Chrome runs.
55 class BrowserMonitor {
56 public:
57 BrowserMonitor(base::RunLoop* run_loop, const base::char16* registry_path);
58 ~BrowserMonitor();
60 // Initiates the asynchronous monitoring process, returns true on success.
61 // |on_initialized_event| will be signaled immediately before blocking on the
62 // exit of |process|.
63 bool StartWatching(const base::char16* registry_path,
64 base::Process process,
65 base::win::ScopedHandle on_initialized_event);
67 private:
68 // Called from EndSessionWatcherWindow on a end session messages.
69 void OnEndSessionMessage(UINT message, LPARAM lparam);
71 // Blocking function that runs on |background_thread_|. Signals
72 // |on_initialized_event| before waiting for the browser process to exit.
73 void Watch(base::win::ScopedHandle on_initialized_event);
75 // Posted to main thread from Watch when browser exits.
76 void BrowserExited();
78 // The funnel used to record events for this browser.
79 browser_watcher::ExitFunnel exit_funnel_;
81 browser_watcher::ExitCodeWatcher exit_code_watcher_;
82 browser_watcher::EndSessionWatcherWindow end_session_watcher_window_;
84 // The thread that runs Watch().
85 base::Thread background_thread_;
87 // Set when the browser has exited, used to stretch the watcher's lifetime
88 // when WM_ENDSESSION occurs before browser exit.
89 base::WaitableEvent browser_exited_;
91 // The run loop for the main thread and its task runner.
92 base::RunLoop* run_loop_;
93 scoped_refptr<base::SequencedTaskRunner> main_thread_;
95 DISALLOW_COPY_AND_ASSIGN(BrowserMonitor);
98 BrowserMonitor::BrowserMonitor(base::RunLoop* run_loop,
99 const base::char16* registry_path) :
100 browser_exited_(true, false), // manual reset, initially non-signalled.
101 exit_code_watcher_(registry_path),
102 end_session_watcher_window_(
103 base::Bind(&BrowserMonitor::OnEndSessionMessage,
104 base::Unretained(this))),
105 background_thread_("BrowserWatcherThread"),
106 run_loop_(run_loop),
107 main_thread_(base::MessageLoopProxy::current()) {
110 BrowserMonitor::~BrowserMonitor() {
113 bool BrowserMonitor::StartWatching(
114 const base::char16* registry_path,
115 base::Process process,
116 base::win::ScopedHandle on_initialized_event) {
117 if (!exit_code_watcher_.Initialize(process.Pass()))
118 return false;
120 if (!exit_funnel_.Init(registry_path,
121 exit_code_watcher_.process().Handle())) {
122 return false;
125 if (!background_thread_.StartWithOptions(
126 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))) {
127 return false;
130 if (!background_thread_.task_runner()->PostTask(
131 FROM_HERE, base::Bind(&BrowserMonitor::Watch, base::Unretained(this),
132 base::Passed(on_initialized_event.Pass())))) {
133 background_thread_.Stop();
134 return false;
137 return true;
140 void BrowserMonitor::OnEndSessionMessage(UINT message, LPARAM lparam) {
141 DCHECK_EQ(main_thread_, base::MessageLoopProxy::current());
143 if (message == WM_QUERYENDSESSION) {
144 exit_funnel_.RecordEvent(L"WatcherQueryEndSession");
145 } else if (message == WM_ENDSESSION) {
146 exit_funnel_.RecordEvent(L"WatcherEndSession");
148 if (lparam & ENDSESSION_CLOSEAPP)
149 exit_funnel_.RecordEvent(L"ES_CloseApp");
150 if (lparam & ENDSESSION_CRITICAL)
151 exit_funnel_.RecordEvent(L"ES_Critical");
152 if (lparam & ENDSESSION_LOGOFF)
153 exit_funnel_.RecordEvent(L"ES_Logoff");
154 const LPARAM kKnownBits =
155 ENDSESSION_CLOSEAPP | ENDSESSION_CRITICAL | ENDSESSION_LOGOFF;
156 if (lparam & ~kKnownBits)
157 exit_funnel_.RecordEvent(L"ES_Other");
159 // If the browser hasn't exited yet, dally for a bit to try and stretch this
160 // process' lifetime to give it some more time to capture the browser exit.
161 browser_exited_.TimedWait(base::TimeDelta::FromSeconds(kDelayTimeSeconds));
163 run_loop_->Quit();
166 void BrowserMonitor::Watch(base::win::ScopedHandle on_initialized_event) {
167 // This needs to run on an IO thread.
168 DCHECK_NE(main_thread_, base::MessageLoopProxy::current());
170 // Signal our client now that the Kasko reporter is initialized and we have
171 // cleared all of the obstacles that might lead to an early exit.
172 ::SetEvent(on_initialized_event.Get());
173 on_initialized_event.Close();
175 exit_code_watcher_.WaitForExit();
176 exit_funnel_.RecordEvent(L"BrowserExit");
178 // Note that the browser has exited.
179 browser_exited_.Signal();
181 main_thread_->PostTask(FROM_HERE,
182 base::Bind(&BrowserMonitor::BrowserExited, base::Unretained(this)));
185 void BrowserMonitor::BrowserExited() {
186 // This runs in the main thread.
187 DCHECK_EQ(main_thread_, base::MessageLoopProxy::current());
189 // Our background thread has served it's purpose.
190 background_thread_.Stop();
192 const int exit_code = exit_code_watcher_.exit_code();
193 if (exit_code >= 0 && exit_code <= 28) {
194 // The browser exited with a well-known exit code, quit this process
195 // immediately.
196 run_loop_->Quit();
197 } else {
198 // The browser exited abnormally, wait around for a little bit to see
199 // whether this instance will get a logoff message.
200 main_thread_->PostDelayedTask(
201 FROM_HERE,
202 run_loop_->QuitClosure(),
203 base::TimeDelta::FromSeconds(kDelayTimeSeconds));
207 void OnWindowEvent(
208 const base::string16& registry_path,
209 base::Process process,
210 const base::Callback<void(const base::Process&)>& on_hung_callback,
211 browser_watcher::WindowHangMonitor::WindowEvent window_event) {
212 browser_watcher::ExitFunnel exit_funnel;
213 if (exit_funnel.Init(registry_path.c_str(), process.Handle())) {
214 switch (window_event) {
215 case browser_watcher::WindowHangMonitor::WINDOW_NOT_FOUND:
216 exit_funnel.RecordEvent(L"MessageWindowNotFound");
217 break;
218 case browser_watcher::WindowHangMonitor::WINDOW_HUNG:
219 exit_funnel.RecordEvent(L"MessageWindowHung");
220 if (!on_hung_callback.is_null())
221 on_hung_callback.Run(process);
222 break;
223 case browser_watcher::WindowHangMonitor::WINDOW_VANISHED:
224 exit_funnel.RecordEvent(L"MessageWindowVanished");
225 break;
226 default:
227 NOTREACHED();
228 break;
233 #ifdef KASKO
234 void DumpHungBrowserProcess(const base::string16& channel,
235 const base::Process& process) {
236 // TODO(erikwright): Rather than recreating these crash keys here, it would be
237 // ideal to read them directly from the browser process.
239 // This is looking up the version of chrome_watcher.dll, which is equivalent
240 // for our purposes to chrome.dll.
241 scoped_ptr<FileVersionInfo> version_info(
242 FileVersionInfo::CreateFileVersionInfoForModule(
243 reinterpret_cast<HMODULE>(&__ImageBase)));
244 using CrashKeyStrings = std::pair<base::string16, base::string16>;
245 std::vector<CrashKeyStrings> crash_key_strings;
246 if (version_info.get()) {
247 crash_key_strings.push_back(
248 CrashKeyStrings(L"prod", version_info->product_short_name()));
249 base::string16 version = version_info->product_version();
250 if (!version_info->is_official_build())
251 version.append(base::ASCIIToUTF16("-devel"));
252 crash_key_strings.push_back(CrashKeyStrings(L"ver", version));
253 } else {
254 // No version info found. Make up the values.
255 crash_key_strings.push_back(CrashKeyStrings(L"prod", L"Chrome"));
256 crash_key_strings.push_back(CrashKeyStrings(L"ver", L"0.0.0.0-devel"));
258 crash_key_strings.push_back(CrashKeyStrings(L"channel", channel));
259 crash_key_strings.push_back(CrashKeyStrings(L"plat", L"Win32"));
260 crash_key_strings.push_back(CrashKeyStrings(L"ptype", L"browser"));
261 crash_key_strings.push_back(
262 CrashKeyStrings(L"pid", base::IntToString16(process.Pid())));
263 crash_key_strings.push_back(CrashKeyStrings(L"hung-process", L"1"));
265 std::vector<const base::char16*> key_buffers;
266 std::vector<const base::char16*> value_buffers;
267 for (auto& strings : crash_key_strings) {
268 key_buffers.push_back(strings.first.c_str());
269 value_buffers.push_back(strings.second.c_str());
271 key_buffers.push_back(nullptr);
272 value_buffers.push_back(nullptr);
273 // TODO(erikwright): Make the dump-type channel-dependent.
274 kasko::api::SendReportForProcess(process.Handle(),
275 kasko::api::LARGER_DUMP_TYPE,
276 key_buffers.data(), value_buffers.data());
278 #endif // KASKO
280 } // namespace
282 // The main entry point to the watcher, declared as extern "C" to avoid name
283 // mangling.
284 extern "C" int WatcherMain(const base::char16* registry_path,
285 HANDLE process_handle,
286 HANDLE on_initialized_event_handle,
287 const base::char16* browser_data_directory,
288 const base::char16* message_window_name,
289 const base::char16* channel_name) {
290 base::Process process(process_handle);
291 base::win::ScopedHandle on_initialized_event(on_initialized_event_handle);
293 // The exit manager is in charge of calling the dtors of singletons.
294 base::AtExitManager exit_manager;
295 // Initialize the commandline singleton from the environment.
296 base::CommandLine::Init(0, nullptr);
298 logging::LogEventProvider::Initialize(kChromeWatcherTraceProviderName);
300 // Arrange to be shut down as late as possible, as we want to outlive
301 // chrome.exe in order to report its exit status.
302 ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);
304 base::Callback<void(const base::Process&)> on_hung_callback;
306 #ifdef KASKO
307 bool launched_kasko = kasko::api::InitializeReporter(
308 GetKaskoEndpoint(process.Pid()).c_str(),
309 L"https://clients2.google.com/cr/report",
310 base::FilePath(browser_data_directory)
311 .Append(L"Crash Reports")
312 .value()
313 .c_str(),
314 base::FilePath(browser_data_directory)
315 .Append(kPermanentlyFailedReportsSubdir)
316 .value()
317 .c_str());
318 if (launched_kasko &&
319 base::StringPiece16(channel_name) == installer::kChromeChannelCanary) {
320 on_hung_callback = base::Bind(&DumpHungBrowserProcess, channel_name);
322 #endif // KASKO
324 // Run a UI message loop on the main thread.
325 base::MessageLoop msg_loop(base::MessageLoop::TYPE_UI);
326 msg_loop.set_thread_name("WatcherMainThread");
328 base::RunLoop run_loop;
329 BrowserMonitor monitor(&run_loop, registry_path);
330 if (!monitor.StartWatching(registry_path, process.Duplicate(),
331 on_initialized_event.Pass())) {
332 return 1;
336 // Scoped to force |hang_monitor| destruction before Kasko is shut down.
337 browser_watcher::WindowHangMonitor hang_monitor(
338 base::TimeDelta::FromSeconds(60), base::TimeDelta::FromSeconds(20),
339 base::Bind(&OnWindowEvent, registry_path,
340 base::Passed(process.Duplicate()), on_hung_callback));
341 hang_monitor.Initialize(process.Duplicate(), message_window_name);
343 run_loop.Run();
346 #ifdef KASKO
347 if (launched_kasko)
348 kasko::api::ShutdownReporter();
349 #endif // KASKO
351 // Wind logging down.
352 logging::LogEventProvider::Uninitialize();
354 return 0;
357 static_assert(
358 base::is_same<decltype(&WatcherMain), ChromeWatcherMainFunction>::value,
359 "WatcherMain() has wrong type");