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.
7 #include "base/at_exit.h"
9 #include "base/bind_helpers.h"
10 #include "base/command_line.h"
11 #include "base/logging_win.h"
12 #include "base/macros.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/process/process.h"
17 #include "base/run_loop.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/synchronization/waitable_event.h"
20 #include "base/template_util.h"
21 #include "base/threading/thread.h"
22 #include "base/time/time.h"
23 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
24 #include "components/browser_watcher/endsession_watcher_window_win.h"
25 #include "components/browser_watcher/exit_code_watcher_win.h"
26 #include "components/browser_watcher/exit_funnel_win.h"
30 // Use the same log facility as Chrome for convenience.
31 // {7FE69228-633E-4f06-80C1-527FEA23E3A7}
32 const GUID kChromeWatcherTraceProviderName
= {
33 0x7fe69228, 0x633e, 0x4f06,
34 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
36 // Takes care of monitoring a browser. This class watches for a browser's exit
37 // code, as well as listening for WM_ENDSESSION messages. Events are recorded
38 // in an exit funnel, for reporting the next time Chrome runs.
39 class BrowserMonitor
{
41 BrowserMonitor(base::RunLoop
* run_loop
, const base::char16
* registry_path
);
44 // Starts the monitor, returns true on success.
45 bool StartWatching(const base::char16
* registry_path
,
46 base::Process process
);
49 // Called from EndSessionWatcherWindow on a end session messages.
50 void OnEndSessionMessage(UINT message
, LPARAM lparam
);
52 // Blocking function that runs on |background_thread_|.
55 // Posted to main thread from Watch when browser exits.
58 // True if BrowserExited has run.
61 // The funnel used to record events for this browser.
62 browser_watcher::ExitFunnel exit_funnel_
;
64 browser_watcher::ExitCodeWatcher exit_code_watcher_
;
65 browser_watcher::EndSessionWatcherWindow end_session_watcher_window_
;
67 // The thread that runs Watch().
68 base::Thread background_thread_
;
70 // The run loop for the main thread and its task runner.
71 base::RunLoop
* run_loop_
;
72 scoped_refptr
<base::SequencedTaskRunner
> main_thread_
;
74 DISALLOW_COPY_AND_ASSIGN(BrowserMonitor
);
77 BrowserMonitor::BrowserMonitor(base::RunLoop
* run_loop
,
78 const base::char16
* registry_path
) :
79 browser_exited_(false),
80 exit_code_watcher_(registry_path
),
81 end_session_watcher_window_(
82 base::Bind(&BrowserMonitor::OnEndSessionMessage
,
83 base::Unretained(this))),
84 background_thread_("BrowserWatcherThread"),
86 main_thread_(base::MessageLoopProxy::current()) {
89 BrowserMonitor::~BrowserMonitor() {
92 bool BrowserMonitor::StartWatching(const base::char16
* registry_path
,
93 base::Process process
) {
94 if (!exit_code_watcher_
.Initialize(process
.Pass()))
97 if (!exit_funnel_
.Init(registry_path
,
98 exit_code_watcher_
.process().Handle())) {
102 if (!background_thread_
.StartWithOptions(
103 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0))) {
107 if (!background_thread_
.task_runner()->PostTask(FROM_HERE
,
108 base::Bind(&BrowserMonitor::Watch
, base::Unretained(this)))) {
109 background_thread_
.Stop();
116 void BrowserMonitor::OnEndSessionMessage(UINT message
, LPARAM lparam
) {
117 DCHECK_EQ(main_thread_
, base::MessageLoopProxy::current());
119 if (message
== WM_QUERYENDSESSION
) {
120 exit_funnel_
.RecordEvent(L
"WatcherQueryEndSession");
121 } else if (message
== WM_ENDSESSION
) {
122 exit_funnel_
.RecordEvent(L
"WatcherEndSession");
124 if (lparam
& ENDSESSION_CLOSEAPP
)
125 exit_funnel_
.RecordEvent(L
"ES_CloseApp");
126 if (lparam
& ENDSESSION_CRITICAL
)
127 exit_funnel_
.RecordEvent(L
"ES_Critical");
128 if (lparam
& ENDSESSION_LOGOFF
)
129 exit_funnel_
.RecordEvent(L
"ES_Logoff");
130 const LPARAM kKnownBits
=
131 ENDSESSION_CLOSEAPP
| ENDSESSION_CRITICAL
| ENDSESSION_LOGOFF
;
132 if (lparam
& ~kKnownBits
)
133 exit_funnel_
.RecordEvent(L
"ES_Other");
135 // Belt-and-suspenders; make sure our message loop exits ASAP.
140 void BrowserMonitor::Watch() {
141 // This needs to run on an IO thread.
142 DCHECK_NE(main_thread_
, base::MessageLoopProxy::current());
144 exit_code_watcher_
.WaitForExit();
145 exit_funnel_
.RecordEvent(L
"BrowserExit");
147 main_thread_
->PostTask(FROM_HERE
,
148 base::Bind(&BrowserMonitor::BrowserExited
, base::Unretained(this)));
151 void BrowserMonitor::BrowserExited() {
152 // This runs in the main thread.
153 DCHECK_EQ(main_thread_
, base::MessageLoopProxy::current());
155 // Note that the browser has exited.
156 browser_exited_
= true;
158 // Our background thread has served it's purpose.
159 background_thread_
.Stop();
161 const int exit_code
= exit_code_watcher_
.exit_code();
162 if (exit_code
>= 0 && exit_code
<= 28) {
163 // The browser exited with a well-known exit code, quit this process
167 // The browser exited abnormally, wait around for a little bit to see
168 // whether this instance will get a logoff message.
169 main_thread_
->PostDelayedTask(FROM_HERE
,
170 run_loop_
->QuitClosure(),
171 base::TimeDelta::FromSeconds(30));
177 // The main entry point to the watcher, declared as extern "C" to avoid name
179 extern "C" int WatcherMain(const base::char16
* registry_path
,
180 HANDLE process_handle
) {
181 base::Process
process(process_handle
);
183 // The exit manager is in charge of calling the dtors of singletons.
184 base::AtExitManager exit_manager
;
185 // Initialize the commandline singleton from the environment.
186 base::CommandLine::Init(0, nullptr);
188 logging::LogEventProvider::Initialize(kChromeWatcherTraceProviderName
);
190 // Arrange to be shut down as late as possible, as we want to outlive
191 // chrome.exe in order to report its exit status.
192 ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY
);
194 // Run a UI message loop on the main thread.
195 base::MessageLoop
msg_loop(base::MessageLoop::TYPE_UI
);
196 msg_loop
.set_thread_name("WatcherMainThread");
198 base::RunLoop run_loop
;
199 BrowserMonitor
monitor(&run_loop
, registry_path
);
200 if (!monitor
.StartWatching(registry_path
, process
.Pass()))
205 // Wind logging down.
206 logging::LogEventProvider::Uninitialize();
212 base::is_same
<decltype(&WatcherMain
), ChromeWatcherMainFunction
>::value
,
213 "WatcherMain() has wrong type");