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 54using base::Time; 55using base::TimeDelta; 56using content::BrowserThread; 57 58namespace browser_shutdown { 59 60// Whether the browser is trying to quit (e.g., Quit chosen from menu). 61bool g_trying_to_quit = false; 62 63// Whether the browser should quit without closing browsers. 64bool g_shutting_down_without_closing_browsers = false; 65 66#if defined(OS_WIN) 67upgrade_util::RelaunchMode g_relaunch_mode = 68 upgrade_util::RELAUNCH_MODE_DEFAULT; 69#endif 70 71Time* shutdown_started_ = NULL; 72ShutdownType shutdown_type_ = NOT_VALID; 73int shutdown_num_processes_; 74int shutdown_num_processes_slow_; 75 76const char kShutdownMsFile[] = "chrome_shutdown_ms.txt"; 77 78void RegisterPrefs(PrefRegistrySimple* registry) { 79 registry->RegisterIntegerPref(prefs::kShutdownType, NOT_VALID); 80 registry->RegisterIntegerPref(prefs::kShutdownNumProcesses, 0); 81 registry->RegisterIntegerPref(prefs::kShutdownNumProcessesSlow, 0); 82} 83 84ShutdownType GetShutdownType() { 85 return shutdown_type_; 86} 87 88void 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 120base::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 126bool 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 181void 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 260void 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 304void 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 321void SetTryingToQuit(bool quitting) { 322 g_trying_to_quit = quitting; 323} 324 325bool IsTryingToQuit() { 326 return g_trying_to_quit; 327} 328 329bool ShuttingDownWithoutClosingBrowsers() { 330 return g_shutting_down_without_closing_browsers; 331} 332 333void SetShuttingDownWithoutClosingBrowsers(bool without_close) { 334 g_shutting_down_without_closing_browsers = without_close; 335} 336 337} // namespace browser_shutdown 338