blob: 3f1069d1bbbb30434ca6599216a3afcf07b6da26 [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 "content/common/content_export.h"
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.
// The implementation will gracefully handle calls where finish_time <
// schedule_time.
// The implementation will gracefully handle successive calls with
// |schedule_times| that are out of order.
virtual void TaskOrEventFinishedOnUIThread(base::TimeTicks schedule_time,
base::TimeTicks finish_time);
// Must be called from the IO thread.
// virtual for testing.
virtual void TaskOrEventFinishedOnIOThread(base::TimeTicks schedule_time,
base::TimeTicks finish_time);
// 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;
};
protected:
// Emits an UMA metric for responsiveness of a single measurement interval.
// Exposed for testing.
virtual void EmitResponsiveness(size_t janky_slices);
// 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.
// The caller guarantees that Jank.end_time < |end_time|.
//
// 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(
std::vector<JankList> janks_from_multiple_threads,
base::TimeTicks start_time,
base::TimeTicks end_time);
// Accessor for |janks_on_ui_thread_|. Must be called from the UI thread.
JankList& GetJanksOnUIThread();
// Accessor for |janks_on_io_thread_|. Requires that |io_thread_lock_| has
// already been taken. May be called from any thread.
JankList& GetJanksOnIOThread();
// 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);
// This should only be accessed via the accessor, which checks that the caller
// is on the UI thread.
JankList janks_on_ui_thread_;
// 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_;
// This should only be accessed via the accessor, which checks that
// |io_thread_lock_| has been acquired.
JankList janks_on_io_thread_;
// 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_;
// The number of times the responsiveness metric has been emitted.
int emission_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(Calculator);
};
} // namespace responsiveness
} // namespace content
#endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_CALCULATOR_H_