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_process/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 upgrade_util::RelaunchMode g_relaunch_mode = 68 upgrade_util::RELAUNCH_MODE_DEFAULT; 69 #endif 70 71 Time* shutdown_started_ = NULL; 72 ShutdownType shutdown_type_ = NOT_VALID; 73 int shutdown_num_processes_; 74 int shutdown_num_processes_slow_; 75 76 const char kShutdownMsFile[] = "chrome_shutdown_ms.txt"; 77 78 void RegisterPrefs(PrefRegistrySimple* registry) { 79 registry->RegisterIntegerPref(prefs::kShutdownType, NOT_VALID); 80 registry->RegisterIntegerPref(prefs::kShutdownNumProcesses, 0); 81 registry->RegisterIntegerPref(prefs::kShutdownNumProcessesSlow, 0); 82 } 83 84 ShutdownType GetShutdownType() { 85 return shutdown_type_; 86 } 87 88 void OnShutdownStarting(ShutdownType type) { 89 if (shutdown_type_ != NOT_VALID) 90 return; 91 92 #if !defined(OS_CHROMEOS) 93 // Start the shutdown tracing. Note that On ChromeOS we have started this 94 // already. 95 chrome::StartShutdownTracing(); 96 #endif 97 98 shutdown_type_ = type; 99 // For now, we're only counting the number of renderer processes 100 // since we can't safely count the number of plugin processes from this 101 // thread, and we'd really like to avoid anything which might add further 102 // delays to shutdown time. 103 DCHECK(!shutdown_started_); 104 shutdown_started_ = new Time(Time::Now()); 105 106 // Call FastShutdown on all of the RenderProcessHosts. This will be 107 // a no-op in some cases, so we still need to go through the normal 108 // shutdown path for the ones that didn't exit here. 109 shutdown_num_processes_ = 0; 110 shutdown_num_processes_slow_ = 0; 111 for (content::RenderProcessHost::iterator i( 112 content::RenderProcessHost::AllHostsIterator()); 113 !i.IsAtEnd(); i.Advance()) { 114 ++shutdown_num_processes_; 115 if (!i.GetCurrentValue()->FastShutdownIfPossible()) 116 ++shutdown_num_processes_slow_; 117 } 118 } 119 120 base::FilePath GetShutdownMsPath() { 121 base::FilePath shutdown_ms_file; 122 PathService::Get(chrome::DIR_USER_DATA, &shutdown_ms_file); 123 return shutdown_ms_file.AppendASCII(kShutdownMsFile); 124 } 125 126 bool ShutdownPreThreadsStop() { 127 #if defined(OS_CHROMEOS) 128 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker( 129 "BrowserShutdownStarted", false); 130 #endif 131 132 // Shutdown the IPC channel to the service processes. 133 ServiceProcessControl::GetInstance()->Disconnect(); 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 PrefService* prefs = g_browser_process->local_state(); 139 140 MetricsService* metrics = g_browser_process->metrics_service(); 141 if (metrics) 142 metrics->RecordCompletedSessionEnd(); 143 144 if (shutdown_type_ > NOT_VALID && shutdown_num_processes_ > 0) { 145 // Record the shutdown info so that we can put it into a histogram at next 146 // startup. 147 prefs->SetInteger(prefs::kShutdownType, shutdown_type_); 148 prefs->SetInteger(prefs::kShutdownNumProcesses, shutdown_num_processes_); 149 prefs->SetInteger(prefs::kShutdownNumProcessesSlow, 150 shutdown_num_processes_slow_); 151 } 152 153 // Check local state for the restart flag so we can restart the session below. 154 bool restart_last_session = false; 155 if (prefs->HasPrefPath(prefs::kRestartLastSessionOnShutdown)) { 156 restart_last_session = 157 prefs->GetBoolean(prefs::kRestartLastSessionOnShutdown); 158 prefs->ClearPref(prefs::kRestartLastSessionOnShutdown); 159 #if defined(OS_WIN) 160 if (restart_last_session) { 161 if (prefs->HasPrefPath(prefs::kRelaunchMode)) { 162 g_relaunch_mode = upgrade_util::RelaunchModeStringToEnum( 163 prefs->GetString(prefs::kRelaunchMode)); 164 prefs->ClearPref(prefs::kRelaunchMode); 165 } 166 } 167 #endif 168 } 169 170 prefs->CommitPendingWrite(); 171 172 #if defined(ENABLE_RLZ) 173 // Cleanup any statics created by RLZ. Must be done before NotificationService 174 // is destroyed. 175 RLZTracker::CleanupRlz(); 176 #endif 177 178 return restart_last_session; 179 } 180 181 void ShutdownPostThreadsStop(bool restart_last_session) { 182 // The jank'o'meter requires that the browser process has been destroyed 183 // before calling UninstallJankometer(). 184 delete g_browser_process; 185 g_browser_process = NULL; 186 187 // crbug.com/95079 - This needs to happen after the browser process object 188 // goes away. 189 ProfileManager::NukeDeletedProfilesFromDisk(); 190 191 #if defined(OS_CHROMEOS) 192 chromeos::BootTimesLoader::Get()->AddLogoutTimeMarker("BrowserDeleted", 193 true); 194 #endif 195 196 // Uninstall Jank-O-Meter here after the IO thread is no longer running. 197 UninstallJankometer(); 198 199 #if defined(OS_WIN) 200 if (!browser_util::IsBrowserAlreadyRunning() && 201 shutdown_type_ != browser_shutdown::END_SESSION) { 202 upgrade_util::SwapNewChromeExeIfPresent(); 203 } 204 #endif 205 206 if (restart_last_session) { 207 #if !defined(OS_CHROMEOS) 208 // Make sure to relaunch the browser with the original command line plus 209 // the Restore Last Session flag. Note that Chrome can be launched (ie. 210 // through ShellExecute on Windows) with a switch argument terminator at 211 // the end (double dash, as described in b/1366444) plus a URL, 212 // which prevents us from appending to the command line directly (issue 213 // 46182). We therefore use GetSwitches to copy the command line (it stops 214 // at the switch argument terminator). 215 CommandLine old_cl(*CommandLine::ForCurrentProcess()); 216 scoped_ptr<CommandLine> new_cl(new CommandLine(old_cl.GetProgram())); 217 std::map<std::string, CommandLine::StringType> switches = 218 old_cl.GetSwitches(); 219 // Remove the switches that shouldn't persist across restart. 220 about_flags::RemoveFlagsSwitches(&switches); 221 switches::RemoveSwitchesForAutostart(&switches); 222 // Append the old switches to the new command line. 223 for (std::map<std::string, CommandLine::StringType>::const_iterator i = 224 switches.begin(); i != switches.end(); ++i) { 225 CommandLine::StringType switch_value = i->second; 226 if (!switch_value.empty()) 227 new_cl->AppendSwitchNative(i->first, i->second); 228 else 229 new_cl->AppendSwitch(i->first); 230 } 231 232 #if defined(OS_WIN) 233 upgrade_util::RelaunchChromeWithMode(*new_cl.get(), g_relaunch_mode); 234 #else 235 upgrade_util::RelaunchChromeBrowser(*new_cl.get()); 236 #endif // defined(OS_WIN) 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 base::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 chrome::NotifyAndTerminate(false); 257 #endif 258 } 259 260 void ReadLastShutdownFile(ShutdownType type, 261 int num_procs, 262 int num_procs_slow) { 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 264 265 base::FilePath shutdown_ms_file = GetShutdownMsPath(); 266 std::string shutdown_ms_str; 267 int64 shutdown_ms = 0; 268 if (base::ReadFileToString(shutdown_ms_file, &shutdown_ms_str)) 269 base::StringToInt64(shutdown_ms_str, &shutdown_ms); 270 base::DeleteFile(shutdown_ms_file, false); 271 272 if (type == NOT_VALID || shutdown_ms == 0 || num_procs == 0) 273 return; 274 275 const char* time_fmt = "Shutdown.%s.time"; 276 const char* time_per_fmt = "Shutdown.%s.time_per_process"; 277 std::string time; 278 std::string time_per; 279 if (type == WINDOW_CLOSE) { 280 time = base::StringPrintf(time_fmt, "window_close"); 281 time_per = base::StringPrintf(time_per_fmt, "window_close"); 282 } else if (type == BROWSER_EXIT) { 283 time = base::StringPrintf(time_fmt, "browser_exit"); 284 time_per = base::StringPrintf(time_per_fmt, "browser_exit"); 285 } else if (type == END_SESSION) { 286 time = base::StringPrintf(time_fmt, "end_session"); 287 time_per = base::StringPrintf(time_per_fmt, "end_session"); 288 } else { 289 NOTREACHED(); 290 } 291 292 if (time.empty()) 293 return; 294 295 // TODO(erikkay): change these to UMA histograms after a bit more testing. 296 UMA_HISTOGRAM_TIMES(time.c_str(), 297 TimeDelta::FromMilliseconds(shutdown_ms)); 298 UMA_HISTOGRAM_TIMES(time_per.c_str(), 299 TimeDelta::FromMilliseconds(shutdown_ms / num_procs)); 300 UMA_HISTOGRAM_COUNTS_100("Shutdown.renderers.total", num_procs); 301 UMA_HISTOGRAM_COUNTS_100("Shutdown.renderers.slow", num_procs_slow); 302 } 303 304 void ReadLastShutdownInfo() { 305 PrefService* prefs = g_browser_process->local_state(); 306 ShutdownType type = 307 static_cast<ShutdownType>(prefs->GetInteger(prefs::kShutdownType)); 308 int num_procs = prefs->GetInteger(prefs::kShutdownNumProcesses); 309 int num_procs_slow = prefs->GetInteger(prefs::kShutdownNumProcessesSlow); 310 // clear the prefs immediately so we don't pick them up on a future run 311 prefs->SetInteger(prefs::kShutdownType, NOT_VALID); 312 prefs->SetInteger(prefs::kShutdownNumProcesses, 0); 313 prefs->SetInteger(prefs::kShutdownNumProcessesSlow, 0); 314 315 // Read and delete the file on the file thread. 316 BrowserThread::PostTask( 317 BrowserThread::FILE, FROM_HERE, 318 base::Bind(&ReadLastShutdownFile, type, num_procs, num_procs_slow)); 319 } 320 321 void SetTryingToQuit(bool quitting) { 322 g_trying_to_quit = quitting; 323 } 324 325 bool IsTryingToQuit() { 326 return g_trying_to_quit; 327 } 328 329 bool ShuttingDownWithoutClosingBrowsers() { 330 return g_shutting_down_without_closing_browsers; 331 } 332 333 void SetShuttingDownWithoutClosingBrowsers(bool without_close) { 334 g_shutting_down_without_closing_browsers = without_close; 335 } 336 337 } // namespace browser_shutdown 338