Home | History | Annotate | Download | only in browser
      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