1 // Copyright (c) 2012 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/bind.h" 11 #include "base/command_line.h" 12 #include "base/file_util.h" 13 #include "base/files/file_path.h" 14 #include "base/metrics/histogram.h" 15 #include "base/path_service.h" 16 #include "base/prefs/pref_registry_simple.h" 17 #include "base/prefs/pref_service.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/stringprintf.h" 20 #include "base/threading/thread.h" 21 #include "base/threading/thread_restrictions.h" 22 #include "base/time/time.h" 23 #include "build/build_config.h" 24 #include "chrome/browser/about_flags.h" 25 #include "chrome/browser/browser_process.h" 26 #include "chrome/browser/first_run/upgrade_util.h" 27 #include "chrome/browser/jankometer.h" 28 #include "chrome/browser/lifetime/application_lifetime.h" 29 #include "chrome/browser/metrics/metrics_service.h" 30 #include "chrome/browser/profiles/profile_manager.h" 31 #include "chrome/browser/service/service_process_control.h" 32 #include "chrome/common/chrome_paths.h" 33 #include "chrome/common/chrome_switches.h" 34 #include "chrome/common/pref_names.h" 35 #include "chrome/common/switch_utils.h" 36 #include "content/public/browser/browser_thread.h" 37 #include "content/public/browser/render_process_host.h" 38 #include "content/public/browser/render_view_host.h" 39 #include "ui/base/resource/resource_bundle.h" 40 41 #if defined(OS_WIN) 42 #include "chrome/browser/browser_util_win.h" 43 #include "chrome/browser/first_run/upgrade_util_win.h" 44 #endif 45 46 #if defined(ENABLE_RLZ) 47 #include "chrome/browser/rlz/rlz.h" 48 #endif 49 50 #if defined(OS_CHROMEOS) 51 #include "chrome/browser/chromeos/boot_times_loader.h" 52 #endif 53 54 using base::Time; 55 using base::TimeDelta; 56 using content::BrowserThread; 57 58 namespace browser_shutdown { 59 60 // Whether the browser is trying to quit (e.g., Quit chosen from menu). 61 bool g_trying_to_quit = false; 62 63 // Whether the browser should quit without closing browsers. 64 bool g_shutting_down_without_closing_browsers = false; 65 66 #if defined(OS_WIN) 67 // Whether the next restart should happen in the opposite mode; desktop or 68 // metro mode. Windows 8 only. 69 bool g_mode_switch = false; 70 #endif 71 72 Time* shutdown_started_ = NULL; 73 ShutdownType shutdown_type_ = NOT_VALID; 74 int shutdown_num_processes_; 75 int shutdown_num_processes_slow_; 76 77 const char kShutdownMsFile[] = "chrome_shutdown_ms.txt"; 78 79 void RegisterPrefs(PrefRegistrySimple* registry) { 80 registry->RegisterIntegerPref(prefs::kShutdownType, NOT_VALID); 81 registry->RegisterIntegerPref(prefs::kShutdownNumProcesses, 0); 82 registry->RegisterIntegerPref(prefs::kShutdownNumProcessesSlow, 0); 83 } 84 85 ShutdownType GetShutdownType() { 86 return shutdown_type_; 87 } 88 89 void OnShutdownStarting(ShutdownType type) { 90 if (shutdown_type_ != NOT_VALID) 91 return; 92 93 shutdown_type_ = type; 94 // For now, we're only counting the number of renderer processes 95 // since we can't safely count the number of plugin processes from this 96 // thread, and we'd really like to avoid anything which might add further 97 // delays to shutdown time. 98 DCHECK(!shutdown_started_); 99 shutdown_started_ = new Time(Time::Now()); 100 101 // Call FastShutdown on all of the RenderProcessHosts. This will be 102 // a no-op in some cases, so we still need to go through the normal 103 // shutdown path for the ones that didn't exit here. 104 shutdown_num_processes_ = 0; 105 shutdown_num_processes_slow_ = 0; 106 for (content::RenderProcessHost::iterator i( 107 content::RenderProcessHost::AllHostsIterator()); 108 !i.IsAtEnd(); i.Advance()) { 109 ++shutdown_num_processes_; 110 if (!i.GetCurrentValue()->FastShutdownIfPossible()) 111 ++shutdown_num_processes_slow_; 112 } 113 } 114 115 base::FilePath GetShutdownMsPath() { 116 base::FilePath shutdown_ms_file; 117 PathService::Get(chrome::DIR_USER_DATA, &shutdown_ms_file); 118 return shutdown_ms_file.AppendASCII(kShutdownMsFile); 119 } 120 121 bool ShutdownPreThreadsStop() { 122 #if defined(OS_CHROMEOS) 123 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( 124 "BrowserShutdownStarted", false); 125 #endif 126 127 // Shutdown the IPC channel to the service processes. 128 ServiceProcessControl::GetInstance()->Disconnect(); 129 130 // WARNING: During logoff/shutdown (WM_ENDSESSION) we may not have enough 131 // time to get here. If you have something that *must* happen on end session, 132 // consider putting it in BrowserProcessImpl::EndSession. 133 PrefService* prefs = g_browser_process->local_state(); 134 135 MetricsService* metrics = g_browser_process->metrics_service(); 136 if (metrics) 137 metrics->RecordCompletedSessionEnd(); 138 139 if (shutdown_type_ > NOT_VALID && shutdown_num_processes_ > 0) { 140 // Record the shutdown info so that we can put it into a histogram at next 141 // startup. 142 prefs->SetInteger(prefs::kShutdownType, shutdown_type_); 143 prefs->SetInteger(prefs::kShutdownNumProcesses, shutdown_num_processes_); 144 prefs->SetInteger(prefs::kShutdownNumProcessesSlow, 145 shutdown_num_processes_slow_); 146 } 147 148 // Check local state for the restart flag so we can restart the session below. 149 bool restart_last_session = false; 150 if (prefs->HasPrefPath(prefs::kRestartLastSessionOnShutdown)) { 151 restart_last_session = 152 prefs->GetBoolean(prefs::kRestartLastSessionOnShutdown); 153 prefs->ClearPref(prefs::kRestartLastSessionOnShutdown); 154 #if defined(OS_WIN) 155 if (restart_last_session) { 156 if (prefs->HasPrefPath(prefs::kRestartSwitchMode)) { 157 g_mode_switch = prefs->GetBoolean(prefs::kRestartSwitchMode); 158 prefs->SetBoolean(prefs::kRestartSwitchMode, false); 159 } 160 } 161 #endif 162 } 163 164 prefs->CommitPendingWrite(); 165 166 #if defined(ENABLE_RLZ) 167 // Cleanup any statics created by RLZ. Must be done before NotificationService 168 // is destroyed. 169 RLZTracker::CleanupRlz(); 170 #endif 171 172 return restart_last_session; 173 } 174 175 void ShutdownPostThreadsStop(bool restart_last_session) { 176 // The jank'o'meter requires that the browser process has been destroyed 177 // before calling UninstallJankometer(). 178 delete g_browser_process; 179 g_browser_process = NULL; 180 181 // crbug.com/95079 - This needs to happen after the browser process object 182 // goes away. 183 ProfileManager::NukeDeletedProfilesFromDisk(); 184 185 #if defined(OS_CHROMEOS) 186 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker("BrowserDeleted", 187 true); 188 #endif 189 190 // Uninstall Jank-O-Meter here after the IO thread is no longer running. 191 UninstallJankometer(); 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 226 #if defined(OS_WIN) 227 // On Windows 8 we can relaunch in metro or desktop mode. 228 if (g_mode_switch) 229 upgrade_util::RelaunchChromeWithModeSwitch(*new_cl.get()); 230 else 231 upgrade_util::RelaunchChromeBrowser(*new_cl.get()); 232 #else 233 upgrade_util::RelaunchChromeBrowser(*new_cl.get()); 234 #endif // defined(OS_WIN) 235 236 #else 237 NOTIMPLEMENTED(); 238 #endif // !defined(OS_CHROMEOS) 239 } 240 241 if (shutdown_type_ > NOT_VALID && shutdown_num_processes_ > 0) { 242 // Measure total shutdown time as late in the process as possible 243 // and then write it to a file to be read at startup. 244 // We can't use prefs since all services are shutdown at this point. 245 TimeDelta shutdown_delta = Time::Now() - *shutdown_started_; 246 std::string shutdown_ms = 247 base::Int64ToString(shutdown_delta.InMilliseconds()); 248 int len = static_cast<int>(shutdown_ms.length()) + 1; 249 base::FilePath shutdown_ms_file = GetShutdownMsPath(); 250 file_util::WriteFile(shutdown_ms_file, shutdown_ms.c_str(), len); 251 } 252 253 #if defined(OS_CHROMEOS) 254 chrome::NotifyAndTerminate(false); 255 #endif 256 } 257 258 void ReadLastShutdownFile(ShutdownType type, 259 int num_procs, 260 int num_procs_slow) { 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 262 263 base::FilePath shutdown_ms_file = GetShutdownMsPath(); 264 std::string shutdown_ms_str; 265 int64 shutdown_ms = 0; 266 if (file_util::ReadFileToString(shutdown_ms_file, &shutdown_ms_str)) 267 base::StringToInt64(shutdown_ms_str, &shutdown_ms); 268 base::DeleteFile(shutdown_ms_file, false); 269 270 if (type == NOT_VALID || shutdown_ms == 0 || num_procs == 0) 271 return; 272 273 const char* time_fmt = "Shutdown.%s.time"; 274 const char* time_per_fmt = "Shutdown.%s.time_per_process"; 275 std::string time; 276 std::string time_per; 277 if (type == WINDOW_CLOSE) { 278 time = base::StringPrintf(time_fmt, "window_close"); 279 time_per = base::StringPrintf(time_per_fmt, "window_close"); 280 } else if (type == BROWSER_EXIT) { 281 time = base::StringPrintf(time_fmt, "browser_exit"); 282 time_per = base::StringPrintf(time_per_fmt, "browser_exit"); 283 } else if (type == END_SESSION) { 284 time = base::StringPrintf(time_fmt, "end_session"); 285 time_per = base::StringPrintf(time_per_fmt, "end_session"); 286 } else { 287 NOTREACHED(); 288 } 289 290 if (time.empty()) 291 return; 292 293 // TODO(erikkay): change these to UMA histograms after a bit more testing. 294 UMA_HISTOGRAM_TIMES(time.c_str(), 295 TimeDelta::FromMilliseconds(shutdown_ms)); 296 UMA_HISTOGRAM_TIMES(time_per.c_str(), 297 TimeDelta::FromMilliseconds(shutdown_ms / num_procs)); 298 UMA_HISTOGRAM_COUNTS_100("Shutdown.renderers.total", num_procs); 299 UMA_HISTOGRAM_COUNTS_100("Shutdown.renderers.slow", num_procs_slow); 300 } 301 302 void ReadLastShutdownInfo() { 303 PrefService* prefs = g_browser_process->local_state(); 304 ShutdownType type = 305 static_cast<ShutdownType>(prefs->GetInteger(prefs::kShutdownType)); 306 int num_procs = prefs->GetInteger(prefs::kShutdownNumProcesses); 307 int num_procs_slow = prefs->GetInteger(prefs::kShutdownNumProcessesSlow); 308 // clear the prefs immediately so we don't pick them up on a future run 309 prefs->SetInteger(prefs::kShutdownType, NOT_VALID); 310 prefs->SetInteger(prefs::kShutdownNumProcesses, 0); 311 prefs->SetInteger(prefs::kShutdownNumProcessesSlow, 0); 312 313 // Read and delete the file on the file thread. 314 BrowserThread::PostTask( 315 BrowserThread::FILE, FROM_HERE, 316 base::Bind(&ReadLastShutdownFile, type, num_procs, num_procs_slow)); 317 } 318 319 void SetTryingToQuit(bool quitting) { 320 g_trying_to_quit = quitting; 321 } 322 323 bool IsTryingToQuit() { 324 return g_trying_to_quit; 325 } 326 327 bool ShuttingDownWithoutClosingBrowsers() { 328 return g_shutting_down_without_closing_browsers; 329 } 330 331 void SetShuttingDownWithoutClosingBrowsers(bool without_close) { 332 g_shutting_down_without_closing_browsers = without_close; 333 } 334 335 } // namespace browser_shutdown 336