1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/page_load_metrics/browser/page_load_metrics_memory_tracker.h"
#include <map>
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "components/page_load_metrics/browser/features.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/process_node.h"
#include "components/performance_manager/public/render_frame_host_proxy.h"
namespace page_load_metrics {
namespace {
using performance_manager::v8_memory::V8DetailedMemoryExecutionContextData;
using performance_manager::v8_memory::V8DetailedMemoryProcessData;
using performance_manager::v8_memory::V8DetailedMemoryRequest;
// WeakPtrs cannot be used as the key to a map without a custom comparator, so
// we use a raw pointer for the map key and bundle the WeakPtr in a struct
// to be used as the value.
struct ObserverWeakPtrAndMemoryUpdates {
base::WeakPtr<MetricsWebContentsObserver> weak_ptr;
std::vector<MemoryUpdate> updates;
ObserverWeakPtrAndMemoryUpdates(
base::WeakPtr<MetricsWebContentsObserver> wk_ptr,
MemoryUpdate update)
: weak_ptr(wk_ptr) {
updates.emplace_back(update);
}
};
} // namespace
// Results of the V8PerAdFrameMemoryPollParamsStudy indicated that at the
// ~99.8th percentile, collecting at 10-second or 60-second intervals
// yields nearly equivalent results, as does using kBounded or kLazy mode.
// As there is about 10% to 20% overhead total GC time, we chose the less
// aggressive kLazy mode with a 60-second polling interval.
// For further results please see crbug.com/1116087.
PageLoadMetricsMemoryTracker::PageLoadMetricsMemoryTracker() {
if (base::FeatureList::IsEnabled(
page_load_metrics::features::kV8PerFrameMemoryMonitoring)) {
memory_request_ = std::make_unique<V8DetailedMemoryRequest>(
base::Seconds(60), V8DetailedMemoryRequest::MeasurementMode::kLazy);
memory_request_->AddObserver(this);
memory_request_->StartMeasurement();
}
}
PageLoadMetricsMemoryTracker::~PageLoadMetricsMemoryTracker() = default;
void PageLoadMetricsMemoryTracker::Shutdown() {
if (memory_request_) {
memory_request_->RemoveObserver(this);
memory_request_.reset();
}
}
void PageLoadMetricsMemoryTracker::OnV8MemoryMeasurementAvailable(
const performance_manager::ProcessNode* process_node,
const V8DetailedMemoryProcessData* process_data) {
std::map<MetricsWebContentsObserver*, ObserverWeakPtrAndMemoryUpdates>
memory_update_map;
// Iterate through frames with available measurements.
for (const performance_manager::FrameNode* frame_node :
process_node->GetFrameNodes()) {
const auto* frame_data =
V8DetailedMemoryExecutionContextData::ForFrameNode(frame_node);
if (!frame_data) {
continue;
}
content::RenderFrameHost* rfh = frame_node->GetRenderFrameHostProxy().Get();
// We lose a small amount of data due to a RenderFrameHost
// sometimes no longer being alive by the time that a report is received.
// UMA suggests we miss about 0.078% of updates on desktop and about 0.11%
// on mobile (as measured 10/30/2020).
// See crbug.com/1116087.
if (!rfh) {
continue;
}
int64_t delta_bytes =
UpdateMemoryUsageAndGetDelta(rfh, frame_data->v8_bytes_used());
// Only send updates that are nontrivial.
if (delta_bytes == 0) {
continue;
}
// Note that at this point, we are guaranteed that the frame is alive, and
// frames cannot exist without an owning WebContents.
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(rfh);
MetricsWebContentsObserver* observer =
MetricsWebContentsObserver::FromWebContents(web_contents);
if (!observer) {
continue;
}
auto emplace_pair = memory_update_map.emplace(std::make_pair(
observer, ObserverWeakPtrAndMemoryUpdates(
observer->AsWeakPtr(),
MemoryUpdate(rfh->GetGlobalId(), delta_bytes))));
if (!emplace_pair.second) {
emplace_pair.first->second.updates.emplace_back(
MemoryUpdate(rfh->GetGlobalId(), delta_bytes));
}
}
// Dispatch memory updates to each observer. Note that we store references to
// MetricsWebContentsObservers as weakptrs. This is done to ensure that if a
// WebContents was torn down synchronously as the result of a memory update
// in a different WebContents, we would not have a dangling pointer.
for (const auto& map_pair : memory_update_map) {
MetricsWebContentsObserver* observer = map_pair.second.weak_ptr.get();
if (!observer) {
continue;
}
observer->OnV8MemoryChanged(map_pair.second.updates);
}
}
void PageLoadMetricsMemoryTracker::OnRenderFrameDeleted(
content::RenderFrameHost* render_frame_host,
MetricsWebContentsObserver* observer) {
DCHECK(render_frame_host);
DCHECK(observer);
auto it = per_frame_memory_usage_map_.find(render_frame_host->GetRoutingID());
if (it == per_frame_memory_usage_map_.end()) {
return;
}
// The routing id for |render_frame_host| has been found in our usage map.
// We assume that the renderer has released the frame and that its
// contents will be picked up by the next GC. So for all intents and
// purposes, the memory is freed at this point, and we remove the entry from
// our usage map and notify observers of the delta.
int64_t delta_bytes = -it->second;
per_frame_memory_usage_map_.erase(it);
// Only send updates that are nontrivial.
if (delta_bytes == 0) {
return;
}
std::vector<MemoryUpdate> update(
{MemoryUpdate(render_frame_host->GetGlobalId(), delta_bytes)});
observer->OnV8MemoryChanged(update);
}
int64_t PageLoadMetricsMemoryTracker::UpdateMemoryUsageAndGetDelta(
content::RenderFrameHost* render_frame_host,
uint64_t current_bytes_used) {
DCHECK(render_frame_host);
int64_t delta_bytes = current_bytes_used;
int routing_id = render_frame_host->GetRoutingID();
auto it = per_frame_memory_usage_map_.find(routing_id);
if (it != per_frame_memory_usage_map_.end()) {
delta_bytes -= it->second;
it->second = current_bytes_used;
} else {
per_frame_memory_usage_map_[routing_id] = current_bytes_used;
}
return delta_bytes;
}
} // namespace page_load_metrics
|