| OLD | NEW |
| 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <windows.h> | 5 #include <windows.h> |
| 6 | 6 |
| 7 #include "base/at_exit.h" | 7 #include "base/at_exit.h" |
| 8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/bind_helpers.h" | 9 #include "base/bind_helpers.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 #include "components/browser_watcher/exit_funnel_win.h" | 26 #include "components/browser_watcher/exit_funnel_win.h" |
| 27 | 27 |
| 28 namespace { | 28 namespace { |
| 29 | 29 |
| 30 // Use the same log facility as Chrome for convenience. | 30 // Use the same log facility as Chrome for convenience. |
| 31 // {7FE69228-633E-4f06-80C1-527FEA23E3A7} | 31 // {7FE69228-633E-4f06-80C1-527FEA23E3A7} |
| 32 const GUID kChromeWatcherTraceProviderName = { | 32 const GUID kChromeWatcherTraceProviderName = { |
| 33 0x7fe69228, 0x633e, 0x4f06, | 33 0x7fe69228, 0x633e, 0x4f06, |
| 34 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } }; | 34 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } }; |
| 35 | 35 |
| 36 // The amount of time we wait around for a WM_ENDSESSION or a process exit. |
| 37 const int kDelayTimeSeconds = 30; |
| 38 |
| 36 // Takes care of monitoring a browser. This class watches for a browser's exit | 39 // 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 in | 40 // code, as well as listening for WM_ENDSESSION messages. Events are recorded in |
| 38 // an exit funnel, for reporting the next time Chrome runs. | 41 // an exit funnel, for reporting the next time Chrome runs. |
| 39 class BrowserMonitor { | 42 class BrowserMonitor { |
| 40 public: | 43 public: |
| 41 BrowserMonitor(base::RunLoop* run_loop, const base::char16* registry_path); | 44 BrowserMonitor(base::RunLoop* run_loop, const base::char16* registry_path); |
| 42 ~BrowserMonitor(); | 45 ~BrowserMonitor(); |
| 43 | 46 |
| 44 // Initiates the asynchronous monitoring process, returns true on success. | 47 // Initiates the asynchronous monitoring process, returns true on success. |
| 45 // |on_initialized_event| will be signaled immediately before blocking on the | 48 // |on_initialized_event| will be signaled immediately before blocking on the |
| 46 // exit of |process|. | 49 // exit of |process|. |
| 47 bool StartWatching(const base::char16* registry_path, | 50 bool StartWatching(const base::char16* registry_path, |
| 48 base::Process process, | 51 base::Process process, |
| 49 base::win::ScopedHandle on_initialized_event); | 52 base::win::ScopedHandle on_initialized_event); |
| 50 | 53 |
| 51 private: | 54 private: |
| 52 // Called from EndSessionWatcherWindow on a end session messages. | 55 // Called from EndSessionWatcherWindow on a end session messages. |
| 53 void OnEndSessionMessage(UINT message, LPARAM lparam); | 56 void OnEndSessionMessage(UINT message, LPARAM lparam); |
| 54 | 57 |
| 55 // Blocking function that runs on |background_thread_|. Signals | 58 // Blocking function that runs on |background_thread_|. Signals |
| 56 // |on_initialized_event| before waiting for the browser process to exit. | 59 // |on_initialized_event| before waiting for the browser process to exit. |
| 57 void Watch(base::win::ScopedHandle on_initialized_event); | 60 void Watch(base::win::ScopedHandle on_initialized_event); |
| 58 | 61 |
| 59 // Posted to main thread from Watch when browser exits. | 62 // Posted to main thread from Watch when browser exits. |
| 60 void BrowserExited(); | 63 void BrowserExited(); |
| 61 | 64 |
| 62 // True if BrowserExited has run. | |
| 63 bool browser_exited_; | |
| 64 | |
| 65 // The funnel used to record events for this browser. | 65 // The funnel used to record events for this browser. |
| 66 browser_watcher::ExitFunnel exit_funnel_; | 66 browser_watcher::ExitFunnel exit_funnel_; |
| 67 | 67 |
| 68 browser_watcher::ExitCodeWatcher exit_code_watcher_; | 68 browser_watcher::ExitCodeWatcher exit_code_watcher_; |
| 69 browser_watcher::EndSessionWatcherWindow end_session_watcher_window_; | 69 browser_watcher::EndSessionWatcherWindow end_session_watcher_window_; |
| 70 | 70 |
| 71 // The thread that runs Watch(). | 71 // The thread that runs Watch(). |
| 72 base::Thread background_thread_; | 72 base::Thread background_thread_; |
| 73 | 73 |
| 74 // Set when the browser has exited, used to stretch the watcher's lifetime |
| 75 // when WM_ENDSESSION occurs before browser exit. |
| 76 base::WaitableEvent browser_exited_; |
| 77 |
| 74 // The run loop for the main thread and its task runner. | 78 // The run loop for the main thread and its task runner. |
| 75 base::RunLoop* run_loop_; | 79 base::RunLoop* run_loop_; |
| 76 scoped_refptr<base::SequencedTaskRunner> main_thread_; | 80 scoped_refptr<base::SequencedTaskRunner> main_thread_; |
| 77 | 81 |
| 78 DISALLOW_COPY_AND_ASSIGN(BrowserMonitor); | 82 DISALLOW_COPY_AND_ASSIGN(BrowserMonitor); |
| 79 }; | 83 }; |
| 80 | 84 |
| 81 BrowserMonitor::BrowserMonitor(base::RunLoop* run_loop, | 85 BrowserMonitor::BrowserMonitor(base::RunLoop* run_loop, |
| 82 const base::char16* registry_path) : | 86 const base::char16* registry_path) : |
| 83 browser_exited_(false), | 87 browser_exited_(true, false), // manual reset, initially non-signalled. |
| 84 exit_code_watcher_(registry_path), | 88 exit_code_watcher_(registry_path), |
| 85 end_session_watcher_window_( | 89 end_session_watcher_window_( |
| 86 base::Bind(&BrowserMonitor::OnEndSessionMessage, | 90 base::Bind(&BrowserMonitor::OnEndSessionMessage, |
| 87 base::Unretained(this))), | 91 base::Unretained(this))), |
| 88 background_thread_("BrowserWatcherThread"), | 92 background_thread_("BrowserWatcherThread"), |
| 89 run_loop_(run_loop), | 93 run_loop_(run_loop), |
| 90 main_thread_(base::MessageLoopProxy::current()) { | 94 main_thread_(base::MessageLoopProxy::current()) { |
| 91 } | 95 } |
| 92 | 96 |
| 93 BrowserMonitor::~BrowserMonitor() { | 97 BrowserMonitor::~BrowserMonitor() { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 exit_funnel_.RecordEvent(L"ES_CloseApp"); | 136 exit_funnel_.RecordEvent(L"ES_CloseApp"); |
| 133 if (lparam & ENDSESSION_CRITICAL) | 137 if (lparam & ENDSESSION_CRITICAL) |
| 134 exit_funnel_.RecordEvent(L"ES_Critical"); | 138 exit_funnel_.RecordEvent(L"ES_Critical"); |
| 135 if (lparam & ENDSESSION_LOGOFF) | 139 if (lparam & ENDSESSION_LOGOFF) |
| 136 exit_funnel_.RecordEvent(L"ES_Logoff"); | 140 exit_funnel_.RecordEvent(L"ES_Logoff"); |
| 137 const LPARAM kKnownBits = | 141 const LPARAM kKnownBits = |
| 138 ENDSESSION_CLOSEAPP | ENDSESSION_CRITICAL | ENDSESSION_LOGOFF; | 142 ENDSESSION_CLOSEAPP | ENDSESSION_CRITICAL | ENDSESSION_LOGOFF; |
| 139 if (lparam & ~kKnownBits) | 143 if (lparam & ~kKnownBits) |
| 140 exit_funnel_.RecordEvent(L"ES_Other"); | 144 exit_funnel_.RecordEvent(L"ES_Other"); |
| 141 | 145 |
| 142 // Belt-and-suspenders; make sure our message loop exits ASAP. | 146 // If the browser hasn't exited yet, dally for a bit to try and stretch this |
| 143 if (browser_exited_) | 147 // process' lifetime to give it some more time to capture the browser exit. |
| 144 run_loop_->Quit(); | 148 browser_exited_.TimedWait(base::TimeDelta::FromSeconds(kDelayTimeSeconds)); |
| 149 |
| 150 run_loop_->Quit(); |
| 145 } | 151 } |
| 146 | 152 |
| 147 void BrowserMonitor::Watch(base::win::ScopedHandle on_initialized_event) { | 153 void BrowserMonitor::Watch(base::win::ScopedHandle on_initialized_event) { |
| 148 // This needs to run on an IO thread. | 154 // This needs to run on an IO thread. |
| 149 DCHECK_NE(main_thread_, base::MessageLoopProxy::current()); | 155 DCHECK_NE(main_thread_, base::MessageLoopProxy::current()); |
| 150 | 156 |
| 151 // Signal our client now that the Kasko reporter is initialized and we have | 157 // Signal our client now that the Kasko reporter is initialized and we have |
| 152 // cleared all of the obstacles that might lead to an early exit. | 158 // cleared all of the obstacles that might lead to an early exit. |
| 153 ::SetEvent(on_initialized_event.Get()); | 159 ::SetEvent(on_initialized_event.Get()); |
| 154 on_initialized_event.Close(); | 160 on_initialized_event.Close(); |
| 155 | 161 |
| 156 exit_code_watcher_.WaitForExit(); | 162 exit_code_watcher_.WaitForExit(); |
| 157 exit_funnel_.RecordEvent(L"BrowserExit"); | 163 exit_funnel_.RecordEvent(L"BrowserExit"); |
| 158 | 164 |
| 165 // Note that the browser has exited. |
| 166 browser_exited_.Signal(); |
| 167 |
| 159 main_thread_->PostTask(FROM_HERE, | 168 main_thread_->PostTask(FROM_HERE, |
| 160 base::Bind(&BrowserMonitor::BrowserExited, base::Unretained(this))); | 169 base::Bind(&BrowserMonitor::BrowserExited, base::Unretained(this))); |
| 161 } | 170 } |
| 162 | 171 |
| 163 void BrowserMonitor::BrowserExited() { | 172 void BrowserMonitor::BrowserExited() { |
| 164 // This runs in the main thread. | 173 // This runs in the main thread. |
| 165 DCHECK_EQ(main_thread_, base::MessageLoopProxy::current()); | 174 DCHECK_EQ(main_thread_, base::MessageLoopProxy::current()); |
| 166 | 175 |
| 167 // Note that the browser has exited. | |
| 168 browser_exited_ = true; | |
| 169 | |
| 170 // Our background thread has served it's purpose. | 176 // Our background thread has served it's purpose. |
| 171 background_thread_.Stop(); | 177 background_thread_.Stop(); |
| 172 | 178 |
| 173 const int exit_code = exit_code_watcher_.exit_code(); | 179 const int exit_code = exit_code_watcher_.exit_code(); |
| 174 if (exit_code >= 0 && exit_code <= 28) { | 180 if (exit_code >= 0 && exit_code <= 28) { |
| 175 // The browser exited with a well-known exit code, quit this process | 181 // The browser exited with a well-known exit code, quit this process |
| 176 // immediately. | 182 // immediately. |
| 177 run_loop_->Quit(); | 183 run_loop_->Quit(); |
| 178 } else { | 184 } else { |
| 179 // The browser exited abnormally, wait around for a little bit to see | 185 // The browser exited abnormally, wait around for a little bit to see |
| 180 // whether this instance will get a logoff message. | 186 // whether this instance will get a logoff message. |
| 181 main_thread_->PostDelayedTask(FROM_HERE, | 187 main_thread_->PostDelayedTask( |
| 182 run_loop_->QuitClosure(), | 188 FROM_HERE, |
| 183 base::TimeDelta::FromSeconds(30)); | 189 run_loop_->QuitClosure(), |
| 190 base::TimeDelta::FromSeconds(kDelayTimeSeconds)); |
| 184 } | 191 } |
| 185 } | 192 } |
| 186 | 193 |
| 187 } // namespace | 194 } // namespace |
| 188 | 195 |
| 189 // The main entry point to the watcher, declared as extern "C" to avoid name | 196 // The main entry point to the watcher, declared as extern "C" to avoid name |
| 190 // mangling. | 197 // mangling. |
| 191 extern "C" int WatcherMain(const base::char16* registry_path, | 198 extern "C" int WatcherMain(const base::char16* registry_path, |
| 192 HANDLE process_handle, | 199 HANDLE process_handle, |
| 193 HANDLE on_initialized_event_handle) { | 200 HANDLE on_initialized_event_handle) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 220 | 227 |
| 221 // Wind logging down. | 228 // Wind logging down. |
| 222 logging::LogEventProvider::Uninitialize(); | 229 logging::LogEventProvider::Uninitialize(); |
| 223 | 230 |
| 224 return 0; | 231 return 0; |
| 225 } | 232 } |
| 226 | 233 |
| 227 static_assert( | 234 static_assert( |
| 228 base::is_same<decltype(&WatcherMain), ChromeWatcherMainFunction>::value, | 235 base::is_same<decltype(&WatcherMain), ChromeWatcherMainFunction>::value, |
| 229 "WatcherMain() has wrong type"); | 236 "WatcherMain() has wrong type"); |
| OLD | NEW |