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