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