Commit 4ab7c4ff authored by Sebastien Marchand's avatar Sebastien Marchand Committed by Chromium LUCI CQ

[battery] Base for the usage scenario tracker

This will be used to report UKMs that will let us analyze how different
usage scenarios affect Chrome's battery consumption, see
https://docs.google.com/document/d/18cX0Aug0YuUAfW7v3eOeDzilS1IzcnCoFp616zzI_Vo/edit#heading=h.ryxkkcajp5g8

Bug: 1153193
Change-Id: Iac7a309b3cd649597ae6a3f6dd92de21080bf6ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2567613Reviewed-by: default avatarBrian White <bcwhite@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Commit-Queue: Sébastien Marchand <sebmarchand@chromium.org>
Cr-Commit-Position: refs/heads/master@{#836852}
parent db12b1ba
......@@ -828,6 +828,8 @@ static_library("browser") {
"metrics/thread_watcher_report_hang.h",
"metrics/ukm_background_recorder_service.cc",
"metrics/ukm_background_recorder_service.h",
"metrics/usage_scenario_data_store.cc",
"metrics/usage_scenario_data_store.h",
"metrics/variations/chrome_variations_service_client.cc",
"metrics/variations/chrome_variations_service_client.h",
"native_file_system/chrome_native_file_system_permission_context.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/metrics/usage_scenario_data_store.h"
#include <algorithm>
#include "base/time/time.h"
UsageScenarioDataStore::UsageScenarioDataStore() = default;
UsageScenarioDataStore::~UsageScenarioDataStore() = default;
UsageScenarioDataStore::IntervalData::IntervalData() = default;
UsageScenarioDataStoreImpl::UsageScenarioDataStoreImpl()
: start_time_(base::TimeTicks::Now()) {}
UsageScenarioDataStoreImpl::~UsageScenarioDataStoreImpl() = default;
UsageScenarioDataStoreImpl::IntervalData
UsageScenarioDataStoreImpl::ResetIntervalData() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto now = base::TimeTicks::Now();
FinalizeIntervalData(now);
IntervalData ret = interval_data_;
ret.uptime_at_interval_end = now - start_time_;
// Start by resetting the interval data entirely.
interval_data_ = {};
// Set the |interval_data_| fields that are based on the current state.
// The maximum number of tabs and visible windows for the next interval is now
// equal to the current counts for these metrics.
interval_data_.max_tab_count = current_tab_count_;
interval_data_.max_visible_window_count = current_visible_window_count_;
if (!is_playing_full_screen_video_single_monitor_since_.is_null()) {
is_playing_full_screen_video_single_monitor_since_ = now;
}
if (!has_opened_webrtc_connection_since_.is_null()) {
has_opened_webrtc_connection_since_ = now;
}
return ret;
}
void UsageScenarioDataStoreImpl::OnTabAdded() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
++current_tab_count_;
interval_data_.max_tab_count =
std::max(interval_data_.max_tab_count, current_tab_count_);
}
void UsageScenarioDataStoreImpl::OnTabClosed() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GT(current_tab_count_, 0U);
--current_tab_count_;
DCHECK_GE(current_tab_count_, current_visible_window_count_);
++interval_data_.tabs_closed_during_interval;
}
void UsageScenarioDataStoreImpl::OnWindowVisible() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
++current_visible_window_count_;
DCHECK_GE(current_tab_count_, current_visible_window_count_);
interval_data_.max_visible_window_count = std::max(
interval_data_.max_visible_window_count, current_visible_window_count_);
}
void UsageScenarioDataStoreImpl::OnWindowHidden() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GT(current_visible_window_count_, 0U);
--current_visible_window_count_;
}
void UsageScenarioDataStoreImpl::OnTopLevelNavigation() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
++interval_data_.top_level_navigation_count;
}
void UsageScenarioDataStoreImpl::OnUserInteraction() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
++interval_data_.user_interaction_count;
}
void UsageScenarioDataStoreImpl::OnFullScreenVideoStartsOnSingleMonitor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(is_playing_full_screen_video_single_monitor_since_.is_null());
is_playing_full_screen_video_single_monitor_since_ = base::TimeTicks::Now();
}
void UsageScenarioDataStoreImpl::OnFullScreenVideoEndsOnSingleMonitor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!is_playing_full_screen_video_single_monitor_since_.is_null());
interval_data_.time_playing_video_full_screen_single_monitor +=
base::TimeTicks::Now() -
is_playing_full_screen_video_single_monitor_since_;
is_playing_full_screen_video_single_monitor_since_ = base::TimeTicks();
}
void UsageScenarioDataStoreImpl::OnWebRTCConnectionOpened() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Grab the current timestamp if there's no remaining WebRTC connection.
if (webrtc_open_connection_count_ == 0) {
DCHECK(has_opened_webrtc_connection_since_.is_null());
has_opened_webrtc_connection_since_ = base::TimeTicks::Now();
}
++webrtc_open_connection_count_;
DCHECK_GE(current_tab_count_, webrtc_open_connection_count_);
}
void UsageScenarioDataStoreImpl::OnWebRTCConnectionClosed() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GT(webrtc_open_connection_count_, 0U);
--webrtc_open_connection_count_;
DCHECK_GE(current_tab_count_, webrtc_open_connection_count_);
// If this was the last tab using WebRTC then the interval data should be
// updated.
if (webrtc_open_connection_count_ == 0) {
DCHECK(!has_opened_webrtc_connection_since_.is_null());
interval_data_.time_with_open_webrtc_connection +=
base::TimeTicks::Now() - has_opened_webrtc_connection_since_;
has_opened_webrtc_connection_since_ = base::TimeTicks();
}
}
void UsageScenarioDataStoreImpl::FinalizeIntervalData(base::TimeTicks now) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Update feature usage durations in |interval_data_|.
if (!is_playing_full_screen_video_single_monitor_since_.is_null()) {
interval_data_.time_playing_video_full_screen_single_monitor +=
now - is_playing_full_screen_video_single_monitor_since_;
}
if (!has_opened_webrtc_connection_since_.is_null()) {
interval_data_.time_with_open_webrtc_connection +=
now - has_opened_webrtc_connection_since_;
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_METRICS_USAGE_SCENARIO_DATA_STORE_H_
#define CHROME_BROWSER_METRICS_USAGE_SCENARIO_DATA_STORE_H_
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
// Stores the data necessary to analyze the usage pattern during a given
// interval of time. There are 2 types of data tracked by this class:
// - Current state data: e.g. the current uptime.
// - Interval data: e.g. whether or not there's been a user interaction since
// the last call to ResetIntervalData.
//
// By default this class assumes that no tabs exists when it's created. If this
// isn't true then the data providers need to make the appropriate calls to set
// the correct initial state.
//
// The interval's length needs to be enforced by the owner of this class, it
// should call ResetIntervalData regularly to get the usage data and reset it.
class UsageScenarioDataStore {
public:
UsageScenarioDataStore();
UsageScenarioDataStore(const UsageScenarioDataStore& rhs) = delete;
UsageScenarioDataStore& operator=(const UsageScenarioDataStore& rhs) = delete;
virtual ~UsageScenarioDataStore() = 0;
// Used to store data between 2 calls to ResetIntervalData.
struct IntervalData {
IntervalData();
// The uptime at the end of the interval.
base::TimeDelta uptime_at_interval_end;
// The maximum number of tabs that existed at the same time.
uint16_t max_tab_count = 0;
// The maximum number of windows that have been visible at the same time.
uint16_t max_visible_window_count = 0;
// Number of main frame different-document navigations in tabs.
uint16_t top_level_navigation_count = 0;
// The number of tabs that have been closed.
uint16_t tabs_closed_during_interval = 0;
// Number of user interaction (scroll, click or typing).
uint16_t user_interaction_count = 0;
// The time spent playing video full screen in a single-monitor situation.
base::TimeDelta time_playing_video_full_screen_single_monitor;
// The time spent with at least one opened WebRTC connection.
base::TimeDelta time_with_open_webrtc_connection;
};
// Reset the interval data with the current state information and returns the
// data for the past interval (since the last call to ResetIntervalData or the
// creation of this object if this is the first call).
virtual IntervalData ResetIntervalData() = 0;
};
// Concrete implementation of a UsageScenarioDataStore that expose the functions
// allowing to update its internal state.
//
// This class isn't thread safe and all functions should be called from a single
// sequence. This is enforced via a sequence checker.
class UsageScenarioDataStoreImpl : public UsageScenarioDataStore {
public:
UsageScenarioDataStoreImpl();
UsageScenarioDataStoreImpl(const UsageScenarioDataStoreImpl& rhs) = delete;
UsageScenarioDataStoreImpl& operator=(const UsageScenarioDataStoreImpl& rhs) =
delete;
~UsageScenarioDataStoreImpl() override;
IntervalData ResetIntervalData() override;
// Set of functions used to maintain the current state, these should only be
// called by a UsageScenarioDataInfoProvider instance. It is important to log
// all events to ensure the integrity of the data store, e.g. if a tab
// currently using WebRTC is closed the 2 following functions should be
// called:
// - OnTabStopUsingWebRTC()
// - OnTabClosed()
void OnTabAdded();
void OnTabClosed();
void OnWindowVisible();
void OnWindowHidden();
void OnTopLevelNavigation();
void OnUserInteraction();
void OnFullScreenVideoStartsOnSingleMonitor();
void OnFullScreenVideoEndsOnSingleMonitor();
void OnWebRTCConnectionOpened();
void OnWebRTCConnectionClosed();
const IntervalData& GetIntervalDataForTesting() { return interval_data_; }
private:
// Finalize the interval data based on the data contained in
// |interval_details_|.
void FinalizeIntervalData(base::TimeTicks now);
// The current tab count.
uint16_t current_tab_count_ = 0;
// The current number of visible windows.
uint16_t current_visible_window_count_ = 0;
// The timestamp of the beginning of a full screen video session when
// there's only one monitor available. Reset to |now| when an interval ends
// (when ResetIntervalData is called).
base::TimeTicks is_playing_full_screen_video_single_monitor_since_;
// The number of opened WebRTC connections.
uint16_t webrtc_open_connection_count_ = 0;
// The timestamp of the beginning of the WebRTC session that has caused
// |webrtc_connection_count| to increase to 1. Reset to |now| when an interval
// ends (when ResetIntervalData is called).
base::TimeTicks has_opened_webrtc_connection_since_;
// The application start time.
const base::TimeTicks start_time_;
IntervalData interval_data_;
SEQUENCE_CHECKER(sequence_checker_);
};
#endif // CHROME_BROWSER_METRICS_USAGE_SCENARIO_DATA_STORE_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/metrics/usage_scenario_data_store.h"
#include <tuple>
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr base::TimeDelta kShortDelay = base::TimeDelta::FromSeconds(1);
} // namespace
class UsageScenarioDataStoreTest : public testing::Test {
public:
UsageScenarioDataStoreTest() = default;
~UsageScenarioDataStoreTest() override = default;
UsageScenarioDataStoreTest(const UsageScenarioDataStoreTest& other) = delete;
UsageScenarioDataStoreTest& operator=(const UsageScenarioDataStoreTest&) =
delete;
UsageScenarioDataStoreImpl* data_store() { return &data_store_; }
const UsageScenarioDataStore::IntervalData& interval_data() {
return data_store_.GetIntervalDataForTesting();
}
UsageScenarioDataStore::IntervalData ResetIntervalData() {
return data_store_.ResetIntervalData();
}
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
private:
UsageScenarioDataStoreImpl data_store_;
};
TEST_F(UsageScenarioDataStoreTest, Uptime) {
auto data = ResetIntervalData();
EXPECT_TRUE(data.uptime_at_interval_end.is_zero());
task_environment_.FastForwardBy(kShortDelay);
data = ResetIntervalData();
EXPECT_EQ(kShortDelay, data.uptime_at_interval_end);
task_environment_.FastForwardBy(kShortDelay);
data = ResetIntervalData();
EXPECT_EQ(2 * kShortDelay, data.uptime_at_interval_end);
}
TEST_F(UsageScenarioDataStoreTest, TabCount) {
EXPECT_EQ(0U, interval_data().max_tab_count);
data_store()->OnTabAdded();
EXPECT_EQ(1U, interval_data().max_tab_count);
data_store()->OnTabAdded();
EXPECT_EQ(2U, interval_data().max_tab_count);
data_store()->OnTabClosed();
EXPECT_EQ(2U, interval_data().max_tab_count);
auto data = ResetIntervalData();
EXPECT_EQ(2U, data.max_tab_count);
data_store()->OnTabClosed();
data = ResetIntervalData();
EXPECT_EQ(1U, data.max_tab_count);
}
TEST_F(UsageScenarioDataStoreTest, TabClosedDuringInterval) {
EXPECT_EQ(0U, interval_data().max_tab_count);
data_store()->OnTabAdded();
data_store()->OnTabAdded();
data_store()->OnTabAdded();
data_store()->OnTabAdded();
EXPECT_EQ(4U, interval_data().max_tab_count);
data_store()->OnTabClosed();
data_store()->OnTabClosed();
data_store()->OnTabClosed();
EXPECT_EQ(4U, interval_data().max_tab_count);
EXPECT_EQ(3U, interval_data().tabs_closed_during_interval);
auto data = ResetIntervalData();
EXPECT_EQ(4U, data.max_tab_count);
EXPECT_EQ(3U, data.tabs_closed_during_interval);
data_store()->OnTabClosed();
data = ResetIntervalData();
EXPECT_EQ(1U, data.max_tab_count);
EXPECT_EQ(1U, data.tabs_closed_during_interval);
}
TEST_F(UsageScenarioDataStoreTest, VisibleWindowCount) {
data_store()->OnTabAdded();
data_store()->OnTabAdded();
EXPECT_EQ(0U, interval_data().max_visible_window_count);
data_store()->OnWindowVisible();
EXPECT_EQ(1U, interval_data().max_visible_window_count);
data_store()->OnWindowVisible();
EXPECT_EQ(2U, interval_data().max_visible_window_count);
data_store()->OnWindowHidden();
EXPECT_EQ(2U, interval_data().max_visible_window_count);
auto data = ResetIntervalData();
EXPECT_EQ(2U, data.max_visible_window_count);
data_store()->OnWindowHidden();
data = ResetIntervalData();
EXPECT_EQ(1U, data.max_visible_window_count);
}
TEST_F(UsageScenarioDataStoreTest, TopLevelNavigation) {
EXPECT_EQ(0U, interval_data().top_level_navigation_count);
data_store()->OnTopLevelNavigation();
EXPECT_EQ(1U, interval_data().top_level_navigation_count);
data_store()->OnTopLevelNavigation();
EXPECT_EQ(2U, interval_data().top_level_navigation_count);
auto data = ResetIntervalData();
EXPECT_EQ(2U, data.top_level_navigation_count);
data = ResetIntervalData();
EXPECT_EQ(0U, data.top_level_navigation_count);
}
TEST_F(UsageScenarioDataStoreTest, UserInteraction) {
EXPECT_EQ(0U, interval_data().user_interaction_count);
data_store()->OnUserInteraction();
EXPECT_EQ(1U, interval_data().user_interaction_count);
data_store()->OnUserInteraction();
EXPECT_EQ(2U, interval_data().user_interaction_count);
auto data = ResetIntervalData();
EXPECT_EQ(2U, data.user_interaction_count);
data = ResetIntervalData();
EXPECT_EQ(0U, data.user_interaction_count);
}
TEST_F(UsageScenarioDataStoreTest, FullScreenVideoOnSingleMonitorBasic) {
data_store()->OnFullScreenVideoStartsOnSingleMonitor();
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnFullScreenVideoEndsOnSingleMonitor();
task_environment_.FastForwardBy(kShortDelay);
auto data = ResetIntervalData();
EXPECT_EQ(kShortDelay, data.time_playing_video_full_screen_single_monitor);
}
TEST_F(UsageScenarioDataStoreTest,
FullScreenVideoOnSingleMonitorOverMultipleIntervals) {
data_store()->OnFullScreenVideoStartsOnSingleMonitor();
task_environment_.FastForwardBy(kShortDelay);
auto data = ResetIntervalData();
EXPECT_EQ(kShortDelay, data.time_playing_video_full_screen_single_monitor);
task_environment_.FastForwardBy(kShortDelay);
data = ResetIntervalData();
EXPECT_EQ(kShortDelay, data.time_playing_video_full_screen_single_monitor);
task_environment_.FastForwardBy(kShortDelay / 2);
data_store()->OnFullScreenVideoEndsOnSingleMonitor();
task_environment_.FastForwardBy(kShortDelay);
data = ResetIntervalData();
EXPECT_EQ(kShortDelay / 2,
data.time_playing_video_full_screen_single_monitor);
}
TEST_F(UsageScenarioDataStoreTest,
FullScreenVideoOnSingleMonitorMultipleSessionsDuringInterval) {
constexpr int kIterations = 2;
for (int i = 0; i < kIterations; ++i) {
data_store()->OnFullScreenVideoStartsOnSingleMonitor();
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnFullScreenVideoEndsOnSingleMonitor();
task_environment_.FastForwardBy(kShortDelay);
}
auto data = ResetIntervalData();
task_environment_.FastForwardBy(kShortDelay);
EXPECT_EQ(kIterations * kShortDelay,
data.time_playing_video_full_screen_single_monitor);
}
TEST_F(UsageScenarioDataStoreTest, WebRTCUsageBasic) {
data_store()->OnTabAdded();
data_store()->OnWebRTCConnectionOpened();
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnWebRTCConnectionClosed();
task_environment_.FastForwardBy(kShortDelay);
auto data = ResetIntervalData();
EXPECT_EQ(kShortDelay, data.time_with_open_webrtc_connection);
}
TEST_F(UsageScenarioDataStoreTest, WebRTCUsageOverMultipleIntervals) {
data_store()->OnTabAdded();
data_store()->OnWebRTCConnectionOpened();
task_environment_.FastForwardBy(kShortDelay);
auto data = ResetIntervalData();
EXPECT_EQ(kShortDelay, data.time_with_open_webrtc_connection);
task_environment_.FastForwardBy(kShortDelay);
data = ResetIntervalData();
EXPECT_EQ(kShortDelay, data.time_with_open_webrtc_connection);
task_environment_.FastForwardBy(kShortDelay / 2);
data_store()->OnWebRTCConnectionClosed();
data = ResetIntervalData();
EXPECT_EQ(kShortDelay / 2, data.time_with_open_webrtc_connection);
}
TEST_F(UsageScenarioDataStoreTest, WebRTCUsageMultipleSessionsDuringInterval) {
data_store()->OnTabAdded();
constexpr int kIterations = 2;
for (int i = 0; i < kIterations; ++i) {
data_store()->OnWebRTCConnectionOpened();
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnWebRTCConnectionClosed();
task_environment_.FastForwardBy(kShortDelay);
}
auto data = ResetIntervalData();
task_environment_.FastForwardBy(kShortDelay);
EXPECT_EQ(kIterations * kShortDelay, data.time_with_open_webrtc_connection);
}
TEST_F(UsageScenarioDataStoreTest, WebRTCUsageInMultipleTabsSingleInterval) {
data_store()->OnTabAdded();
data_store()->OnTabAdded();
data_store()->OnTabAdded();
data_store()->OnWebRTCConnectionOpened();
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnWebRTCConnectionOpened();
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnWebRTCConnectionClosed();
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnWebRTCConnectionClosed();
task_environment_.FastForwardBy(kShortDelay);
auto data = ResetIntervalData();
EXPECT_EQ(3 * kShortDelay, data.time_with_open_webrtc_connection);
}
TEST_F(UsageScenarioDataStoreTest, WebRTCUsageInMultipleTabsMultipleInterval) {
data_store()->OnTabAdded();
data_store()->OnTabAdded();
data_store()->OnTabAdded();
data_store()->OnWebRTCConnectionOpened();
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnWebRTCConnectionOpened();
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnWebRTCConnectionClosed();
task_environment_.FastForwardBy(kShortDelay);
auto data = ResetIntervalData();
EXPECT_EQ(3 * kShortDelay, data.time_with_open_webrtc_connection);
task_environment_.FastForwardBy(kShortDelay);
data_store()->OnWebRTCConnectionClosed();
data = ResetIntervalData();
EXPECT_EQ(kShortDelay, data.time_with_open_webrtc_connection);
task_environment_.FastForwardBy(kShortDelay);
data = ResetIntervalData();
EXPECT_EQ(base::TimeDelta(), data.time_with_open_webrtc_connection);
}
......@@ -3524,6 +3524,7 @@ test("unit_tests") {
"../browser/metrics/process_memory_metrics_emitter_unittest.cc",
"../browser/metrics/tab_footprint_aggregator_unittest.cc",
"../browser/metrics/thread_watcher_unittest.cc",
"../browser/metrics/usage_scenario_data_store_unittest.cc",
"../browser/native_file_system/chrome_native_file_system_permission_context_unittest.cc",
"../browser/native_file_system/origin_scoped_native_file_system_permission_context_unittest.cc",
"../browser/navigation_predictor/navigation_predictor_renderer_warmup_client_unittest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment