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