ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / chrome_watcher / chrome_watcher_main.cc
blobb67f13d66d5a3fed14bdd7ced9beccf61fec1fbf
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/files/file_path.h"
12 #include "base/logging_win.h"
13 #include "base/macros.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/message_loop/message_loop_proxy.h"
17 #include "base/process/process.h"
18 #include "base/run_loop.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/synchronization/waitable_event.h"
21 #include "base/template_util.h"
22 #include "base/threading/thread.h"
23 #include "base/time/time.h"
24 #include "base/win/scoped_handle.h"
25 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
26 #include "components/browser_watcher/endsession_watcher_window_win.h"
27 #include "components/browser_watcher/exit_code_watcher_win.h"
28 #include "components/browser_watcher/exit_funnel_win.h"
30 #ifdef SYZYASAN
31 #include "syzygy/kasko/api/reporter.h"
32 #endif
34 namespace {
36 // Use the same log facility as Chrome for convenience.
37 // {7FE69228-633E-4f06-80C1-527FEA23E3A7}
38 const GUID kChromeWatcherTraceProviderName = {
39 0x7fe69228, 0x633e, 0x4f06,
40 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
42 // The amount of time we wait around for a WM_ENDSESSION or a process exit.
43 const int kDelayTimeSeconds = 30;
45 // Takes care of monitoring a browser. This class watches for a browser's exit
46 // code, as well as listening for WM_ENDSESSION messages. Events are recorded in
47 // an exit funnel, for reporting the next time Chrome runs.
48 class BrowserMonitor {
49 public:
50 BrowserMonitor(base::RunLoop* run_loop, const base::char16* registry_path);
51 ~BrowserMonitor();
53 // Initiates the asynchronous monitoring process, returns true on success.
54 // |on_initialized_event| will be signaled immediately before blocking on the
55 // exit of |process|.
56 bool StartWatching(const base::char16* registry_path,
57 base::Process process,
58 base::win::ScopedHandle on_initialized_event);
60 private:
61 // Called from EndSessionWatcherWindow on a end session messages.
62 void OnEndSessionMessage(UINT message, LPARAM lparam);
64 // Blocking function that runs on |background_thread_|. Signals
65 // |on_initialized_event| before waiting for the browser process to exit.
66 void Watch(base::win::ScopedHandle on_initialized_event);
68 // Posted to main thread from Watch when browser exits.
69 void BrowserExited();
71 // The funnel used to record events for this browser.
72 browser_watcher::ExitFunnel exit_funnel_;
74 browser_watcher::ExitCodeWatcher exit_code_watcher_;
75 browser_watcher::EndSessionWatcherWindow end_session_watcher_window_;
77 // The thread that runs Watch().
78 base::Thread background_thread_;
80 // Set when the browser has exited, used to stretch the watcher's lifetime
81 // when WM_ENDSESSION occurs before browser exit.
82 base::WaitableEvent browser_exited_;
84 // The run loop for the main thread and its task runner.
85 base::RunLoop* run_loop_;
86 scoped_refptr<base::SequencedTaskRunner> main_thread_;
88 DISALLOW_COPY_AND_ASSIGN(BrowserMonitor);
91 BrowserMonitor::BrowserMonitor(base::RunLoop* run_loop,
92 const base::char16* registry_path) :
93 browser_exited_(true, false), // manual reset, initially non-signalled.
94 exit_code_watcher_(registry_path),
95 end_session_watcher_window_(
96 base::Bind(&BrowserMonitor::OnEndSessionMessage,
97 base::Unretained(this))),
98 background_thread_("BrowserWatcherThread"),
99 run_loop_(run_loop),
100 main_thread_(base::MessageLoopProxy::current()) {
103 BrowserMonitor::~BrowserMonitor() {
106 bool BrowserMonitor::StartWatching(
107 const base::char16* registry_path,
108 base::Process process,
109 base::win::ScopedHandle on_initialized_event) {
110 if (!exit_code_watcher_.Initialize(process.Pass()))
111 return false;
113 if (!exit_funnel_.Init(registry_path,
114 exit_code_watcher_.process().Handle())) {
115 return false;
118 if (!background_thread_.StartWithOptions(
119 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))) {
120 return false;
123 if (!background_thread_.task_runner()->PostTask(
124 FROM_HERE, base::Bind(&BrowserMonitor::Watch, base::Unretained(this),
125 base::Passed(on_initialized_event.Pass())))) {
126 background_thread_.Stop();
127 return false;
130 return true;
133 void BrowserMonitor::OnEndSessionMessage(UINT message, LPARAM lparam) {
134 DCHECK_EQ(main_thread_, base::MessageLoopProxy::current());
136 if (message == WM_QUERYENDSESSION) {
137 exit_funnel_.RecordEvent(L"WatcherQueryEndSession");
138 } else if (message == WM_ENDSESSION) {
139 exit_funnel_.RecordEvent(L"WatcherEndSession");
141 if (lparam & ENDSESSION_CLOSEAPP)
142 exit_funnel_.RecordEvent(L"ES_CloseApp");
143 if (lparam & ENDSESSION_CRITICAL)
144 exit_funnel_.RecordEvent(L"ES_Critical");
145 if (lparam & ENDSESSION_LOGOFF)
146 exit_funnel_.RecordEvent(L"ES_Logoff");
147 const LPARAM kKnownBits =
148 ENDSESSION_CLOSEAPP | ENDSESSION_CRITICAL | ENDSESSION_LOGOFF;
149 if (lparam & ~kKnownBits)
150 exit_funnel_.RecordEvent(L"ES_Other");
152 // If the browser hasn't exited yet, dally for a bit to try and stretch this
153 // process' lifetime to give it some more time to capture the browser exit.
154 browser_exited_.TimedWait(base::TimeDelta::FromSeconds(kDelayTimeSeconds));
156 run_loop_->Quit();
159 void BrowserMonitor::Watch(base::win::ScopedHandle on_initialized_event) {
160 // This needs to run on an IO thread.
161 DCHECK_NE(main_thread_, base::MessageLoopProxy::current());
163 // Signal our client now that the Kasko reporter is initialized and we have
164 // cleared all of the obstacles that might lead to an early exit.
165 ::SetEvent(on_initialized_event.Get());
166 on_initialized_event.Close();
168 exit_code_watcher_.WaitForExit();
169 exit_funnel_.RecordEvent(L"BrowserExit");
171 // Note that the browser has exited.
172 browser_exited_.Signal();
174 main_thread_->PostTask(FROM_HERE,
175 base::Bind(&BrowserMonitor::BrowserExited, base::Unretained(this)));
178 void BrowserMonitor::BrowserExited() {
179 // This runs in the main thread.
180 DCHECK_EQ(main_thread_, base::MessageLoopProxy::current());
182 // Our background thread has served it's purpose.
183 background_thread_.Stop();
185 const int exit_code = exit_code_watcher_.exit_code();
186 if (exit_code >= 0 && exit_code <= 28) {
187 // The browser exited with a well-known exit code, quit this process
188 // immediately.
189 run_loop_->Quit();
190 } else {
191 // The browser exited abnormally, wait around for a little bit to see
192 // whether this instance will get a logoff message.
193 main_thread_->PostDelayedTask(
194 FROM_HERE,
195 run_loop_->QuitClosure(),
196 base::TimeDelta::FromSeconds(kDelayTimeSeconds));
200 } // namespace
202 // The main entry point to the watcher, declared as extern "C" to avoid name
203 // mangling.
204 extern "C" int WatcherMain(const base::char16* registry_path,
205 HANDLE process_handle,
206 HANDLE on_initialized_event_handle,
207 const base::char16* browser_data_directory) {
208 base::Process process(process_handle);
209 base::win::ScopedHandle on_initialized_event(on_initialized_event_handle);
211 // The exit manager is in charge of calling the dtors of singletons.
212 base::AtExitManager exit_manager;
213 // Initialize the commandline singleton from the environment.
214 base::CommandLine::Init(0, nullptr);
216 logging::LogEventProvider::Initialize(kChromeWatcherTraceProviderName);
218 // Arrange to be shut down as late as possible, as we want to outlive
219 // chrome.exe in order to report its exit status.
220 ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);
222 #ifdef SYZYASAN
223 bool launched_kasko = kasko::api::InitializeReporter(
224 GetKaskoEndpoint(process.Pid()).c_str(),
225 L"https://clients2.google.com/cr/staging_report",
226 base::FilePath(browser_data_directory)
227 .Append(L"Crash Reports")
228 .value()
229 .c_str(),
230 base::FilePath(browser_data_directory)
231 .Append(kPermanentlyFailedReportsSubdir)
232 .value()
233 .c_str());
234 #endif // SYZYASAN
236 // Run a UI message loop on the main thread.
237 base::MessageLoop msg_loop(base::MessageLoop::TYPE_UI);
238 msg_loop.set_thread_name("WatcherMainThread");
240 base::RunLoop run_loop;
241 BrowserMonitor monitor(&run_loop, registry_path);
242 if (!monitor.StartWatching(registry_path, process.Pass(),
243 on_initialized_event.Pass())) {
244 return 1;
247 run_loop.Run();
249 #ifdef SYZYASAN
250 if (launched_kasko)
251 kasko::api::ShutdownReporter();
252 #endif // SYZYASAN
254 // Wind logging down.
255 logging::LogEventProvider::Uninitialize();
257 return 0;
260 static_assert(
261 base::is_same<decltype(&WatcherMain), ChromeWatcherMainFunction>::value,
262 "WatcherMain() has wrong type");