Commit cd540b30 authored by Joe Mason's avatar Joe Mason Committed by Commit Bot

[PM] Add a lazy mode to V8PerFrameMemoryRequest

R=chrisha

Bug: 1080672
Change-Id: I4b24d1e77e9dcc228d606f6d484127b04ca0dadc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2339219
Commit-Queue: Joe Mason <joenotcharles@chromium.org>
Reviewed-by: default avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798718}
parent 6695ffa6
...@@ -264,9 +264,8 @@ class V8PerFrameMemoryDecorator ...@@ -264,9 +264,8 @@ class V8PerFrameMemoryDecorator
base::Value DescribeFrameNodeData(const FrameNode* node) const override; base::Value DescribeFrameNodeData(const FrameNode* node) const override;
base::Value DescribeProcessNodeData(const ProcessNode* node) const override; base::Value DescribeProcessNodeData(const ProcessNode* node) const override;
// Returns the amount of time to wait between requests for each process. // Returns the next measurement request that should be scheduled.
// Returns a zero TimeDelta if no requests should be made. V8PerFrameMemoryRequest* GetNextRequest() const;
base::TimeDelta GetMinTimeBetweenRequestsPerProcess() const;
// Implementation details below this point. // Implementation details below this point.
...@@ -288,7 +287,7 @@ class V8PerFrameMemoryDecorator ...@@ -288,7 +287,7 @@ class V8PerFrameMemoryDecorator
Graph* graph_ = nullptr; Graph* graph_ = nullptr;
// List of requests sorted by sample_frequency (lowest first). // List of requests sorted by min_time_between_requests (lowest first).
std::vector<V8PerFrameMemoryRequest*> measurement_requests_; std::vector<V8PerFrameMemoryRequest*> measurement_requests_;
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
...@@ -296,25 +295,54 @@ class V8PerFrameMemoryDecorator ...@@ -296,25 +295,54 @@ class V8PerFrameMemoryDecorator
class V8PerFrameMemoryRequest { class V8PerFrameMemoryRequest {
public: public:
enum class MeasurementMode {
// Measurements will be taken at the next GC after a request is received.
// If no GC happens within a bounded time an extra GC will be scheduled.
kBounded,
// Measurements will only be taken at the next scheduled GC after a request
// is received.
kLazy,
kDefault = kBounded,
};
// Creates a request but does not start the measurements. Call // Creates a request but does not start the measurements. Call
// StartMeasurement to add it to the request list. // StartMeasurement to add it to the request list.
explicit V8PerFrameMemoryRequest(const base::TimeDelta& sample_frequency); //
// Measurement requests will be sent repeatedly to each process, with at
// least |min_time_between_requests| (which must be greater than 0) between
// each repetition. The next GC after each request is received will be
// instrumented, which adds some overhead. |mode| determines whether extra
// GC's can be scheduled, which would add even more overhead.
explicit V8PerFrameMemoryRequest(
const base::TimeDelta& min_time_between_requests,
MeasurementMode mode = MeasurementMode::kDefault);
// Creates a request and calls StartMeasurement with the given |graph| and
// |min_time_between_requests|, using the default measurement mode.
V8PerFrameMemoryRequest(const base::TimeDelta& min_time_between_requests,
Graph* graph);
// Creates a request and calls StartMeasurement. This will request // Creates a request and calls StartMeasurement with the given |graph|,
// measurements for all ProcessNode's in |graph| with frequency // |min_time_between_requests|, and |mode|.
// |sample_frequency|. V8PerFrameMemoryRequest(const base::TimeDelta& min_time_between_requests,
V8PerFrameMemoryRequest(const base::TimeDelta& sample_frequency, MeasurementMode mode,
Graph* graph); Graph* graph);
~V8PerFrameMemoryRequest(); ~V8PerFrameMemoryRequest();
V8PerFrameMemoryRequest(const V8PerFrameMemoryRequest&) = delete; V8PerFrameMemoryRequest(const V8PerFrameMemoryRequest&) = delete;
V8PerFrameMemoryRequest& operator=(const V8PerFrameMemoryRequest&) = delete; V8PerFrameMemoryRequest& operator=(const V8PerFrameMemoryRequest&) = delete;
const base::TimeDelta& sample_frequency() const { return sample_frequency_; } const base::TimeDelta& min_time_between_requests() const {
return min_time_between_requests_;
}
MeasurementMode mode() const { return mode_; }
// Requests measurements for all ProcessNode's in |graph| with this object's // Requests measurements for all ProcessNode's in |graph|. This must only be
// sample frequency. This must only be called once for each // called once for each V8PerFrameMemoryRequest.
// V8PerFrameMemoryRequest.
void StartMeasurement(Graph* graph); void StartMeasurement(Graph* graph);
// Adds/removes an observer. // Adds/removes an observer.
...@@ -326,10 +354,11 @@ class V8PerFrameMemoryRequest { ...@@ -326,10 +354,11 @@ class V8PerFrameMemoryRequest {
// Private constructor for V8PerFrameMemoryRequestAnySeq. Saves // Private constructor for V8PerFrameMemoryRequestAnySeq. Saves
// |off_sequence_request| as a pointer to the off-sequence object that // |off_sequence_request| as a pointer to the off-sequence object that
// triggered the request and starts measurements with frequency // triggered the request and starts measurements with frequency
// |sample_frequency|. // |min_time_between_requests|.
V8PerFrameMemoryRequest( V8PerFrameMemoryRequest(
util::PassKey<V8PerFrameMemoryRequestAnySeq>, util::PassKey<V8PerFrameMemoryRequestAnySeq>,
const base::TimeDelta& sample_frequency, const base::TimeDelta& min_time_between_requests,
MeasurementMode mode,
base::WeakPtr<V8PerFrameMemoryRequestAnySeq> off_sequence_request); base::WeakPtr<V8PerFrameMemoryRequestAnySeq> off_sequence_request);
// V8PerFrameMemoryDecorator calls OnDecoratorUnregistered when it is removed // V8PerFrameMemoryDecorator calls OnDecoratorUnregistered when it is removed
...@@ -343,7 +372,8 @@ class V8PerFrameMemoryRequest { ...@@ -343,7 +372,8 @@ class V8PerFrameMemoryRequest {
const ProcessNode* process_node) const; const ProcessNode* process_node) const;
private: private:
base::TimeDelta sample_frequency_; base::TimeDelta min_time_between_requests_;
MeasurementMode mode_;
V8PerFrameMemoryDecorator* decorator_ = nullptr; V8PerFrameMemoryDecorator* decorator_ = nullptr;
base::ObserverList<V8PerFrameMemoryObserver, /*check_empty=*/true> observers_; base::ObserverList<V8PerFrameMemoryObserver, /*check_empty=*/true> observers_;
...@@ -450,8 +480,11 @@ class V8PerFrameMemoryObserverAnySeq : public base::CheckedObserver { ...@@ -450,8 +480,11 @@ class V8PerFrameMemoryObserverAnySeq : public base::CheckedObserver {
// Wrapper that can instantiate a V8PerFrameMemoryRequest from any sequence. // Wrapper that can instantiate a V8PerFrameMemoryRequest from any sequence.
class V8PerFrameMemoryRequestAnySeq { class V8PerFrameMemoryRequestAnySeq {
public: public:
using MeasurementMode = V8PerFrameMemoryRequest::MeasurementMode;
explicit V8PerFrameMemoryRequestAnySeq( explicit V8PerFrameMemoryRequestAnySeq(
const base::TimeDelta& sample_frequency); const base::TimeDelta& min_time_between_requests,
MeasurementMode mode = MeasurementMode::kDefault);
~V8PerFrameMemoryRequestAnySeq(); ~V8PerFrameMemoryRequestAnySeq();
V8PerFrameMemoryRequestAnySeq(const V8PerFrameMemoryRequestAnySeq&) = delete; V8PerFrameMemoryRequestAnySeq(const V8PerFrameMemoryRequestAnySeq&) = delete;
......
...@@ -48,6 +48,8 @@ class V8PerFrameMemoryDecorator::ObserverNotifier { ...@@ -48,6 +48,8 @@ class V8PerFrameMemoryDecorator::ObserverNotifier {
namespace { namespace {
using MeasurementMode = V8PerFrameMemoryRequest::MeasurementMode;
using PerFrameUsagePtr = blink::mojom::PerFrameV8MemoryUsageDataPtr; using PerFrameUsagePtr = blink::mojom::PerFrameV8MemoryUsageDataPtr;
// Comparator that generates a strict total order of PerFrameUsagePtr's when // Comparator that generates a strict total order of PerFrameUsagePtr's when
...@@ -185,7 +187,7 @@ class NodeAttachedProcessData ...@@ -185,7 +187,7 @@ class NodeAttachedProcessData
void ScheduleNextMeasurement(); void ScheduleNextMeasurement();
private: private:
void StartMeasurement(); void StartMeasurement(MeasurementMode mode);
void EnsureRemote(); void EnsureRemote();
void OnPerFrameV8MemoryUsageData( void OnPerFrameV8MemoryUsageData(
blink::mojom::PerProcessV8MemoryUsageDataPtr result); blink::mojom::PerProcessV8MemoryUsageDataPtr result);
...@@ -228,10 +230,14 @@ void NodeAttachedProcessData::ScheduleNextMeasurement() { ...@@ -228,10 +230,14 @@ void NodeAttachedProcessData::ScheduleNextMeasurement() {
return; return;
} }
V8PerFrameMemoryRequest* next_request = nullptr;
auto* decorator = auto* decorator =
V8PerFrameMemoryDecorator::GetFromGraph(process_node_->GetGraph()); V8PerFrameMemoryDecorator::GetFromGraph(process_node_->GetGraph());
if (!decorator || if (decorator) {
decorator->GetMinTimeBetweenRequestsPerProcess().is_zero()) { next_request = decorator->GetNextRequest();
}
if (!next_request) {
// All measurements have been cancelled, or decorator was removed from // All measurements have been cancelled, or decorator was removed from
// graph. // graph.
state_ = State::kIdle; state_ = State::kIdle;
...@@ -243,17 +249,20 @@ void NodeAttachedProcessData::ScheduleNextMeasurement() { ...@@ -243,17 +249,20 @@ void NodeAttachedProcessData::ScheduleNextMeasurement() {
state_ = State::kWaiting; state_ = State::kWaiting;
if (last_request_time_.is_null()) { if (last_request_time_.is_null()) {
// This is the first measurement. Perform it immediately. // This is the first measurement. Perform it immediately.
StartMeasurement(); StartMeasurement(next_request->mode());
return; return;
} }
// TODO(joenotcharles): Make sure kLazy requests can't starve kBounded
// requests.
base::TimeTicks next_request_time = base::TimeTicks next_request_time =
last_request_time_ + decorator->GetMinTimeBetweenRequestsPerProcess(); last_request_time_ + next_request->min_time_between_requests();
timer_.Start(FROM_HERE, next_request_time - base::TimeTicks::Now(), this, timer_.Start(FROM_HERE, next_request_time - base::TimeTicks::Now(),
&NodeAttachedProcessData::StartMeasurement); base::BindOnce(&NodeAttachedProcessData::StartMeasurement,
base::Unretained(this), next_request->mode()));
} }
void NodeAttachedProcessData::StartMeasurement() { void NodeAttachedProcessData::StartMeasurement(MeasurementMode mode) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(state_, State::kWaiting); DCHECK_EQ(state_, State::kWaiting);
state_ = State::kMeasuring; state_ = State::kMeasuring;
...@@ -268,7 +277,9 @@ void NodeAttachedProcessData::StartMeasurement() { ...@@ -268,7 +277,9 @@ void NodeAttachedProcessData::StartMeasurement() {
// NodeAttachedProcessData when the last V8PerFrameMemoryRequest is deleted, // NodeAttachedProcessData when the last V8PerFrameMemoryRequest is deleted,
// which could happen at any time. // which could happen at any time.
resource_usage_reporter_->GetPerFrameV8MemoryUsageData( resource_usage_reporter_->GetPerFrameV8MemoryUsageData(
blink::mojom::V8PerFrameMemoryReporter::Mode::DEFAULT, mode == MeasurementMode::kLazy
? blink::mojom::V8PerFrameMemoryReporter::Mode::LAZY
: blink::mojom::V8PerFrameMemoryReporter::Mode::DEFAULT,
base::BindOnce(&NodeAttachedProcessData::OnPerFrameV8MemoryUsageData, base::BindOnce(&NodeAttachedProcessData::OnPerFrameV8MemoryUsageData,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
} }
...@@ -371,16 +382,25 @@ void SetBindV8PerFrameMemoryReporterCallbackForTesting( ...@@ -371,16 +382,25 @@ void SetBindV8PerFrameMemoryReporterCallbackForTesting(
// V8PerFrameMemoryRequest // V8PerFrameMemoryRequest
V8PerFrameMemoryRequest::V8PerFrameMemoryRequest( V8PerFrameMemoryRequest::V8PerFrameMemoryRequest(
const base::TimeDelta& sample_frequency) const base::TimeDelta& min_time_between_requests,
: sample_frequency_(sample_frequency) { MeasurementMode mode)
DCHECK_GT(sample_frequency_, base::TimeDelta()); : min_time_between_requests_(min_time_between_requests), mode_(mode) {
DCHECK_GT(min_time_between_requests_, base::TimeDelta());
} }
V8PerFrameMemoryRequest::V8PerFrameMemoryRequest( V8PerFrameMemoryRequest::V8PerFrameMemoryRequest(
const base::TimeDelta& sample_frequency, const base::TimeDelta& min_time_between_requests,
Graph* graph) Graph* graph)
: sample_frequency_(sample_frequency) { : V8PerFrameMemoryRequest(min_time_between_requests,
DCHECK_GT(sample_frequency_, base::TimeDelta()); MeasurementMode::kDefault) {
StartMeasurement(graph);
}
V8PerFrameMemoryRequest::V8PerFrameMemoryRequest(
const base::TimeDelta& min_time_between_requests,
MeasurementMode mode,
Graph* graph)
: V8PerFrameMemoryRequest(min_time_between_requests, mode) {
StartMeasurement(graph); StartMeasurement(graph);
} }
...@@ -388,12 +408,13 @@ V8PerFrameMemoryRequest::V8PerFrameMemoryRequest( ...@@ -388,12 +408,13 @@ V8PerFrameMemoryRequest::V8PerFrameMemoryRequest(
// sequence. // sequence.
V8PerFrameMemoryRequest::V8PerFrameMemoryRequest( V8PerFrameMemoryRequest::V8PerFrameMemoryRequest(
util::PassKey<V8PerFrameMemoryRequestAnySeq>, util::PassKey<V8PerFrameMemoryRequestAnySeq>,
const base::TimeDelta& sample_frequency, const base::TimeDelta& min_time_between_requests,
MeasurementMode mode,
base::WeakPtr<V8PerFrameMemoryRequestAnySeq> off_sequence_request) base::WeakPtr<V8PerFrameMemoryRequestAnySeq> off_sequence_request)
: sample_frequency_(sample_frequency), : V8PerFrameMemoryRequest(min_time_between_requests, mode) {
off_sequence_request_(std::move(off_sequence_request)),
off_sequence_request_sequence_(base::SequencedTaskRunnerHandle::Get()) {
DETACH_FROM_SEQUENCE(sequence_checker_); DETACH_FROM_SEQUENCE(sequence_checker_);
off_sequence_request_ = std::move(off_sequence_request);
off_sequence_request_sequence_ = base::SequencedTaskRunnerHandle::Get();
// Unretained is safe since |this| will be destroyed on the graph sequence. // Unretained is safe since |this| will be destroyed on the graph sequence.
PerformanceManager::CallOnGraph( PerformanceManager::CallOnGraph(
FROM_HERE, base::BindOnce(&V8PerFrameMemoryRequest::StartMeasurement, FROM_HERE, base::BindOnce(&V8PerFrameMemoryRequest::StartMeasurement,
...@@ -586,12 +607,10 @@ base::Value V8PerFrameMemoryDecorator::DescribeProcessNodeData( ...@@ -586,12 +607,10 @@ base::Value V8PerFrameMemoryDecorator::DescribeProcessNodeData(
return dict; return dict;
} }
base::TimeDelta V8PerFrameMemoryDecorator::GetMinTimeBetweenRequestsPerProcess() V8PerFrameMemoryRequest* V8PerFrameMemoryDecorator::GetNextRequest() const {
const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return measurement_requests_.empty() return measurement_requests_.empty() ? nullptr
? base::TimeDelta() : measurement_requests_.front();
: measurement_requests_.front()->sample_frequency();
} }
void V8PerFrameMemoryDecorator::AddMeasurementRequest( void V8PerFrameMemoryDecorator::AddMeasurementRequest(
...@@ -607,7 +626,14 @@ void V8PerFrameMemoryDecorator::AddMeasurementRequest( ...@@ -607,7 +626,14 @@ void V8PerFrameMemoryDecorator::AddMeasurementRequest(
for (std::vector<V8PerFrameMemoryRequest*>::const_iterator it = for (std::vector<V8PerFrameMemoryRequest*>::const_iterator it =
measurement_requests_.begin(); measurement_requests_.begin();
it != measurement_requests_.end(); ++it) { it != measurement_requests_.end(); ++it) {
if (request->sample_frequency() < (*it)->sample_frequency()) { if (request->min_time_between_requests() <
(*it)->min_time_between_requests() ||
// Make sure bounded request sort before lazy requests so that they
// aren't starved.
(request->min_time_between_requests() ==
(*it)->min_time_between_requests() &&
request->mode() ==
V8PerFrameMemoryRequest::MeasurementMode::kBounded)) {
measurement_requests_.insert(it, request); measurement_requests_.insert(it, request);
UpdateProcessMeasurementSchedules(); UpdateProcessMeasurementSchedules();
return; return;
...@@ -636,8 +662,8 @@ void V8PerFrameMemoryDecorator::UpdateProcessMeasurementSchedules() const { ...@@ -636,8 +662,8 @@ void V8PerFrameMemoryDecorator::UpdateProcessMeasurementSchedules() const {
for (size_t i = 1; i < measurement_requests_.size(); ++i) { for (size_t i = 1; i < measurement_requests_.size(); ++i) {
DCHECK(measurement_requests_[i - 1]); DCHECK(measurement_requests_[i - 1]);
DCHECK(measurement_requests_[i]); DCHECK(measurement_requests_[i]);
DCHECK_LE(measurement_requests_[i - 1]->sample_frequency(), DCHECK_LE(measurement_requests_[i - 1]->min_time_between_requests(),
measurement_requests_[i]->sample_frequency()); measurement_requests_[i]->min_time_between_requests());
} }
#endif #endif
for (const ProcessNode* node : graph_->GetAllProcessNodes()) { for (const ProcessNode* node : graph_->GetAllProcessNodes()) {
...@@ -666,7 +692,8 @@ void V8PerFrameMemoryDecorator::NotifyObserversOnMeasurementAvailable( ...@@ -666,7 +692,8 @@ void V8PerFrameMemoryDecorator::NotifyObserversOnMeasurementAvailable(
// V8PerFrameMemoryRequestAnySeq // V8PerFrameMemoryRequestAnySeq
V8PerFrameMemoryRequestAnySeq::V8PerFrameMemoryRequestAnySeq( V8PerFrameMemoryRequestAnySeq::V8PerFrameMemoryRequestAnySeq(
const base::TimeDelta& sample_frequency) { const base::TimeDelta& min_time_between_requests,
MeasurementMode mode) {
// |request_| must be initialized in the constructor body so that // |request_| must be initialized in the constructor body so that
// |weak_factory_| is completely constructed. // |weak_factory_| is completely constructed.
// //
...@@ -674,8 +701,8 @@ V8PerFrameMemoryRequestAnySeq::V8PerFrameMemoryRequestAnySeq( ...@@ -674,8 +701,8 @@ V8PerFrameMemoryRequestAnySeq::V8PerFrameMemoryRequestAnySeq(
// constructor. After construction the V8PerFrameMemoryRequest must only be // constructor. After construction the V8PerFrameMemoryRequest must only be
// accessed on the graph sequence. // accessed on the graph sequence.
request_ = base::WrapUnique(new V8PerFrameMemoryRequest( request_ = base::WrapUnique(new V8PerFrameMemoryRequest(
util::PassKey<V8PerFrameMemoryRequestAnySeq>(), sample_frequency, util::PassKey<V8PerFrameMemoryRequestAnySeq>(), min_time_between_requests,
weak_factory_.GetWeakPtr())); mode, weak_factory_.GetWeakPtr()));
} }
V8PerFrameMemoryRequestAnySeq::~V8PerFrameMemoryRequestAnySeq() { V8PerFrameMemoryRequestAnySeq::~V8PerFrameMemoryRequestAnySeq() {
......
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