blob: 71e9ff023674a9ac23dc9763a085a3fe3dc136a9 [file] [log] [blame]
// Copyright 2018 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 CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_CALCULATOR_H_
#define CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_CALCULATOR_H_
#include <vector>
#include "base/macros.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/common/content_export.h"
#if defined(OS_ANDROID)
#include "base/android/application_status_listener.h"
#endif
namespace content {
namespace responsiveness {
// This class receives execution latency on events and tasks, and uses that to
// estimate responsiveness.
//
// All members are UI-thread affine, with the exception of
// |janks_on_io_thread_| which is protected by |io_thread_lock_|.
class CONTENT_EXPORT Calculator {
public:
Calculator();
virtual ~Calculator();
// Must be called from the UI thread.
// virtual for testing.
// Assumes that |execution_finish_time| is the current time.
// The implementation will gracefully handle successive calls with unordered
// |queue_time|s.
virtual void TaskOrEventFinishedOnUIThread(
base::TimeTicks queue_time,
base::TimeTicks execution_start_time,
base::TimeTicks execution_finish_time);
// Must be called from the IO thread.
// virtual for testing.
// The implementation will gracefully handle successive calls with unordered
// |queue_time|s.
virtual void TaskOrEventFinishedOnIOThread(
base::TimeTicks queue_time,
base::TimeTicks execution_start_time,
base::TimeTicks execution_finish_time);
// Change the Power state of the process. Must be called from the UI thread.
void SetProcessSuspended(bool suspended);
// Each janking task/event is fully defined by |start_time| and |end_time|.
// Note that |duration| = |end_time| - |start_time|.
struct Jank {
Jank(base::TimeTicks start_time, base::TimeTicks end_time);
base::TimeTicks start_time;
base::TimeTicks end_time;
};
// Types of jank recorded by this Calculator. Public for testing.
enum class JankType {
kExecution,
kQueueAndExecution,
};
protected:
// Emits an UMA metric for responsiveness of a single measurement interval.
// Exposed for testing.
virtual void EmitResponsiveness(JankType jank_type,
size_t janky_slices,
bool was_process_suspended);
// Exposed for testing.
base::TimeTicks GetLastCalculationTime();
private:
using JankList = std::vector<Jank>;
// If sufficient time has passed since the last calculation, then calculate
// responsiveness again and update |last_calculation_time_|.
//
// We only trigger this from the UI thread since triggering it from the IO
// thread would require us to grab the lock, which could cause contention. We
// only need this to trigger every 30s or so, and we generally expect there to
// be some activity on the UI thread if Chrome is actually in use.
void CalculateResponsivenessIfNecessary(base::TimeTicks current_time);
// Responsiveness is calculated by:
// 1) Discretizing time into small intervals.
// 2) In each interval, looking to see if there is a Janky event. If so, the
// interval is marked as |janky|.
// 3) Computing the percentage of intervals that are janky.
//
// This method intentionally takes a std::vector<JankList>, as we may want to
// extend it in the future to take JankLists from other threads/processes.
void CalculateResponsiveness(
JankType jank_type,
std::vector<JankList> janks_from_multiple_threads,
base::TimeTicks start_time,
base::TimeTicks end_time);
// Accessors for |execution_janks_on_ui_thread_| and
// ||queue_and_execution_janks_on_ui_thread_|. Must be called from the UI
// thread.
JankList& GetExecutionJanksOnUIThread();
JankList& GetQueueAndExecutionJanksOnUIThread();
#if defined(OS_ANDROID)
// Callback invoked when the application state changes.
void OnApplicationStateChanged(base::android::ApplicationState state);
#endif
// This method:
// 1) Removes all Janks with Jank.end_time < |end_time| from |janks|.
// 2) Returns all Janks with Jank.start_time < |end_time|.
JankList TakeJanksOlderThanTime(JankList* janks, base::TimeTicks end_time);
// Used to generate a unique id when emitting Large Jank trace events
int g_num_large_ui_janks_ = 0;
int g_num_large_io_janks_ = 0;
// Janks from tasks/events with a long execution time on the UI thread. Should
// only be accessed via the accessor, which checks that the caller is on the
// UI thread.
JankList execution_janks_on_ui_thread_;
// Janks from tasks/events with a long queueing + execution time on the UI
// thread. Should only be accessed via the accessor, which checks that the
// caller is on the UI thread.
JankList queue_and_execution_janks_on_ui_thread_;
#if defined(OS_ANDROID)
// Stores the current visibility state of the application. Accessed only on
// the UI thread.
bool is_application_visible_ = false;
#endif
// Whether or not the process is suspended (Power management). Accessed only
// on the UI thread.
bool is_process_suspended_ = false;
// Stores whether to process was suspended since last metric computation.
// Accessed only on the UI thread.
bool was_process_suspended_ = false;
// We expect there to be low contention and this lock to cause minimal
// overhead. If performance of this lock proves to be a problem, we can move
// to a lock-free data structure.
base::Lock io_thread_lock_;
// Janks from tasks/events with a long execution time on the IO thread.
JankList execution_janks_on_io_thread_ GUARDED_BY(io_thread_lock_);
// Janks from tasks/events with a long queueing + execution time on the IO
// thread.
JankList queue_and_execution_janks_on_io_thread_ GUARDED_BY(io_thread_lock_);
// The last time at which metrics were emitted. All janks older than this time
// have been consumed. Newer janks are still in their JankLists waiting to be
// consumed.
base::TimeTicks last_calculation_time_;
// This class keeps track of the time at which any activity occurred on the UI
// thread. If a sufficiently long period of time passes without any activity,
// then it's assumed that the process was suspended. In this case, we should
// not emit any responsiveness metrics.
//
// Note that the process may be suspended while a task or event is being
// executed, so a very long execution time should be treated similarly.
base::TimeTicks most_recent_activity_time_;
#if defined(OS_ANDROID)
// Listener for changes in application state, unregisters itself when
// destroyed.
const std::unique_ptr<base::android::ApplicationStatusListener>
application_status_listener_;
#endif
DISALLOW_COPY_AND_ASSIGN(Calculator);
};
} // namespace responsiveness
} // namespace content
#endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_CALCULATOR_H_