This source file includes following definitions.
- TimeToString
- StringToTime
- MaybeGetURLFromRenderView
- DeleteDatabaseOnBackgroundThread
- disable_timer_autostart_for_testing_
- SetDatabasePath
- GetInstance
- Initialize
- InitOnBackgroundThread
- FinishInit
- StartGatherCycle
- RegisterForNotifications
- CheckForUncleanExits
- AddUncleanExitEventOnBackgroundThread
- CheckForVersionUpdateOnBackgroundThread
- AddEvent
- AddEventOnBackgroundThread
- AddMetricOnBackgroundThread
- NotifyInitialized
- DoTimedCollections
- GatherMetricsMapOnUIThread
- MarkProcessAsAlive
- UpdateLiveProfiles
- UpdateLiveProfilesHelper
- GatherMetricsMapOnIOThread
- StoreMetricsOnBackgroundThread
- BytesReadOnIOThread
- Observe
- AddExtensionEvent
- AddRendererClosedEvent
#include "chrome/browser/performance_monitor/performance_monitor.h"
#include <set>
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/process/process_iterator.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/worker_pool.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/performance_monitor/constants.h"
#include "chrome/browser/performance_monitor/performance_monitor_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_iterator.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/extensions/extension_constants.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/load_notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_iterator.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/extension.h"
#include "net/url_request/url_request.h"
using content::BrowserThread;
using extensions::Extension;
using extensions::UnloadedExtensionInfo;
namespace performance_monitor {
namespace {
#if !defined(OS_ANDROID)
std::string TimeToString(base::Time time) {
  int64 time_int64 = time.ToInternalValue();
  return base::Int64ToString(time_int64);
}
#endif  
bool StringToTime(std::string time, base::Time* output) {
  int64 time_int64 = 0;
  if (!base::StringToInt64(time, &time_int64))
    return false;
  *output = base::Time::FromInternalValue(time_int64);
  return true;
}
bool MaybeGetURLFromRenderView(const content::RenderViewHost* view,
                               std::string* url) {
  content::WebContents* web_contents =
      content::WebContents::FromRenderViewHost(view);
  if (Profile::FromBrowserContext(
          web_contents->GetBrowserContext())->IsOffTheRecord()) {
    return false;
  }
  *url = web_contents->GetURL().spec();
  return true;
}
void DeleteDatabaseOnBackgroundThread(Database* database) {
  delete database;
}
}  
bool PerformanceMonitor::initialized_ = false;
PerformanceMonitor::PerformanceDataForIOThread::PerformanceDataForIOThread()
    : network_bytes_read(0) {
}
PerformanceMonitor::PerformanceMonitor()
    : gather_interval_in_seconds_(kDefaultGatherIntervalInSeconds),
      database_logging_enabled_(false),
      timer_(FROM_HERE,
             base::TimeDelta::FromSeconds(kSampleIntervalInSeconds),
             this,
             &PerformanceMonitor::DoTimedCollections),
      disable_timer_autostart_for_testing_(false) {
}
PerformanceMonitor::~PerformanceMonitor() {
  BrowserThread::PostBlockingPoolSequencedTask(
      Database::kDatabaseSequenceToken,
      FROM_HERE,
      base::Bind(&DeleteDatabaseOnBackgroundThread, database_.release()));
}
bool PerformanceMonitor::SetDatabasePath(const base::FilePath& path) {
  if (!database_.get()) {
    database_path_ = path;
    return true;
  }
  
  return false;
}
PerformanceMonitor* PerformanceMonitor::GetInstance() {
  return Singleton<PerformanceMonitor>::get();
}
void PerformanceMonitor::Initialize() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kPerformanceMonitorGathering)) {
    database_logging_enabled_ = true;
    std::string switch_value = CommandLine::ForCurrentProcess()->
        GetSwitchValueASCII(switches::kPerformanceMonitorGathering);
    if (!switch_value.empty()) {
      int specified_interval = 0;
      if (!base::StringToInt(switch_value, &specified_interval) ||
          specified_interval <= 0) {
        LOG(ERROR) << "Invalid value for switch: '"
                   << switches::kPerformanceMonitorGathering
                   << "'; please use an integer greater than 0.";
      } else {
        gather_interval_in_seconds_ = std::max(specified_interval,
                                               kSampleIntervalInSeconds);
      }
    }
  }
  DCHECK(gather_interval_in_seconds_ >= kSampleIntervalInSeconds);
  next_collection_time_ = base::Time::Now() +
      base::TimeDelta::FromSeconds(gather_interval_in_seconds_);
  util::PostTaskToDatabaseThreadAndReply(
      FROM_HERE,
      base::Bind(&PerformanceMonitor::InitOnBackgroundThread,
                 base::Unretained(this)),
      base::Bind(&PerformanceMonitor::FinishInit,
                 base::Unretained(this)));
}
void PerformanceMonitor::InitOnBackgroundThread() {
  CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (database_logging_enabled_) {
    if (!database_)
      database_ = Database::Create(database_path_);
    if (!database_) {
      LOG(ERROR) << "Could not initialize database; aborting initialization.";
      database_logging_enabled_ = false;
      return;
    }
    
    
    Metric metric;
    if (database_->GetRecentStatsForActivityAndMetric(METRIC_NETWORK_BYTES_READ,
                                                      &metric)) {
      performance_data_for_io_thread_.network_bytes_read = metric.value;
    }
  }
}
void PerformanceMonitor::FinishInit() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  
  if (database_logging_enabled_) {
    RegisterForNotifications();
    CheckForUncleanExits();
    BrowserThread::PostBlockingPoolSequencedTask(
        Database::kDatabaseSequenceToken,
        FROM_HERE,
        base::Bind(&PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread,
                   base::Unretained(this)));
  }
  
  
  
  
  
  
  
  util::PostTaskToDatabaseThreadAndReply(
      FROM_HERE,
      base::Bind(&base::DoNothing),
      base::Bind(&PerformanceMonitor::NotifyInitialized,
                 base::Unretained(this)));
}
void PerformanceMonitor::StartGatherCycle() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  
  if (!disable_timer_autostart_for_testing_)
    timer_.Reset();
}
void PerformanceMonitor::RegisterForNotifications() {
  DCHECK(database_logging_enabled_);
  
  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
      content::NotificationService::AllSources());
  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_ENABLED,
      content::NotificationService::AllSources());
  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
      content::NotificationService::AllSources());
  registrar_.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE,
      content::NotificationService::AllSources());
  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
      content::NotificationService::AllSources());
  
  registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG,
      content::NotificationService::AllSources());
  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
      content::NotificationService::AllSources());
  
  registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
      content::NotificationService::AllSources());
  
  registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
      content::NotificationService::AllSources());
}
void PerformanceMonitor::CheckForUncleanExits() {
  DCHECK(database_logging_enabled_);
  std::vector<Profile*> profiles =
      g_browser_process->profile_manager()->GetLoadedProfiles();
  for (std::vector<Profile*>::const_iterator iter = profiles.begin();
       iter != profiles.end(); ++iter) {
    if ((*iter)->GetLastSessionExitType() == Profile::EXIT_CRASHED) {
      BrowserThread::PostBlockingPoolSequencedTask(
          Database::kDatabaseSequenceToken,
          FROM_HERE,
          base::Bind(&PerformanceMonitor::AddUncleanExitEventOnBackgroundThread,
                     base::Unretained(this),
                     (*iter)->GetDebugName()));
    }
  }
}
void PerformanceMonitor::AddUncleanExitEventOnBackgroundThread(
    const std::string& profile_name) {
  DCHECK(database_logging_enabled_);
  std::string database_key = kStateProfilePrefix + profile_name;
  std::string last_active_string = database_->GetStateValue(database_key);
  
  
  
  if (last_active_string.empty())
    return;
  base::Time last_active_time;
  CHECK(StringToTime(last_active_string, &last_active_time));
  scoped_ptr<Event> event =
      util::CreateUncleanExitEvent(last_active_time, profile_name);
  database_->AddEvent(*event.get());
}
void PerformanceMonitor::CheckForVersionUpdateOnBackgroundThread() {
  CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(database_logging_enabled_);
  chrome::VersionInfo version;
  DCHECK(version.is_valid());
  std::string current_version = version.Version();
  std::string previous_version = database_->GetStateValue(kStateChromeVersion);
  
  
  DCHECK(current_version >= previous_version);
  
  
  
  
  if (current_version > previous_version) {
    database_->AddStateValue(kStateChromeVersion, current_version);
    if (!previous_version.empty()) {
      scoped_ptr<Event> event = util::CreateChromeUpdateEvent(
          base::Time::Now(), previous_version, current_version);
      database_->AddEvent(*event.get());
    }
  }
}
void PerformanceMonitor::AddEvent(scoped_ptr<Event> event) {
  DCHECK(database_logging_enabled_);
  BrowserThread::PostBlockingPoolSequencedTask(
      Database::kDatabaseSequenceToken,
      FROM_HERE,
      base::Bind(&PerformanceMonitor::AddEventOnBackgroundThread,
                 base::Unretained(this),
                 base::Passed(&event)));
}
void PerformanceMonitor::AddEventOnBackgroundThread(scoped_ptr<Event> event) {
  database_->AddEvent(*event.get());
}
void PerformanceMonitor::AddMetricOnBackgroundThread(const Metric& metric) {
  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(database_logging_enabled_);
  database_->AddMetric(metric);
}
void PerformanceMonitor::NotifyInitialized() {
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_PERFORMANCE_MONITOR_INITIALIZED,
      content::Source<PerformanceMonitor>(this),
      content::NotificationService::NoDetails());
  initialized_ = true;
}
void PerformanceMonitor::DoTimedCollections() {
#if !defined(OS_ANDROID)
  
  if (database_logging_enabled_)
    UpdateLiveProfiles();
#endif
  GatherMetricsMapOnUIThread();
}
void PerformanceMonitor::GatherMetricsMapOnUIThread() {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  static int current_update_sequence = 0;
  
  
  current_update_sequence++;
  
  for (content::RenderProcessHost::iterator rph_iter =
           content::RenderProcessHost::AllHostsIterator();
       !rph_iter.IsAtEnd(); rph_iter.Advance()) {
    base::ProcessHandle handle = rph_iter.GetCurrentValue()->GetHandle();
    MarkProcessAsAlive(handle, content::PROCESS_TYPE_RENDERER,
                       current_update_sequence);
  }
  BrowserThread::PostTask(
      BrowserThread::IO,
      FROM_HERE,
      base::Bind(&PerformanceMonitor::GatherMetricsMapOnIOThread,
                 base::Unretained(this),
                 current_update_sequence));
}
void PerformanceMonitor::MarkProcessAsAlive(const base::ProcessHandle& handle,
                                        int process_type,
                                        int current_update_sequence) {
  if (handle == 0) {
    
    return;
  }
  MetricsMap::iterator process_metrics_iter = metrics_map_.find(handle);
  if (process_metrics_iter == metrics_map_.end()) {
    
    metrics_map_[handle]
        .Initialize(handle, process_type, current_update_sequence);
  } else {
    
    ProcessMetricsHistory& process_metrics = process_metrics_iter->second;
    process_metrics.set_last_update_sequence(current_update_sequence);
  }
}
#if !defined(OS_ANDROID)
void PerformanceMonitor::UpdateLiveProfiles() {
  std::string time = TimeToString(base::Time::Now());
  scoped_ptr<std::set<std::string> > active_profiles(
      new std::set<std::string>());
  for (chrome::BrowserIterator it; !it.done(); it.Next())
    active_profiles->insert(it->profile()->GetDebugName());
  BrowserThread::PostBlockingPoolSequencedTask(
      Database::kDatabaseSequenceToken,
      FROM_HERE,
      base::Bind(&PerformanceMonitor::UpdateLiveProfilesHelper,
                 base::Unretained(this),
                 base::Passed(&active_profiles),
                 time));
}
void PerformanceMonitor::UpdateLiveProfilesHelper(
    scoped_ptr<std::set<std::string> > active_profiles,
    std::string time) {
  CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(database_logging_enabled_);
  for (std::set<std::string>::const_iterator iter = active_profiles->begin();
       iter != active_profiles->end(); ++iter) {
    database_->AddStateValue(kStateProfilePrefix + *iter, time);
  }
}
#endif
void PerformanceMonitor::GatherMetricsMapOnIOThread(
    int current_update_sequence) {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  
  
  for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
    const content::ChildProcessData& child_process_data = iter.GetData();
    base::ProcessHandle handle = child_process_data.handle;
    MarkProcessAsAlive(handle, child_process_data.process_type,
                       current_update_sequence);
  }
  
  MarkProcessAsAlive(base::GetCurrentProcessHandle(),
                     content::PROCESS_TYPE_BROWSER, current_update_sequence);
  BrowserThread::PostBlockingPoolSequencedTask(
      Database::kDatabaseSequenceToken,
      FROM_HERE,
      base::Bind(&PerformanceMonitor::StoreMetricsOnBackgroundThread,
                 base::Unretained(this), current_update_sequence,
                 performance_data_for_io_thread_));
}
void PerformanceMonitor::StoreMetricsOnBackgroundThread(
    int current_update_sequence,
    const PerformanceDataForIOThread& performance_data_for_io_thread) {
  CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
  base::Time time_now = base::Time::Now();
  
  
  bool end_of_cycle = time_now >= next_collection_time_;
  if (end_of_cycle) {
    next_collection_time_ +=
        base::TimeDelta::FromSeconds(gather_interval_in_seconds_);
  }
  double cpu_usage = 0.0;
  size_t private_memory_sum = 0;
  size_t shared_memory_sum = 0;
  
  MetricsMap::iterator iter = metrics_map_.begin();
  while (iter != metrics_map_.end()) {
    ProcessMetricsHistory& process_metrics = iter->second;
    if (process_metrics.last_update_sequence() != current_update_sequence) {
      
      metrics_map_.erase(iter++);
    } else {
      process_metrics.SampleMetrics();
      if (end_of_cycle) {
        
        cpu_usage += process_metrics.GetAverageCPUUsage();
        size_t private_memory = 0;
        size_t shared_memory = 0;
        process_metrics.GetAverageMemoryBytes(&private_memory, &shared_memory);
        private_memory_sum += private_memory;
        shared_memory_sum += shared_memory;
        process_metrics.EndOfCycle();
      }
      ++iter;
    }
  }
  
  if (end_of_cycle && database_logging_enabled_) {
    if (!metrics_map_.empty()) {
      database_->AddMetric(Metric(METRIC_CPU_USAGE, time_now, cpu_usage));
      database_->AddMetric(Metric(METRIC_PRIVATE_MEMORY_USAGE,
                                  time_now,
                                  static_cast<double>(private_memory_sum)));
      database_->AddMetric(Metric(METRIC_SHARED_MEMORY_USAGE,
                                  time_now,
                                  static_cast<double>(shared_memory_sum)));
    }
    database_->AddMetric(
        Metric(METRIC_NETWORK_BYTES_READ,
               time_now,
               static_cast<double>(
                   performance_data_for_io_thread.network_bytes_read)));
  }
  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(&PerformanceMonitor::StartGatherCycle,
                 base::Unretained(this)));
}
void PerformanceMonitor::BytesReadOnIOThread(const net::URLRequest& request,
                                             const int bytes_read) {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  if (initialized_ && !request.url().SchemeIsFile())
    performance_data_for_io_thread_.network_bytes_read += bytes_read;
}
void PerformanceMonitor::Observe(int type,
                                 const content::NotificationSource& source,
                                 const content::NotificationDetails& details) {
  DCHECK(database_logging_enabled_);
  switch (type) {
    case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
      AddExtensionEvent(
          EVENT_EXTENSION_INSTALL,
          content::Details<const extensions::InstalledExtensionInfo>(details)->
              extension);
      break;
    }
    case chrome::NOTIFICATION_EXTENSION_ENABLED: {
      AddExtensionEvent(EVENT_EXTENSION_ENABLE,
                        content::Details<Extension>(details).ptr());
      break;
    }
    case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
      const UnloadedExtensionInfo* info =
          content::Details<UnloadedExtensionInfo>(details).ptr();
      
      if (info->reason == UnloadedExtensionInfo::REASON_DISABLE) {
        AddExtensionEvent(EVENT_EXTENSION_DISABLE,
                          info->extension);
      }
      break;
    }
    case chrome::NOTIFICATION_CRX_INSTALLER_DONE: {
      const extensions::CrxInstaller* installer =
          content::Source<extensions::CrxInstaller>(source).ptr();
      const extensions::Extension* extension =
          content::Details<Extension>(details).ptr();
      
      
      if (extension &&
          installer->install_cause() == extension_misc::INSTALL_CAUSE_UPDATE) {
        AddExtensionEvent(EVENT_EXTENSION_UPDATE, extension);
      }
      break;
    }
    case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
      AddExtensionEvent(EVENT_EXTENSION_UNINSTALL,
                        content::Details<Extension>(details).ptr());
      break;
    }
    case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG: {
      std::string url;
      content::RenderWidgetHost* widget =
          content::Source<content::RenderWidgetHost>(source).ptr();
      if (widget->IsRenderView()) {
        content::RenderViewHost* view = content::RenderViewHost::From(widget);
        MaybeGetURLFromRenderView(view, &url);
      }
      AddEvent(util::CreateRendererFailureEvent(base::Time::Now(),
                                                EVENT_RENDERER_HANG,
                                                url));
      break;
    }
    case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
      AddRendererClosedEvent(
          content::Source<content::RenderProcessHost>(source).ptr(),
          *content::Details<content::RenderProcessHost::RendererClosedDetails>(
              details).ptr());
      break;
    }
    case chrome::NOTIFICATION_PROFILE_ADDED: {
      Profile* profile = content::Source<Profile>(source).ptr();
      if (profile->GetLastSessionExitType() == Profile::EXIT_CRASHED) {
        BrowserThread::PostBlockingPoolSequencedTask(
            Database::kDatabaseSequenceToken,
            FROM_HERE,
            base::Bind(
                &PerformanceMonitor::AddUncleanExitEventOnBackgroundThread,
                base::Unretained(this),
                profile->GetDebugName()));
      }
      break;
    }
    case content::NOTIFICATION_LOAD_STOP: {
      const content::LoadNotificationDetails* load_details =
          content::Details<content::LoadNotificationDetails>(details).ptr();
      if (!load_details)
        break;
      BrowserThread::PostBlockingPoolSequencedTask(
          Database::kDatabaseSequenceToken,
          FROM_HERE,
          base::Bind(
              &PerformanceMonitor::AddMetricOnBackgroundThread,
              base::Unretained(this),
              Metric(METRIC_PAGE_LOAD_TIME,
                     base::Time::Now(),
                     static_cast<double>(
                         load_details->load_time.ToInternalValue()))));
      break;
    }
    default: {
      NOTREACHED();
      break;
    }
  }
}
void PerformanceMonitor::AddExtensionEvent(EventType type,
                                              const Extension* extension) {
  DCHECK(type == EVENT_EXTENSION_INSTALL ||
         type == EVENT_EXTENSION_UNINSTALL ||
         type == EVENT_EXTENSION_UPDATE ||
         type == EVENT_EXTENSION_ENABLE ||
         type == EVENT_EXTENSION_DISABLE);
  AddEvent(util::CreateExtensionEvent(type,
                                      base::Time::Now(),
                                      extension->id(),
                                      extension->name(),
                                      extension->url().spec(),
                                      extension->location(),
                                      extension->VersionString(),
                                      extension->description()));
}
void PerformanceMonitor::AddRendererClosedEvent(
    content::RenderProcessHost* host,
    const content::RenderProcessHost::RendererClosedDetails& details) {
  
  if (details.status == base::TERMINATION_STATUS_NORMAL_TERMINATION ||
      details.status == base::TERMINATION_STATUS_STILL_RUNNING)
    return;
  
  EventType type =
      details.status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ?
      EVENT_RENDERER_KILLED : EVENT_RENDERER_CRASH;
  
  
  
  std::string url_list;
  scoped_ptr<content::RenderWidgetHostIterator> widgets(
      content::RenderWidgetHost::GetRenderWidgetHosts());
  while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
    if (widget->GetProcess()->GetID() != host->GetID())
      continue;
    if (!widget->IsRenderView())
      continue;
    content::RenderViewHost* view = content::RenderViewHost::From(widget);
    std::string url;
    if (!MaybeGetURLFromRenderView(view, &url))
      continue;
    if (!url_list.empty())
      url_list += ", ";
    url_list += url;
  }
  AddEvent(util::CreateRendererFailureEvent(base::Time::Now(), type, url_list));
}
}