1// Copyright (c) 2011 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#include "chrome/browser/browser_shutdown.h" 6 7#include <map> 8#include <string> 9 10#include "base/command_line.h" 11#include "base/file_path.h" 12#include "base/file_util.h" 13#include "base/metrics/histogram.h" 14#include "base/path_service.h" 15#include "base/process_util.h" 16#include "base/string_number_conversions.h" 17#include "base/string_util.h" 18#include "base/synchronization/waitable_event.h" 19#include "base/threading/thread.h" 20#include "base/threading/thread_restrictions.h" 21#include "base/time.h" 22#include "build/build_config.h" 23#include "chrome/browser/about_flags.h" 24#include "chrome/browser/browser_process.h" 25#include "chrome/browser/first_run/upgrade_util.h" 26#include "chrome/browser/jankometer.h" 27#include "chrome/browser/metrics/metrics_service.h" 28#include "chrome/browser/prefs/pref_service.h" 29#include "chrome/browser/profiles/profile_manager.h" 30#include "chrome/browser/service/service_process_control_manager.h" 31#include "chrome/browser/ui/browser_list.h" 32#include "chrome/browser/ui/webui/chrome_url_data_manager.h" 33#include "chrome/common/chrome_paths.h" 34#include "chrome/common/chrome_switches.h" 35#include "chrome/common/pref_names.h" 36#include "chrome/common/switch_utils.h" 37#include "content/browser/browser_thread.h" 38#include "content/browser/plugin_process_host.h" 39#include "content/browser/renderer_host/render_process_host.h" 40#include "content/browser/renderer_host/render_view_host.h" 41#include "content/browser/renderer_host/render_widget_host.h" 42#include "net/predictor_api.h" 43#include "ui/base/resource/resource_bundle.h" 44 45#if defined(OS_WIN) 46#include "chrome/browser/browser_util_win.h" 47#include "chrome/browser/first_run/upgrade_util_win.h" 48#include "chrome/browser/rlz/rlz.h" 49#endif 50 51#if defined(OS_CHROMEOS) 52#include "chrome/browser/chromeos/boot_times_loader.h" 53#include "chrome/browser/chromeos/cros/cros_library.h" 54#include "chrome/browser/chromeos/cros/login_library.h" 55#include "chrome/browser/chromeos/system_key_event_listener.h" 56#endif 57 58using base::Time; 59using base::TimeDelta; 60 61namespace browser_shutdown { 62 63// Whether the browser is trying to quit (e.g., Quit chosen from menu). 64bool g_trying_to_quit = false; 65 66Time shutdown_started_; 67ShutdownType shutdown_type_ = NOT_VALID; 68int shutdown_num_processes_; 69int shutdown_num_processes_slow_; 70 71bool delete_resources_on_shutdown = true; 72 73const char kShutdownMsFile[] = "chrome_shutdown_ms.txt"; 74 75void RegisterPrefs(PrefService* local_state) { 76 local_state->RegisterIntegerPref(prefs::kShutdownType, NOT_VALID); 77 local_state->RegisterIntegerPref(prefs::kShutdownNumProcesses, 0); 78 local_state->RegisterIntegerPref(prefs::kShutdownNumProcessesSlow, 0); 79} 80 81ShutdownType GetShutdownType() { 82 return shutdown_type_; 83} 84 85void OnShutdownStarting(ShutdownType type) { 86 if (shutdown_type_ != NOT_VALID) 87 return; 88 89 shutdown_type_ = type; 90 // For now, we're only counting the number of renderer processes 91 // since we can't safely count the number of plugin processes from this 92 // thread, and we'd really like to avoid anything which might add further 93 // delays to shutdown time. 94 shutdown_started_ = Time::Now(); 95 96 // Call FastShutdown on all of the RenderProcessHosts. This will be 97 // a no-op in some cases, so we still need to go through the normal 98 // shutdown path for the ones that didn't exit here. 99 shutdown_num_processes_ = 0; 100 shutdown_num_processes_slow_ = 0; 101 for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator()); 102 !i.IsAtEnd(); i.Advance()) { 103 ++shutdown_num_processes_; 104 if (!i.GetCurrentValue()->FastShutdownIfPossible()) 105 ++shutdown_num_processes_slow_; 106 } 107} 108 109FilePath GetShutdownMsPath() { 110 FilePath shutdown_ms_file; 111 PathService::Get(chrome::DIR_USER_DATA, &shutdown_ms_file); 112 return shutdown_ms_file.AppendASCII(kShutdownMsFile); 113} 114 115void Shutdown() { 116#if defined(OS_CHROMEOS) 117 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( 118 "BrowserShutdownStarted", false); 119#endif 120 // During shutdown we will end up some blocking operations. But the 121 // work needs to get done and we're going to wait for them no matter 122 // what thread they're on, so don't worry about it slowing down 123 // shutdown. 124 base::ThreadRestrictions::SetIOAllowed(true); 125 126 // Shutdown all IPC channels to service processes. 127 ServiceProcessControlManager::GetInstance()->Shutdown(); 128 129#if defined(OS_CHROMEOS) 130 // The system key event listener needs to be shut down earlier than when 131 // Singletons are finally destroyed in AtExitManager. 132 chromeos::SystemKeyEventListener::GetInstance()->Stop(); 133#endif 134 135 // WARNING: During logoff/shutdown (WM_ENDSESSION) we may not have enough 136 // time to get here. If you have something that *must* happen on end session, 137 // consider putting it in BrowserProcessImpl::EndSession. 138 DCHECK(g_browser_process); 139 140 // Notifies we are going away. 141 g_browser_process->shutdown_event()->Signal(); 142 143 PrefService* prefs = g_browser_process->local_state(); 144 ProfileManager* profile_manager = g_browser_process->profile_manager(); 145 PrefService* user_prefs = profile_manager->GetDefaultProfile()->GetPrefs(); 146 147 chrome_browser_net::SavePredictorStateForNextStartupAndTrim(user_prefs); 148 149 MetricsService* metrics = g_browser_process->metrics_service(); 150 if (metrics) 151 metrics->RecordCompletedSessionEnd(); 152 153 if (shutdown_type_ > NOT_VALID && shutdown_num_processes_ > 0) { 154 // Record the shutdown info so that we can put it into a histogram at next 155 // startup. 156 prefs->SetInteger(prefs::kShutdownType, shutdown_type_); 157 prefs->SetInteger(prefs::kShutdownNumProcesses, shutdown_num_processes_); 158 prefs->SetInteger(prefs::kShutdownNumProcessesSlow, 159 shutdown_num_processes_slow_); 160 } 161 162 // Check local state for the restart flag so we can restart the session below. 163 bool restart_last_session = false; 164 if (prefs->HasPrefPath(prefs::kRestartLastSessionOnShutdown)) { 165 restart_last_session = 166 prefs->GetBoolean(prefs::kRestartLastSessionOnShutdown); 167 prefs->ClearPref(prefs::kRestartLastSessionOnShutdown); 168 } 169 170 prefs->SavePersistentPrefs(); 171 172#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) 173 // Cleanup any statics created by RLZ. Must be done before NotificationService 174 // is destroyed. 175 RLZTracker::CleanupRlz(); 176#endif 177 178 // The jank'o'meter requires that the browser process has been destroyed 179 // before calling UninstallJankometer(). 180 delete g_browser_process; 181 g_browser_process = NULL; 182#if defined(OS_CHROMEOS) 183 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker("BrowserDeleted", 184 true); 185#endif 186 187 // Uninstall Jank-O-Meter here after the IO thread is no longer running. 188 UninstallJankometer(); 189 190 if (delete_resources_on_shutdown) 191 ResourceBundle::CleanupSharedInstance(); 192 193#if defined(OS_WIN) 194 if (!browser_util::IsBrowserAlreadyRunning() && 195 shutdown_type_ != browser_shutdown::END_SESSION) { 196 upgrade_util::SwapNewChromeExeIfPresent(); 197 } 198#endif 199 200 if (restart_last_session) { 201#if !defined(OS_CHROMEOS) 202 // Make sure to relaunch the browser with the original command line plus 203 // the Restore Last Session flag. Note that Chrome can be launched (ie. 204 // through ShellExecute on Windows) with a switch argument terminator at 205 // the end (double dash, as described in b/1366444) plus a URL, 206 // which prevents us from appending to the command line directly (issue 207 // 46182). We therefore use GetSwitches to copy the command line (it stops 208 // at the switch argument terminator). 209 CommandLine old_cl(*CommandLine::ForCurrentProcess()); 210 scoped_ptr<CommandLine> new_cl(new CommandLine(old_cl.GetProgram())); 211 std::map<std::string, CommandLine::StringType> switches = 212 old_cl.GetSwitches(); 213 // Remove the switches that shouldn't persist across restart. 214 about_flags::RemoveFlagsSwitches(&switches); 215 switches::RemoveSwitchesForAutostart(&switches); 216 // Append the old switches to the new command line. 217 for (std::map<std::string, CommandLine::StringType>::const_iterator i = 218 switches.begin(); i != switches.end(); ++i) { 219 CommandLine::StringType switch_value = i->second; 220 if (!switch_value.empty()) 221 new_cl->AppendSwitchNative(i->first, i->second); 222 else 223 new_cl->AppendSwitch(i->first); 224 } 225 // Ensure restore last session is set. 226 if (!new_cl->HasSwitch(switches::kRestoreLastSession)) 227 new_cl->AppendSwitch(switches::kRestoreLastSession); 228 229#if defined(OS_WIN) || defined(OS_LINUX) 230 upgrade_util::RelaunchChromeBrowser(*new_cl.get()); 231#endif // defined(OS_WIN) || defined(OS_LINUX) 232 233#if defined(OS_MACOSX) 234 new_cl->AppendSwitch(switches::kActivateOnLaunch); 235 base::LaunchApp(*new_cl.get(), false, false, NULL); 236#endif // defined(OS_MACOSX) 237 238#else 239 NOTIMPLEMENTED(); 240#endif // !defined(OS_CHROMEOS) 241 } 242 243 if (shutdown_type_ > NOT_VALID && shutdown_num_processes_ > 0) { 244 // Measure total shutdown time as late in the process as possible 245 // and then write it to a file to be read at startup. 246 // We can't use prefs since all services are shutdown at this point. 247 TimeDelta shutdown_delta = Time::Now() - shutdown_started_; 248 std::string shutdown_ms = 249 base::Int64ToString(shutdown_delta.InMilliseconds()); 250 int len = static_cast<int>(shutdown_ms.length()) + 1; 251 FilePath shutdown_ms_file = GetShutdownMsPath(); 252 file_util::WriteFile(shutdown_ms_file, shutdown_ms.c_str(), len); 253 } 254 255#if defined(OS_CHROMEOS) 256 BrowserList::NotifyAndTerminate(false); 257#endif 258 259 ChromeURLDataManager::DeleteDataSources(); 260} 261 262void ReadLastShutdownFile( 263 ShutdownType type, 264 int num_procs, 265 int num_procs_slow) { 266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 267 268 FilePath shutdown_ms_file = GetShutdownMsPath(); 269 std::string shutdown_ms_str; 270 int64 shutdown_ms = 0; 271 if (file_util::ReadFileToString(shutdown_ms_file, &shutdown_ms_str)) 272 base::StringToInt64(shutdown_ms_str, &shutdown_ms); 273 file_util::Delete(shutdown_ms_file, false); 274 275 if (type == NOT_VALID || shutdown_ms == 0 || num_procs == 0) 276 return; 277 278 const char *time_fmt = "Shutdown.%s.time"; 279 const char *time_per_fmt = "Shutdown.%s.time_per_process"; 280 std::string time; 281 std::string time_per; 282 if (type == WINDOW_CLOSE) { 283 time = StringPrintf(time_fmt, "window_close"); 284 time_per = StringPrintf(time_per_fmt, "window_close"); 285 } else if (type == BROWSER_EXIT) { 286 time = StringPrintf(time_fmt, "browser_exit"); 287 time_per = StringPrintf(time_per_fmt, "browser_exit"); 288 } else if (type == END_SESSION) { 289 time = StringPrintf(time_fmt, "end_session"); 290 time_per = StringPrintf(time_per_fmt, "end_session"); 291 } else { 292 NOTREACHED(); 293 } 294 295 if (time.empty()) 296 return; 297 298 // TODO(erikkay): change these to UMA histograms after a bit more testing. 299 UMA_HISTOGRAM_TIMES(time.c_str(), 300 TimeDelta::FromMilliseconds(shutdown_ms)); 301 UMA_HISTOGRAM_TIMES(time_per.c_str(), 302 TimeDelta::FromMilliseconds(shutdown_ms / num_procs)); 303 UMA_HISTOGRAM_COUNTS_100("Shutdown.renderers.total", num_procs); 304 UMA_HISTOGRAM_COUNTS_100("Shutdown.renderers.slow", num_procs_slow); 305} 306 307void ReadLastShutdownInfo() { 308 PrefService* prefs = g_browser_process->local_state(); 309 ShutdownType type = 310 static_cast<ShutdownType>(prefs->GetInteger(prefs::kShutdownType)); 311 int num_procs = prefs->GetInteger(prefs::kShutdownNumProcesses); 312 int num_procs_slow = prefs->GetInteger(prefs::kShutdownNumProcessesSlow); 313 // clear the prefs immediately so we don't pick them up on a future run 314 prefs->SetInteger(prefs::kShutdownType, NOT_VALID); 315 prefs->SetInteger(prefs::kShutdownNumProcesses, 0); 316 prefs->SetInteger(prefs::kShutdownNumProcessesSlow, 0); 317 318 // Read and delete the file on the file thread. 319 BrowserThread::PostTask( 320 BrowserThread::FILE, FROM_HERE, 321 NewRunnableFunction( 322 &ReadLastShutdownFile, type, num_procs, num_procs_slow)); 323} 324 325void SetTryingToQuit(bool quitting) { 326 g_trying_to_quit = quitting; 327} 328 329bool IsTryingToQuit() { 330 return g_trying_to_quit; 331} 332 333bool ShuttingDownWithoutClosingBrowsers() { 334#if defined(USE_X11) 335 if (GetShutdownType() == browser_shutdown::END_SESSION) 336 return true; 337#endif 338 return false; 339} 340 341} // namespace browser_shutdown 342