chromium/chrome/browser/metrics/perf/profile_provider_chromeos.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/metrics/perf/profile_provider_chromeos.h"

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/power_monitor/power_monitor.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/perf/metric_provider.h"
#include "chrome/browser/metrics/perf/perf_events_collector.h"
#include "chrome/browser/metrics/perf/windowed_incognito_observer.h"
#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
#include "components/services/heap_profiling/public/cpp/settings.h"
#include "content/public/common/content_switches.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"

namespace metrics {

namespace {

const char kJankinessTriggerStatusHistogram[] =
    "ChromeOS.CWP.JankinessTriggerStatus";

// The default value of minimum interval between jankiness collections is 30
// minutes.
const int kDefaultJankinessCollectionMinIntervalSec = 30 * 60;

enum class JankinessTriggerStatus {
  // Attempt to collect a profile triggered by browser jankiness.
  kCollectionAttempted,
  // The collection is throttled.
  kThrottled,
  kMaxValue = kThrottled
};

// Returns true if a normal user is logged in. Returns false otherwise (e.g. if
// logged in as a guest or as a kiosk app).
bool IsNormalUserLoggedIn() {
  return ash::LoginState::Get()->IsUserAuthenticated();
}

}  // namespace

ProfileProvider::ProfileProvider()
    : jankiness_collection_min_interval_(
          base::Seconds(kDefaultJankinessCollectionMinIntervalSec)) {
  // Initialize the WindowedIncognitoMonitor on the UI thread.
  WindowedIncognitoMonitor::Init();
  // Register a perf events collector.
  collectors_.push_back(std::make_unique<MetricProvider>(
      std::make_unique<PerfCollector>(), g_browser_process->profile_manager()));
}

ProfileProvider::~ProfileProvider() {
  ash::LoginState::Get()->RemoveObserver(this);
  chromeos::PowerManagerClient::Get()->RemoveObserver(this);
  base::PowerMonitor::RemovePowerThermalObserver(this);
  if (jank_monitor_) {
    jank_monitor_->RemoveObserver(this);
    jank_monitor_->Destroy();
  }
}

void ProfileProvider::Init() {
  for (auto& collector : collectors_) {
    collector->Init();
  }

  // Register as an observer of login state changes.
  ash::LoginState::Get()->AddObserver(this);

  // Register as an observer of power manager events.
  chromeos::PowerManagerClient::Get()->AddObserver(this);

  // Register as an observer of session restore.
  on_session_restored_callback_subscription_ =
      SessionRestore::RegisterOnSessionRestoredCallback(base::BindRepeating(
          &ProfileProvider::OnSessionRestoreDone, weak_factory_.GetWeakPtr()));

  // Register as an observer of thermal state changes.
  base::PowerThermalObserver::DeviceThermalState thermal_state =
      base::PowerMonitor::AddPowerStateObserverAndReturnPowerThermalState(this);
  OnThermalStateChange(thermal_state);

  // Check the login state. At the time of writing, this class is instantiated
  // before login. A subsequent login would activate the profiling. However,
  // that behavior may change in the future so that the user is already logged
  // when this class is instantiated. By calling LoggedInStateChanged() here,
  // ProfileProvider will recognize that the system is already logged in.
  LoggedInStateChanged();

  // Set up the JankMonitor for watching browser jankiness.
  jank_monitor_ = content::JankMonitor::Create();
  jank_monitor_->SetUp();
  jank_monitor_->AddObserver(this);
}

bool ProfileProvider::GetSampledProfiles(
    std::vector<SampledProfile>* sampled_profiles) {
  bool result = false;
  for (auto& collector : collectors_) {
    bool written = collector->GetSampledProfiles(sampled_profiles);
    result = result || written;
  }
  return result;
}

void ProfileProvider::OnRecordingEnabled() {
  for (auto& collector : collectors_) {
    collector->EnableRecording();
  }
}

void ProfileProvider::OnRecordingDisabled() {
  for (auto& collector : collectors_) {
    collector->DisableRecording();
  }
}

void ProfileProvider::LoggedInStateChanged() {
  if (IsNormalUserLoggedIn()) {
    for (auto& collector : collectors_) {
      collector->OnUserLoggedIn();
    }
  } else {
    for (auto& collector : collectors_) {
      collector->Deactivate();
    }
  }
}

void ProfileProvider::SuspendDone(base::TimeDelta sleep_duration) {
  // A zero value for the suspend duration indicates that the suspend was
  // canceled. Do not collect anything if that's the case.
  if (sleep_duration.is_zero())
    return;

  // Do not collect a profile unless logged in. The system behavior when closing
  // the lid or idling when not logged in is currently to shut down instead of
  // suspending. But it's good to enforce the rule here in case that changes.
  if (!IsNormalUserLoggedIn())
    return;

  // Inform each collector that a successful suspend has completed.
  for (auto& collector : collectors_) {
    collector->SuspendDone(sleep_duration);
  }
}

void ProfileProvider::OnSessionRestoreDone(Profile* profile,
                                           int num_tabs_restored) {
  // Do not collect a profile unless logged in as a normal user.
  if (!IsNormalUserLoggedIn())
    return;

  // Inform each collector of a session restore event.
  for (auto& collector : collectors_) {
    collector->OnSessionRestoreDone(num_tabs_restored);
  }
}

void ProfileProvider::OnJankStarted() {
  if (!IsNormalUserLoggedIn())
    return;

  // For JANKY_TASK collection, require successive collections to happen between
  // this duration at minimum. Subsequent janky task detected within this
  // interval will be throttled.
  if (!last_jank_start_time_.is_null() &&
      base::TimeTicks::Now() - last_jank_start_time_ <
          jankiness_collection_min_interval_) {
    UMA_HISTOGRAM_ENUMERATION(kJankinessTriggerStatusHistogram,
                              JankinessTriggerStatus::kThrottled);
    return;
  }

  UMA_HISTOGRAM_ENUMERATION(kJankinessTriggerStatusHistogram,
                            JankinessTriggerStatus::kCollectionAttempted);
  last_jank_start_time_ = base::TimeTicks::Now();

  // Inform each collector that a jank is observed.
  for (auto& collector : collectors_) {
    collector->OnJankStarted();
  }
}

void ProfileProvider::OnJankStopped() {
  if (!IsNormalUserLoggedIn())
    return;

  // Inform each collector that a jank has stopped.
  for (auto& collector : collectors_) {
    collector->OnJankStopped();
  }
}

void ProfileProvider::OnThermalStateChange(
    base::PowerThermalObserver::DeviceThermalState new_state) {
  // Pass the new thermal state to each collector.
  for (auto& collector : collectors_) {
    collector->SetThermalState(new_state);
  }
}

void ProfileProvider::OnSpeedLimitChange(int new_limit) {
  // Pass the new speed limit to each collector.
  for (auto& collector : collectors_) {
    collector->SetSpeedLimit(new_limit);
  }
}

}  // namespace metrics