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
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/v8_compile_hints/v8_compile_hints_tab_helper.h"
#include <optional>
#include "base/feature_list.h"
#include "base/memory/writable_shared_memory_region.h"
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/v8_compile_hints/v8_compile_hints_tab_helper.h"
#include "components/optimization_guide/core/optimization_guide_decider.h"
#include "components/optimization_guide/proto/hints.pb.h"
#include "content/public/browser/navigation_handle.h"
#include "third_party/blink/public/common/features.h"
namespace v8_compile_hints {
V8CompileHintsTabHelper::~V8CompileHintsTabHelper() = default;
V8CompileHintsTabHelper::V8CompileHintsTabHelper(
content::WebContents* web_contents,
optimization_guide::OptimizationGuideDecider* optimization_guide_decider)
: content::WebContentsObserver(web_contents),
content::WebContentsUserData<V8CompileHintsTabHelper>(*web_contents),
optimization_guide_decider_(optimization_guide_decider),
web_contents_(web_contents) {
CHECK(base::FeatureList::IsEnabled(blink::features::kConsumeCompileHints));
optimization_guide_decider_->RegisterOptimizationTypes(
{optimization_guide::proto::V8_COMPILE_HINTS});
}
void V8CompileHintsTabHelper::MaybeCreateForWebContents(
content::WebContents* web_contents) {
if (!base::FeatureList::IsEnabled(blink::features::kConsumeCompileHints)) {
return;
}
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
optimization_guide::OptimizationGuideDecider* optimization_guide_decider =
OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
if (!optimization_guide_decider) {
return;
}
web_contents->SetUserData(&kUserDataKey,
std::make_unique<V8CompileHintsTabHelper>(
web_contents, optimization_guide_decider));
}
void V8CompileHintsTabHelper::PrimaryPageChanged(content::Page& page) {
if (!optimization_guide_decider_) {
return;
}
const GURL& current_main_frame_url =
page.GetMainDocument().GetLastCommittedURL();
if (!current_main_frame_url.SchemeIsHTTPOrHTTPS()) {
return;
}
// TODO(chromium:1406506): Consider avoiding sending the data unnecessarily if
// we're doing a back-forward navigation. This can be done e.g., by attaching
// a bool flag to DocumentUserData.
on_optimization_guide_decision_.Reset(
base::BindOnce(&V8CompileHintsTabHelper::OnOptimizationGuideDecision,
base::Unretained(this)));
optimization_guide_decider_->CanApplyOptimization(
current_main_frame_url, optimization_guide::proto::V8_COMPILE_HINTS,
on_optimization_guide_decision_.callback());
}
void V8CompileHintsTabHelper::OnOptimizationGuideDecision(
optimization_guide::OptimizationGuideDecision decision,
const optimization_guide::OptimizationMetadata& metadata) {
auto model = metadata.ParsedMetadata<proto::Model>();
if (!model || !model->has_sample_count() || !model->has_clear_ones() ||
!model->has_clear_zeros() ||
static_cast<size_t>(model->bloom_filter().size()) != kModelInt64Count) {
base::UmaHistogramEnumeration(kModelQualityHistogramName,
V8CompileHintsModelQuality::kNoModel);
return;
}
// Reject models which are not good enough. The model goodness is estimated by
// checking whether the "clear zeros" and "clear ones" counts match an
// expected Bloom filter.
constexpr int32_t kClearZerosMin = 32000;
constexpr int32_t kClearOnesMin = 1000;
if (model->clear_zeros() < kClearZerosMin ||
model->clear_ones() < kClearOnesMin) {
base::UmaHistogramEnumeration(kModelQualityHistogramName,
V8CompileHintsModelQuality::kBadModel);
return;
}
base::UmaHistogramEnumeration(kModelQualityHistogramName,
V8CompileHintsModelQuality::kGoodModel);
SendDataToRenderer(*model);
}
void V8CompileHintsTabHelper::SendDataToRenderer(const proto::Model& model) {
if (send_data_to_renderer_for_testing_) {
send_data_to_renderer_for_testing_.Run(model);
return;
}
constexpr size_t kBufferSize = kModelInt64Count * 8;
mojo::ScopedSharedBufferHandle mojo_buffer =
mojo::SharedBufferHandle::Create(kBufferSize);
if (!mojo_buffer.is_valid()) {
DVLOG(1) << "V8CompileHintsTabHelper shared memory handle is not valid";
return;
}
base::WritableSharedMemoryRegion writable_region =
mojo::UnwrapWritableSharedMemoryRegion(std::move(mojo_buffer));
if (!writable_region.IsValid()) {
DVLOG(1) << "V8CompileHintsTabHelper shared memory region is not valid";
return;
}
base::WritableSharedMemoryMapping shared_memory_mapping =
writable_region.Map();
if (!shared_memory_mapping.IsValid()) {
DVLOG(1) << "V8CompileHintsTabHelper shared memory mapping is not valid";
return;
}
int64_t* memory = shared_memory_mapping.GetMemoryAs<int64_t>();
for (size_t i = 0; i < kModelInt64Count; ++i) {
memory[i] = model.bloom_filter().Get(i);
}
base::ReadOnlySharedMemoryRegion read_only_region =
base::WritableSharedMemoryRegion::ConvertToReadOnly(
std::move(writable_region));
if (!read_only_region.IsValid()) {
DVLOG(1) << "V8CompileHintsTabHelper read only shared memory region is "
"not valid";
return;
}
web_contents_->SetV8CompileHints(std::move(read_only_region));
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(V8CompileHintsTabHelper);
} // namespace v8_compile_hints
|