mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-26 20:21:35 +08:00
Update On Tue Apr 29 20:34:58 CEST 2025
This commit is contained in:
@@ -1 +1 @@
|
||||
135.0.7049.38
|
||||
136.0.7103.44
|
||||
|
@@ -74,6 +74,7 @@ default_args = {
|
||||
devtools_visibility = [ "*" ]
|
||||
|
||||
clang_unsafe_buffers_paths = "//build/config/unsafe_buffers_paths.txt"
|
||||
clang_warning_suppression_file = "//build/config/warning_suppression.txt"
|
||||
}
|
||||
|
||||
# These are the targets to skip header checking by default. The files in targets
|
||||
|
@@ -286,6 +286,7 @@ Christophe Dumez <ch.dumez@samsung.com>
|
||||
Christopher Dale <chrelad@gmail.com>
|
||||
Chunbo Hua <chunbo.hua@intel.com>
|
||||
Claudio DeSouza <claudiomdsjr@gmail.com>
|
||||
Clay Miller <clay@smockle.com>
|
||||
Clemens Fruhwirth <clemens@endorphin.org>
|
||||
Clement Scheelfeldt Skau <clementskau@gmail.com>
|
||||
Clinton Staley <clintstaley@gmail.com>
|
||||
@@ -503,6 +504,7 @@ Haitao Feng <haitao.feng@intel.com>
|
||||
Halley Zhao <halley.zhao@intel.com>
|
||||
Halton Huo <halton.huo@gmail.com>
|
||||
Halton Huo <halton.huo@intel.com>
|
||||
Hamed A. Elgizery <hamedashraf2004@gmail.com>
|
||||
Hans Hillen <hans.hillen@gmail.com>
|
||||
Hansel Lee <mr.hansel.lee@gmail.com>
|
||||
Hanwen Zheng <eserinc.z@gmail.com>
|
||||
@@ -537,6 +539,7 @@ Hongbo Min <hongbo.min@intel.com>
|
||||
Horia Olaru <horia.olaru@gmail.com>
|
||||
Horia Olaru <olaru@adobe.com>
|
||||
Hosung You <hosung.you@samsung.com>
|
||||
Howie Ji <imaginejhy@163.com>
|
||||
Huai Wang <gkvjwa@gmail.com>
|
||||
Huapeng Li <huapengl@amazon.com>
|
||||
Huayong Xu <huayong.xu@samsung.com>
|
||||
@@ -825,6 +828,7 @@ Kirill Ovchinnikov <kirill.ovchinn@gmail.com>
|
||||
Klemen Forstnerič <klemen.forstneric@gmail.com>
|
||||
Kodam Nagaraju <k2.nagaraju@samsung.com>
|
||||
Konrad Dzwinel <kdzwinel@gmail.com>
|
||||
Koushik Kumar Bug <koushikbug123@gmail.com>
|
||||
Kousuke Takaki <yoseio@brainoid.dev>
|
||||
Kovacs Zeteny <brightbulbapp@gmail.com>
|
||||
Krishna Chaitanya <krish.botta@samsung.com>
|
||||
@@ -1041,6 +1045,7 @@ Momoko Hattori <momohatt10@gmail.com>
|
||||
Mostafa Sedaghat joo <mostafa.sedaghat@gmail.com>
|
||||
Mrunal Kapade <mrunal.kapade@intel.com>
|
||||
Muhammad Mahad <mahadtxt@gmail.com>
|
||||
Muhammad Saqlain <2mesaqlain@gmail.com>
|
||||
Munira Tursunova <moonira@google.com>
|
||||
Myeongjin Cho <myeongjin.cho@navercorp.com>
|
||||
Myles C. Maxfield <mymax@amazon.com>
|
||||
@@ -1284,6 +1289,7 @@ Saravanan KR <sramajay@cisco.com>
|
||||
Sathish Kuppuswamy <sathish.kuppuswamy@intel.com>
|
||||
Satoshi Matsuzaki <satoshi.matsuzaki@gmail.com>
|
||||
Satyajit Sahu <satyajit.sahu@amd.com>
|
||||
Satvic Dhawan <satvicdhawan14@gmail.com>
|
||||
Sayan Nayak <sayan.nayak@samsung.com>
|
||||
Sayan Sivakumaran <sivakusayan@gmail.com>
|
||||
Scott D Phillips <scott.d.phillips@intel.com>
|
||||
@@ -1366,6 +1372,7 @@ Song Fangzhen <songfangzhen@bytedance.com>
|
||||
Song Qinglin <songql@dingdao.com>
|
||||
Song Qinglin <songqinglin@gmail.com>
|
||||
Song YeWen <ffmpeg@gmail.com>
|
||||
Sonu Thomas <sonu.thomas@amd.com>
|
||||
Sooho Park <sooho1000@gmail.com>
|
||||
Soojung Choi <crystal2840@gmail.com>
|
||||
Soorya R <soorya.r@samsung.com>
|
||||
|
@@ -8,6 +8,7 @@
|
||||
# you add a new build file, there must be some path of dependencies from this
|
||||
# file to your new one or GN won't know about it.
|
||||
|
||||
import("//build/config/cast.gni")
|
||||
import("//build/config/compiler/compiler.gni")
|
||||
import("//build/config/cronet/config.gni")
|
||||
import("//build/config/dcheck_always_on.gni")
|
||||
@@ -76,8 +77,4 @@ _lines = [
|
||||
"",
|
||||
] + build_gn_logs
|
||||
|
||||
# GN evaluates each .gn file once per toolchain, so restricting to default
|
||||
# toolchain will ensure write_file() is called only once.
|
||||
assert(current_toolchain == default_toolchain)
|
||||
|
||||
write_file("$root_build_dir/gn_logs.txt", _lines)
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -68,7 +68,12 @@ declare_args() {
|
||||
# Set to true to enable mutex priority inheritance. See the comments in
|
||||
# LockImpl::PriorityInheritanceAvailable() in lock_impl_posix.cc for the
|
||||
# platform requirements to safely enable priority inheritance.
|
||||
enable_mutex_priority_inheritance = false
|
||||
#
|
||||
# Enabled only on 64-bit Android. On 32 bit Android, PI mutexes have an extra
|
||||
# level of indirection, so we don't bother enabling it. See:
|
||||
# https://cs.android.com/android/platform/superproject/+/android14-qpr3-release:bionic/libc/bionic/pthread_mutex.cpp;drc=9108f258adad329a537e10461fbd526d5b9ad8bf;l=242
|
||||
enable_mutex_priority_inheritance =
|
||||
is_android && (current_cpu == "x64" || current_cpu == "arm64")
|
||||
|
||||
# Control whether the ios stack sampling profiler is enabled. This flag is
|
||||
# only supported on iOS 64-bit architecture, but some project build //base
|
||||
@@ -81,12 +86,17 @@ declare_args() {
|
||||
# Chromecast builds have full control over the platform and ensure that
|
||||
# the kernel and glibc versions used have patched the vulnerabilities,
|
||||
# so it is safe to use mutex priority inheritance on Chromecast platform.
|
||||
assert(!enable_mutex_priority_inheritance || is_castos || is_cast_android,
|
||||
assert(!enable_mutex_priority_inheritance ||
|
||||
(is_castos || is_cast_android || is_android),
|
||||
"Do not enable PI mutexes without consulting the security team")
|
||||
|
||||
assert(!is_nacl || is_nacl_saigo,
|
||||
"base must not be built in most nacl toolchains")
|
||||
|
||||
# Prevent //base having to support a toolchain that uses a different
|
||||
# llvm/libc++ version.
|
||||
assert(!is_wasm, "base must not be built in most wasm toolchains")
|
||||
|
||||
# This is here instead of in //build because //build is DEPS'd in by a few
|
||||
# subprojects that still support MSVC.
|
||||
assert(!is_win || is_clang,
|
||||
@@ -241,7 +251,6 @@ component("base") {
|
||||
"containers/to_value_list.h",
|
||||
"containers/to_vector.h",
|
||||
"containers/unique_ptr_adapters.h",
|
||||
"containers/util.h",
|
||||
"containers/vector_buffer.h",
|
||||
"critical_closure.h",
|
||||
"dcheck_is_on.h",
|
||||
@@ -376,6 +385,7 @@ component("base") {
|
||||
"memory/ref_counted_memory.h",
|
||||
"memory/safe_ref.h",
|
||||
"memory/safe_ref_traits.h",
|
||||
"memory/safety_checks.cc",
|
||||
"memory/safety_checks.h",
|
||||
"memory/scoped_policy.h",
|
||||
"memory/scoped_refptr.h",
|
||||
@@ -1462,8 +1472,6 @@ component("base") {
|
||||
"files/scoped_temp_file.h",
|
||||
"json/json_file_value_serializer.cc",
|
||||
"json/json_file_value_serializer.h",
|
||||
"logging/rust_log_integration.cc",
|
||||
"logging/rust_log_integration.h",
|
||||
"memory/discardable_memory.cc",
|
||||
"memory/discardable_memory.h",
|
||||
"memory/discardable_memory_allocator.cc",
|
||||
@@ -2094,8 +2102,10 @@ component("base") {
|
||||
sources += [ "process/process_mac.cc" ]
|
||||
}
|
||||
|
||||
# We include launch_mac on simulator builds so unittests can fork.
|
||||
if (target_environment == "simulator") {
|
||||
# We include launch_mac on simulator builds so unittests can fork. But,
|
||||
# platforms like tvOS continue to use launch_ios since they do not support
|
||||
# multi-processes.
|
||||
if (target_environment == "simulator" && target_platform == "iphoneos") {
|
||||
sources += [
|
||||
"process/kill_mac.cc",
|
||||
"process/launch_mac.cc",
|
||||
@@ -2427,14 +2437,38 @@ if (is_linux || is_chromeos) {
|
||||
buildflag_header("cfi_buildflags") {
|
||||
header = "cfi_buildflags.h"
|
||||
|
||||
flags = [
|
||||
# TODO(pcc): remove CFI_CAST_CHECK, see https://crbug.com/626794.
|
||||
"CFI_CAST_CHECK=$is_cfi && $use_cfi_cast",
|
||||
"CFI_DIAG=$is_cfi && $use_cfi_diag",
|
||||
"CFI_ICALL_CHECK=$is_cfi && $use_cfi_icall",
|
||||
"CFI_ENFORCEMENT_TRAP=$is_cfi && !$use_cfi_diag",
|
||||
"CFI_ENFORCEMENT_DIAGNOSTIC=$is_cfi && $use_cfi_diag && !$use_cfi_recover",
|
||||
]
|
||||
flags = []
|
||||
|
||||
# TODO(pcc): remove CFI_CAST_CHECK, see https://crbug.com/626794.
|
||||
if (is_cfi && use_cfi_cast) {
|
||||
flags += [ "CFI_CAST_CHECK=true" ]
|
||||
} else {
|
||||
flags += [ "CFI_CAST_CHECK=false" ]
|
||||
}
|
||||
|
||||
if (is_cfi && use_cfi_diag) {
|
||||
flags += [ "CFI_DIAG=true" ]
|
||||
} else {
|
||||
flags += [ "CFI_DIAG=false" ]
|
||||
}
|
||||
|
||||
if (is_cfi && use_cfi_icall) {
|
||||
flags += [ "CFI_ICALL_CHECK=true" ]
|
||||
} else {
|
||||
flags += [ "CFI_ICALL_CHECK=false" ]
|
||||
}
|
||||
|
||||
if (is_cfi && !use_cfi_diag) {
|
||||
flags += [ "CFI_ENFORCEMENT_TRAP=true" ]
|
||||
} else {
|
||||
flags += [ "CFI_ENFORCEMENT_TRAP=false" ]
|
||||
}
|
||||
|
||||
if (is_cfi && use_cfi_diag && !use_cfi_recover) {
|
||||
flags += [ "CFI_ENFORCEMENT_DIAGNOSTIC=true" ]
|
||||
} else {
|
||||
flags += [ "CFI_ENFORCEMENT_DIAGNOSTIC=false" ]
|
||||
}
|
||||
}
|
||||
|
||||
buildflag_header("debugging_buildflags") {
|
||||
|
@@ -3,7 +3,6 @@
|
||||
set noparent
|
||||
# NOTE: keep this in sync with global-owners-override@chromium.org owners
|
||||
# by emailing lsc-policy@chromium.org when this list changes.
|
||||
altimin@chromium.org
|
||||
dcheng@chromium.org
|
||||
fdoray@chromium.org
|
||||
gab@chromium.org
|
||||
@@ -35,15 +34,10 @@ per-file feature_list*=isherman@chromium.org
|
||||
|
||||
# Logging-related changes:
|
||||
per-file check*=olivierli@chromium.org
|
||||
per-file check*=pbos@chromium.org
|
||||
per-file dcheck*=olivierli@chromium.org
|
||||
per-file dcheck*=pbos@chromium.org
|
||||
per-file logging*=olivierli@chromium.org
|
||||
per-file logging*=pbos@chromium.org
|
||||
per-file notimplemented.h=olivierli@chromium.org
|
||||
per-file notimplemented.h=pbos@chromium.org
|
||||
per-file notreached.h=olivierli@chromium.org
|
||||
per-file notreached.h=pbos@chromium.org
|
||||
|
||||
# Restricted since rand_util.h also backs the cryptographically secure RNG.
|
||||
per-file rand_util*=set noparent
|
||||
|
@@ -638,7 +638,7 @@ void CheckDanglingRawPtrBufferEmpty() {
|
||||
g_stack_trace_buffer = DanglingRawPtrBuffer();
|
||||
#else
|
||||
bool errors = false;
|
||||
for (auto entry : g_stack_trace_buffer) {
|
||||
for (const auto& entry : g_stack_trace_buffer) {
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
@@ -1380,4 +1380,10 @@ std::string PartitionAllocSupport::ExtractDanglingPtrSignatureForTests(
|
||||
}
|
||||
#endif
|
||||
|
||||
void CheckHeapIntegrity(const void* ptr) {
|
||||
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
|
||||
partition_alloc::PartitionRoot::CheckMetadataIntegrity(ptr);
|
||||
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
|
||||
}
|
||||
|
||||
} // namespace base::allocator
|
||||
|
@@ -158,6 +158,13 @@ class BASE_EXPORT MemoryReclaimerSupport {
|
||||
bool has_pending_task_ = false;
|
||||
};
|
||||
|
||||
// Utility function to detect Double-Free or Out-of-Bounds writes.
|
||||
// This function can be called to memory assumed to be valid.
|
||||
// If not, this may crash (not guaranteed).
|
||||
// This is useful if you want to investigate crashes at `free()`,
|
||||
// to know which point at execution it goes wrong.
|
||||
BASE_EXPORT void CheckHeapIntegrity(const void* ptr);
|
||||
|
||||
} // namespace base::allocator
|
||||
|
||||
#endif // BASE_ALLOCATOR_PARTITION_ALLOC_SUPPORT_H_
|
||||
|
@@ -72,7 +72,8 @@ if (is_nacl) {
|
||||
current_cpu == "loong64" || current_cpu == "riscv64" ||
|
||||
current_cpu == "mips64el") {
|
||||
has_64_bit_pointers = true
|
||||
} else if (current_cpu == "x86" || current_cpu == "arm" || current_cpu == "mipsel") {
|
||||
} else if (current_cpu == "x86" || current_cpu == "arm" ||
|
||||
current_cpu == "wasm" || current_cpu == "mipsel") {
|
||||
has_64_bit_pointers = false
|
||||
} else {
|
||||
assert(false, "Unknown CPU: $current_cpu")
|
||||
@@ -94,8 +95,8 @@ use_large_empty_slot_span_ring = true
|
||||
# mallopt can be loaded after API 26.
|
||||
# mallopt M_BIONIC_SET_HEAP_TAGGING_LEVEL can be called after API 31.
|
||||
# Disables for OpenWrt Musl which lacks the required ifunc support.
|
||||
has_memory_tagging =
|
||||
current_cpu == "arm64" && is_clang && !is_asan && is_linux && current_os != "openwrt"
|
||||
has_memory_tagging = current_cpu == "arm64" && is_clang && !is_asan &&
|
||||
!is_hwasan && is_linux && current_os != "openwrt"
|
||||
|
||||
declare_args() {
|
||||
# Debug configuration.
|
||||
|
@@ -176,6 +176,7 @@ pa_buildflag_header("buildflags") {
|
||||
"IS_CAST_ANDROID=$is_cast_android",
|
||||
"IS_CHROMEOS=$is_chromeos",
|
||||
"IS_DEBUG=$partition_alloc_is_debug",
|
||||
"IS_HWASAN=$is_hwasan",
|
||||
"RAW_PTR_ZERO_ON_CONSTRUCT=$raw_ptr_zero_on_construct",
|
||||
"RAW_PTR_ZERO_ON_DESTRUCT=$raw_ptr_zero_on_destruct",
|
||||
"RAW_PTR_ZERO_ON_MOVE=$raw_ptr_zero_on_move",
|
||||
@@ -416,6 +417,7 @@ if (is_clang_or_gcc) {
|
||||
"freeslot_bitmap_constants.h",
|
||||
"gwp_asan_support.cc",
|
||||
"gwp_asan_support.h",
|
||||
"in_slot_metadata.cc",
|
||||
"in_slot_metadata.h",
|
||||
"internal_allocator.cc",
|
||||
"internal_allocator.h",
|
||||
@@ -1077,6 +1079,7 @@ source_set("test_support") {
|
||||
"extended_api.h",
|
||||
"partition_alloc_base/threading/platform_thread_for_testing.h",
|
||||
"partition_alloc_for_testing.h",
|
||||
"pointers/raw_ptr_counting_impl_for_test.cc",
|
||||
"pointers/raw_ptr_counting_impl_for_test.h",
|
||||
]
|
||||
if (is_posix) {
|
||||
|
@@ -27,7 +27,8 @@ namespace partition_alloc::internal {
|
||||
|
||||
constexpr bool IsBtiEnabled(uint64_t ifunc_hwcap,
|
||||
struct __ifunc_arg_t* ifunc_hw) {
|
||||
#if PA_BUILDFLAG(PA_ARCH_CPU_ARM64) && defined(HAS_HW_CAPS)
|
||||
#if PA_BUILDFLAG(PA_ARCH_CPU_ARM64) && defined(HAS_HW_CAPS) && \
|
||||
!PA_BUILDFLAG(IS_HWASAN)
|
||||
return (ifunc_hwcap & _IFUNC_ARG_HWCAP) && (ifunc_hw->_hwcap2 & HWCAP2_BTI);
|
||||
#else
|
||||
return false;
|
||||
|
@@ -48,7 +48,7 @@ void DecommitPages(uintptr_t address, size_t size) {
|
||||
// Callers rely on the pages being zero-initialized when recommitting them.
|
||||
// |DecommitSystemPages| doesn't guarantee this on all operating systems, in
|
||||
// particular on macOS, but |DecommitAndZeroSystemPages| does.
|
||||
DecommitAndZeroSystemPages(address, size, kPageTag);
|
||||
PA_CHECK(DecommitAndZeroSystemPages(address, size, kPageTag));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@@ -23,8 +23,18 @@ namespace {
|
||||
PartitionOptions GwpAsanPartitionOptions() {
|
||||
PartitionOptions options;
|
||||
options.backup_ref_ptr = PartitionOptions::kEnabled;
|
||||
|
||||
// GWP-ASan does not reserve space for cookies.
|
||||
options.use_cookie_if_supported = PartitionOptions::kDisabled;
|
||||
return options;
|
||||
}
|
||||
|
||||
PartitionRoot* RootInstance() {
|
||||
static internal::base::NoDestructor<PartitionRoot> root(
|
||||
GwpAsanPartitionOptions());
|
||||
return root.get();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
@@ -32,8 +42,7 @@ void* GwpAsanSupport::MapRegion(size_t slot_count,
|
||||
std::vector<uint16_t>& free_list) {
|
||||
PA_CHECK(slot_count > 0);
|
||||
|
||||
static internal::base::NoDestructor<PartitionRoot> root(
|
||||
GwpAsanPartitionOptions());
|
||||
static PartitionRoot* root = RootInstance();
|
||||
|
||||
const size_t kSlotSize = 2 * internal::SystemPageSize();
|
||||
uint16_t bucket_index = PartitionRoot::SizeToBucketIndex(
|
||||
@@ -59,9 +68,9 @@ void* GwpAsanSupport::MapRegion(size_t slot_count,
|
||||
std::numeric_limits<size_t>::max() / kSuperPageSize);
|
||||
uintptr_t super_page_span_start;
|
||||
{
|
||||
internal::ScopedGuard locker{internal::PartitionRootLock(root.get())};
|
||||
internal::ScopedGuard locker{internal::PartitionRootLock(root)};
|
||||
super_page_span_start = bucket->AllocNewSuperPageSpanForGwpAsan(
|
||||
root.get(), super_page_count, AllocFlags::kNone);
|
||||
root, super_page_count, AllocFlags::kNone);
|
||||
|
||||
if (!super_page_span_start) {
|
||||
return nullptr;
|
||||
@@ -93,7 +102,7 @@ void* GwpAsanSupport::MapRegion(size_t slot_count,
|
||||
partition_page_idx += bucket->get_pages_per_slot_span()) {
|
||||
auto* slot_span_metadata =
|
||||
&page_metadata[partition_page_idx].slot_span_metadata;
|
||||
bucket->InitializeSlotSpanForGwpAsan(slot_span_metadata, root.get());
|
||||
bucket->InitializeSlotSpanForGwpAsan(slot_span_metadata, root);
|
||||
auto slot_span_start =
|
||||
internal::SlotSpanMetadata<internal::MetadataKind::kReadOnly>::
|
||||
ToSlotSpanStart(slot_span_metadata);
|
||||
@@ -131,6 +140,13 @@ bool GwpAsanSupport::CanReuse(uintptr_t slot_start) {
|
||||
->CanBeReusedByGwpAsan();
|
||||
}
|
||||
|
||||
// static
|
||||
void GwpAsanSupport::DestructForTesting() {
|
||||
static PartitionRoot* root = RootInstance();
|
||||
internal::ScopedGuard locker{internal::PartitionRootLock(root)};
|
||||
root->DestructForTesting(); // IN-TEST
|
||||
}
|
||||
|
||||
} // namespace partition_alloc
|
||||
|
||||
#endif // PA_BUILDFLAG(ENABLE_GWP_ASAN_SUPPORT)
|
||||
|
@@ -111,6 +111,7 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) GwpAsanSupport {
|
||||
public:
|
||||
static void* MapRegion(size_t slot_count, std::vector<uint16_t>& free_list);
|
||||
static bool CanReuse(uintptr_t slot_start);
|
||||
static void DestructForTesting();
|
||||
};
|
||||
|
||||
} // namespace partition_alloc
|
||||
|
@@ -0,0 +1,101 @@
|
||||
// Copyright 2025 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "partition_alloc/in_slot_metadata.h"
|
||||
|
||||
#include "partition_alloc/build_config.h"
|
||||
#include "partition_alloc/buildflags.h"
|
||||
#include "partition_alloc/partition_alloc_base/logging.h"
|
||||
#include "partition_alloc/partition_bucket.h"
|
||||
#include "partition_alloc/partition_freelist_entry.h"
|
||||
#include "partition_alloc/partition_page.h"
|
||||
#include "partition_alloc/partition_root.h"
|
||||
#include "partition_alloc/thread_cache.h"
|
||||
|
||||
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
|
||||
|
||||
namespace partition_alloc::internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// If double-free, the freed `slot` will be a freelist entry.
|
||||
bool IsInFreelist(uintptr_t slot_start,
|
||||
SlotSpanMetadata<MetadataKind::kReadOnly>* slot_span,
|
||||
size_t& position) {
|
||||
size_t slot_size = slot_span->bucket->slot_size;
|
||||
|
||||
// Next check if the `slot_start` is in the `slot_span`'s freelist.
|
||||
PartitionFreelistEntry* node = slot_span->get_freelist_head();
|
||||
const PartitionFreelistDispatcher* dispatcher =
|
||||
PartitionRoot::FromSlotSpanMetadata(slot_span)->get_freelist_dispatcher();
|
||||
|
||||
size_t length = slot_span->GetFreelistLength();
|
||||
size_t index = 0;
|
||||
while (node != nullptr && index < length) {
|
||||
if (UntagAddr(reinterpret_cast<uintptr_t>(node)) == slot_start) {
|
||||
// This means `double-free`.
|
||||
position = index;
|
||||
return true;
|
||||
}
|
||||
// GetNext() causes crash if the freelist is corrupted.
|
||||
node = dispatcher->GetNext(node, slot_size);
|
||||
++index;
|
||||
}
|
||||
position = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED void DoubleFreeDetected(
|
||||
size_t position) {
|
||||
// If the double free happens very soon, `position` will be small.
|
||||
// We can use the value to estimate how large buffer we need to remember
|
||||
// freed slots. i.e. |slot_size * position| bytes.
|
||||
PA_DEBUG_DATA_ON_STACK("entrypos", position);
|
||||
// If we want to add more data related to the double-free, we will
|
||||
// add PA_DEBUG_DATA_ON_STACK() here.
|
||||
PA_NO_CODE_FOLDING();
|
||||
PA_IMMEDIATE_CRASH();
|
||||
}
|
||||
|
||||
[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED void CorruptionDetected() {
|
||||
// If we want to add more data related to the corruption, we will
|
||||
// add PA_DEBUG_DATA_ON_STACK() here.
|
||||
PA_NO_CODE_FOLDING();
|
||||
PA_IMMEDIATE_CRASH();
|
||||
}
|
||||
|
||||
[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED void
|
||||
InSlotMetadata::DoubleFreeOrCorruptionDetected(
|
||||
InSlotMetadata::CountType count,
|
||||
uintptr_t slot_start,
|
||||
SlotSpanMetadata<MetadataKind::kReadOnly>* slot_span) {
|
||||
// Lock the PartitionRoot here, because to travserse SlotSpanMetadata's
|
||||
// freelist, we need PartitionRootLock().
|
||||
PartitionRoot* root = PartitionRoot::FromSlotSpanMetadata(slot_span);
|
||||
size_t slot_size = slot_span->bucket->slot_size;
|
||||
size_t position = 0;
|
||||
ScopedGuard scope(PartitionRootLock(root));
|
||||
|
||||
PA_DEBUG_DATA_ON_STACK("refcount", count);
|
||||
// Record `slot_size` here. If crashes inside IsInFreelist(), minidump
|
||||
// will have `slot_size` in its stack data.
|
||||
PA_DEBUG_DATA_ON_STACK("slotsize", slot_size);
|
||||
|
||||
if (auto* thread_cache = root->GetThreadCache()) {
|
||||
if (thread_cache->IsInFreelist(slot_start, slot_size, position)) {
|
||||
DoubleFreeDetected(position);
|
||||
}
|
||||
}
|
||||
if (IsInFreelist(slot_start, slot_span, position)) {
|
||||
DoubleFreeDetected(position);
|
||||
}
|
||||
|
||||
CorruptionDetected();
|
||||
}
|
||||
|
||||
} // namespace partition_alloc::internal
|
||||
|
||||
#endif // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
|
@@ -251,7 +251,9 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) InSlotMetadata {
|
||||
|
||||
// Returns true if the allocation should be reclaimed.
|
||||
// This function should be called by the allocator during Free().
|
||||
PA_ALWAYS_INLINE bool ReleaseFromAllocator() {
|
||||
PA_ALWAYS_INLINE bool ReleaseFromAllocator(
|
||||
uintptr_t slot_start,
|
||||
SlotSpanMetadata<MetadataKind::kReadOnly>* slot_span) {
|
||||
CheckCookieIfSupported();
|
||||
|
||||
CountType old_count =
|
||||
@@ -263,7 +265,7 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) InSlotMetadata {
|
||||
// overwritten by the freelist pointer (or its shadow) for very small slots,
|
||||
// thus masking the error away.
|
||||
if (!(old_count & kMemoryHeldByAllocatorBit)) [[unlikely]] {
|
||||
DoubleFreeOrCorruptionDetected(old_count);
|
||||
DoubleFreeOrCorruptionDetected(old_count, slot_start, slot_span);
|
||||
}
|
||||
|
||||
// Release memory when no raw_ptr<> exists anymore:
|
||||
@@ -301,6 +303,17 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) InSlotMetadata {
|
||||
return alive;
|
||||
}
|
||||
|
||||
// Assertion to allocation which ought to be alive.
|
||||
PA_ALWAYS_INLINE void EnsureAlive(
|
||||
uintptr_t slot_start,
|
||||
SlotSpanMetadata<MetadataKind::kReadOnly>* slot_span) {
|
||||
CountType count = count_.load(std::memory_order_relaxed);
|
||||
if (!(count & kMemoryHeldByAllocatorBit)) {
|
||||
DoubleFreeOrCorruptionDetected(count, slot_start, slot_span);
|
||||
}
|
||||
CheckCookieIfSupported();
|
||||
}
|
||||
|
||||
// Called when a raw_ptr is not banning dangling ptrs, but the user still
|
||||
// wants to ensure the pointer is not currently dangling. This is currently
|
||||
// used in UnretainedWrapper to make sure callbacks are not invoked with
|
||||
@@ -446,12 +459,10 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) InSlotMetadata {
|
||||
}
|
||||
#endif // PA_CONFIG(IN_SLOT_METADATA_CHECK_COOKIE)
|
||||
|
||||
[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED void
|
||||
DoubleFreeOrCorruptionDetected(CountType count) {
|
||||
PA_DEBUG_DATA_ON_STACK("refcount", count);
|
||||
PA_NO_CODE_FOLDING();
|
||||
PA_IMMEDIATE_CRASH();
|
||||
}
|
||||
[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED static void
|
||||
DoubleFreeOrCorruptionDetected(CountType count,
|
||||
uintptr_t slot_start,
|
||||
SlotSpanMetadata<MetadataKind::kReadOnly>*);
|
||||
|
||||
// Note that in free slots, this is overwritten by encoded freelist
|
||||
// pointer(s). The way the pointers are encoded on 64-bit little-endian
|
||||
|
@@ -20,7 +20,6 @@
|
||||
|
||||
#if PA_BUILDFLAG(IS_LINUX) || PA_BUILDFLAG(IS_CHROMEOS)
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
@@ -135,6 +134,8 @@ PlatformThreadId PlatformThread::CurrentId() {
|
||||
return gettid();
|
||||
#elif PA_BUILDFLAG(IS_FUCHSIA)
|
||||
return zx_thread_self();
|
||||
#elif PA_BUILDFLAG(IS_ASMJS)
|
||||
return pthread_self();
|
||||
#elif PA_BUILDFLAG(IS_SOLARIS) || PA_BUILDFLAG(IS_QNX)
|
||||
return pthread_self();
|
||||
#elif PA_BUILDFLAG(IS_POSIX) && PA_BUILDFLAG(IS_AIX)
|
||||
|
@@ -12,12 +12,12 @@
|
||||
#if PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
namespace partition_alloc::internal {
|
||||
[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED void CookieCorruptionDetected(
|
||||
unsigned char* cookie_ptr,
|
||||
const unsigned char* cookie_ptr,
|
||||
size_t slot_usable_size) {
|
||||
using CookieValue = std::conditional_t<kCookieSize == 4, uint32_t, uint64_t>;
|
||||
static_assert(sizeof(CookieValue) <= kCookieSize);
|
||||
CookieValue cookie =
|
||||
*static_cast<CookieValue*>(static_cast<void*>(cookie_ptr));
|
||||
*static_cast<const CookieValue*>(static_cast<const void*>(cookie_ptr));
|
||||
PA_DEBUG_DATA_ON_STACK("slotsize", slot_usable_size);
|
||||
PA_DEBUG_DATA_ON_STACK("cookie", cookie);
|
||||
|
||||
|
@@ -37,11 +37,12 @@ inline constexpr unsigned char kCookieValue[] = {
|
||||
|
||||
constexpr size_t kPartitionCookieSizeAdjustment = kCookieSize;
|
||||
|
||||
[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED PA_COMPONENT_EXPORT(
|
||||
PARTITION_ALLOC) void CookieCorruptionDetected(unsigned char* cookie_ptr,
|
||||
size_t slot_usable_size);
|
||||
[[noreturn]] PA_NOINLINE PA_NOT_TAIL_CALLED
|
||||
PA_COMPONENT_EXPORT(PARTITION_ALLOC) void CookieCorruptionDetected(
|
||||
const unsigned char* cookie_ptr,
|
||||
size_t slot_usable_size);
|
||||
|
||||
PA_ALWAYS_INLINE void PartitionCookieCheckValue(unsigned char* cookie_ptr,
|
||||
PA_ALWAYS_INLINE void PartitionCookieCheckValue(const unsigned char* cookie_ptr,
|
||||
size_t slot_usable_size) {
|
||||
for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr) {
|
||||
if (*cookie_ptr != kCookieValue[i]) {
|
||||
@@ -60,7 +61,7 @@ PA_ALWAYS_INLINE void PartitionCookieWriteValue(unsigned char* cookie_ptr) {
|
||||
|
||||
constexpr size_t kPartitionCookieSizeAdjustment = 0;
|
||||
|
||||
PA_ALWAYS_INLINE void PartitionCookieCheckValue(unsigned char* address,
|
||||
PA_ALWAYS_INLINE void PartitionCookieCheckValue(const unsigned char* address,
|
||||
size_t slot_usable_size) {}
|
||||
|
||||
PA_ALWAYS_INLINE void PartitionCookieWriteValue(unsigned char* cookie_ptr) {}
|
||||
|
@@ -78,6 +78,28 @@ class PA_LOCKABLE Lock {
|
||||
// PA_BUILDFLAG(ENABLE_PARTITION_LOCK_REENTRANCY_CHECK)
|
||||
lock_.Release();
|
||||
}
|
||||
|
||||
bool TryAcquire() PA_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
|
||||
// Unlike `Acquire()`, `TryAcquire()` does not provide any reentrancy
|
||||
// protection. Instead, it just fails and returns `false`.
|
||||
// Hence this should not be used in (de)allocation code path.
|
||||
if (!lock_.Try()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if PA_BUILDFLAG(DCHECKS_ARE_ON) || \
|
||||
PA_BUILDFLAG(ENABLE_PARTITION_LOCK_REENTRANCY_CHECK)
|
||||
#if PA_BUILDFLAG(ENABLE_THREAD_ISOLATION)
|
||||
LiftThreadIsolationScope lift_thread_isolation_restrictions;
|
||||
#endif
|
||||
|
||||
base::PlatformThreadRef current_thread = base::PlatformThread::CurrentRef();
|
||||
owning_thread_ref_.store(current_thread, std::memory_order_release);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssertAcquired() const PA_ASSERT_EXCLUSIVE_LOCK() {
|
||||
lock_.AssertAcquired();
|
||||
#if PA_BUILDFLAG(DCHECKS_ARE_ON) || \
|
||||
|
@@ -1005,7 +1005,7 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) SlotStart {
|
||||
}
|
||||
|
||||
template <bool enforce = PA_CONFIG(ENFORCE_SLOT_STARTS)>
|
||||
PA_ALWAYS_INLINE static SlotStart FromObject(void* tagged_object) {
|
||||
PA_ALWAYS_INLINE static SlotStart FromObject(const void* tagged_object) {
|
||||
uintptr_t untagged_slot_start =
|
||||
internal::UntagAddr(reinterpret_cast<uintptr_t>(tagged_object));
|
||||
return SlotStart::FromUntaggedAddr<enforce>(untagged_slot_start);
|
||||
|
@@ -1232,7 +1232,7 @@ void PartitionRoot::Init(PartitionOptions opts) {
|
||||
#if PA_CONFIG(EXTRAS_REQUIRED)
|
||||
settings.extras_size = 0;
|
||||
|
||||
if (Settings::use_cookie) {
|
||||
if (settings.use_cookie) {
|
||||
settings.extras_size += internal::kPartitionCookieSizeAdjustment;
|
||||
}
|
||||
|
||||
@@ -1294,6 +1294,11 @@ void PartitionRoot::Init(PartitionOptions opts) {
|
||||
}
|
||||
#endif // !PA_CONFIG(THREAD_CACHE_SUPPORTED)
|
||||
|
||||
#if PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
settings.use_cookie =
|
||||
opts.use_cookie_if_supported == PartitionOptions::kEnabled;
|
||||
#endif // PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
|
||||
#if PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR)
|
||||
internal::PartitionRootEnumerator::Instance().Register(this);
|
||||
#endif
|
||||
@@ -1366,18 +1371,17 @@ void PartitionRoot::EnableThreadCacheIfSupported() {
|
||||
// become visible to another thread before the effects of
|
||||
// `internal::ThreadCacheInit()` are visible. To prevent that, we fake thread
|
||||
// cache creation being in-progress while this is running.
|
||||
//
|
||||
// This synchronizes with the acquire load in `MaybeInitThreadCacheAndAlloc()`
|
||||
// to ensure that we don't create (and thus use) a ThreadCache before
|
||||
// ThreadCache::Init()'s effects are visible.
|
||||
int before =
|
||||
thread_caches_being_constructed_.fetch_add(1, std::memory_order_acquire);
|
||||
PA_CHECK(before == 0);
|
||||
ThreadCache::Init(this);
|
||||
// Create thread cache for this thread so that we can start using it right
|
||||
// after.
|
||||
ThreadCache::Create(this);
|
||||
thread_caches_being_constructed_.fetch_sub(1, std::memory_order_release);
|
||||
|
||||
{
|
||||
::partition_alloc::internal::ScopedGuard construction_guard{
|
||||
thread_cache_construction_lock};
|
||||
|
||||
ThreadCache::Init(this);
|
||||
// Create thread cache for this thread so that we can start using it right
|
||||
// after.
|
||||
ThreadCache::Create(this);
|
||||
}
|
||||
|
||||
settings.with_thread_cache = true;
|
||||
#endif // PA_CONFIG(THREAD_CACHE_SUPPORTED)
|
||||
}
|
||||
@@ -1502,10 +1506,12 @@ bool PartitionRoot::TryReallocInPlaceForDirectMap(
|
||||
}
|
||||
|
||||
// Write a new trailing cookie.
|
||||
if (Settings::use_cookie) {
|
||||
#if PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
if (settings.use_cookie) {
|
||||
auto* object = static_cast<unsigned char*>(SlotStartToObject(slot_start));
|
||||
internal::PartitionCookieWriteValue(object + GetSlotUsableSize(slot_span));
|
||||
}
|
||||
#endif // PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1551,10 +1557,12 @@ bool PartitionRoot::TryReallocInPlaceForNormalBuckets(
|
||||
// PA_BUILDFLAG(DCHECKS_ARE_ON)
|
||||
// Write a new trailing cookie only when it is possible to keep track
|
||||
// raw size (otherwise we wouldn't know where to look for it later).
|
||||
if (Settings::use_cookie) {
|
||||
#if PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
if (settings.use_cookie) {
|
||||
internal::PartitionCookieWriteValue(static_cast<unsigned char*>(object) +
|
||||
GetSlotUsableSize(slot_span));
|
||||
}
|
||||
#endif // PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
}
|
||||
|
||||
// Always record a realloc() as a free() + malloc(), even if it's in
|
||||
@@ -1917,17 +1925,9 @@ void PartitionRoot::SetGlobalEmptySlotSpanRingIndexForTesting(int16_t index) {
|
||||
|
||||
ThreadCache* PartitionRoot::MaybeInitThreadCache() {
|
||||
auto* tcache = ThreadCache::Get();
|
||||
// See comment in `EnableThreadCacheIfSupport()` for why this is an acquire
|
||||
// load.
|
||||
if (ThreadCache::IsTombstone(tcache) ||
|
||||
thread_caches_being_constructed_.load(std::memory_order_acquire)) {
|
||||
// Two cases:
|
||||
// 1. Thread is being terminated, don't try to use the thread cache, and
|
||||
// don't try to resurrect it.
|
||||
// 2. Someone, somewhere is currently allocating a thread cache. This may
|
||||
// be us, in which case we are re-entering and should not create a thread
|
||||
// cache. If it is not us, then this merely delays thread cache
|
||||
// construction a bit, which is not an issue.
|
||||
if (ThreadCache::IsTombstone(tcache)) {
|
||||
// Thread is being terminated, don't try to use the thread cache, and don't
|
||||
// try to resurrect it.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1940,16 +1940,40 @@ ThreadCache* PartitionRoot::MaybeInitThreadCache() {
|
||||
// variable. This would end up here again, which is not what we want (and
|
||||
// likely is not supported by libc).
|
||||
//
|
||||
// To avoid this sort of reentrancy, increase the count of thread caches that
|
||||
// are currently allocating a thread cache.
|
||||
//
|
||||
// Note that there is no deadlock or data inconsistency concern, since we do
|
||||
// not hold the lock, and has such haven't touched any internal data.
|
||||
int before =
|
||||
thread_caches_being_constructed_.fetch_add(1, std::memory_order_relaxed);
|
||||
PA_CHECK(before < std::numeric_limits<int>::max());
|
||||
// Note that there is no data inconsistency concern, since we do not hold
|
||||
// the global `lock_`, and has such haven't touched any internal data.
|
||||
if (!thread_cache_construction_lock.TryAcquire()) {
|
||||
// Someone, somewhere is currently allocating a thread cache. This may be
|
||||
// us, in which case we are re-entering and should not create a thread
|
||||
// cache. If it is not us, then this merely delays thread cache
|
||||
// construction a bit, which is not an issue.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
tcache = ThreadCache::Create(this);
|
||||
thread_cache_construction_lock.Release();
|
||||
|
||||
return tcache;
|
||||
}
|
||||
|
||||
ThreadCache* PartitionRoot::ForceInitThreadCache() {
|
||||
auto* tcache = ThreadCache::Get();
|
||||
if (ThreadCache::IsTombstone(tcache)) {
|
||||
// Thread is being terminated, don't try to use the thread cache, and don't
|
||||
// try to resurrect it.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// As noted in comments for `MaybeInitThreadCache()`, TLS variable creation
|
||||
// may allocate.
|
||||
// Unlike `MaybeInitThreadCache()`, this function `Acquire()`s the lock and
|
||||
// reentrancy means deadlock here (should crash on debug builds).
|
||||
// Therefore (de)allocation code path in PartitionAlloc must not use this
|
||||
// function.
|
||||
::partition_alloc::internal::ScopedGuard construction_guard{
|
||||
thread_cache_construction_lock};
|
||||
tcache = ThreadCache::Create(this);
|
||||
thread_caches_being_constructed_.fetch_sub(1, std::memory_order_relaxed);
|
||||
|
||||
return tcache;
|
||||
}
|
||||
@@ -1979,7 +2003,11 @@ PA_NOINLINE void PartitionRoot::QuarantineForBrp(
|
||||
if (hook) [[unlikely]] {
|
||||
hook(object, usable_size);
|
||||
} else {
|
||||
// TODO(https://crbug.com/371135823): Enable zapping again once finished
|
||||
// investigation.
|
||||
#if !PA_BUILDFLAG(IS_IOS)
|
||||
internal::SecureMemset(object, internal::kQuarantinedByte, usable_size);
|
||||
#endif // !PA_BUILDFLAG(IS_IOS)
|
||||
}
|
||||
}
|
||||
#endif // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
|
||||
@@ -2016,6 +2044,60 @@ void PartitionRoot::EnableShadowMetadata(internal::PoolHandleMask mask) {
|
||||
}
|
||||
#endif // PA_CONFIG(ENABLE_SHADOW_METADATA)
|
||||
|
||||
// static
|
||||
void PartitionRoot::CheckMetadataIntegrity(const void* ptr) {
|
||||
uintptr_t address = internal::ObjectInnerPtr2Addr(ptr);
|
||||
if (!IsManagedByPartitionAlloc(address)) {
|
||||
// Not managed by PA; cannot help to determine its integrity.
|
||||
return;
|
||||
} else if (internal::IsManagedByDirectMap(address)) {
|
||||
// OOB for direct-mapped allocations is likely immediate crash.
|
||||
// No extra benefit from additional checks.
|
||||
return;
|
||||
}
|
||||
PA_CHECK(internal::IsManagedByNormalBuckets(address));
|
||||
|
||||
auto* root = FromAddrInFirstSuperpage(address);
|
||||
|
||||
ReadOnlySlotSpanMetadata* slot_span =
|
||||
ReadOnlySlotSpanMetadata::FromAddr(address);
|
||||
PA_CHECK(PartitionRoot::FromSlotSpanMetadata(slot_span) == root);
|
||||
|
||||
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) || \
|
||||
PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
uintptr_t slot_span_start =
|
||||
ReadOnlySlotSpanMetadata::ToSlotSpanStart(slot_span);
|
||||
size_t offset_in_slot_span = address - slot_span_start;
|
||||
|
||||
auto* bucket = slot_span->bucket;
|
||||
uintptr_t untagged_slot_start =
|
||||
slot_span_start +
|
||||
bucket->slot_size * bucket->GetSlotNumber(offset_in_slot_span);
|
||||
#endif // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) ||
|
||||
// PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
|
||||
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
|
||||
if (root->brp_enabled()) {
|
||||
auto* in_slot_metadata = InSlotMetadataPointerFromSlotStartAndSize(
|
||||
untagged_slot_start, slot_span->bucket->slot_size);
|
||||
in_slot_metadata->EnsureAlive(untagged_slot_start, slot_span);
|
||||
}
|
||||
#endif // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
|
||||
|
||||
#if PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
if (root->settings.use_cookie) {
|
||||
// Verify the cookie after the allocated region.
|
||||
// If this assert fires, you probably corrupted memory.
|
||||
const size_t usable_size = root->GetSlotUsableSize(slot_span);
|
||||
|
||||
uintptr_t cookie_address = untagged_slot_start + usable_size;
|
||||
internal::PartitionCookieCheckValue(
|
||||
static_cast<const unsigned char*>(internal::TagAddr(cookie_address)),
|
||||
usable_size);
|
||||
}
|
||||
#endif // PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
}
|
||||
|
||||
// Explicitly define common template instantiations to reduce compile time.
|
||||
#define EXPORT_TEMPLATE \
|
||||
template PA_EXPORT_TEMPLATE_DEFINE(PA_COMPONENT_EXPORT(PARTITION_ALLOC))
|
||||
|
@@ -166,6 +166,7 @@ struct PartitionOptions {
|
||||
static constexpr auto kEnabled = EnableToggle::kEnabled;
|
||||
|
||||
EnableToggle thread_cache = kDisabled;
|
||||
EnableToggle use_cookie_if_supported = kEnabled;
|
||||
EnableToggle backup_ref_ptr = kDisabled;
|
||||
AllowToggle use_configurable_pool = kDisallowed;
|
||||
|
||||
@@ -249,7 +250,7 @@ struct alignas(64) PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionRoot {
|
||||
bool with_thread_cache = false;
|
||||
|
||||
#if PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
static constexpr bool use_cookie = true;
|
||||
bool use_cookie = true;
|
||||
#else
|
||||
static constexpr bool use_cookie = false;
|
||||
#endif // PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
@@ -380,7 +381,11 @@ struct alignas(64) PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionRoot {
|
||||
|
||||
// Integrity check = ~reinterpret_cast<uintptr_t>(this).
|
||||
uintptr_t inverted_self = 0;
|
||||
std::atomic<int> thread_caches_being_constructed_{0};
|
||||
|
||||
// A lock which is hold during thread cache construction.
|
||||
// Any (de)allocation code path should not try to `Acquire()` this lock to
|
||||
// prevent deadlocks. Instead, `TryAcquire()`.
|
||||
internal::Lock thread_cache_construction_lock;
|
||||
|
||||
size_t scheduler_loop_quarantine_branch_capacity_in_bytes = 0;
|
||||
internal::LightweightQuarantineRoot scheduler_loop_quarantine_root;
|
||||
@@ -901,7 +906,7 @@ struct alignas(64) PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionRoot {
|
||||
|
||||
void SetSchedulerLoopQuarantineThreadLocalBranchCapacity(
|
||||
size_t capacity_in_bytes) {
|
||||
ThreadCache* thread_cache = this->GetOrCreateThreadCache();
|
||||
ThreadCache* thread_cache = this->EnsureThreadCache();
|
||||
PA_CHECK(ThreadCache::IsValid(thread_cache));
|
||||
thread_cache->GetSchedulerLoopQuarantineBranch().SetCapacityInBytes(
|
||||
capacity_in_bytes);
|
||||
@@ -932,6 +937,8 @@ struct alignas(64) PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionRoot {
|
||||
}
|
||||
#endif // PA_CONFIG(ENABLE_SHADOW_METADATA)
|
||||
|
||||
PA_NOINLINE static void CheckMetadataIntegrity(const void* object);
|
||||
|
||||
private:
|
||||
static inline StraightenLargerSlotSpanFreeListsMode
|
||||
straighten_larger_slot_span_free_lists_ =
|
||||
@@ -1046,10 +1053,16 @@ struct alignas(64) PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionRoot {
|
||||
PA_ALWAYS_INLINE void RawFreeLocked(uintptr_t slot_start)
|
||||
PA_EXCLUSIVE_LOCKS_REQUIRED(internal::PartitionRootLock(this));
|
||||
ThreadCache* MaybeInitThreadCache();
|
||||
ThreadCache* ForceInitThreadCache();
|
||||
|
||||
// May return an invalid thread cache.
|
||||
PA_ALWAYS_INLINE ThreadCache* GetOrCreateThreadCache();
|
||||
PA_ALWAYS_INLINE ThreadCache* GetThreadCache();
|
||||
// Similar to `GetOrCreateThreadCache()`, but this creates a new thread cache
|
||||
// with `ForceInitThreadCache()`. This can be slow since it acquires a lock,
|
||||
// and hence with a risk of deadlock.
|
||||
// Must NOT be used inside (de)allocation code path.
|
||||
PA_ALWAYS_INLINE ThreadCache* EnsureThreadCache();
|
||||
|
||||
PA_ALWAYS_INLINE internal::LightweightQuarantineBranch&
|
||||
GetSchedulerLoopQuarantineBranch();
|
||||
@@ -1080,6 +1093,9 @@ struct alignas(64) PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionRoot {
|
||||
#endif // PA_CONFIG(USE_PARTITION_ROOT_ENUMERATOR)
|
||||
|
||||
friend class ThreadCache;
|
||||
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
|
||||
friend class internal::InSlotMetadata;
|
||||
#endif // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
@@ -1249,6 +1265,7 @@ PA_ALWAYS_INLINE void PartitionAllocFreeForRefCounting(uintptr_t slot_start) {
|
||||
|
||||
// Iterating over the entire slot can be really expensive.
|
||||
#if PA_BUILDFLAG(EXPENSIVE_DCHECKS_ARE_ON)
|
||||
#if !PA_BUILDFLAG(IS_IOS)
|
||||
auto hook = PartitionAllocHooks::GetQuarantineOverrideHook();
|
||||
// If we have a hook the object segment is not necessarily filled
|
||||
// with |kQuarantinedByte|.
|
||||
@@ -1259,6 +1276,7 @@ PA_ALWAYS_INLINE void PartitionAllocFreeForRefCounting(uintptr_t slot_start) {
|
||||
PA_DCHECK(object[i] == kQuarantinedByte);
|
||||
}
|
||||
}
|
||||
#endif // !PA_BUILDFLAG(IS_IOS)
|
||||
DebugMemset(SlotStartAddr2Ptr(slot_start), kFreedByte,
|
||||
slot_span->GetUtilizedSlotSize());
|
||||
#endif // PA_BUILDFLAG(EXPENSIVE_DCHECKS_ARE_ON)
|
||||
@@ -1588,13 +1606,15 @@ PA_ALWAYS_INLINE void PartitionRoot::FreeNoHooksImmediate(
|
||||
// For more context, see the other "Layout inside the slot" comment inside
|
||||
// AllocInternalNoHooks().
|
||||
|
||||
if (Settings::use_cookie) {
|
||||
#if PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
if (settings.use_cookie) {
|
||||
// Verify the cookie after the allocated region.
|
||||
// If this assert fires, you probably corrupted memory.
|
||||
const size_t usable_size = GetSlotUsableSize(slot_span);
|
||||
internal::PartitionCookieCheckValue(
|
||||
static_cast<unsigned char*>(object) + usable_size, usable_size);
|
||||
}
|
||||
#endif // PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
|
||||
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
|
||||
if (brp_enabled()) [[likely]] {
|
||||
@@ -1612,7 +1632,8 @@ PA_ALWAYS_INLINE void PartitionRoot::FreeNoHooksImmediate(
|
||||
QuarantineForBrp(slot_span, object);
|
||||
}
|
||||
|
||||
if (!(ref_count->ReleaseFromAllocator())) [[unlikely]] {
|
||||
if (!(ref_count->ReleaseFromAllocator(slot_start, slot_span)))
|
||||
[[unlikely]] {
|
||||
PA_CHECK(was_zapped);
|
||||
total_size_of_brp_quarantined_bytes.fetch_add(
|
||||
slot_span->GetSlotSizeForBookkeeping(), std::memory_order_relaxed);
|
||||
@@ -2301,10 +2322,12 @@ PA_ALWAYS_INLINE void* PartitionRoot::AllocInternalNoHooks(
|
||||
void* object = SlotStartToObject(slot_start);
|
||||
|
||||
// Add the cookie after the allocation.
|
||||
if (Settings::use_cookie) {
|
||||
#if PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
if (settings.use_cookie) {
|
||||
internal::PartitionCookieWriteValue(static_cast<unsigned char*>(object) +
|
||||
usable_size);
|
||||
}
|
||||
#endif // PA_BUILDFLAG(USE_PARTITION_COOKIE)
|
||||
|
||||
// Fill the region kUninitializedByte (on debug builds, if not requested to 0)
|
||||
// or 0 (if requested and not 0 already).
|
||||
@@ -2599,6 +2622,17 @@ ThreadCache* PartitionRoot::GetThreadCache() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ThreadCache* PartitionRoot::EnsureThreadCache() {
|
||||
ThreadCache* thread_cache = nullptr;
|
||||
if (settings.with_thread_cache) [[likely]] {
|
||||
thread_cache = ThreadCache::Get();
|
||||
if (!ThreadCache::IsValid(thread_cache)) [[unlikely]] {
|
||||
thread_cache = ForceInitThreadCache();
|
||||
}
|
||||
}
|
||||
return thread_cache;
|
||||
}
|
||||
|
||||
// private.
|
||||
internal::LightweightQuarantineBranch&
|
||||
PartitionRoot::GetSchedulerLoopQuarantineBranch() {
|
||||
|
@@ -102,16 +102,16 @@ struct RawPtrCountingImplForTest : public base::internal::RawPtrNoOpImpl {
|
||||
get_for_duplication_cnt = 0;
|
||||
}
|
||||
|
||||
static inline int wrap_raw_ptr_cnt = INT_MIN;
|
||||
static inline int release_wrapped_ptr_cnt = INT_MIN;
|
||||
static inline int get_for_dereference_cnt = INT_MIN;
|
||||
static inline int get_for_extraction_cnt = INT_MIN;
|
||||
static inline int get_for_comparison_cnt = INT_MIN;
|
||||
static inline int wrapped_ptr_swap_cnt = INT_MIN;
|
||||
static inline int wrapped_ptr_less_cnt = INT_MIN;
|
||||
static inline int pointer_to_member_operator_cnt = INT_MIN;
|
||||
static inline int wrap_raw_ptr_for_dup_cnt = INT_MIN;
|
||||
static inline int get_for_duplication_cnt = INT_MIN;
|
||||
static int wrap_raw_ptr_cnt;
|
||||
static int release_wrapped_ptr_cnt;
|
||||
static int get_for_dereference_cnt;
|
||||
static int get_for_extraction_cnt;
|
||||
static int get_for_comparison_cnt;
|
||||
static int wrapped_ptr_swap_cnt;
|
||||
static int wrapped_ptr_less_cnt;
|
||||
static int pointer_to_member_operator_cnt;
|
||||
static int wrap_raw_ptr_for_dup_cnt;
|
||||
static int get_for_duplication_cnt;
|
||||
};
|
||||
|
||||
} // namespace base::test
|
||||
|
@@ -864,4 +864,39 @@ void ThreadCache::PurgeInternalHelper() {
|
||||
}
|
||||
}
|
||||
|
||||
bool ThreadCache::IsInFreelist(uintptr_t address,
|
||||
size_t bucket_index,
|
||||
size_t& position) {
|
||||
PA_REENTRANCY_GUARD(is_in_thread_cache_);
|
||||
|
||||
auto& bucket = buckets_[bucket_index];
|
||||
if (!bucket.freelist_head) [[unlikely]] {
|
||||
return false;
|
||||
}
|
||||
internal::PartitionFreelistEntry* entry = bucket.freelist_head;
|
||||
|
||||
const internal::PartitionFreelistDispatcher* freelist_dispatcher =
|
||||
get_freelist_dispatcher_from_root();
|
||||
size_t index = 0;
|
||||
size_t length = bucket.count;
|
||||
while (entry != nullptr && index < length) {
|
||||
if (address == internal::SlotStartPtr2Addr(entry)) {
|
||||
position = index;
|
||||
return true;
|
||||
}
|
||||
#if PA_BUILDFLAG(USE_FREELIST_DISPATCHER)
|
||||
internal::PartitionFreelistEntry* next =
|
||||
freelist_dispatcher->GetNextForThreadCacheTrue(entry, bucket.slot_size);
|
||||
#else
|
||||
internal::PartitionFreelistEntry* next =
|
||||
freelist_dispatcher->GetNextForThreadCache<true>(entry,
|
||||
bucket.slot_size);
|
||||
#endif // PA_BUILDFLAG(USE_FREELIST_DISPATCHER)
|
||||
entry = next;
|
||||
++index;
|
||||
}
|
||||
position = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace partition_alloc
|
||||
|
@@ -392,6 +392,10 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) ThreadCache {
|
||||
return *scheduler_loop_quarantine_branch_;
|
||||
}
|
||||
|
||||
// Returns true if the given address is in the thread cache's freelist.
|
||||
// Otherwise, returns false.
|
||||
bool IsInFreelist(uintptr_t address, size_t bucket_index, size_t& position);
|
||||
|
||||
private:
|
||||
friend class tools::HeapDumper;
|
||||
friend class tools::ThreadCacheInspector;
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include "base/android/jni_string.h"
|
||||
#include "base/android/scoped_java_ref.h"
|
||||
#include "base/check.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
|
||||
// Must come after all headers that specialize FromJniType() / ToJniType().
|
||||
@@ -101,7 +102,7 @@ static void JNI_AndroidInfo_FillFields(
|
||||
DCHECK(!holder.has_value());
|
||||
auto java_string_to_const_char =
|
||||
[](const jni_zero::JavaParamRef<jstring>& str) {
|
||||
return strdup(ConvertJavaStringToUTF8(str).c_str());
|
||||
return UNSAFE_TODO(strdup(ConvertJavaStringToUTF8(str).c_str()));
|
||||
};
|
||||
holder = AndroidInfo{
|
||||
.device = java_string_to_const_char(device),
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "base/android/jni_array.h"
|
||||
#include "base/android/jni_string.h"
|
||||
#include "base/android/scoped_java_ref.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
|
||||
// Must come after all headers that specialize FromJniType() / ToJniType().
|
||||
@@ -69,7 +70,7 @@ static void JNI_ApkInfo_FillFields(
|
||||
DCHECK(!holder.has_value());
|
||||
auto java_string_to_const_char =
|
||||
[](const jni_zero::JavaParamRef<jstring>& str) {
|
||||
return strdup(ConvertJavaStringToUTF8(str).c_str());
|
||||
return UNSAFE_TODO(strdup(ConvertJavaStringToUTF8(str).c_str()));
|
||||
};
|
||||
holder = ApkInfo{
|
||||
.host_package_name = java_string_to_const_char(hostPackageName),
|
||||
|
@@ -16,6 +16,7 @@ namespace {
|
||||
// Array of features exposed through the Java BaseFeatureMap API. Entries in
|
||||
// this array may either refer to features defined in //base features.
|
||||
const base::Feature* const kFeaturesExposedToJava[] = {
|
||||
&features::kBackgroundNotPerceptibleBinding,
|
||||
&features::kPostPowerMonitorBroadcastReceiverInitToBackground,
|
||||
&features::kPostGetMyMemoryStateToBackground,
|
||||
};
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include "base/android/jni_array.h"
|
||||
#include "base/android/jni_string.h"
|
||||
#include "base/android/scoped_java_ref.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
|
||||
// Must come after all headers that specialize FromJniType() / ToJniType().
|
||||
@@ -58,7 +59,7 @@ static void JNI_DeviceInfo_FillFields(
|
||||
DCHECK(!holder.has_value());
|
||||
auto java_string_to_const_char =
|
||||
[](const jni_zero::JavaParamRef<jstring>& str) {
|
||||
return strdup(ConvertJavaStringToUTF8(str).c_str());
|
||||
return UNSAFE_TODO(strdup(ConvertJavaStringToUTF8(str).c_str()));
|
||||
};
|
||||
holder =
|
||||
DeviceInfo{.gms_version_code = java_string_to_const_char(gmsVersionCode),
|
||||
@@ -74,7 +75,8 @@ const char* gms_version_code() {
|
||||
}
|
||||
|
||||
void set_gms_version_code_for_test(const std::string& gms_version_code) {
|
||||
get_device_info().gms_version_code = strdup(gms_version_code.c_str());
|
||||
get_device_info().gms_version_code =
|
||||
UNSAFE_TODO(strdup(gms_version_code.c_str()));
|
||||
Java_DeviceInfo_setGmsVersionCodeForTest(AttachCurrentThread(),
|
||||
gms_version_code);
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "base/time/time.h"
|
||||
|
||||
// Must come after all headers that specialize FromJniType() / ToJniType().
|
||||
@@ -306,8 +307,11 @@ InputHintChecker::ScopedOverrideInstance::~ScopedOverrideInstance() {
|
||||
g_test_instance = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
void InputHintChecker::RecordInputHintResult(InputHintResult result) {
|
||||
if (!metric_subsampling_disabled_ &&
|
||||
!base::ShouldRecordSubsampledMetric(0.001)) {
|
||||
return;
|
||||
}
|
||||
UMA_HISTOGRAM_ENUMERATION("Android.InputHintChecker.InputHintResult", result);
|
||||
}
|
||||
|
||||
@@ -319,8 +323,7 @@ void JNI_InputHintChecker_SetView(_JNIEnv* env,
|
||||
void JNI_InputHintChecker_OnCompositorViewHolderTouchEvent(_JNIEnv* env) {
|
||||
auto& checker = InputHintChecker::GetInstance();
|
||||
if (checker.is_after_input_yield()) {
|
||||
InputHintChecker::RecordInputHintResult(
|
||||
InputHintResult::kCompositorViewTouchEvent);
|
||||
checker.RecordInputHintResult(InputHintResult::kCompositorViewTouchEvent);
|
||||
}
|
||||
checker.set_is_after_input_yield(false);
|
||||
}
|
||||
@@ -347,6 +350,7 @@ jboolean JNI_InputHintChecker_HasInputWithThrottlingForTesting(_JNIEnv* env) {
|
||||
void JNI_InputHintChecker_SetIsAfterInputYieldForTesting( // IN-TEST
|
||||
_JNIEnv* env,
|
||||
jboolean after) {
|
||||
InputHintChecker::GetInstance().disable_metric_subsampling();
|
||||
InputHintChecker::GetInstance().set_is_after_input_yield(after);
|
||||
}
|
||||
|
||||
|
@@ -83,8 +83,11 @@ class BASE_EXPORT InputHintChecker {
|
||||
void set_is_after_input_yield(bool after) { is_after_input_yield_ = after; }
|
||||
bool is_after_input_yield() { return is_after_input_yield_; }
|
||||
|
||||
// Used to test UMA metric recording.
|
||||
void disable_metric_subsampling() { metric_subsampling_disabled_ = true; }
|
||||
|
||||
// Records the UMA metric based on the InputHintResult.
|
||||
static void RecordInputHintResult(InputHintResult result);
|
||||
void RecordInputHintResult(InputHintResult result);
|
||||
|
||||
bool IsInitializedForTesting();
|
||||
bool FailedToInitializeForTesting();
|
||||
@@ -105,6 +108,7 @@ class BASE_EXPORT InputHintChecker {
|
||||
bool HasInputImpl(JNIEnv* env, jobject o);
|
||||
|
||||
bool is_after_input_yield_ = false;
|
||||
bool metric_subsampling_disabled_ = false;
|
||||
|
||||
// Last time the input hint was requested. Used for throttling.
|
||||
base::TimeTicks last_checked_;
|
||||
|
@@ -21,7 +21,7 @@
|
||||
#include "base/library_loader_jni/LibraryLoader_jni.h"
|
||||
|
||||
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
|
||||
#include "base/android/orderfile/orderfile_instrumentation.h"
|
||||
#include "base/android/orderfile/orderfile_instrumentation.h" // nogncheck
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
@@ -37,7 +37,7 @@
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
|
||||
#include "base/android/orderfile/orderfile_instrumentation.h"
|
||||
#include "base/android/orderfile/orderfile_instrumentation.h" // nogncheck
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
|
@@ -2,6 +2,7 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//build/config/android/abi.gni")
|
||||
import("//build/config/android/config.gni")
|
||||
|
||||
if (use_order_profiling && (target_cpu == "arm" || target_cpu == "arm64" ||
|
||||
|
@@ -39,9 +39,15 @@ namespace {
|
||||
enum class MetricsFailure {
|
||||
kAlreadyRunning,
|
||||
kSizeMismatch,
|
||||
kMaxValue = kSizeMismatch
|
||||
kMeasureFailure,
|
||||
kMaxValue = kMeasureFailure
|
||||
};
|
||||
|
||||
// These values are logged to UMA. Entries should not be renumbered and
|
||||
// numeric values should never be reused. Please keep in sync with
|
||||
// "PreFreezeReadProcMapsType" in tools/metrics/histograms/enums.xml.
|
||||
enum class ReadProcMaps { kFailed, kEmpty, kSuccess, kMaxValue = kSuccess };
|
||||
|
||||
// This constant is chosen arbitrarily, to allow time for the background tasks
|
||||
// to finish running BEFORE collecting metrics.
|
||||
const base::TimeDelta kDelayForMetrics = base::Seconds(2);
|
||||
@@ -72,6 +78,10 @@ std::string GetPreFreezeMetricName(std::string_view name,
|
||||
return StrCat({"Memory.PreFreeze2.", process_type, ".", name, ".", suffix});
|
||||
}
|
||||
|
||||
std::string GetSelfCompactionMetricName(std::string_view name) {
|
||||
return StrCat({"Memory.SelfCompact2.Renderer.", name});
|
||||
}
|
||||
|
||||
std::string GetSelfCompactionMetricName(std::string_view name,
|
||||
std::string_view suffix) {
|
||||
return StrCat({"Memory.SelfCompact2.Renderer.", name, ".", suffix});
|
||||
@@ -110,47 +120,6 @@ void MaybeRecordPreFreezeMetric(std::optional<uint64_t> value_bytes,
|
||||
static_cast<int>(BytesToMiB(value_bytes.value())));
|
||||
}
|
||||
|
||||
void RecordSelfCompactionMetric(size_t value_bytes,
|
||||
std::string_view metric_name,
|
||||
std::string_view suffix) {
|
||||
UmaHistogramMemoryMB(GetSelfCompactionMetricName(metric_name, suffix),
|
||||
static_cast<int>(BytesToMiB(value_bytes)));
|
||||
}
|
||||
|
||||
void RecordSelfCompactionMetrics(const debug::SmapsRollup& value,
|
||||
std::string_view suffix) {
|
||||
RecordSelfCompactionMetric(value.rss, "Rss", suffix);
|
||||
RecordSelfCompactionMetric(value.pss, "Pss", suffix);
|
||||
RecordSelfCompactionMetric(value.pss_anon, "PssAnon", suffix);
|
||||
RecordSelfCompactionMetric(value.pss_file, "PssFile", suffix);
|
||||
RecordSelfCompactionMetric(value.swap_pss, "SwapPss", suffix);
|
||||
}
|
||||
|
||||
void RecordSelfCompactionDiffMetric(size_t before_value_bytes,
|
||||
size_t after_value_bytes,
|
||||
std::string_view name,
|
||||
std::string_view suffix) {
|
||||
size_t diff_non_negative = std::max(before_value_bytes, after_value_bytes) -
|
||||
std::min(before_value_bytes, after_value_bytes);
|
||||
const std::string full_suffix = StrCat(
|
||||
{"Diff.", suffix, ".",
|
||||
before_value_bytes < after_value_bytes ? "Increase" : "Decrease"});
|
||||
RecordSelfCompactionMetric(diff_non_negative, name, full_suffix);
|
||||
}
|
||||
|
||||
void RecordSelfCompactionDiffMetrics(const debug::SmapsRollup before,
|
||||
const debug::SmapsRollup after,
|
||||
std::string_view suffix) {
|
||||
RecordSelfCompactionDiffMetric(before.rss, after.rss, "Rss", suffix);
|
||||
RecordSelfCompactionDiffMetric(before.pss, after.pss, "Pss", suffix);
|
||||
RecordSelfCompactionDiffMetric(before.pss_anon, after.pss_anon, "PssAnon",
|
||||
suffix);
|
||||
RecordSelfCompactionDiffMetric(before.pss_file, after.pss_file, "PssFile",
|
||||
suffix);
|
||||
RecordSelfCompactionDiffMetric(before.swap_pss, after.swap_pss, "SwapPss",
|
||||
suffix);
|
||||
}
|
||||
|
||||
std::optional<uint64_t> Diff(std::optional<uint64_t> before,
|
||||
std::optional<uint64_t> after) {
|
||||
if (!before.has_value() || !before.has_value()) {
|
||||
@@ -235,6 +204,12 @@ void PreFreezeBackgroundMemoryTrimmer::RecordMetrics() {
|
||||
|
||||
std::optional<uint64_t> value_after = metric->Measure();
|
||||
|
||||
if (!value_after) {
|
||||
UmaHistogramEnumeration("Memory.PreFreeze2.RecordMetricsFailureType",
|
||||
MetricsFailure::kMeasureFailure);
|
||||
continue;
|
||||
}
|
||||
|
||||
MaybeRecordPreFreezeMetric(value_before, metric->name(), "Before");
|
||||
MaybeRecordPreFreezeMetric(value_after, metric->name(), "After");
|
||||
MaybeRecordPreFreezeMetric(Diff(value_before, value_after), metric->name(),
|
||||
@@ -306,30 +281,75 @@ void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ShouldContinueSelfCompaction(self_compaction_triggered_at_)) {
|
||||
if (!ShouldContinueCompaction(compaction_triggered_at_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Record absolute values of each metric.
|
||||
RecordSelfCompactionMetrics(*smaps_before_, "Before");
|
||||
RecordSelfCompactionMetrics(*smaps_after_, "After");
|
||||
RecordSelfCompactionMetrics(*smaps_after_1s_, "After1s");
|
||||
RecordSelfCompactionMetrics(*smaps_after_10s_, "After10s");
|
||||
RecordSelfCompactionMetrics(*smaps_after_60s_, "After60s");
|
||||
RecordCompactionMetrics(*smaps_before_, "Before");
|
||||
RecordCompactionMetrics(*smaps_after_, "After");
|
||||
RecordCompactionMetrics(*smaps_after_1s_, "After1s");
|
||||
RecordCompactionMetrics(*smaps_after_10s_, "After10s");
|
||||
RecordCompactionMetrics(*smaps_after_60s_, "After60s");
|
||||
|
||||
// Record diff of before and after to see how much memory was compacted.
|
||||
RecordSelfCompactionDiffMetrics(*smaps_before_, *smaps_after_, "BeforeAfter");
|
||||
RecordCompactionDiffMetrics(*smaps_before_, *smaps_after_, "BeforeAfter");
|
||||
|
||||
// Record diff after a delay, so we can see if any memory comes back after
|
||||
// compaction.
|
||||
RecordSelfCompactionDiffMetrics(*smaps_after_, *smaps_after_1s_, "After1s");
|
||||
RecordSelfCompactionDiffMetrics(*smaps_after_, *smaps_after_10s_, "After10s");
|
||||
RecordSelfCompactionDiffMetrics(*smaps_after_, *smaps_after_60s_, "After60s");
|
||||
RecordCompactionDiffMetrics(*smaps_after_, *smaps_after_1s_, "After1s");
|
||||
RecordCompactionDiffMetrics(*smaps_after_, *smaps_after_10s_, "After10s");
|
||||
RecordCompactionDiffMetrics(*smaps_after_, *smaps_after_60s_, "After60s");
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::RecordCompactionMetric(
|
||||
size_t value_bytes,
|
||||
std::string_view metric_name,
|
||||
std::string_view suffix) {
|
||||
UmaHistogramMemoryMB(GetMetricName(metric_name, suffix),
|
||||
static_cast<int>(BytesToMiB(value_bytes)));
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::
|
||||
RecordCompactionMetrics(const debug::SmapsRollup& value,
|
||||
std::string_view suffix) {
|
||||
RecordCompactionMetric(value.rss, "Rss", suffix);
|
||||
RecordCompactionMetric(value.pss, "Pss", suffix);
|
||||
RecordCompactionMetric(value.pss_anon, "PssAnon", suffix);
|
||||
RecordCompactionMetric(value.pss_file, "PssFile", suffix);
|
||||
RecordCompactionMetric(value.swap_pss, "SwapPss", suffix);
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::
|
||||
RecordCompactionDiffMetric(size_t before_value_bytes,
|
||||
size_t after_value_bytes,
|
||||
std::string_view name,
|
||||
std::string_view suffix) {
|
||||
size_t diff_non_negative = std::max(before_value_bytes, after_value_bytes) -
|
||||
std::min(before_value_bytes, after_value_bytes);
|
||||
const std::string full_suffix = StrCat(
|
||||
{"Diff.", suffix, ".",
|
||||
before_value_bytes < after_value_bytes ? "Increase" : "Decrease"});
|
||||
RecordCompactionMetric(diff_non_negative, name, full_suffix);
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::
|
||||
RecordCompactionDiffMetrics(const debug::SmapsRollup& before,
|
||||
const debug::SmapsRollup& after,
|
||||
std::string_view suffix) {
|
||||
RecordCompactionDiffMetric(before.rss, after.rss, "Rss", suffix);
|
||||
RecordCompactionDiffMetric(before.pss, after.pss, "Pss", suffix);
|
||||
RecordCompactionDiffMetric(before.pss_anon, after.pss_anon, "PssAnon",
|
||||
suffix);
|
||||
RecordCompactionDiffMetric(before.pss_file, after.pss_file, "PssFile",
|
||||
suffix);
|
||||
RecordCompactionDiffMetric(before.swap_pss, after.swap_pss, "SwapPss",
|
||||
suffix);
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::RecordSmapsRollup(
|
||||
std::optional<debug::SmapsRollup>* target) {
|
||||
if (!ShouldContinueSelfCompaction(self_compaction_triggered_at_)) {
|
||||
if (!ShouldContinueCompaction(compaction_triggered_at_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -345,7 +365,7 @@ void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::
|
||||
FROM_HERE, {base::TaskPriority::BEST_EFFORT, MayBlock()},
|
||||
base::BindOnce(&PreFreezeBackgroundMemoryTrimmer::CompactionMetric::
|
||||
RecordSmapsRollup,
|
||||
// target is a member a of |this|, so it's lifetime is
|
||||
// |target| is a member a of |this|, so it's lifetime is
|
||||
// always ok here.
|
||||
this, base::Unretained(target)),
|
||||
delay);
|
||||
@@ -464,64 +484,59 @@ void PreFreezeBackgroundMemoryTrimmer::SetOnStartSelfCompactionCallback(
|
||||
}
|
||||
|
||||
// static
|
||||
bool PreFreezeBackgroundMemoryTrimmer::SelfCompactionIsSupported() {
|
||||
bool PreFreezeBackgroundMemoryTrimmer::CompactionIsSupported() {
|
||||
return IsMadvisePageoutSupported();
|
||||
}
|
||||
|
||||
// static
|
||||
bool PreFreezeBackgroundMemoryTrimmer::ShouldContinueSelfCompaction(
|
||||
base::TimeTicks self_compaction_triggered_at) {
|
||||
base::AutoLock locker(lock());
|
||||
return Instance().self_compaction_last_cancelled_ <
|
||||
self_compaction_triggered_at;
|
||||
bool PreFreezeBackgroundMemoryTrimmer::ShouldContinueCompaction(
|
||||
const PreFreezeBackgroundMemoryTrimmer::CompactionState& state) {
|
||||
return ShouldContinueCompaction(state.triggered_at_);
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::MaybePostSelfCompactionTask(
|
||||
scoped_refptr<base::SequencedTaskRunner> task_runner,
|
||||
std::vector<debug::MappedMemoryRegion> regions,
|
||||
scoped_refptr<CompactionMetric> metric,
|
||||
uint64_t max_size,
|
||||
base::TimeTicks triggered_at) {
|
||||
TRACE_EVENT0("base", "MaybePostSelfCompactionTask");
|
||||
if (ShouldContinueSelfCompaction(triggered_at) && !regions.empty()) {
|
||||
// static
|
||||
bool PreFreezeBackgroundMemoryTrimmer::ShouldContinueCompaction(
|
||||
base::TimeTicks compaction_triggered_at) {
|
||||
base::AutoLock locker(lock());
|
||||
return Instance().compaction_last_cancelled_ < compaction_triggered_at;
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::MaybePostCompactionTask(
|
||||
std::unique_ptr<CompactionState> state,
|
||||
scoped_refptr<CompactionMetric> metric) {
|
||||
TRACE_EVENT0("base", "MaybePostCompactionTask");
|
||||
if (ShouldContinueCompaction(*state) && !state->regions_.empty()) {
|
||||
auto task_runner = state->task_runner_;
|
||||
task_runner->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
// |base::Unretained| is safe here because we never destroy |this|.
|
||||
base::BindOnce(&PreFreezeBackgroundMemoryTrimmer::SelfCompactionTask,
|
||||
base::Unretained(this), task_runner, std::move(regions),
|
||||
std::move(metric), max_size, triggered_at),
|
||||
GetDelayBetweenSelfCompaction());
|
||||
base::BindOnce(&PreFreezeBackgroundMemoryTrimmer::CompactionTask,
|
||||
base::Unretained(this), std::move(state),
|
||||
std::move(metric)),
|
||||
GetDelayBetweenCompaction());
|
||||
} else {
|
||||
FinishSelfCompaction(std::move(metric), triggered_at);
|
||||
FinishCompaction(std::move(state), std::move(metric));
|
||||
}
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::SelfCompactionTask(
|
||||
scoped_refptr<base::SequencedTaskRunner> task_runner,
|
||||
std::vector<debug::MappedMemoryRegion> regions,
|
||||
scoped_refptr<CompactionMetric> metric,
|
||||
uint64_t max_size,
|
||||
base::TimeTicks triggered_at) {
|
||||
if (!ShouldContinueSelfCompaction(triggered_at)) {
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactionTask(
|
||||
std::unique_ptr<CompactionState> state,
|
||||
scoped_refptr<CompactionMetric> metric) {
|
||||
if (!ShouldContinueCompaction(*state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE_EVENT0("base", "SelfCompactionTask");
|
||||
TRACE_EVENT0("base", "CompactionTask");
|
||||
|
||||
CompactMemory(®ions, max_size);
|
||||
CompactMemory(&state->regions_, state->max_bytes_);
|
||||
|
||||
MaybePostSelfCompactionTask(std::move(task_runner), std::move(regions),
|
||||
std::move(metric), max_size, triggered_at);
|
||||
MaybePostCompactionTask(std::move(state), std::move(metric));
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::StartSelfCompaction(
|
||||
scoped_refptr<base::SequencedTaskRunner> task_runner,
|
||||
std::vector<debug::MappedMemoryRegion> regions,
|
||||
uint64_t max_bytes,
|
||||
base::TimeTicks triggered_at) {
|
||||
scoped_refptr<CompactionMetric> metric =
|
||||
MakeRefCounted<CompactionMetric>(triggered_at, base::TimeTicks::Now());
|
||||
TRACE_EVENT0("base", "StartSelfCompaction");
|
||||
void PreFreezeBackgroundMemoryTrimmer::StartCompaction(
|
||||
std::unique_ptr<CompactionState> state) {
|
||||
scoped_refptr<CompactionMetric> metric = state->MakeCompactionMetric();
|
||||
TRACE_EVENT0("base", "StartCompaction");
|
||||
base::trace_event::EmitNamedTrigger("start-self-compaction");
|
||||
{
|
||||
base::AutoLock locker(lock());
|
||||
@@ -533,89 +548,120 @@ void PreFreezeBackgroundMemoryTrimmer::StartSelfCompaction(
|
||||
}
|
||||
}
|
||||
metric->RecordBeforeMetrics();
|
||||
MaybePostSelfCompactionTask(std::move(task_runner), std::move(regions),
|
||||
std::move(metric), max_bytes, triggered_at);
|
||||
MaybePostCompactionTask(std::move(state), std::move(metric));
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::FinishSelfCompaction(
|
||||
scoped_refptr<CompactionMetric> metric,
|
||||
base::TimeTicks triggered_at) {
|
||||
TRACE_EVENT0("base", "FinishSelfCompaction");
|
||||
void PreFreezeBackgroundMemoryTrimmer::FinishCompaction(
|
||||
std::unique_ptr<CompactionState> state,
|
||||
scoped_refptr<CompactionMetric> metric) {
|
||||
TRACE_EVENT0("base", "FinishCompaction");
|
||||
{
|
||||
base::AutoLock locker(lock());
|
||||
self_compaction_last_finished_ = base::TimeTicks::Now();
|
||||
compaction_last_finished_ = base::TimeTicks::Now();
|
||||
}
|
||||
if (ShouldContinueSelfCompaction(triggered_at)) {
|
||||
if (ShouldContinueCompaction(*state)) {
|
||||
metric->RecordDelayedMetrics();
|
||||
base::AutoLock locker(lock());
|
||||
metric->RecordTimeMetrics(self_compaction_last_finished_,
|
||||
self_compaction_last_cancelled_);
|
||||
metric->RecordTimeMetrics(compaction_last_finished_,
|
||||
compaction_last_cancelled_);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
base::TimeDelta
|
||||
PreFreezeBackgroundMemoryTrimmer::GetDelayBetweenSelfCompaction() {
|
||||
base::TimeDelta PreFreezeBackgroundMemoryTrimmer::GetDelayBetweenCompaction() {
|
||||
// We choose a random, small amount of time here, so that we are not trying
|
||||
// to compact in every process at the same time.
|
||||
return base::Milliseconds(base::RandInt(100, 300));
|
||||
}
|
||||
|
||||
// static
|
||||
void PreFreezeBackgroundMemoryTrimmer::MaybeCancelSelfCompaction(
|
||||
SelfCompactCancellationReason cancellation_reason) {
|
||||
Instance().MaybeCancelSelfCompactionInternal(cancellation_reason);
|
||||
void PreFreezeBackgroundMemoryTrimmer::MaybeCancelCompaction(
|
||||
CompactCancellationReason cancellation_reason) {
|
||||
Instance().MaybeCancelCompactionInternal(cancellation_reason);
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::MaybeCancelSelfCompactionInternal(
|
||||
SelfCompactCancellationReason cancellation_reason) {
|
||||
void PreFreezeBackgroundMemoryTrimmer::MaybeCancelCompactionInternal(
|
||||
CompactCancellationReason cancellation_reason) {
|
||||
base::AutoLock locker(lock());
|
||||
process_compacted_metadata_.reset();
|
||||
// Check for the last time cancelled here in order to avoid recording this
|
||||
// metric multiple times. Also, only record this metric if a compaction is
|
||||
// currently running.
|
||||
if (self_compaction_last_cancelled_ < self_compaction_last_triggered_ &&
|
||||
self_compaction_last_finished_ < self_compaction_last_triggered_) {
|
||||
UmaHistogramEnumeration("Memory.SelfCompact2.Renderer.CancellationReason2",
|
||||
if (compaction_last_cancelled_ < compaction_last_triggered_ &&
|
||||
compaction_last_finished_ < compaction_last_triggered_) {
|
||||
UmaHistogramEnumeration(GetSelfCompactionMetricName("CancellationReason2"),
|
||||
cancellation_reason);
|
||||
}
|
||||
self_compaction_last_finished_ = self_compaction_last_cancelled_ =
|
||||
compaction_last_finished_ = compaction_last_cancelled_ =
|
||||
base::TimeTicks::Now();
|
||||
}
|
||||
|
||||
PreFreezeBackgroundMemoryTrimmer::CompactionState::CompactionState(
|
||||
scoped_refptr<SequencedTaskRunner> task_runner,
|
||||
base::TimeTicks triggered_at,
|
||||
uint64_t max_bytes)
|
||||
: task_runner_(std::move(task_runner)),
|
||||
triggered_at_(triggered_at),
|
||||
max_bytes_(max_bytes) {}
|
||||
|
||||
PreFreezeBackgroundMemoryTrimmer::CompactionState::~CompactionState() = default;
|
||||
|
||||
bool PreFreezeBackgroundMemoryTrimmer::CompactionState::IsFeatureEnabled()
|
||||
const {
|
||||
return base::FeatureList::IsEnabled(kShouldFreezeSelf);
|
||||
}
|
||||
|
||||
std::string PreFreezeBackgroundMemoryTrimmer::CompactionState::GetMetricName(
|
||||
std::string_view name) const {
|
||||
return GetSelfCompactionMetricName(name);
|
||||
}
|
||||
|
||||
scoped_refptr<PreFreezeBackgroundMemoryTrimmer::CompactionMetric>
|
||||
PreFreezeBackgroundMemoryTrimmer::CompactionState::MakeCompactionMetric()
|
||||
const {
|
||||
return MakeRefCounted<CompactionMetric>(triggered_at_,
|
||||
base::TimeTicks::Now());
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactionState::MaybeReadProcMaps() {
|
||||
DCHECK(regions_.empty());
|
||||
auto did_read_proc_maps = ReadProcMaps::kSuccess;
|
||||
if (IsFeatureEnabled()) {
|
||||
std::string proc_maps;
|
||||
if (!debug::ReadProcMaps(&proc_maps) ||
|
||||
!ParseProcMaps(proc_maps, ®ions_)) {
|
||||
did_read_proc_maps = ReadProcMaps::kFailed;
|
||||
} else if (regions_.size() == 0) {
|
||||
did_read_proc_maps = ReadProcMaps::kEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
UmaHistogramEnumeration(GetMetricName("ReadProcMaps"), did_read_proc_maps);
|
||||
}
|
||||
|
||||
// static
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactSelf(
|
||||
scoped_refptr<base::SequencedTaskRunner> task_runner,
|
||||
base::TimeTicks triggered_at) {
|
||||
// MADV_PAGEOUT was only added in Linux 5.4, so do nothing in earlier
|
||||
// versions.
|
||||
if (!SelfCompactionIsSupported()) {
|
||||
if (!CompactionIsSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ShouldContinueSelfCompaction(triggered_at)) {
|
||||
if (!ShouldContinueCompaction(triggered_at)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE_EVENT0("base", "CompactSelf");
|
||||
std::vector<debug::MappedMemoryRegion> regions;
|
||||
|
||||
auto state = std::make_unique<CompactionState>(
|
||||
std::move(task_runner), triggered_at,
|
||||
MiBToBytes(kShouldFreezeSelfMaxSize.Get()));
|
||||
state->MaybeReadProcMaps();
|
||||
|
||||
// We still start the task in the control group, in order to record metrics.
|
||||
if (base::FeatureList::IsEnabled(kShouldFreezeSelf)) {
|
||||
std::string proc_maps;
|
||||
if (!debug::ReadProcMaps(&proc_maps) ||
|
||||
!ParseProcMaps(proc_maps, ®ions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (regions.size() == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Instance().StartSelfCompaction(std::move(task_runner), std::move(regions),
|
||||
MiBToBytes(kShouldFreezeSelfMaxSize.Get()),
|
||||
triggered_at);
|
||||
Instance().StartCompaction(std::move(state));
|
||||
}
|
||||
|
||||
// static
|
||||
@@ -703,7 +749,7 @@ void PreFreezeBackgroundMemoryTrimmer::OnSelfFreezeInternal(
|
||||
scoped_refptr<SequencedTaskRunner> task_runner) {
|
||||
const auto triggered_at = base::TimeTicks::Now();
|
||||
base::AutoLock locker(lock());
|
||||
self_compaction_last_triggered_ = triggered_at;
|
||||
compaction_last_triggered_ = triggered_at;
|
||||
if (base::FeatureList::IsEnabled(kShouldFreezeSelf)) {
|
||||
RunPreFreezeTasks();
|
||||
}
|
||||
@@ -719,7 +765,7 @@ void PreFreezeBackgroundMemoryTrimmer::OnPreFreeze() {
|
||||
// If we have scheduled a self compaction task, cancel it, since App Freezer
|
||||
// will handle the compaction for us, and we don't want to potentially run
|
||||
// self compaction after we have resumed.
|
||||
MaybeCancelSelfCompaction(SelfCompactCancellationReason::kAppFreezer);
|
||||
MaybeCancelCompaction(CompactCancellationReason::kAppFreezer);
|
||||
Instance().OnPreFreezeInternal();
|
||||
}
|
||||
|
||||
@@ -835,11 +881,11 @@ size_t PreFreezeBackgroundMemoryTrimmer::GetNumberOfValuesBeforeForTesting()
|
||||
}
|
||||
|
||||
// static
|
||||
void PreFreezeBackgroundMemoryTrimmer::ResetSelfCompactionForTesting() {
|
||||
void PreFreezeBackgroundMemoryTrimmer::ResetCompactionForTesting() {
|
||||
base::AutoLock locker(lock());
|
||||
Instance().self_compaction_last_cancelled_ = base::TimeTicks::Min();
|
||||
Instance().self_compaction_last_finished_ = base::TimeTicks::Min();
|
||||
Instance().self_compaction_last_triggered_ = base::TimeTicks::Min();
|
||||
Instance().compaction_last_cancelled_ = base::TimeTicks::Min();
|
||||
Instance().compaction_last_finished_ = base::TimeTicks::Min();
|
||||
Instance().compaction_last_triggered_ = base::TimeTicks::Min();
|
||||
}
|
||||
|
||||
// static
|
||||
@@ -921,11 +967,22 @@ PreFreezeBackgroundMemoryTrimmer::PreFreezeMetric::~PreFreezeMetric() = default;
|
||||
PreFreezeBackgroundMemoryTrimmer::CompactionMetric::CompactionMetric(
|
||||
base::TimeTicks triggered_at,
|
||||
base::TimeTicks started_at)
|
||||
: self_compaction_triggered_at_(triggered_at),
|
||||
self_compaction_started_at_(started_at) {}
|
||||
: compaction_triggered_at_(triggered_at),
|
||||
compaction_started_at_(started_at) {}
|
||||
PreFreezeBackgroundMemoryTrimmer::CompactionMetric::~CompactionMetric() =
|
||||
default;
|
||||
|
||||
std::string PreFreezeBackgroundMemoryTrimmer::CompactionMetric::GetMetricName(
|
||||
std::string_view name) const {
|
||||
return GetSelfCompactionMetricName(name);
|
||||
}
|
||||
|
||||
std::string PreFreezeBackgroundMemoryTrimmer::CompactionMetric::GetMetricName(
|
||||
std::string_view name,
|
||||
std::string_view suffix) const {
|
||||
return GetSelfCompactionMetricName(name, suffix);
|
||||
}
|
||||
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::RecordBeforeMetrics() {
|
||||
RecordSmapsRollup(&smaps_before_);
|
||||
}
|
||||
@@ -941,9 +998,9 @@ void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::
|
||||
void PreFreezeBackgroundMemoryTrimmer::CompactionMetric::RecordTimeMetrics(
|
||||
base::TimeTicks last_finished,
|
||||
base::TimeTicks last_cancelled) {
|
||||
UmaHistogramMediumTimes("Memory.SelfCompact2.Renderer.SelfCompactionTime",
|
||||
last_finished - self_compaction_started_at_);
|
||||
UmaHistogramMediumTimes("Memory.SelfCompact2.Renderer.TimeSinceLastCancel",
|
||||
UmaHistogramMediumTimes(GetMetricName("SelfCompactionTime"),
|
||||
last_finished - compaction_started_at_);
|
||||
UmaHistogramMediumTimes(GetMetricName("TimeSinceLastCancel"),
|
||||
last_finished - last_cancelled);
|
||||
}
|
||||
|
||||
|
@@ -37,7 +37,7 @@ class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
|
||||
public:
|
||||
// These values are persisted to logs. Entries should not be renumbered and
|
||||
// numeric values should never be reused.
|
||||
enum class SelfCompactCancellationReason {
|
||||
enum class CompactCancellationReason {
|
||||
kAppFreezer,
|
||||
kPageResumed,
|
||||
kMaxValue = kPageResumed
|
||||
@@ -121,16 +121,12 @@ class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
|
||||
static void SetOnStartSelfCompactionCallback(base::RepeatingClosure callback)
|
||||
LOCKS_EXCLUDED(lock());
|
||||
|
||||
static bool SelfCompactionIsSupported();
|
||||
|
||||
// Compacts the memory for the process.
|
||||
void CompactSelf(scoped_refptr<SequencedTaskRunner> task_runner,
|
||||
base::TimeTicks triggered_at);
|
||||
static bool CompactionIsSupported();
|
||||
|
||||
// If we are currently running self compaction, cancel it. If it was running,
|
||||
// record a metric with the reason for the cancellation.
|
||||
static void MaybeCancelSelfCompaction(
|
||||
SelfCompactCancellationReason cancellation_reason);
|
||||
static void MaybeCancelCompaction(
|
||||
CompactCancellationReason cancellation_reason);
|
||||
|
||||
static void SetSupportsModernTrimForTesting(bool is_supported);
|
||||
static void ClearMetricsForTesting() LOCKS_EXCLUDED(lock());
|
||||
@@ -141,7 +137,7 @@ class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
|
||||
bool DidRegisterTasksForTesting() const;
|
||||
|
||||
static void OnPreFreezeForTesting() LOCKS_EXCLUDED(lock()) { OnPreFreeze(); }
|
||||
static void ResetSelfCompactionForTesting();
|
||||
static void ResetCompactionForTesting();
|
||||
|
||||
static std::optional<uint64_t> CompactRegion(
|
||||
debug::MappedMemoryRegion region);
|
||||
@@ -164,6 +160,7 @@ class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
|
||||
friend class base::OneShotDelayedBackgroundTimer;
|
||||
friend class PreFreezeBackgroundMemoryTrimmerTest;
|
||||
friend class PreFreezeSelfCompactionTest;
|
||||
FRIEND_TEST_ALL_PREFIXES(PreFreezeSelfCompactionTest, Disabled);
|
||||
FRIEND_TEST_ALL_PREFIXES(PreFreezeSelfCompactionTest, Cancel);
|
||||
FRIEND_TEST_ALL_PREFIXES(PreFreezeSelfCompactionTest, NotCanceled);
|
||||
FRIEND_TEST_ALL_PREFIXES(PreFreezeSelfCompactionTest, OnSelfFreezeCancel);
|
||||
@@ -211,8 +208,8 @@ class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
|
||||
CompactionMetric(base::TimeTicks triggered_at, base::TimeTicks started_at);
|
||||
|
||||
void RecordDelayedMetrics();
|
||||
void RecordTimeMetrics(base::TimeTicks self_compaction_last_finished,
|
||||
base::TimeTicks self_compaction_last_cancelled);
|
||||
void RecordTimeMetrics(base::TimeTicks compaction_last_finished,
|
||||
base::TimeTicks compaction_last_cancelled);
|
||||
|
||||
void RecordBeforeMetrics();
|
||||
void MaybeRecordCompactionMetrics() LOCKS_EXCLUDED(lock());
|
||||
@@ -224,14 +221,30 @@ class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
|
||||
LOCKS_EXCLUDED(lock());
|
||||
void RecordSmapsRollupWithDelay(std::optional<debug::SmapsRollup>* target,
|
||||
base::TimeDelta delay);
|
||||
std::string GetMetricName(std::string_view name) const;
|
||||
std::string GetMetricName(std::string_view name,
|
||||
std::string_view suffix) const;
|
||||
void RecordCompactionMetrics(const debug::SmapsRollup& value,
|
||||
std::string_view suffix);
|
||||
void RecordCompactionMetric(size_t value_bytes,
|
||||
std::string_view metric_name,
|
||||
std::string_view suffix);
|
||||
void RecordCompactionDiffMetrics(const debug::SmapsRollup& before,
|
||||
const debug::SmapsRollup& after,
|
||||
std::string_view suffix);
|
||||
void RecordCompactionDiffMetric(size_t before_value_bytes,
|
||||
size_t after_value_bytes,
|
||||
std::string_view name,
|
||||
std::string_view suffix);
|
||||
|
||||
// When the self compaction was first triggered. There is a delay between
|
||||
// this time and when we actually begin the compaction.
|
||||
base::TimeTicks self_compaction_triggered_at_;
|
||||
base::TimeTicks compaction_triggered_at_;
|
||||
// When the self compaction first started. This should generally be
|
||||
// |self_compaction_triggered_at_ +
|
||||
// |compaction_triggered_at_ +
|
||||
// kShouldFreezeSelfDelayAfterPreFreezeTasks.Get()|, but may be longer if
|
||||
// the task was delayed.
|
||||
base::TimeTicks self_compaction_started_at_;
|
||||
base::TimeTicks compaction_started_at_;
|
||||
// We use std::optional here because:
|
||||
// - We record these incrementally.
|
||||
// - We may stop recording at some point.
|
||||
@@ -243,32 +256,50 @@ class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
|
||||
std::optional<debug::SmapsRollup> smaps_after_60s_;
|
||||
};
|
||||
|
||||
class CompactionState final {
|
||||
public:
|
||||
CompactionState(scoped_refptr<SequencedTaskRunner> task_runner,
|
||||
base::TimeTicks triggered_at,
|
||||
uint64_t max_bytes);
|
||||
~CompactionState();
|
||||
|
||||
bool IsFeatureEnabled() const;
|
||||
std::string GetMetricName(std::string_view name) const;
|
||||
void MaybeReadProcMaps();
|
||||
scoped_refptr<CompactionMetric> MakeCompactionMetric() const;
|
||||
|
||||
scoped_refptr<SequencedTaskRunner> task_runner_;
|
||||
std::vector<debug::MappedMemoryRegion> regions_;
|
||||
const base::TimeTicks triggered_at_;
|
||||
const uint64_t max_bytes_;
|
||||
};
|
||||
|
||||
PreFreezeBackgroundMemoryTrimmer();
|
||||
|
||||
static base::Lock& lock() { return Instance().lock_; }
|
||||
|
||||
void StartSelfCompaction(scoped_refptr<base::SequencedTaskRunner> task_runner,
|
||||
std::vector<debug::MappedMemoryRegion> regions,
|
||||
uint64_t max_size,
|
||||
base::TimeTicks triggered_at) LOCKS_EXCLUDED(lock());
|
||||
static base::TimeDelta GetDelayBetweenSelfCompaction();
|
||||
void MaybePostSelfCompactionTask(
|
||||
scoped_refptr<base::SequencedTaskRunner> task_runner,
|
||||
std::vector<debug::MappedMemoryRegion> regions,
|
||||
scoped_refptr<CompactionMetric> metric,
|
||||
uint64_t max_size,
|
||||
base::TimeTicks triggered_at) LOCKS_EXCLUDED(lock());
|
||||
void SelfCompactionTask(scoped_refptr<base::SequencedTaskRunner> task_runner,
|
||||
std::vector<debug::MappedMemoryRegion> regions,
|
||||
scoped_refptr<CompactionMetric> metric,
|
||||
uint64_t max_size,
|
||||
base::TimeTicks triggered_at) LOCKS_EXCLUDED(lock());
|
||||
void FinishSelfCompaction(scoped_refptr<CompactionMetric> metric,
|
||||
base::TimeTicks triggered_at)
|
||||
// Compacts the memory for the process.
|
||||
void CompactSelf(scoped_refptr<SequencedTaskRunner> task_runner,
|
||||
base::TimeTicks triggered_at);
|
||||
|
||||
void StartCompaction(std::unique_ptr<CompactionState> state)
|
||||
LOCKS_EXCLUDED(lock());
|
||||
static base::TimeDelta GetDelayBetweenCompaction();
|
||||
void MaybePostCompactionTask(std::unique_ptr<CompactionState> state,
|
||||
scoped_refptr<CompactionMetric> metric)
|
||||
LOCKS_EXCLUDED(lock());
|
||||
void CompactionTask(std::unique_ptr<CompactionState> state,
|
||||
scoped_refptr<CompactionMetric> metric)
|
||||
LOCKS_EXCLUDED(lock());
|
||||
void FinishCompaction(std::unique_ptr<CompactionState> state,
|
||||
scoped_refptr<CompactionMetric> metric)
|
||||
LOCKS_EXCLUDED(lock());
|
||||
|
||||
static bool ShouldContinueSelfCompaction(
|
||||
base::TimeTicks self_compaction_triggered_at) LOCKS_EXCLUDED(lock());
|
||||
static bool ShouldContinueCompaction(
|
||||
const PreFreezeBackgroundMemoryTrimmer::CompactionState& state)
|
||||
LOCKS_EXCLUDED(lock());
|
||||
static bool ShouldContinueCompaction(base::TimeTicks compaction_triggered_at)
|
||||
LOCKS_EXCLUDED(lock());
|
||||
|
||||
static std::optional<uint64_t> CompactMemory(
|
||||
std::vector<debug::MappedMemoryRegion>* regions,
|
||||
@@ -307,8 +338,8 @@ class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
|
||||
|
||||
void OnSelfFreezeInternal(scoped_refptr<SequencedTaskRunner> task_runner);
|
||||
|
||||
void MaybeCancelSelfCompactionInternal(
|
||||
SelfCompactCancellationReason cancellation_reason) LOCKS_EXCLUDED(lock());
|
||||
void MaybeCancelCompactionInternal(
|
||||
CompactCancellationReason cancellation_reason) LOCKS_EXCLUDED(lock());
|
||||
|
||||
void PostMetricsTasksIfModern() EXCLUSIVE_LOCKS_REQUIRED(lock());
|
||||
void PostMetricsTask() EXCLUSIVE_LOCKS_REQUIRED(lock());
|
||||
@@ -332,14 +363,14 @@ class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer {
|
||||
// work for us. This situation should be relatively rare, because we
|
||||
// attempt to not do self compaction if we know that we are going to
|
||||
// frozen by App Freezer.
|
||||
base::TimeTicks self_compaction_last_cancelled_ GUARDED_BY(lock()) =
|
||||
base::TimeTicks compaction_last_cancelled_ GUARDED_BY(lock()) =
|
||||
base::TimeTicks::Min();
|
||||
// When we last triggered self compaction. Used to record metrics.
|
||||
base::TimeTicks self_compaction_last_triggered_ GUARDED_BY(lock()) =
|
||||
base::TimeTicks compaction_last_triggered_ GUARDED_BY(lock()) =
|
||||
base::TimeTicks::Min();
|
||||
// When we last finished self compaction (either successfully, or from
|
||||
// being cancelled). Used to record metrics.
|
||||
base::TimeTicks self_compaction_last_finished_ GUARDED_BY(lock()) =
|
||||
base::TimeTicks compaction_last_finished_ GUARDED_BY(lock()) =
|
||||
base::TimeTicks::Min();
|
||||
std::optional<base::ScopedSampleMetadata> process_compacted_metadata_
|
||||
GUARDED_BY(lock());
|
||||
|
@@ -40,6 +40,38 @@ ScopedInputEvent& ScopedInputEvent::operator=(ScopedInputEvent&& other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_BASE_TRACING)
|
||||
void ScopedInputEvent::WriteIntoTrace(
|
||||
perfetto::TracedProto<perfetto::protos::pbzero::EventForwarder> forwarder)
|
||||
const {
|
||||
if (!a_input_event_) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int history_size =
|
||||
static_cast<const int>(AMotionEvent_getHistorySize(a_input_event_));
|
||||
forwarder->set_history_size(history_size);
|
||||
|
||||
forwarder->set_latest_time_ns(AMotionEvent_getEventTime(a_input_event_));
|
||||
if (history_size > 0) {
|
||||
forwarder->set_oldest_time_ns(AMotionEvent_getHistoricalEventTime(
|
||||
a_input_event_, /* history_index= */ 0));
|
||||
}
|
||||
forwarder->set_down_time_ns(AMotionEvent_getDownTime(a_input_event_));
|
||||
|
||||
forwarder->set_x_pixel(
|
||||
AMotionEvent_getX(a_input_event_, /* pointer_index= */ 0));
|
||||
forwarder->set_y_pixel(
|
||||
AMotionEvent_getY(a_input_event_, /* pointer_index= */ 0));
|
||||
|
||||
const int action =
|
||||
AMotionEvent_getAction(a_input_event_) & AMOTION_EVENT_ACTION_MASK;
|
||||
forwarder->set_action(
|
||||
static_cast<perfetto::protos::pbzero::EventForwarder::AMotionEventAction>(
|
||||
action));
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_BASE_TRACING)
|
||||
|
||||
void ScopedInputEvent::DestroyIfNeeded() {
|
||||
if (a_input_event_ == nullptr) {
|
||||
return;
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/trace_event/base_tracing.h"
|
||||
|
||||
namespace base::android {
|
||||
|
||||
@@ -31,6 +32,12 @@ class BASE_EXPORT ScopedInputEvent {
|
||||
|
||||
const AInputEvent* a_input_event() const { return a_input_event_.get(); }
|
||||
|
||||
#if BUILDFLAG(ENABLE_BASE_TRACING)
|
||||
void WriteIntoTrace(
|
||||
perfetto::TracedProto<perfetto::protos::pbzero::EventForwarder> forwarder)
|
||||
const;
|
||||
#endif // BUILDFLAG(ENABLE_BASE_TRACING)
|
||||
|
||||
private:
|
||||
void DestroyIfNeeded();
|
||||
|
||||
|
@@ -14,4 +14,9 @@ void PostTaskAndroid::SignalNativeSchedulerReady() {
|
||||
Java_PostTask_onNativeSchedulerReady(jni_zero::AttachCurrentThread());
|
||||
}
|
||||
|
||||
// static
|
||||
void PostTaskAndroid::ResetTaskRunnerForTesting() {
|
||||
Java_PostTask_resetTaskRunner(jni_zero::AttachCurrentThread());
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
@@ -20,6 +20,9 @@ class BASE_EXPORT PostTaskAndroid {
|
||||
// Routes tasks posted via the Java PostTask APIs through the C++ PostTask
|
||||
// APIs. Invoked once the C++ PostTask APIs are fully initialized.
|
||||
static void SignalNativeSchedulerReady();
|
||||
|
||||
// Called to resets all the task runners for after each unit test.
|
||||
static void ResetTaskRunnerForTesting();
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
@@ -349,8 +349,9 @@ static void JNI_TraceEvent_WebViewStartupStartChromiumLocked(
|
||||
JNIEnv* env,
|
||||
jlong start_time_ms,
|
||||
jlong duration_ms,
|
||||
jint call_site,
|
||||
jboolean from_ui_thread) {
|
||||
jint start_call_site,
|
||||
jint finish_call_site,
|
||||
jint startup_mode) {
|
||||
#if BUILDFLAG(ENABLE_BASE_TRACING)
|
||||
auto t = perfetto::Track::ThreadScoped(
|
||||
reinterpret_cast<void*>(trace_event::GetNextGlobalTraceId()));
|
||||
@@ -362,10 +363,15 @@ static void JNI_TraceEvent_WebViewStartupStartChromiumLocked(
|
||||
auto* webview_startup =
|
||||
ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>()
|
||||
->set_webview_startup();
|
||||
webview_startup->set_from_ui_thread((bool)from_ui_thread);
|
||||
webview_startup->set_call_site(
|
||||
webview_startup->set_start_call_site(
|
||||
(perfetto::protos::pbzero::perfetto_pbzero_enum_WebViewStartup::
|
||||
CallSite)call_site);
|
||||
CallSite)start_call_site);
|
||||
webview_startup->set_finish_call_site(
|
||||
(perfetto::protos::pbzero::perfetto_pbzero_enum_WebViewStartup::
|
||||
CallSite)finish_call_site);
|
||||
webview_startup->set_startup_mode(
|
||||
(perfetto::protos::pbzero::perfetto_pbzero_enum_WebViewStartup::
|
||||
StartupMode)startup_mode);
|
||||
});
|
||||
TRACE_EVENT_END("android_webview.timeline", t,
|
||||
TimeTicks() + Milliseconds(start_time_ms + duration_ms));
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include "base/apple/osstatus_logging.h"
|
||||
#include "base/apple/scoped_cftyperef.h"
|
||||
#include "base/check.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/containers/adapters.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
@@ -310,7 +311,8 @@ const char* BaseBundleID() {
|
||||
void SetBaseBundleID(const char* new_base_bundle_id) {
|
||||
if (new_base_bundle_id != base_bundle_id) {
|
||||
free((void*)base_bundle_id);
|
||||
base_bundle_id = new_base_bundle_id ? strdup(new_base_bundle_id) : nullptr;
|
||||
base_bundle_id =
|
||||
new_base_bundle_id ? UNSAFE_TODO(strdup(new_base_bundle_id)) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "build/blink_buildflags.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
// This file defines wrappers to allow C++ code to hold references to
|
||||
|
@@ -18,6 +18,9 @@ GENERATE_STRONG_OBJC_TYPE(NSCursor)
|
||||
GENERATE_STRONG_OBJC_TYPE(NSEvent)
|
||||
#elif BUILDFLAG(IS_IOS)
|
||||
GENERATE_STRONG_OBJC_TYPE(UIEvent)
|
||||
#if BUILDFLAG(USE_BLINK)
|
||||
GENERATE_STRONG_OBJC_TYPE(BEKeyEntry)
|
||||
#endif // BUILDFLAG(USE_BLINK)
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/compiler_specific.h"
|
||||
|
||||
namespace base::apple {
|
||||
|
||||
@@ -59,8 +60,8 @@ void ScopedObjCClassSwizzler::Init(Class target,
|
||||
const char* new_types = method_getTypeEncoding(new_selector_impl_);
|
||||
DCHECK(old_types);
|
||||
DCHECK(new_types);
|
||||
DCHECK_EQ(0, strcmp(old_types, new_types));
|
||||
if (!old_types || !new_types || strcmp(old_types, new_types)) {
|
||||
DCHECK_EQ(0, UNSAFE_TODO(strcmp(old_types, new_types)));
|
||||
if (!old_types || !new_types || UNSAFE_TODO(strcmp(old_types, new_types))) {
|
||||
old_selector_impl_ = new_selector_impl_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
@@ -25,13 +25,13 @@ bool EnvOverridePathProvider(int key, FilePath* result) {
|
||||
// Allow passing this in the environment, for more flexibility in build
|
||||
// tree configurations (sub-project builds, gyp --output_dir, etc.)
|
||||
std::unique_ptr<Environment> env(Environment::Create());
|
||||
std::string cr_source_root;
|
||||
FilePath path;
|
||||
if (env->GetVar("CR_SOURCE_ROOT", &cr_source_root)) {
|
||||
std::optional<std::string> cr_source_root = env->GetVar("CR_SOURCE_ROOT");
|
||||
if (cr_source_root.has_value()) {
|
||||
FilePath path;
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
path = FilePath(UTF8ToWide(cr_source_root));
|
||||
path = FilePath(UTF8ToWide(cr_source_root.value()));
|
||||
#else
|
||||
path = FilePath(cr_source_root);
|
||||
path = FilePath(cr_source_root.value());
|
||||
#endif
|
||||
if (!path.IsAbsolute()) {
|
||||
FilePath root;
|
||||
|
@@ -80,15 +80,16 @@ bool PathProviderWin(int key, FilePath* result) {
|
||||
if (base::win::OSInfo::GetInstance()->IsWowX86OnAMD64() ||
|
||||
base::win::OSInfo::GetInstance()->IsWowX86OnARM64()) {
|
||||
std::unique_ptr<base::Environment> env(base::Environment::Create());
|
||||
std::string programfiles_w6432;
|
||||
// 32-bit process running in WOW64 sets ProgramW6432 environment
|
||||
// variable. See
|
||||
// https://msdn.microsoft.com/library/windows/desktop/aa384274.aspx.
|
||||
if (!env->GetVar("ProgramW6432", &programfiles_w6432)) {
|
||||
std::optional<std::string> programfiles_w6432 =
|
||||
env->GetVar("ProgramW6432");
|
||||
if (!programfiles_w6432.has_value()) {
|
||||
return false;
|
||||
}
|
||||
// GetVar returns UTF8 - convert back to Wide.
|
||||
cur = FilePath(UTF8ToWide(programfiles_w6432));
|
||||
cur = FilePath(UTF8ToWide(programfiles_w6432.value()));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
@@ -243,38 +243,42 @@ class DCheckErrnoLogMessage : public ErrnoLogMessage {
|
||||
|
||||
} // namespace
|
||||
|
||||
CheckError::CheckError(LogMessage* log_message) : log_message_(log_message) {}
|
||||
|
||||
CheckError CheckError::Check(const char* condition,
|
||||
base::NotFatalUntil fatal_milestone,
|
||||
const base::Location& location) {
|
||||
auto* const log_message = new CheckLogMessage(
|
||||
location, GetCheckSeverity(fatal_milestone), fatal_milestone);
|
||||
// TODO(pbos): Make this output CHECK instead of Check.
|
||||
log_message->stream() << "Check failed: " << condition << ". ";
|
||||
return CheckError(log_message);
|
||||
}
|
||||
|
||||
CheckError CheckError::CheckOp(char* log_message_str,
|
||||
base::NotFatalUntil fatal_milestone,
|
||||
const base::Location& location) {
|
||||
LogMessage* CheckError::CheckOp(char* log_message_str,
|
||||
base::NotFatalUntil fatal_milestone,
|
||||
const base::Location& location) {
|
||||
auto* const log_message = new CheckLogMessage(
|
||||
location, GetCheckSeverity(fatal_milestone), fatal_milestone);
|
||||
log_message->stream() << log_message_str;
|
||||
// TODO(pbos): Make this output CHECK instead of Check.
|
||||
log_message->stream() << "Check failed: " << log_message_str;
|
||||
free(log_message_str);
|
||||
return CheckError(log_message);
|
||||
return log_message;
|
||||
}
|
||||
|
||||
CheckError CheckError::DCheck(const char* condition,
|
||||
const base::Location& location) {
|
||||
auto* const log_message = new DCheckLogMessage(location);
|
||||
log_message->stream() << "Check failed: " << condition << ". ";
|
||||
log_message->stream() << "DCHECK failed: " << condition << ". ";
|
||||
return CheckError(log_message);
|
||||
}
|
||||
|
||||
CheckError CheckError::DCheckOp(char* log_message_str,
|
||||
const base::Location& location) {
|
||||
LogMessage* CheckError::DCheckOp(char* log_message_str,
|
||||
const base::Location& location) {
|
||||
auto* const log_message = new DCheckLogMessage(location);
|
||||
log_message->stream() << log_message_str;
|
||||
log_message->stream() << "DCHECK failed: " << log_message_str;
|
||||
free(log_message_str);
|
||||
return CheckError(log_message);
|
||||
return log_message;
|
||||
}
|
||||
|
||||
CheckError CheckError::DumpWillBeCheck(const char* condition,
|
||||
@@ -282,18 +286,20 @@ CheckError CheckError::DumpWillBeCheck(const char* condition,
|
||||
auto* const log_message =
|
||||
new CheckLogMessage(location, GetDumpSeverity(),
|
||||
base::NotFatalUntil::NoSpecifiedMilestoneInternal);
|
||||
// TODO(pbos): Make this output CHECK instead of Check.
|
||||
log_message->stream() << "Check failed: " << condition << ". ";
|
||||
return CheckError(log_message);
|
||||
}
|
||||
|
||||
CheckError CheckError::DumpWillBeCheckOp(char* log_message_str,
|
||||
const base::Location& location) {
|
||||
LogMessage* CheckError::DumpWillBeCheckOp(char* log_message_str,
|
||||
const base::Location& location) {
|
||||
auto* const log_message =
|
||||
new CheckLogMessage(location, GetDumpSeverity(),
|
||||
base::NotFatalUntil::NoSpecifiedMilestoneInternal);
|
||||
log_message->stream() << log_message_str;
|
||||
// TODO(pbos): Make this output CHECK instead of Check.
|
||||
log_message->stream() << "Check failed: " << log_message_str;
|
||||
free(log_message_str);
|
||||
return CheckError(log_message);
|
||||
return log_message;
|
||||
}
|
||||
|
||||
CheckError CheckError::DPCheck(const char* condition,
|
||||
@@ -304,7 +310,7 @@ CheckError CheckError::DPCheck(const char* condition,
|
||||
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
|
||||
auto* const log_message = new DCheckErrnoLogMessage(location, err_code);
|
||||
#endif
|
||||
log_message->stream() << "Check failed: " << condition << ". ";
|
||||
log_message->stream() << "DCHECK failed: " << condition << ". ";
|
||||
return CheckError(log_message);
|
||||
}
|
||||
|
||||
@@ -312,6 +318,7 @@ CheckError CheckError::NotImplemented(const char* function,
|
||||
const base::Location& location) {
|
||||
auto* const log_message = new LogMessage(
|
||||
location.file_name(), location.line_number(), LOGGING_ERROR);
|
||||
// TODO(pbos): Make this output NOTIMPLEMENTED instead of Not implemented.
|
||||
log_message->stream() << "Not implemented reached in " << function;
|
||||
return CheckError(log_message);
|
||||
}
|
||||
@@ -340,8 +347,6 @@ CheckError::~CheckError() {
|
||||
}
|
||||
}
|
||||
|
||||
CheckError::CheckError(LogMessage* log_message) : log_message_(log_message) {}
|
||||
|
||||
// Note: This function ends up in crash stack traces. If its full name changes,
|
||||
// the crash server's magic signature logic needs to be updated. See
|
||||
// cl/306632920.
|
||||
@@ -360,18 +365,20 @@ CheckNoreturnError CheckNoreturnError::Check(const char* condition,
|
||||
auto* const log_message =
|
||||
new CheckLogMessage(location, LOGGING_FATAL,
|
||||
base::NotFatalUntil::NoSpecifiedMilestoneInternal);
|
||||
// TODO(pbos): Make this output CHECK instead of Check.
|
||||
log_message->stream() << "Check failed: " << condition << ". ";
|
||||
return CheckNoreturnError(log_message);
|
||||
}
|
||||
|
||||
CheckNoreturnError CheckNoreturnError::CheckOp(char* log_message_str,
|
||||
const base::Location& location) {
|
||||
LogMessage* CheckNoreturnError::CheckOp(char* log_message_str,
|
||||
const base::Location& location) {
|
||||
auto* const log_message =
|
||||
new CheckLogMessage(location, LOGGING_FATAL,
|
||||
base::NotFatalUntil::NoSpecifiedMilestoneInternal);
|
||||
log_message->stream() << log_message_str;
|
||||
// TODO(pbos): Make this output CHECK instead of Check.
|
||||
log_message->stream() << "Check failed: " << log_message_str;
|
||||
free(log_message_str);
|
||||
return CheckNoreturnError(log_message);
|
||||
return log_message;
|
||||
}
|
||||
|
||||
CheckNoreturnError CheckNoreturnError::PCheck(const char* condition,
|
||||
@@ -384,6 +391,7 @@ CheckNoreturnError CheckNoreturnError::PCheck(const char* condition,
|
||||
auto* const log_message = new ErrnoLogMessage(
|
||||
location.file_name(), location.line_number(), LOGGING_FATAL, err_code);
|
||||
#endif
|
||||
// TODO(pbos): Make this output CHECK instead of Check.
|
||||
log_message->stream() << "Check failed: " << condition << ". ";
|
||||
return CheckNoreturnError(log_message);
|
||||
}
|
||||
@@ -397,8 +405,7 @@ NotReachedError NotReachedError::NotReached(base::NotFatalUntil fatal_milestone,
|
||||
auto* const log_message = new NotReachedLogMessage(
|
||||
location, GetCheckSeverity(fatal_milestone), fatal_milestone);
|
||||
|
||||
// TODO(pbos): Consider a better message for NotReached(), this is here to
|
||||
// match existing behavior + test expectations.
|
||||
// TODO(pbos): Make this output "NOTREACHED hit." like the other NOTREACHEDs.
|
||||
log_message->stream() << "Check failed: false. ";
|
||||
return NotReachedError(log_message);
|
||||
}
|
||||
|
@@ -75,6 +75,9 @@ class LogMessage;
|
||||
// Class used for raising a check error upon destruction.
|
||||
class BASE_EXPORT CheckError {
|
||||
public:
|
||||
// Takes ownership of `log_message`.
|
||||
explicit CheckError(LogMessage* log_message);
|
||||
|
||||
// All instances that take a base::Location should use
|
||||
// base::Location::CurrentWithoutFunctionName() by default since we
|
||||
// immediately pass file_name() and line_number() to LogMessage's constructor
|
||||
@@ -84,25 +87,27 @@ class BASE_EXPORT CheckError {
|
||||
// developer builds and official+DCHECK where all CHECK failures generate
|
||||
// logs.
|
||||
|
||||
// TODO(pbos): Make all static methods that currently return some version of
|
||||
// CheckError return LogMessage*.
|
||||
static CheckError Check(const char* condition,
|
||||
base::NotFatalUntil fatal_milestone,
|
||||
const base::Location& location =
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
// Takes ownership over (free()s after using) `log_message_str`, for use with
|
||||
// CHECK_op macros.
|
||||
static CheckError CheckOp(char* log_message_str,
|
||||
base::NotFatalUntil fatal_milestone,
|
||||
const base::Location& location =
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
static LogMessage* CheckOp(char* log_message_str,
|
||||
base::NotFatalUntil fatal_milestone,
|
||||
const base::Location& location =
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
|
||||
static CheckError DCheck(const char* condition,
|
||||
const base::Location& location =
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
// Takes ownership over (free()s after using) `log_message_str`, for use with
|
||||
// DCHECK_op macros.
|
||||
static CheckError DCheckOp(char* log_message_str,
|
||||
const base::Location& location =
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
static LogMessage* DCheckOp(char* log_message_str,
|
||||
const base::Location& location =
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
|
||||
static CheckError DumpWillBeCheck(
|
||||
const char* condition,
|
||||
@@ -110,7 +115,7 @@ class BASE_EXPORT CheckError {
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
// Takes ownership over (free()s after using) `log_message_str`, for use with
|
||||
// DUMP_WILL_BE_CHECK_op macros.
|
||||
static CheckError DumpWillBeCheckOp(
|
||||
static LogMessage* DumpWillBeCheckOp(
|
||||
char* log_message_str,
|
||||
const base::Location& location =
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
@@ -135,14 +140,12 @@ class BASE_EXPORT CheckError {
|
||||
CheckError& operator=(const CheckError&) = delete;
|
||||
|
||||
template <typename T>
|
||||
std::ostream& operator<<(T&& streamed_type) {
|
||||
return stream() << streamed_type;
|
||||
CheckError& operator<<(T&& streamed_type) {
|
||||
stream() << streamed_type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Takes ownership of `log_message`.
|
||||
explicit CheckError(LogMessage* log_message);
|
||||
|
||||
std::unique_ptr<LogMessage> log_message_;
|
||||
};
|
||||
|
||||
@@ -157,10 +160,9 @@ class BASE_EXPORT CheckNoreturnError : public CheckError {
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
// Takes ownership over (free()s after using) `log_message_str`, for use with
|
||||
// CHECK_op macros.
|
||||
static CheckNoreturnError CheckOp(
|
||||
char* log_message_str,
|
||||
const base::Location& location =
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
static LogMessage* CheckOp(char* log_message_str,
|
||||
const base::Location& location =
|
||||
base::Location::CurrentWithoutFunctionName());
|
||||
|
||||
static CheckNoreturnError PCheck(
|
||||
const char* condition,
|
||||
|
@@ -4,8 +4,7 @@
|
||||
|
||||
#include "base/check_is_test.h"
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/base_export.h"
|
||||
|
||||
namespace {
|
||||
bool g_this_is_a_test = false;
|
||||
@@ -22,16 +21,6 @@ namespace base::test {
|
||||
// `AllowCheckIsTestForTesting`, but is only allowed to be included in test
|
||||
// code. We therefore have to also mark the symbol as exported here.
|
||||
BASE_EXPORT void AllowCheckIsTestForTesting() {
|
||||
// This CHECK ensures that `AllowCheckIsTestForTesting` is called
|
||||
// just once. Since it is called in `base::TestSuite`, this should effectively
|
||||
// prevent calls to `AllowCheckIsTestForTesting` in production code
|
||||
// (assuming that code has unit test coverage).
|
||||
//
|
||||
// This is just in case someone ignores the fact that this function in the
|
||||
// `base::test` namespace and ends on "ForTesting".
|
||||
CHECK(!g_this_is_a_test)
|
||||
<< "AllowCheckIsTestForTesting must not be called more than once";
|
||||
|
||||
g_this_is_a_test = true;
|
||||
}
|
||||
} // namespace base::test
|
||||
|
@@ -106,8 +106,7 @@ char* CreateCheckOpLogMessageString(const char* expr_str,
|
||||
char* v1_str,
|
||||
char* v2_str) {
|
||||
std::stringstream ss;
|
||||
ss << "Check failed: " << expr_str << " (" << v1_str << " vs. " << v2_str
|
||||
<< ")";
|
||||
ss << expr_str << " (" << v1_str << " vs. " << v2_str << ")";
|
||||
free(v1_str);
|
||||
free(v2_str);
|
||||
return strdup(ss.str().c_str());
|
||||
|
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/check.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/dcheck_is_on.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/types/is_arc_pointer.h"
|
||||
@@ -155,7 +156,6 @@ inline char* CheckOpValueStr(const T& v) {
|
||||
// use with CheckOpValueStr() which allocates these strings using strdup().
|
||||
// Returns allocated string (with strdup) for passing into
|
||||
// ::logging::CheckError::(D)CheckOp methods.
|
||||
// TODO(pbos): Annotate this RETURNS_NONNULL after solving compile failure.
|
||||
BASE_EXPORT char* CreateCheckOpLogMessageString(const char* expr_str,
|
||||
char* v1_str,
|
||||
char* v2_str);
|
||||
@@ -165,17 +165,24 @@ BASE_EXPORT char* CreateCheckOpLogMessageString(const char* expr_str,
|
||||
// macro is used in an 'if' clause such as:
|
||||
// if (a == 1)
|
||||
// CHECK_EQ(2, a);
|
||||
#define CHECK_OP_FUNCTION_IMPL(check_failure_function, name, op, val1, val2, \
|
||||
...) \
|
||||
switch (0) \
|
||||
case 0: \
|
||||
default: \
|
||||
if (char* const message_on_fail = ::logging::Check##name##Impl( \
|
||||
(val1), (val2), #val1 " " #op " " #val2); \
|
||||
!message_on_fail) \
|
||||
; \
|
||||
else \
|
||||
check_failure_function(message_on_fail __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define CHECK_OP_FUNCTION_IMPL(check_failure_type, check_log_message_function, \
|
||||
name, op, val1, val2, ...) \
|
||||
switch (0) \
|
||||
case 0: \
|
||||
default: \
|
||||
if (::logging::LogMessage *const message_on_fail = \
|
||||
::logging::Check##name##Impl( \
|
||||
(val1), (val2), \
|
||||
[](char* str1, char* str2) { \
|
||||
return check_log_message_function( \
|
||||
::logging::CreateCheckOpLogMessageString( \
|
||||
#val1 " " #op " " #val2, str1, str2) __VA_OPT__(, ) \
|
||||
__VA_ARGS__); \
|
||||
}); \
|
||||
!message_on_fail) \
|
||||
; \
|
||||
else \
|
||||
(check_failure_type)(message_on_fail)
|
||||
|
||||
#if !CHECK_WILL_STREAM()
|
||||
|
||||
@@ -185,7 +192,8 @@ BASE_EXPORT char* CreateCheckOpLogMessageString(const char* expr_str,
|
||||
#else
|
||||
|
||||
#define CHECK_OP_INTERNAL_IMPL(name, op, val1, val2) \
|
||||
CHECK_OP_FUNCTION_IMPL(::logging::CheckNoreturnError::CheckOp, name, op, \
|
||||
CHECK_OP_FUNCTION_IMPL(::logging::CheckNoreturnError, \
|
||||
::logging::CheckNoreturnError::CheckOp, name, op, \
|
||||
val1, val2)
|
||||
|
||||
#endif
|
||||
@@ -193,28 +201,28 @@ BASE_EXPORT char* CreateCheckOpLogMessageString(const char* expr_str,
|
||||
#define CHECK_OP(name, op, val1, val2, ...) \
|
||||
BASE_IF(BASE_IS_EMPTY(__VA_ARGS__), \
|
||||
CHECK_OP_INTERNAL_IMPL(name, op, val1, val2), \
|
||||
CHECK_OP_FUNCTION_IMPL(::logging::CheckError::CheckOp, name, op, \
|
||||
CHECK_OP_FUNCTION_IMPL(::logging::CheckError, \
|
||||
::logging::CheckError::CheckOp, name, op, \
|
||||
val1, val2, __VA_ARGS__))
|
||||
|
||||
// The second overload avoids address-taking of static members for
|
||||
// fundamental types.
|
||||
#define DEFINE_CHECK_OP_IMPL(name, op) \
|
||||
template <typename T, typename U> \
|
||||
requires(!std::is_fundamental_v<T> || !std::is_fundamental_v<U>) \
|
||||
constexpr char* Check##name##Impl(const T& v1, const U& v2, \
|
||||
const char* expr_str) { \
|
||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) [[likely]] \
|
||||
return nullptr; \
|
||||
return CreateCheckOpLogMessageString(expr_str, CheckOpValueStr(v1), \
|
||||
CheckOpValueStr(v2)); \
|
||||
} \
|
||||
template <typename T, typename U> \
|
||||
requires(std::is_fundamental_v<T> && std::is_fundamental_v<U>) \
|
||||
constexpr char* Check##name##Impl(T v1, U v2, const char* expr_str) { \
|
||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) [[likely]] \
|
||||
return nullptr; \
|
||||
return CreateCheckOpLogMessageString(expr_str, CheckOpValueStr(v1), \
|
||||
CheckOpValueStr(v2)); \
|
||||
#define DEFINE_CHECK_OP_IMPL(name, op) \
|
||||
template <typename T, typename U, typename F> \
|
||||
requires(!std::is_fundamental_v<T> || !std::is_fundamental_v<U>) \
|
||||
constexpr ::logging::LogMessage* Check##name##Impl(const T& v1, const U& v2, \
|
||||
F on_failure) { \
|
||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) [[likely]] \
|
||||
return nullptr; \
|
||||
return on_failure(CheckOpValueStr(v1), CheckOpValueStr(v2)); \
|
||||
} \
|
||||
template <typename T, typename U, typename F> \
|
||||
requires(std::is_fundamental_v<T> && std::is_fundamental_v<U>) \
|
||||
constexpr ::logging::LogMessage* Check##name##Impl(T v1, U v2, \
|
||||
F on_failure) { \
|
||||
if (ANALYZER_ASSUME_TRUE(v1 op v2)) [[likely]] \
|
||||
return nullptr; \
|
||||
return on_failure(CheckOpValueStr(v1), CheckOpValueStr(v2)); \
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
@@ -241,8 +249,10 @@ DEFINE_CHECK_OP_IMPL(GT, > )
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
|
||||
#define DCHECK_OP(name, op, val1, val2) \
|
||||
CHECK_OP_FUNCTION_IMPL(::logging::CheckError::DCheckOp, name, op, val1, val2)
|
||||
#define DCHECK_OP(name, op, val1, val2) \
|
||||
CHECK_OP_FUNCTION_IMPL(::logging::CheckError, \
|
||||
::logging::CheckError::DCheckOp, name, op, val1, \
|
||||
val2)
|
||||
|
||||
#else
|
||||
|
||||
@@ -263,7 +273,8 @@ DEFINE_CHECK_OP_IMPL(GT, > )
|
||||
// clang-format on
|
||||
|
||||
#define DUMP_WILL_BE_CHECK_OP(name, op, val1, val2) \
|
||||
CHECK_OP_FUNCTION_IMPL(::logging::CheckError::DumpWillBeCheckOp, name, op, \
|
||||
CHECK_OP_FUNCTION_IMPL(::logging::CheckError, \
|
||||
::logging::CheckError::DumpWillBeCheckOp, name, op, \
|
||||
val1, val2)
|
||||
|
||||
#define DUMP_WILL_BE_CHECK_EQ(val1, val2) \
|
||||
|
@@ -1,6 +1,11 @@
|
||||
// Copyright 2018 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// This file intentionally uses the `CHECK()` macro instead of the `CHECK_op()`
|
||||
// macros, as `CHECK()` generates significantly less code and is more likely to
|
||||
// optimize reasonably, even in non-official release builds. Please do not
|
||||
// change the `CHECK()` calls back to `CHECK_op()` calls.
|
||||
|
||||
#ifndef BASE_CONTAINERS_CHECKED_ITERATORS_H_
|
||||
#define BASE_CONTAINERS_CHECKED_ITERATORS_H_
|
||||
@@ -12,7 +17,6 @@
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/containers/util.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
@@ -49,7 +53,7 @@ class CheckedContiguousIterator {
|
||||
UNSAFE_BUFFER_USAGE constexpr CheckedContiguousIterator(T* start,
|
||||
const T* end)
|
||||
: start_(start), current_(start), end_(end) {
|
||||
CHECK_LE(start, end);
|
||||
CHECK(start <= end);
|
||||
}
|
||||
|
||||
// Constructs an iterator from `start` to `end`, starting at `current`.
|
||||
@@ -64,8 +68,8 @@ class CheckedContiguousIterator {
|
||||
T* current,
|
||||
const T* end)
|
||||
: start_(start), current_(current), end_(end) {
|
||||
CHECK_LE(start, current);
|
||||
CHECK_LE(current, end);
|
||||
CHECK(start <= current);
|
||||
CHECK(current <= end);
|
||||
}
|
||||
|
||||
constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) =
|
||||
@@ -83,8 +87,8 @@ class CheckedContiguousIterator {
|
||||
// We explicitly don't delegate to the 3-argument constructor here. Its
|
||||
// CHECKs would be redundant, since we expect |other| to maintain its own
|
||||
// invariant. However, DCHECKs never hurt anybody. Presumably.
|
||||
DCHECK_LE(other.start_, other.current_);
|
||||
DCHECK_LE(other.current_, other.end_);
|
||||
DCHECK(other.start_ <= other.current_);
|
||||
DCHECK(other.current_ <= other.end_);
|
||||
}
|
||||
|
||||
~CheckedContiguousIterator() = default;
|
||||
@@ -105,7 +109,7 @@ class CheckedContiguousIterator {
|
||||
}
|
||||
|
||||
constexpr CheckedContiguousIterator& operator++() {
|
||||
CHECK_NE(current_, end_);
|
||||
CHECK(current_ != end_);
|
||||
// SAFETY: `current_ <= end_` is an invariant maintained internally, and the
|
||||
// CHECK above ensures that we are not at the end yet, so incrementing stays
|
||||
// in bounds of the allocation.
|
||||
@@ -120,7 +124,7 @@ class CheckedContiguousIterator {
|
||||
}
|
||||
|
||||
constexpr CheckedContiguousIterator& operator--() {
|
||||
CHECK_NE(current_, start_);
|
||||
CHECK(current_ != start_);
|
||||
// SAFETY: `current_ >= start_` is an invariant maintained internally, and
|
||||
// the CHECK above ensures that we are not at the start yet, so decrementing
|
||||
// stays in bounds of the allocation.
|
||||
@@ -137,8 +141,8 @@ class CheckedContiguousIterator {
|
||||
constexpr CheckedContiguousIterator& operator+=(difference_type rhs) {
|
||||
// NOTE: Since the max allocation size is PTRDIFF_MAX (in our compilers),
|
||||
// subtracting two pointers from the same allocation can not underflow.
|
||||
CHECK_LE(rhs, end_ - current_);
|
||||
CHECK_GE(rhs, start_ - current_);
|
||||
CHECK(rhs <= end_ - current_);
|
||||
CHECK(rhs >= start_ - current_);
|
||||
// SAFETY: `current_ <= end_` is an invariant maintained internally. The
|
||||
// checks above ensure:
|
||||
// `start_ - current_ <= rhs <= end_ - current_`.
|
||||
@@ -164,8 +168,8 @@ class CheckedContiguousIterator {
|
||||
constexpr CheckedContiguousIterator& operator-=(difference_type rhs) {
|
||||
// NOTE: Since the max allocation size is PTRDIFF_MAX (in our compilers),
|
||||
// subtracting two pointers from the same allocation can not underflow.
|
||||
CHECK_GE(rhs, current_ - end_);
|
||||
CHECK_LE(rhs, current_ - start_);
|
||||
CHECK(rhs >= current_ - end_);
|
||||
CHECK(rhs <= current_ - start_);
|
||||
// SAFETY: `start_ <= current_` is an invariant maintained internally. The
|
||||
// checks above ensure:
|
||||
// `current_ - end_ <= rhs <= current_ - start_`.
|
||||
@@ -190,20 +194,20 @@ class CheckedContiguousIterator {
|
||||
}
|
||||
|
||||
constexpr reference operator*() const {
|
||||
CHECK_NE(current_, end_);
|
||||
CHECK(current_ != end_);
|
||||
return *current_;
|
||||
}
|
||||
|
||||
constexpr pointer operator->() const {
|
||||
CHECK_NE(current_, end_);
|
||||
CHECK(current_ != end_);
|
||||
return current_;
|
||||
}
|
||||
|
||||
constexpr reference operator[](difference_type rhs) const {
|
||||
// NOTE: Since the max allocation size is PTRDIFF_MAX (in our compilers),
|
||||
// subtracting two pointers from the same allocation can not underflow.
|
||||
CHECK_GE(rhs, start_ - current_);
|
||||
CHECK_LT(rhs, end_ - current_);
|
||||
CHECK(rhs >= start_ - current_);
|
||||
CHECK(rhs < end_ - current_);
|
||||
// SAFETY: `start_ <= current_ <= end_` is an invariant maintained
|
||||
// internally. The checks above ensure:
|
||||
// `start_ - current_ <= rhs < end_ - current_`.
|
||||
@@ -214,27 +218,10 @@ class CheckedContiguousIterator {
|
||||
return UNSAFE_BUFFERS(current_[rhs]);
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool IsRangeMoveSafe(
|
||||
const CheckedContiguousIterator& from_begin,
|
||||
const CheckedContiguousIterator& from_end,
|
||||
const CheckedContiguousIterator& to) {
|
||||
if (from_end < from_begin) {
|
||||
return false;
|
||||
}
|
||||
const auto from_begin_uintptr = get_uintptr(from_begin.current_);
|
||||
const auto from_end_uintptr = get_uintptr(from_end.current_);
|
||||
const auto to_begin_uintptr = get_uintptr(to.current_);
|
||||
const auto to_end_uintptr =
|
||||
get_uintptr((to + std::distance(from_begin, from_end)).current_);
|
||||
|
||||
return to_begin_uintptr >= from_end_uintptr ||
|
||||
to_end_uintptr <= from_begin_uintptr;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void CheckComparable(const CheckedContiguousIterator& other) const {
|
||||
CHECK_EQ(start_, other.start_);
|
||||
CHECK_EQ(end_, other.end_);
|
||||
CHECK(start_ == other.start_);
|
||||
CHECK(end_ == other.end_);
|
||||
}
|
||||
|
||||
// RAW_PTR_EXCLUSION: The embedding class is stack-scoped.
|
||||
|
@@ -14,6 +14,15 @@ LinkNodeBase::LinkNodeBase(LinkNodeBase* previous, LinkNodeBase* next)
|
||||
: previous_(previous), next_(next) {}
|
||||
|
||||
LinkNodeBase::LinkNodeBase(LinkNodeBase&& rhs) {
|
||||
if (&rhs == rhs.next_) {
|
||||
// rhs is the root node of an empty LinkedList. Add self-references to
|
||||
// match.
|
||||
CHECK_EQ(&rhs, rhs.previous_);
|
||||
next_ = this;
|
||||
previous_ = this;
|
||||
return;
|
||||
}
|
||||
|
||||
next_ = rhs.next_;
|
||||
rhs.next_ = nullptr;
|
||||
previous_ = rhs.previous_;
|
||||
@@ -54,4 +63,15 @@ void LinkNodeBase::InsertAfterBase(LinkNodeBase* e) {
|
||||
e->next_ = this;
|
||||
}
|
||||
|
||||
void LinkNodeBase::MakeSelfReferencingBase() {
|
||||
if (next_ == this) {
|
||||
CHECK_EQ(previous_, this);
|
||||
return;
|
||||
}
|
||||
CHECK_EQ(next_, nullptr);
|
||||
CHECK_EQ(previous_, nullptr);
|
||||
next_ = this;
|
||||
previous_ = this;
|
||||
}
|
||||
|
||||
} // namespace base::internal
|
||||
|
@@ -5,6 +5,8 @@
|
||||
#ifndef BASE_CONTAINERS_LINKED_LIST_H_
|
||||
#define BASE_CONTAINERS_LINKED_LIST_H_
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
|
||||
@@ -82,6 +84,9 @@
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename T>
|
||||
class LinkedList;
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Base class for LinkNode<T> type
|
||||
@@ -107,6 +112,10 @@ class BASE_EXPORT LinkNodeBase {
|
||||
LinkNodeBase* previous_base() const { return previous_; }
|
||||
LinkNodeBase* next_base() const { return next_; }
|
||||
|
||||
// Make `previous_` and `next_` point to `this`. Can only be called when
|
||||
// `next_` and `previous_` are nullptr or already point to `this`.
|
||||
void MakeSelfReferencingBase();
|
||||
|
||||
private:
|
||||
// `previous_` and `next_` are not a raw_ptr<...> for performance reasons:
|
||||
// on-stack pointer + a large number of non-PA pointees through WeakLinkNode +
|
||||
@@ -147,6 +156,12 @@ class LinkNode : public internal::LinkNodeBase {
|
||||
const T* value() const { return static_cast<const T*>(this); }
|
||||
|
||||
T* value() { return static_cast<T*>(this); }
|
||||
|
||||
private:
|
||||
friend class LinkedList<T>;
|
||||
|
||||
// Make this node point to itself like a LinkedList root node.
|
||||
void MakeSelfReferencing() { MakeSelfReferencingBase(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -159,6 +174,13 @@ class LinkedList {
|
||||
LinkedList(const LinkedList&) = delete;
|
||||
LinkedList& operator=(const LinkedList&) = delete;
|
||||
|
||||
// Use move constructor with care. Returning a LinkedList from a function may
|
||||
// be unsafe if the nodes are allocated on the stack. This operation is O(1)
|
||||
// as only head and tail nodes are modified. `other` is left empty.
|
||||
LinkedList(LinkedList&& other) : root_(std::move(other.root_)) {
|
||||
other.root_.MakeSelfReferencing();
|
||||
}
|
||||
|
||||
// Appends |e| to the end of the linked list.
|
||||
void Append(LinkNode<T>* e) { e->InsertBefore(&root_); }
|
||||
|
||||
|
@@ -163,6 +163,9 @@ class LRUCacheBase {
|
||||
|
||||
// Erases the item referenced by the given iterator. An iterator to the item
|
||||
// following it will be returned. The iterator must be valid.
|
||||
// Note that caller should avoid using std::remove_if() with this container as
|
||||
// the iterator from begin()/end() is not designed to have the key modified,
|
||||
// see comment on begin().
|
||||
iterator Erase(iterator pos) {
|
||||
index_.erase(GetKeyFromValue()(*pos));
|
||||
return ordering_.erase(pos);
|
||||
@@ -205,6 +208,10 @@ class LRUCacheBase {
|
||||
// Note that since these iterators are actually iterators over a list, you
|
||||
// can keep them as you insert or delete things (as long as you don't delete
|
||||
// the one you are pointing to) and they will still be valid.
|
||||
// Also, caller should avoid moving the order of items around, or any
|
||||
// operation that modifies the key in the value with these iterators, such as
|
||||
// using std::remove_if(). This is because the key in index_ is not updated
|
||||
// and the container will be corrupted.
|
||||
iterator begin() { return ordering_.begin(); }
|
||||
const_iterator begin() const { return ordering_.begin(); }
|
||||
iterator end() { return ordering_.end(); }
|
||||
|
@@ -1,6 +1,11 @@
|
||||
// Copyright 2017 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// This file intentionally uses the `CHECK()` macro instead of the `CHECK_op()`
|
||||
// macros, as `CHECK()` generates significantly less code and is more likely to
|
||||
// optimize reasonably, even in non-official release builds. Please do not
|
||||
// change the `CHECK()` calls back to `CHECK_op()` calls.
|
||||
|
||||
#ifndef BASE_CONTAINERS_SPAN_H_
|
||||
#define BASE_CONTAINERS_SPAN_H_
|
||||
@@ -477,7 +482,7 @@ class GSL_POINTER span {
|
||||
UNSAFE_BUFFER_USAGE constexpr explicit span(It first,
|
||||
StrictNumeric<size_type> count)
|
||||
: data_(to_address(first)) {
|
||||
CHECK_EQ(size_type{count}, extent);
|
||||
CHECK(size_type{count} == extent);
|
||||
|
||||
// Non-zero `count` implies non-null `data_`. Use `SpanOrSize<T>` to
|
||||
// represent a size that might not be accompanied by the actual data.
|
||||
@@ -677,7 +682,7 @@ class GSL_POINTER span {
|
||||
return UNSAFE_BUFFERS(span<element_type, Count>(data(), Count));
|
||||
}
|
||||
constexpr auto first(StrictNumeric<size_type> count) const {
|
||||
CHECK_LE(size_type{count}, extent);
|
||||
CHECK(size_type{count} <= extent);
|
||||
// SAFETY: `data()` points to at least `extent` elements, so the new data
|
||||
// scope is a strict subset of the old.
|
||||
return UNSAFE_BUFFERS(span<element_type>(data(), count));
|
||||
@@ -694,7 +699,7 @@ class GSL_POINTER span {
|
||||
span<element_type, Count>(data() + (extent - Count), Count));
|
||||
}
|
||||
constexpr auto last(StrictNumeric<size_type> count) const {
|
||||
CHECK_LE(size_type{count}, extent);
|
||||
CHECK(size_type{count} <= extent);
|
||||
// SAFETY: `data()` points to at least `extent` elements, so the new data
|
||||
// scope is a strict subset of the old.
|
||||
return UNSAFE_BUFFERS(
|
||||
@@ -722,7 +727,7 @@ class GSL_POINTER span {
|
||||
}
|
||||
}
|
||||
constexpr auto subspan(StrictNumeric<size_type> offset) const {
|
||||
CHECK_LE(size_type{offset}, extent);
|
||||
CHECK(size_type{offset} <= extent);
|
||||
const size_type remaining = extent - size_type{offset};
|
||||
// SAFETY: `data()` points to at least `extent` elements, so `offset`
|
||||
// specifies a valid element index or the past-the-end index, and
|
||||
@@ -864,7 +869,7 @@ class GSL_POINTER span {
|
||||
constexpr pointer get_at(StrictNumeric<size_type> idx) const
|
||||
requires(extent > 0)
|
||||
{
|
||||
CHECK_LT(size_type{idx}, extent);
|
||||
CHECK(size_type{idx} < extent);
|
||||
// SAFETY: `data()` points to at least `extent` elements, so `idx` must be
|
||||
// the index of a valid element.
|
||||
return UNSAFE_BUFFERS(data() + size_type{idx});
|
||||
@@ -1050,7 +1055,7 @@ class GSL_POINTER span<ElementType, dynamic_extent, InternalPtrType> {
|
||||
constexpr void copy_from(span<const element_type> other)
|
||||
requires(!std::is_const_v<element_type>)
|
||||
{
|
||||
CHECK_EQ(size(), other.size());
|
||||
CHECK(size() == other.size());
|
||||
if (std::is_constant_evaluated()) {
|
||||
// Comparing pointers to different objects at compile time yields
|
||||
// unspecified behavior, which would halt compilation. Instead,
|
||||
@@ -1085,7 +1090,7 @@ class GSL_POINTER span<ElementType, dynamic_extent, InternalPtrType> {
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK_EQ(size(), other.size());
|
||||
CHECK(size() == other.size());
|
||||
// See comments in `copy_from()` re: use of templated comparison objects.
|
||||
DCHECK(std::less_equal{}(to_address(end()), to_address(other.begin())) ||
|
||||
std::greater_equal{}(to_address(begin()), to_address(other.end())));
|
||||
@@ -1108,13 +1113,13 @@ class GSL_POINTER span<ElementType, dynamic_extent, InternalPtrType> {
|
||||
// First `count` elements.
|
||||
template <size_t Count>
|
||||
constexpr auto first() const {
|
||||
CHECK_LE(Count, size());
|
||||
CHECK(Count <= size());
|
||||
// SAFETY: `data()` points to at least `size()` elements, so the new data
|
||||
// scope is a strict subset of the old.
|
||||
return UNSAFE_BUFFERS(span<element_type, Count>(data(), Count));
|
||||
}
|
||||
constexpr auto first(StrictNumeric<size_t> count) const {
|
||||
CHECK_LE(size_type{count}, size());
|
||||
CHECK(size_type{count} <= size());
|
||||
// SAFETY: `data()` points to at least `size()` elements, so the new data
|
||||
// scope is a strict subset of the old.
|
||||
return UNSAFE_BUFFERS(span<element_type>(data(), count));
|
||||
@@ -1123,14 +1128,14 @@ class GSL_POINTER span<ElementType, dynamic_extent, InternalPtrType> {
|
||||
// Last `count` elements.
|
||||
template <size_t Count>
|
||||
constexpr auto last() const {
|
||||
CHECK_LE(Count, size());
|
||||
CHECK(Count <= size());
|
||||
// SAFETY: `data()` points to at least `size()` elements, so the new data
|
||||
// scope is a strict subset of the old.
|
||||
return UNSAFE_BUFFERS(
|
||||
span<element_type, Count>(data() + (size() - Count), Count));
|
||||
}
|
||||
constexpr auto last(StrictNumeric<size_type> count) const {
|
||||
CHECK_LE(size_type{count}, size());
|
||||
CHECK(size_type{count} <= size());
|
||||
// SAFETY: `data()` points to at least `size()` elements, so the new data
|
||||
// scope is a strict subset of the old.
|
||||
return UNSAFE_BUFFERS(
|
||||
@@ -1140,7 +1145,7 @@ class GSL_POINTER span<ElementType, dynamic_extent, InternalPtrType> {
|
||||
// `count` elements beginning at `offset`.
|
||||
template <size_t Offset, size_t Count = dynamic_extent>
|
||||
constexpr auto subspan() const {
|
||||
CHECK_LE(Offset, size());
|
||||
CHECK(Offset <= size());
|
||||
const size_type remaining = size() - Offset;
|
||||
if constexpr (Count == dynamic_extent) {
|
||||
// SAFETY: `data()` points to at least `size()` elements, so `Offset`
|
||||
@@ -1149,14 +1154,14 @@ class GSL_POINTER span<ElementType, dynamic_extent, InternalPtrType> {
|
||||
return UNSAFE_BUFFERS(
|
||||
span<element_type, Count>(data() + Offset, remaining));
|
||||
}
|
||||
CHECK_LE(Count, remaining);
|
||||
CHECK(Count <= remaining);
|
||||
// SAFETY: `data()` points to at least `size()` elements, so `Offset`
|
||||
// specifies a valid element index or the past-the-end index, and `Count` is
|
||||
// no larger than the number of remaining valid elements.
|
||||
return UNSAFE_BUFFERS(span<element_type, Count>(data() + Offset, Count));
|
||||
}
|
||||
constexpr auto subspan(StrictNumeric<size_type> offset) const {
|
||||
CHECK_LE(size_type{offset}, size());
|
||||
CHECK(size_type{offset} <= size());
|
||||
const size_type remaining = size() - size_type{offset};
|
||||
// SAFETY: `data()` points to at least `size()` elements, so `offset`
|
||||
// specifies a valid element index or the past-the-end index, and
|
||||
@@ -1184,7 +1189,7 @@ class GSL_POINTER span<ElementType, dynamic_extent, InternalPtrType> {
|
||||
// `split_at_mut()`.)
|
||||
template <size_t Offset>
|
||||
constexpr auto split_at() const {
|
||||
CHECK_LE(Offset, size());
|
||||
CHECK(Offset <= size());
|
||||
return std::pair(first<Offset>(), subspan<Offset>());
|
||||
}
|
||||
constexpr auto split_at(StrictNumeric<size_type> offset) const {
|
||||
@@ -1313,7 +1318,7 @@ class GSL_POINTER span<ElementType, dynamic_extent, InternalPtrType> {
|
||||
//
|
||||
// (Not in `std::`; necessary when underlying memory is not yet initialized.)
|
||||
constexpr pointer get_at(StrictNumeric<size_type> idx) const {
|
||||
CHECK_LT(size_type{idx}, size());
|
||||
CHECK(size_type{idx} < size());
|
||||
// SAFETY: `data()` points to at least `size()` elements, so `idx` must be
|
||||
// the index of a valid element.
|
||||
return UNSAFE_BUFFERS(data() + size_type{idx});
|
||||
|
@@ -1,21 +0,0 @@
|
||||
// Copyright 2018 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_CONTAINERS_UTIL_H_
|
||||
#define BASE_CONTAINERS_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace base {
|
||||
|
||||
// TODO(crbug.com/40565371): What we really need is for checked_math.h to be
|
||||
// able to do checked arithmetic on pointers.
|
||||
template <typename T>
|
||||
inline uintptr_t get_uintptr(const T* t) {
|
||||
return reinterpret_cast<uintptr_t>(t);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_CONTAINERS_UTIL_H_
|
@@ -20,7 +20,6 @@
|
||||
#include "base/check_op.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/containers/util.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/numerics/checked_math.h"
|
||||
|
||||
|
@@ -264,7 +264,7 @@ StackTrace::StackTrace(span<const void* const> trace)
|
||||
|
||||
// static
|
||||
bool StackTrace::WillSymbolizeToStreamForTesting() {
|
||||
#if BUILDFLAG(SYMBOL_LEVEL) == 0
|
||||
#if BUILDFLAG(HAS_SYMBOLS) == 0
|
||||
// Symbols are not expected to be reliable when gn args specifies
|
||||
// symbol_level=0.
|
||||
return false;
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/debug/proc_maps_linux.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/strings/strcat.h"
|
||||
@@ -71,7 +72,7 @@ bool EnableInProcessStackDumping() {
|
||||
// with SIGPIPE ignored as well.
|
||||
// TODO(phajdan.jr): De-duplicate this SIGPIPE code.
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
UNSAFE_TODO(memset(&action, 0, sizeof(action)));
|
||||
action.sa_handler = SIG_IGN;
|
||||
sigemptyset(&action.sa_mask);
|
||||
return (sigaction(SIGPIPE, &action, NULL) == 0);
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/singleton.h"
|
||||
@@ -266,7 +267,7 @@ class SymbolContext {
|
||||
ULONG64 buffer[(sizeof(SYMBOL_INFO) + kMaxNameLength * sizeof(wchar_t) +
|
||||
sizeof(ULONG64) - 1) /
|
||||
sizeof(ULONG64)];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
UNSAFE_TODO(memset(buffer, 0, sizeof(buffer)));
|
||||
|
||||
// Initialize symbol information retrieval structures.
|
||||
DWORD64 sym_displacement = 0;
|
||||
@@ -346,14 +347,14 @@ void StackTrace::InitTrace(const CONTEXT* context_record) {
|
||||
// context may have had more register state (YMM, etc) than we need to unwind
|
||||
// the stack. Typically StackWalk64 only needs integer and control registers.
|
||||
CONTEXT context_copy;
|
||||
memcpy(&context_copy, context_record, sizeof(context_copy));
|
||||
UNSAFE_TODO(memcpy(&context_copy, context_record, sizeof(context_copy)));
|
||||
context_copy.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
|
||||
|
||||
// When walking an exception stack, we need to use StackWalk64().
|
||||
count_ = 0;
|
||||
// Initialize stack walking.
|
||||
STACKFRAME64 stack_frame;
|
||||
memset(&stack_frame, 0, sizeof(stack_frame));
|
||||
UNSAFE_TODO(memset(&stack_frame, 0, sizeof(stack_frame)));
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
DWORD machine_type = IMAGE_FILE_MACHINE_AMD64;
|
||||
stack_frame.AddrPC.Offset = context_record->Rip;
|
||||
|
@@ -6,14 +6,16 @@
|
||||
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
@@ -24,9 +26,10 @@ namespace {
|
||||
|
||||
class EnvironmentImpl : public Environment {
|
||||
public:
|
||||
bool GetVar(std::string_view variable_name, std::string* result) override {
|
||||
if (GetVarImpl(variable_name, result)) {
|
||||
return true;
|
||||
std::optional<std::string> GetVar(std::string_view variable_name) override {
|
||||
auto result = GetVarImpl(variable_name);
|
||||
if (result.has_value()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Some commonly used variable names are uppercase while others
|
||||
@@ -40,9 +43,9 @@ class EnvironmentImpl : public Environment {
|
||||
} else if (IsAsciiUpper(first_char)) {
|
||||
alternate_case_var = ToLowerASCII(variable_name);
|
||||
} else {
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
return GetVarImpl(alternate_case_var, result);
|
||||
return GetVarImpl(alternate_case_var);
|
||||
}
|
||||
|
||||
bool SetVar(std::string_view variable_name,
|
||||
@@ -55,33 +58,26 @@ class EnvironmentImpl : public Environment {
|
||||
}
|
||||
|
||||
private:
|
||||
bool GetVarImpl(std::string_view variable_name, std::string* result) {
|
||||
std::optional<std::string> GetVarImpl(std::string_view variable_name) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
std::wstring wide_name = UTF8ToWide(variable_name);
|
||||
if (!result) {
|
||||
return ::GetEnvironmentVariable(wide_name.c_str(), nullptr, 0) != 0;
|
||||
}
|
||||
// Documented to be the maximum environment variable size.
|
||||
std::array<wchar_t, 32767> value;
|
||||
DWORD value_length =
|
||||
::GetEnvironmentVariable(wide_name.c_str(), value.data(), value.size());
|
||||
if (value_length == 0) {
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CHECK_LE(value_length, value.size() - 1)
|
||||
<< "value should fit in the buffer (including the null terminator)";
|
||||
WideToUTF8(value.data(), value_length, result);
|
||||
return true;
|
||||
return WideToUTF8(std::wstring_view(value.data(), value_length));
|
||||
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
|
||||
const char* env_value = getenv(std::string(variable_name).c_str());
|
||||
if (!env_value) {
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
// Note that the variable may be defined but empty.
|
||||
if (result) {
|
||||
*result = env_value;
|
||||
}
|
||||
return true;
|
||||
return std::string(env_value);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -127,8 +123,18 @@ std::unique_ptr<Environment> Environment::Create() {
|
||||
return std::make_unique<EnvironmentImpl>();
|
||||
}
|
||||
|
||||
bool Environment::GetVar(std::string_view variable_name, std::string* result) {
|
||||
std::optional<std::string> actual_result = GetVar(variable_name);
|
||||
if (!actual_result.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = std::move(actual_result.value());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Environment::HasVar(std::string_view variable_name) {
|
||||
return GetVar(variable_name, nullptr);
|
||||
return GetVar(variable_name).has_value();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
@@ -30,12 +31,18 @@ class BASE_EXPORT Environment {
|
||||
// Returns the appropriate platform-specific instance.
|
||||
static std::unique_ptr<Environment> Create();
|
||||
|
||||
// Returns an environment variable's value.
|
||||
// Returns std::nullopt if the key is unset.
|
||||
// Note that the variable may be set to an empty string.
|
||||
virtual std::optional<std::string> GetVar(std::string_view variable_name) = 0;
|
||||
|
||||
// DEPRECATED. Prefer GetVar() overload above.
|
||||
// Gets an environment variable's value and stores it in |result|.
|
||||
// Returns false if the key is unset.
|
||||
virtual bool GetVar(std::string_view variable_name, std::string* result) = 0;
|
||||
bool GetVar(std::string_view variable_name, std::string* result);
|
||||
|
||||
// Syntactic sugar for GetVar(variable_name, nullptr);
|
||||
virtual bool HasVar(std::string_view variable_name);
|
||||
// Syntactic sugar for GetVar(variable_name).has_value();
|
||||
bool HasVar(std::string_view variable_name);
|
||||
|
||||
// Returns true on success, otherwise returns false. This method should not
|
||||
// be called in a multi-threaded process.
|
||||
|
@@ -77,12 +77,13 @@ enum FeatureState {
|
||||
// Provides a forward declaration for `feature_object_name` in a header file,
|
||||
// e.g.
|
||||
//
|
||||
// BASE_DECLARE_FEATURE_PARAM(kMyFeatureParam);
|
||||
// BASE_DECLARE_FEATURE_PARAM(int, kMyFeatureParam);
|
||||
//
|
||||
// If the feature needs to be marked as exported, i.e. it is referenced by
|
||||
// multiple components, then write:
|
||||
//
|
||||
// COMPONENT_EXPORT(MY_COMPONENT) BASE_DECLARE_FEATURE_PARAM(kMyFeatureParam);
|
||||
// COMPONENT_EXPORT(MY_COMPONENT)
|
||||
// BASE_DECLARE_FEATURE_PARAM(int, kMyFeatureParam);
|
||||
//
|
||||
// This macro enables optimizations to make the second and later calls faster,
|
||||
// but requires additional memory uses. If you obtain the parameter only once,
|
||||
@@ -95,13 +96,19 @@ enum FeatureState {
|
||||
// Provides a definition for `feature_object_name` with `T`, `feature`, `name`
|
||||
// and `default_value`, with an internal parsed value cache, e.g.
|
||||
//
|
||||
// BASE_FEATURE_PARAM(int, kMyFeatureParam, kMyFeature, "MyFeatureParam", 0);
|
||||
// BASE_FEATURE_PARAM(int, kMyFeatureParam, &kMyFeature, "my_feature_param",
|
||||
// 0);
|
||||
//
|
||||
// `T` is a parameter type, one of bool, int, size_t, double, std::string, and
|
||||
// base::TimeDelta. Enum types are not supported for now.
|
||||
//
|
||||
// It should *not* be defined in header files; do not use this macro in header
|
||||
// files.
|
||||
//
|
||||
// WARNING: If the feature is not enabled, the parameter is not set, or set to
|
||||
// an invalid value (per the param type), then Get() will return the default
|
||||
// value passed to this C++ macro. In particular this will typically return the
|
||||
// default value regardless of the server-side config in control groups.
|
||||
#define BASE_FEATURE_PARAM(T, feature_object_name, feature, name, \
|
||||
default_value) \
|
||||
namespace field_trial_params_internal { \
|
||||
@@ -499,7 +506,7 @@ class BASE_EXPORT FeatureList {
|
||||
std::string_view input);
|
||||
|
||||
// Checks and parses the |enable_feature| (e.g.
|
||||
// FeatureName<Study.Group:Param1/value1/) obtained by applying
|
||||
// FeatureName<Study.Group:param1/value1/) obtained by applying
|
||||
// SplitFeatureListString() to the |enable_features| flag, and sets
|
||||
// |feature_name| to be the feature's name, |study_name| and |group_name| to
|
||||
// be the field trial name and its group name if the field trial is specified
|
||||
|
@@ -104,6 +104,11 @@ BASE_FEATURE(kPartialLowEndModeOnMidRangeDevices,
|
||||
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
// Enable not perceptible binding without cpu priority boosting.
|
||||
BASE_FEATURE(kBackgroundNotPerceptibleBinding,
|
||||
"BackgroundNotPerceptibleBinding",
|
||||
FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
// Whether to report frame metrics to the Android.FrameTimeline.* histograms.
|
||||
BASE_FEATURE(kCollectAndroidFrameTimelineMetrics,
|
||||
"CollectAndroidFrameTimelineMetrics",
|
||||
|
@@ -32,12 +32,17 @@ BASE_EXPORT BASE_DECLARE_FEATURE(kPartialLowEndModeOnMidRangeDevices);
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
BASE_EXPORT BASE_DECLARE_FEATURE(kBackgroundNotPerceptibleBinding);
|
||||
BASE_EXPORT BASE_DECLARE_FEATURE(kCollectAndroidFrameTimelineMetrics);
|
||||
BASE_EXPORT BASE_DECLARE_FEATURE(
|
||||
kPostPowerMonitorBroadcastReceiverInitToBackground);
|
||||
BASE_EXPORT BASE_DECLARE_FEATURE(kPostGetMyMemoryStateToBackground);
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_MUTEX_PRIORITY_INHERITANCE)
|
||||
BASE_EXPORT BASE_DECLARE_FEATURE(kUsePriorityInheritanceMutex);
|
||||
#endif // BUILDFLAG(ENABLE_MUTEX_PRIORITY_INHERITANCE)
|
||||
|
||||
// Policy for emitting profiler metadata from `ThreadController`.
|
||||
enum class EmitThreadControllerProfilerMetadata {
|
||||
// Always emit metadata.
|
||||
|
@@ -34,12 +34,16 @@
|
||||
namespace {
|
||||
template <typename T>
|
||||
T QueryParentsForProperty(io_object_t io_object, const char* key) {
|
||||
return static_cast<T>(IORegistryEntrySearchCFProperty(
|
||||
io_object, kIOServicePlane,
|
||||
CFStringCreateWithCString(kCFAllocatorDefault, key,
|
||||
kCFStringEncodingUTF8),
|
||||
kCFAllocatorDefault,
|
||||
CFStringRef search_key = CFStringCreateWithCString(kCFAllocatorDefault, key,
|
||||
kCFStringEncodingUTF8);
|
||||
if (search_key == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
T result = static_cast<T>(IORegistryEntrySearchCFProperty(
|
||||
io_object, kIOServicePlane, search_key, kCFAllocatorDefault,
|
||||
kIORegistryIterateRecursively | kIORegistryIterateParents));
|
||||
CFRelease(search_key);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
@@ -37,7 +38,7 @@ FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy,
|
||||
// FileEnumerator::FileInfo ----------------------------------------------------
|
||||
|
||||
FileEnumerator::FileInfo::FileInfo() {
|
||||
memset(&find_data_, 0, sizeof(find_data_));
|
||||
UNSAFE_TODO(memset(&find_data_, 0, sizeof(find_data_)));
|
||||
}
|
||||
|
||||
bool FileEnumerator::FileInfo::IsDirectory() const {
|
||||
@@ -115,7 +116,7 @@ FileEnumerator::FileEnumerator(const FilePath& root_path,
|
||||
file_type_ |= (FileType::FILES | FileType::DIRECTORIES);
|
||||
}
|
||||
|
||||
memset(&find_data_, 0, sizeof(find_data_));
|
||||
UNSAFE_TODO(memset(&find_data_, 0, sizeof(find_data_)));
|
||||
pending_paths_.push(root_path);
|
||||
}
|
||||
|
||||
@@ -129,7 +130,7 @@ FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
|
||||
DCHECK(!(file_type_ & FileType::NAMES_ONLY));
|
||||
CHECK(has_find_data_);
|
||||
FileInfo ret;
|
||||
memcpy(&ret.find_data_, &find_data_, sizeof(find_data_));
|
||||
UNSAFE_TODO(memcpy(&ret.find_data_, &find_data_, sizeof(find_data_)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -71,8 +71,8 @@ StringViewType::size_type FindDriveLetter(StringViewType path) {
|
||||
|
||||
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
|
||||
bool EqualDriveLetterCaseInsensitive(StringViewType a, StringViewType b) {
|
||||
size_t a_letter_pos = FindDriveLetter(a);
|
||||
size_t b_letter_pos = FindDriveLetter(b);
|
||||
StringType::size_type a_letter_pos = FindDriveLetter(a);
|
||||
StringType::size_type b_letter_pos = FindDriveLetter(b);
|
||||
|
||||
if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos) {
|
||||
return a == b;
|
||||
@@ -248,7 +248,7 @@ std::vector<FilePath::StringType> FilePath::GetComponents() const {
|
||||
// Capture root, if any.
|
||||
base = current.BaseName();
|
||||
if (!base.value().empty() && base.value() != kCurrentDirectory) {
|
||||
ret_val.push_back(current.BaseName().value());
|
||||
ret_val.push_back(base.value());
|
||||
}
|
||||
|
||||
// Capture drive letter, if any.
|
||||
@@ -281,7 +281,7 @@ bool FilePath::AppendRelativePath(const FilePath& child, FilePath* path) const {
|
||||
|
||||
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
|
||||
// Windows can access case sensitive filesystems, so component
|
||||
// comparisions must be case sensitive, but drive letters are
|
||||
// comparisons must be case sensitive, but drive letters are
|
||||
// never case sensitive.
|
||||
if ((FindDriveLetter(*parent_comp) != StringType::npos) &&
|
||||
(FindDriveLetter(*child_comp) != StringType::npos)) {
|
||||
@@ -328,47 +328,25 @@ FilePath FilePath::DirName() const {
|
||||
FilePath new_path(path_);
|
||||
new_path.StripTrailingSeparatorsInternal();
|
||||
|
||||
// The drive letter, if any, always needs to remain in the output. If there
|
||||
// is no drive letter, as will always be the case on platforms which do not
|
||||
// support drive letters, letter will be npos, or -1, so the comparisons and
|
||||
// resizes below using letter will still be valid.
|
||||
StringType::size_type letter = FindDriveLetter(new_path.path_);
|
||||
|
||||
StringType::size_type last_separator = new_path.path_.find_last_of(
|
||||
kSeparators, StringType::npos, kSeparatorsLength - 1);
|
||||
if (last_separator == StringType::npos) {
|
||||
// path_ is in the current directory.
|
||||
new_path.path_.resize(letter + 1);
|
||||
} else if (last_separator == letter + 1) {
|
||||
// path_ is in the root directory.
|
||||
new_path.path_.resize(letter + 2);
|
||||
} else if (last_separator == letter + 2 &&
|
||||
IsSeparator(new_path.path_[letter + 1])) {
|
||||
// path_ is in "//" (possibly with a drive letter); leave the double
|
||||
// separator intact indicating alternate root.
|
||||
new_path.path_.resize(letter + 3);
|
||||
} else if (last_separator != 0) {
|
||||
bool trim_to_basename = true;
|
||||
#if BUILDFLAG(IS_POSIX)
|
||||
// On Posix, more than two leading separators are always collapsed to one.
|
||||
// See
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
|
||||
// So, do not strip any of the separators, let
|
||||
// StripTrailingSeparatorsInternal() take care of the extra.
|
||||
if (AreAllSeparators(new_path.path_.substr(0, last_separator + 1))) {
|
||||
new_path.path_.resize(last_separator + 1);
|
||||
trim_to_basename = false;
|
||||
// path_ is in the current directory. The drive letter, if any, always
|
||||
// needs to remain in the output. If there is no drive letter, as will
|
||||
// always be the case on platforms which do not support drive letters,
|
||||
// return the current directory.
|
||||
StringType::size_type letter = FindDriveLetter(new_path.path_);
|
||||
if (letter != StringType::npos) {
|
||||
new_path.path_.resize(letter + 1);
|
||||
} else {
|
||||
new_path.path_ = kCurrentDirectory;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_POSIX)
|
||||
if (trim_to_basename) {
|
||||
// path_ is somewhere else, trim the basename.
|
||||
new_path.path_.resize(last_separator);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// path_ is not in the current directory so trim the basename.
|
||||
new_path.path_.resize(last_separator + 1);
|
||||
|
||||
new_path.StripTrailingSeparatorsInternal();
|
||||
if (!new_path.path_.length()) {
|
||||
new_path.path_ = kCurrentDirectory;
|
||||
// Remove any remaining trailing separator(s).
|
||||
new_path.StripTrailingSeparatorsInternal();
|
||||
}
|
||||
|
||||
return new_path;
|
||||
|
@@ -466,7 +466,7 @@ class BASE_EXPORT FilePath {
|
||||
// (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
|
||||
[[nodiscard]] FilePath NormalizePathSeparators() const;
|
||||
|
||||
// Normalize all path separattors to given type on Windows
|
||||
// Normalize all path separators to given type on Windows
|
||||
// (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
|
||||
[[nodiscard]] FilePath NormalizePathSeparatorsTo(CharType separator) const;
|
||||
|
||||
|
@@ -466,11 +466,14 @@ BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir,
|
||||
FilePath::StringViewType prefix,
|
||||
FilePath* new_dir);
|
||||
|
||||
// Creates a directory, as well as creating any parent directories, if they
|
||||
// don't exist. Returns 'true' on successful creation, or if the directory
|
||||
// already exists. The directory is only readable by the current user.
|
||||
// Returns true on success, leaving *error unchanged.
|
||||
// Returns false on failure and sets *error appropriately, if it is non-NULL.
|
||||
// Ensures a directory exists, if necessary, creating it and parent directories.
|
||||
// Returns true if the directory already existed or was created, leaving *error
|
||||
// unchanged.
|
||||
// Returns false on failure and sets *error appropriately if it is non-NULL.
|
||||
//
|
||||
// The created directories can only be accessed by the current user, except on
|
||||
// ChromeOS, where the directories created under `~/MyFiles` or `/media` can
|
||||
// also be accessed by ChromeOS services.
|
||||
BASE_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path,
|
||||
File::Error* error);
|
||||
|
||||
|
@@ -432,6 +432,38 @@ bool PreReadFileSlow(const FilePath& file_path, int64_t max_bytes) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS)
|
||||
|
||||
// Checks if the given path is under ~/MyFiles or /media.
|
||||
// Recognizes the following patterns:
|
||||
// - "/home/chronos/user/MyFiles/<dir>[/...]"
|
||||
// - "/home/chronos/u-<id>/MyFiles/<dir>[/...]"
|
||||
// - "/media/<dir>[/...]"
|
||||
bool IsVisibleToUser(const FilePath& path) {
|
||||
if (!path.IsAbsolute()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector parts = path.GetComponents();
|
||||
|
||||
// Since the path is absolute, the first part should be the root directory.
|
||||
DCHECK(!parts.empty());
|
||||
DCHECK_EQ(parts[0], "/");
|
||||
|
||||
// Is path under /media?
|
||||
if (parts.size() > 2 && parts[1] == "media" && !parts[2].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is path under ~/MyFiles?
|
||||
return parts.size() > 5 && parts[1] == "home" && parts[2] == "chronos" &&
|
||||
(parts[3] == "user" ||
|
||||
(parts[3].starts_with("u-") && parts[3].size() > 2)) &&
|
||||
parts[4] == "MyFiles" && !parts[5].empty();
|
||||
}
|
||||
|
||||
#endif // BUILDFLAG(IS_CHROMEOS)
|
||||
|
||||
} // namespace
|
||||
|
||||
FilePath MakeAbsoluteFilePath(const FilePath& input) {
|
||||
@@ -751,8 +783,8 @@ bool SetPosixFilePermissions(const FilePath& path, int mode) {
|
||||
|
||||
bool ExecutableExistsInPath(Environment* env,
|
||||
const FilePath::StringType& executable) {
|
||||
std::string path;
|
||||
if (!env->GetVar("PATH", &path)) {
|
||||
std::string path = env->GetVar("PATH").value_or("");
|
||||
if (path.empty()) {
|
||||
LOG(ERROR) << "No $PATH variable. Assuming no " << executable << ".";
|
||||
return false;
|
||||
}
|
||||
@@ -902,6 +934,7 @@ bool CreateNewTempDirectory(const FilePath::StringType& prefix,
|
||||
bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) {
|
||||
ScopedBlockingCall scoped_blocking_call(
|
||||
FROM_HERE, BlockingType::MAY_BLOCK); // For call to mkdir().
|
||||
|
||||
// Avoid checking subdirs if directory already exists.
|
||||
if (DirectoryExists(full_path)) {
|
||||
return true;
|
||||
@@ -921,7 +954,15 @@ bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) {
|
||||
|
||||
// Iterate through the missing directories and create.
|
||||
for (const FilePath& subpath : base::Reversed(missing_subpaths)) {
|
||||
if (mkdir(subpath.value().c_str(), 0700) == 0) {
|
||||
mode_t mode = S_IRWXU;
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS)
|
||||
if (IsVisibleToUser(subpath)) {
|
||||
mode |= S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_CHROMEOS)
|
||||
|
||||
if (mkdir(subpath.value().c_str(), mode) == 0) {
|
||||
continue;
|
||||
}
|
||||
// Mkdir failed, but it might have failed with EEXIST, or some other error
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/clang_profiling_buildflags.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "base/features.h"
|
||||
#include "base/files/file_enumerator.h"
|
||||
@@ -868,7 +869,7 @@ bool DevicePathToDriveLetterPath(const FilePath& nt_device_path,
|
||||
device_path.IsParent(nt_device_path)) {
|
||||
*out_drive_letter_path =
|
||||
FilePath(drive + nt_device_path.value().substr(
|
||||
wcslen(device_path_as_string)));
|
||||
UNSAFE_TODO(wcslen(device_path_as_string))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -948,9 +949,9 @@ bool GetFileInfo(const FilePath& file_path, File::Info* results) {
|
||||
FILE* OpenFile(const FilePath& filename, const char* mode) {
|
||||
// 'N' is unconditionally added below, so be sure there is not one already
|
||||
// present before a comma in |mode|.
|
||||
DCHECK(
|
||||
strchr(mode, 'N') == nullptr ||
|
||||
(strchr(mode, ',') != nullptr && strchr(mode, 'N') > strchr(mode, ',')));
|
||||
DCHECK(UNSAFE_TODO(strchr(mode, 'N')) == nullptr ||
|
||||
(UNSAFE_TODO(strchr(mode, ',')) != nullptr &&
|
||||
UNSAFE_TODO(strchr(mode, 'N')) > UNSAFE_TODO(strchr(mode, ','))));
|
||||
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
|
||||
std::wstring w_mode = UTF8ToWide(mode);
|
||||
AppendModeCharacter(L'N', &w_mode);
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/critical_closure.h"
|
||||
@@ -445,14 +446,14 @@ void ImportantFileWriter::ScheduleWriteWithBackgroundDataSerializer(
|
||||
|
||||
void ImportantFileWriter::DoScheduledWrite() {
|
||||
// One of the serializers should be set.
|
||||
DCHECK(!absl::holds_alternative<absl::monostate>(serializer_));
|
||||
DCHECK(!std::holds_alternative<std::monostate>(serializer_));
|
||||
|
||||
const TimeTicks serialization_start = TimeTicks::Now();
|
||||
BackgroundDataProducerCallback data_producer_for_background_sequence;
|
||||
|
||||
if (absl::holds_alternative<DataSerializer*>(serializer_)) {
|
||||
if (std::holds_alternative<DataSerializer*>(serializer_)) {
|
||||
std::optional<std::string> data;
|
||||
data = absl::get<DataSerializer*>(serializer_)->SerializeData();
|
||||
data = std::get<DataSerializer*>(serializer_)->SerializeData();
|
||||
if (!data) {
|
||||
DLOG(WARNING) << "Failed to serialize data to be saved in "
|
||||
<< path_.value();
|
||||
@@ -466,7 +467,7 @@ void ImportantFileWriter::DoScheduledWrite() {
|
||||
std::move(data).value());
|
||||
} else {
|
||||
data_producer_for_background_sequence =
|
||||
absl::get<BackgroundDataSerializer*>(serializer_)
|
||||
std::get<BackgroundDataSerializer*>(serializer_)
|
||||
->GetSerializedDataProducerForBackgroundSequence();
|
||||
|
||||
DCHECK(data_producer_for_background_sequence);
|
||||
@@ -492,7 +493,7 @@ void ImportantFileWriter::RegisterOnNextWriteCallbacks(
|
||||
|
||||
void ImportantFileWriter::ClearPendingWrite() {
|
||||
timer().Stop();
|
||||
serializer_.emplace<absl::monostate>();
|
||||
serializer_.emplace<std::monostate>();
|
||||
}
|
||||
|
||||
void ImportantFileWriter::SetTimerForTesting(OneShotTimer* timer_override) {
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/compiler_specific.h"
|
||||
@@ -19,7 +20,6 @@
|
||||
#include "base/sequence_checker.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "third_party/abseil-cpp/absl/types/variant.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
@@ -213,7 +213,7 @@ class BASE_EXPORT ImportantFileWriter {
|
||||
raw_ptr<OneShotTimer> timer_override_ = nullptr;
|
||||
|
||||
// Serializer which will provide the data to be saved.
|
||||
absl::variant<absl::monostate, DataSerializer*, BackgroundDataSerializer*>
|
||||
std::variant<std::monostate, DataSerializer*, BackgroundDataSerializer*>
|
||||
serializer_;
|
||||
|
||||
// Time delta after which scheduled data will be written to disk.
|
||||
|
@@ -22,7 +22,7 @@ ScopedTempFile& ScopedTempFile::operator=(ScopedTempFile&& other) noexcept {
|
||||
CHECK_NE(path_, other.path_);
|
||||
}
|
||||
if (!Delete()) {
|
||||
DLOG(WARNING) << "Could not delete temp dir in operator=().";
|
||||
DLOG(WARNING) << "Could not delete temp file in operator=().";
|
||||
}
|
||||
path_ = std::move(other.path_);
|
||||
return *this;
|
||||
@@ -30,7 +30,7 @@ ScopedTempFile& ScopedTempFile::operator=(ScopedTempFile&& other) noexcept {
|
||||
|
||||
ScopedTempFile::~ScopedTempFile() {
|
||||
if (!Delete()) {
|
||||
DLOG(WARNING) << "Could not delete temp dir in destructor.";
|
||||
DLOG(WARNING) << "Could not delete temp file in destructor.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ bool ScopedTempFile::Delete() {
|
||||
|
||||
void ScopedTempFile::Reset() {
|
||||
if (!Delete()) {
|
||||
DLOG(WARNING) << "Could not delete temp dir in Reset().";
|
||||
DLOG(WARNING) << "Could not delete temp file in Reset().";
|
||||
}
|
||||
path_.clear();
|
||||
}
|
||||
|
@@ -59,12 +59,13 @@ TestComponentContextForProcess::TestComponentContextForProcess(
|
||||
std::make_unique<sys::ComponentContext>(
|
||||
std::move(incoming_services), published_root_directory.NewRequest()));
|
||||
|
||||
// Connect to the "/svc" directory of the |published_root_directory| and wrap
|
||||
// Open the "/svc" directory of the |published_root_directory| and wrap
|
||||
// that into a ServiceDirectory.
|
||||
fidl::InterfaceHandle<::fuchsia::io::Directory> published_services;
|
||||
status = fdio_service_connect_at(
|
||||
published_root_directory.channel().get(), "svc",
|
||||
published_services.NewRequest().TakeChannel().release());
|
||||
status =
|
||||
fdio_open3_at(published_root_directory.channel().get(), "svc",
|
||||
uint64_t{fuchsia::io::PERM_READABLE},
|
||||
published_services.NewRequest().TakeChannel().release());
|
||||
ZX_CHECK(status == ZX_OK, status) << "fdio_service_connect_at() to /svc";
|
||||
published_services_ =
|
||||
std::make_shared<sys::ServiceDirectory>(std::move(published_services));
|
||||
|
@@ -19,7 +19,6 @@
|
||||
#include "base/functional/callback_internal.h"
|
||||
#include "base/functional/unretained_traits.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_asan_bound_arg_tracker.h"
|
||||
#include "base/memory/raw_ref.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/types/always_false.h"
|
||||
@@ -29,6 +28,10 @@
|
||||
#include "build/build_config.h"
|
||||
#include "third_party/abseil-cpp/absl/functional/function_ref.h"
|
||||
|
||||
#if PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
|
||||
#include "base/memory/raw_ptr_asan_bound_arg_tracker.h"
|
||||
#endif
|
||||
|
||||
// See docs/callback.md for user documentation.
|
||||
//
|
||||
// Concepts:
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace base {
|
||||
|
||||
// absl::visit() needs to be called with a functor object, such as
|
||||
// std::visit() needs to be called with a functor object, such as
|
||||
//
|
||||
// struct Visitor {
|
||||
// std::string operator()(const PackageA& source) {
|
||||
@@ -19,12 +19,12 @@ namespace base {
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// absl::variant<PackageA, PackageB> var = PackageA();
|
||||
// return absl::visit(Visitor(), var);
|
||||
// std::variant<PackageA, PackageB> var = PackageA();
|
||||
// return std::visit(Visitor(), var);
|
||||
//
|
||||
// `Overloaded` enables the above code to be written as:
|
||||
//
|
||||
// absl::visit(
|
||||
// std::visit(
|
||||
// Overloaded{
|
||||
// [](const PackageA& pack) { return "PackageA"; },
|
||||
// [](const PackageB& pack) { return "PackageB"; },
|
||||
|
@@ -7,17 +7,20 @@
|
||||
|
||||
#include "base/functional/overloaded.h"
|
||||
|
||||
#include "third_party/abseil-cpp/absl/types/variant.h"
|
||||
#include <variant>
|
||||
|
||||
namespace base {
|
||||
|
||||
void LambdaMissingForVariantElement() {
|
||||
// `absl::visit()` may only be called on an `Overloaded` that can actually
|
||||
// `std::visit()` may only be called on an `Overloaded` that can actually
|
||||
// handle all potential input variant types.
|
||||
struct A {};
|
||||
struct B {};
|
||||
absl::variant<A, B> var = A{};
|
||||
absl::visit(Overloaded{[](A& pack) { return "A"; }}, var); // expected-error-re@*:* {{no type named 'type' in 'absl::type_traits_internal::result_of<base::Overloaded<(lambda at {{.*}})> (B &)>'}}
|
||||
std::variant<A, B> var = A{};
|
||||
std::visit(Overloaded{[](A& pack) { return "A"; }}, var); // expected-error-re@*:* {{static assertion failed due to requirement {{.*}} `std::visit` requires the visitor to be exhaustive}}
|
||||
// expected-error@*:* {{attempt to use a deleted function}}
|
||||
// expected-error@*:* {{attempt to use a deleted function}}
|
||||
// expected-error@*:* {{cannot deduce return type 'auto' from returned value of type '<overloaded function type>'}}
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
@@ -77,6 +77,26 @@ struct IntPairHash<std::pair<Type1, Type2>> {
|
||||
}
|
||||
};
|
||||
|
||||
// Combine the hash `seed` with the computed hash of `value`.
|
||||
template <typename T>
|
||||
size_t HashCombine(size_t seed, const T& value) {
|
||||
size_t hash;
|
||||
if constexpr (sizeof(size_t) == 8) {
|
||||
hash = HashInts64(seed, std::hash<T>()(value));
|
||||
} else {
|
||||
hash = HashInts32(seed, std::hash<T>()(value));
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Computes the combination of hashes for `values`.
|
||||
template <typename T, typename... V>
|
||||
size_t HashCombine(size_t seed, const T& first, const V&... values) {
|
||||
size_t hash = HashCombine(seed, first);
|
||||
return HashCombine(hash, values...);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_HASH_HASH_H_
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/debug/alias.h"
|
||||
#include "base/environment.h"
|
||||
#include "base/files/file_path.h"
|
||||
@@ -151,7 +152,7 @@ void LazyInitIcuDataFile() {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// TODO(brucedawson): http://crbug.com/445616
|
||||
wchar_t tmp_buffer[_MAX_PATH] = {};
|
||||
wcscpy_s(tmp_buffer, data_path.value().c_str());
|
||||
UNSAFE_TODO(wcscpy_s(tmp_buffer, data_path.value().c_str()));
|
||||
debug::Alias(tmp_buffer);
|
||||
#endif
|
||||
data_path = data_path.AppendASCII(kIcuDataFileName);
|
||||
@@ -159,7 +160,7 @@ void LazyInitIcuDataFile() {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// TODO(brucedawson): http://crbug.com/445616
|
||||
wchar_t tmp_buffer2[_MAX_PATH] = {};
|
||||
wcscpy_s(tmp_buffer2, data_path.value().c_str());
|
||||
UNSAFE_TODO(wcscpy_s(tmp_buffer2, data_path.value().c_str()));
|
||||
debug::Alias(tmp_buffer2);
|
||||
#endif
|
||||
|
||||
@@ -194,7 +195,7 @@ void LazyInitIcuDataFile() {
|
||||
// TODO(brucedawson): http://crbug.com/445616.
|
||||
g_debug_icu_pf_last_error = ::GetLastError();
|
||||
g_debug_icu_pf_error_details = file.error_details();
|
||||
wcscpy_s(g_debug_icu_pf_filename, data_path.value().c_str());
|
||||
UNSAFE_TODO(wcscpy_s(g_debug_icu_pf_filename, data_path.value().c_str()));
|
||||
}
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
}
|
||||
@@ -287,7 +288,7 @@ bool InitializeICUFromDataFile() {
|
||||
int debug_icu_pf_error_details = g_debug_icu_pf_error_details;
|
||||
debug::Alias(&debug_icu_pf_error_details);
|
||||
wchar_t debug_icu_pf_filename[_MAX_PATH] = {};
|
||||
wcscpy_s(debug_icu_pf_filename, g_debug_icu_pf_filename);
|
||||
UNSAFE_TODO(wcscpy_s(debug_icu_pf_filename, g_debug_icu_pf_filename));
|
||||
debug::Alias(&debug_icu_pf_filename);
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
// Excluding Chrome OS from this CHECK due to b/289684640.
|
||||
|
@@ -16,7 +16,6 @@
|
||||
#include "base/feature_list.h"
|
||||
#include "base/features.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
@@ -77,21 +76,6 @@ bool UnprefixedHexStringToInt(std::string_view input, int* output) {
|
||||
return HexStringToInt(input, output);
|
||||
}
|
||||
|
||||
// These values are persisted to logs. Entries should not be renumbered and
|
||||
// numeric values should never be reused.
|
||||
enum class ChromiumJsonExtension {
|
||||
kCComment,
|
||||
kCppComment,
|
||||
kXEscape,
|
||||
kVerticalTabEscape,
|
||||
kControlCharacter,
|
||||
kNewlineInString,
|
||||
kMaxValue = kNewlineInString,
|
||||
};
|
||||
|
||||
const char kExtensionHistogramName[] =
|
||||
"Security.JSONParser.ChromiumExtensionUsage";
|
||||
|
||||
} // namespace
|
||||
|
||||
// This is U+FFFD.
|
||||
@@ -314,8 +298,6 @@ bool JSONParser::EatComment() {
|
||||
const bool comments_allowed = options_ & JSON_ALLOW_COMMENTS;
|
||||
|
||||
if (comment_start == "//") {
|
||||
UmaHistogramEnumeration(kExtensionHistogramName,
|
||||
ChromiumJsonExtension::kCppComment);
|
||||
if (!comments_allowed) {
|
||||
ReportError(JSON_UNEXPECTED_TOKEN, 0);
|
||||
return false;
|
||||
@@ -330,8 +312,6 @@ bool JSONParser::EatComment() {
|
||||
ConsumeChar();
|
||||
}
|
||||
} else if (comment_start == "/*") {
|
||||
UmaHistogramEnumeration(kExtensionHistogramName,
|
||||
ChromiumJsonExtension::kCComment);
|
||||
if (!comments_allowed) {
|
||||
ReportError(JSON_UNEXPECTED_TOKEN, 0);
|
||||
return false;
|
||||
@@ -539,8 +519,6 @@ std::optional<std::string> JSONParser::ConsumeStringRaw() {
|
||||
// UTF-8 \x escape sequences are not allowed in the spec, but they
|
||||
// are supported here for backwards-compatiblity with the old
|
||||
// parser.
|
||||
UmaHistogramEnumeration(kExtensionHistogramName,
|
||||
ChromiumJsonExtension::kXEscape);
|
||||
if (!(options_ & JSON_ALLOW_X_ESCAPES)) {
|
||||
ReportError(JSON_INVALID_ESCAPE, -1);
|
||||
return std::nullopt;
|
||||
@@ -600,8 +578,6 @@ std::optional<std::string> JSONParser::ConsumeStringRaw() {
|
||||
string.push_back('\t');
|
||||
break;
|
||||
case 'v': // Not listed as valid escape sequence in the RFC.
|
||||
UmaHistogramEnumeration(kExtensionHistogramName,
|
||||
ChromiumJsonExtension::kVerticalTabEscape);
|
||||
if (!(options_ & JSON_ALLOW_VERT_TAB)) {
|
||||
ReportError(JSON_INVALID_ESCAPE, -1);
|
||||
return std::nullopt;
|
||||
@@ -662,16 +638,12 @@ JSONParser::ConsumeStringPart() {
|
||||
// quotation mark, reverse solidus, and the control characters (U+0000
|
||||
// through U+001F)".
|
||||
if (*c == '\n' || *c == '\r') {
|
||||
UmaHistogramEnumeration(kExtensionHistogramName,
|
||||
ChromiumJsonExtension::kNewlineInString);
|
||||
if (!(options_ &
|
||||
(JSON_ALLOW_NEWLINES_IN_STRINGS | JSON_ALLOW_CONTROL_CHARS))) {
|
||||
ReportError(JSON_UNSUPPORTED_ENCODING, -1);
|
||||
return {StringResult::kError, {}}; // No need to return consumed data.
|
||||
}
|
||||
} else if (*c <= 0x1F) {
|
||||
UmaHistogramEnumeration(kExtensionHistogramName,
|
||||
ChromiumJsonExtension::kControlCharacter);
|
||||
if (!(options_ & JSON_ALLOW_CONTROL_CHARS)) {
|
||||
ReportError(JSON_UNSUPPORTED_ENCODING, -1);
|
||||
return {StringResult::kError, {}}; // No need to return consumed data.
|
||||
|
@@ -19,69 +19,86 @@
|
||||
#include "third_party/rust/serde_json_lenient/v0_2/wrapper/lib.rs.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
// TODO(crbug.com/40811643): Move the C++ parser into components/nacl to just
|
||||
// run in-process there. Don't compile base::JSONReader on NaCL at all.
|
||||
#if 0 // Disables Rust
|
||||
|
||||
namespace {
|
||||
using serde_json_lenient::ContextPointer;
|
||||
|
||||
const char kSecurityJsonParsingTime[] = "Security.JSONParser.ParsingTime";
|
||||
} // namespace
|
||||
|
||||
ContextPointer& ListAppendList(ContextPointer& ctx) {
|
||||
auto& value = reinterpret_cast<base::Value&>(ctx);
|
||||
value.GetList().Append(base::Value::List());
|
||||
return reinterpret_cast<ContextPointer&>(value.GetList().back());
|
||||
// This namespace defines FFI-friendly functions that are be called from Rust in
|
||||
// //third_party/rust/serde_json_lenient/v0_2/wrapper/.
|
||||
namespace serde_json_lenient {
|
||||
|
||||
base::Value::List& list_append_list(base::Value::List& ctx) {
|
||||
ctx.Append(base::Value::List());
|
||||
return ctx.back().GetList();
|
||||
}
|
||||
|
||||
ContextPointer& ListAppendDict(ContextPointer& ctx) {
|
||||
auto& value = reinterpret_cast<base::Value&>(ctx);
|
||||
value.GetList().Append(base::Value::Dict());
|
||||
return reinterpret_cast<ContextPointer&>(value.GetList().back());
|
||||
base::Value::Dict& list_append_dict(base::Value::List& ctx) {
|
||||
ctx.Append(base::Value::Dict());
|
||||
return ctx.back().GetDict();
|
||||
}
|
||||
|
||||
void ListAppendNone(ContextPointer& ctx) {
|
||||
auto& value = reinterpret_cast<base::Value&>(ctx);
|
||||
value.GetList().Append(base::Value());
|
||||
void list_append_none(base::Value::List& ctx) {
|
||||
ctx.Append(base::Value());
|
||||
}
|
||||
|
||||
template <class T, class As = T>
|
||||
void ListAppendValue(ContextPointer& ctx, T v) {
|
||||
auto& value = reinterpret_cast<base::Value&>(ctx);
|
||||
value.GetList().Append(As{v});
|
||||
void list_append_bool(base::Value::List& ctx, bool val) {
|
||||
ctx.Append(val);
|
||||
}
|
||||
|
||||
ContextPointer& DictSetList(ContextPointer& ctx, rust::Str key) {
|
||||
auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
|
||||
void list_append_i32(base::Value::List& ctx, int32_t val) {
|
||||
ctx.Append(val);
|
||||
}
|
||||
|
||||
void list_append_f64(base::Value::List& ctx, double val) {
|
||||
ctx.Append(val);
|
||||
}
|
||||
|
||||
void list_append_str(base::Value::List& ctx, rust::Str val) {
|
||||
ctx.Append(std::string(val));
|
||||
}
|
||||
|
||||
base::Value::List& dict_set_list(base::Value::Dict& ctx, rust::Str key) {
|
||||
base::Value* value =
|
||||
dict.Set(base::RustStrToStringView(key), base::Value::List());
|
||||
return reinterpret_cast<ContextPointer&>(*value);
|
||||
ctx.Set(base::RustStrToStringView(key), base::Value::List());
|
||||
return value->GetList();
|
||||
}
|
||||
|
||||
ContextPointer& DictSetDict(ContextPointer& ctx, rust::Str key) {
|
||||
auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
|
||||
base::Value::Dict& dict_set_dict(base::Value::Dict& ctx, rust::Str key) {
|
||||
base::Value* value =
|
||||
dict.Set(base::RustStrToStringView(key), base::Value::Dict());
|
||||
return reinterpret_cast<ContextPointer&>(*value);
|
||||
ctx.Set(base::RustStrToStringView(key), base::Value::Dict());
|
||||
return value->GetDict();
|
||||
}
|
||||
|
||||
void DictSetNone(ContextPointer& ctx, rust::Str key) {
|
||||
auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
|
||||
dict.Set(base::RustStrToStringView(key), base::Value());
|
||||
void dict_set_none(base::Value::Dict& ctx, rust::Str key) {
|
||||
ctx.Set(base::RustStrToStringView(key), base::Value());
|
||||
}
|
||||
|
||||
template <class T, class As = T>
|
||||
void DictSetValue(ContextPointer& ctx, rust::Str key, T v) {
|
||||
auto& dict = reinterpret_cast<base::Value&>(ctx).GetDict();
|
||||
dict.Set(base::RustStrToStringView(key), base::Value(As{v}));
|
||||
void dict_set_bool(base::Value::Dict& ctx, rust::Str key, bool val) {
|
||||
ctx.Set(base::RustStrToStringView(key), val);
|
||||
}
|
||||
|
||||
JSONReader::Result DecodeJSONInRust(std::string_view json,
|
||||
int options,
|
||||
size_t max_depth) {
|
||||
const serde_json_lenient::JsonOptions rust_options = {
|
||||
void dict_set_i32(base::Value::Dict& ctx, rust::Str key, int32_t val) {
|
||||
ctx.Set(base::RustStrToStringView(key), val);
|
||||
}
|
||||
|
||||
void dict_set_f64(base::Value::Dict& ctx, rust::Str key, double val) {
|
||||
ctx.Set(base::RustStrToStringView(key), val);
|
||||
}
|
||||
|
||||
void dict_set_str(base::Value::Dict& ctx, rust::Str key, rust::Str val) {
|
||||
ctx.Set(base::RustStrToStringView(key), std::string(val));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
base::JSONReader::Result DecodeJSONInRust(std::string_view json,
|
||||
int options,
|
||||
size_t max_depth) {
|
||||
const JsonOptions rust_options = {
|
||||
.allow_trailing_commas =
|
||||
(options & base::JSON_ALLOW_TRAILING_COMMAS) != 0,
|
||||
.replace_invalid_characters =
|
||||
@@ -93,28 +110,11 @@ JSONReader::Result DecodeJSONInRust(std::string_view json,
|
||||
.allow_x_escapes = (options & base::JSON_ALLOW_X_ESCAPES) != 0,
|
||||
.max_depth = max_depth,
|
||||
};
|
||||
static constexpr serde_json_lenient::Functions functions = {
|
||||
.list_append_none_fn = ListAppendNone,
|
||||
.list_append_bool_fn = ListAppendValue<bool>,
|
||||
.list_append_i32_fn = ListAppendValue<int32_t>,
|
||||
.list_append_f64_fn = ListAppendValue<double>,
|
||||
.list_append_str_fn = ListAppendValue<rust::Str, std::string>,
|
||||
.list_append_list_fn = ListAppendList,
|
||||
.list_append_dict_fn = ListAppendDict,
|
||||
.dict_set_none_fn = DictSetNone,
|
||||
.dict_set_bool_fn = DictSetValue<bool>,
|
||||
.dict_set_i32_fn = DictSetValue<int32_t>,
|
||||
.dict_set_f64_fn = DictSetValue<double>,
|
||||
.dict_set_str_fn = DictSetValue<rust::Str, std::string>,
|
||||
.dict_set_list_fn = DictSetList,
|
||||
.dict_set_dict_fn = DictSetDict,
|
||||
};
|
||||
|
||||
base::Value value(base::Value::Type::LIST);
|
||||
auto& ctx = reinterpret_cast<ContextPointer&>(value);
|
||||
serde_json_lenient::DecodeError error;
|
||||
bool ok = serde_json_lenient::decode_json(
|
||||
base::StringViewToRustSlice(json), rust_options, functions, ctx, error);
|
||||
base::Value::List list;
|
||||
DecodeError error;
|
||||
bool ok =
|
||||
decode_json(base::StringViewToRustSlice(json), rust_options, list, error);
|
||||
|
||||
if (!ok) {
|
||||
return base::unexpected(base::JSONReader::Error{
|
||||
@@ -124,13 +124,16 @@ JSONReader::Result DecodeJSONInRust(std::string_view json,
|
||||
});
|
||||
}
|
||||
|
||||
return std::move(std::move(value.GetList()).back());
|
||||
return std::move(list.back());
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
} // namespace
|
||||
} // namespace serde_json_lenient
|
||||
|
||||
#endif // !BUILDFLAG(IS_NACL)
|
||||
|
||||
namespace base {
|
||||
|
||||
// static
|
||||
std::optional<Value> JSONReader::Read(std::string_view json,
|
||||
int options,
|
||||
@@ -141,7 +144,8 @@ std::optional<Value> JSONReader::Read(std::string_view json,
|
||||
#else // BUILDFLAG(IS_NACL)
|
||||
SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
|
||||
if (UsingRust()) {
|
||||
JSONReader::Result result = DecodeJSONInRust(json, options, max_depth);
|
||||
JSONReader::Result result =
|
||||
serde_json_lenient::DecodeJSONInRust(json, options, max_depth);
|
||||
if (!result.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -194,7 +198,8 @@ JSONReader::Result JSONReader::ReadAndReturnValueWithError(
|
||||
#else // BUILDFLAG(IS_NACL)
|
||||
SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
|
||||
if (UsingRust()) {
|
||||
return DecodeJSONInRust(json, options, internal::kAbsoluteMaxDepth);
|
||||
return serde_json_lenient::DecodeJSONInRust(json, options,
|
||||
internal::kAbsoluteMaxDepth);
|
||||
} else {
|
||||
internal::JSONParser parser(options);
|
||||
auto value = parser.Parse(json);
|
||||
|
@@ -9,11 +9,13 @@
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include "base/json/string_escape.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/to_string.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
@@ -65,13 +67,13 @@ JSONWriter::JSONWriter(int options, std::string* json, size_t max_depth)
|
||||
CHECK_LE(max_depth, internal::kAbsoluteMaxDepth);
|
||||
}
|
||||
|
||||
bool JSONWriter::BuildJSONString(absl::monostate node, size_t depth) {
|
||||
bool JSONWriter::BuildJSONString(std::monostate node, size_t depth) {
|
||||
json_string_->append("null");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSONWriter::BuildJSONString(bool node, size_t depth) {
|
||||
json_string_->append(node ? "true" : "false");
|
||||
json_string_->append(base::ToString(node));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/json/json_common.h"
|
||||
@@ -94,7 +95,7 @@ class BASE_EXPORT JSONWriter {
|
||||
|
||||
// Called recursively to build the JSON string. When completed,
|
||||
// |json_string_| will contain the JSON.
|
||||
bool BuildJSONString(absl::monostate node, size_t depth);
|
||||
bool BuildJSONString(std::monostate node, size_t depth);
|
||||
bool BuildJSONString(bool node, size_t depth);
|
||||
bool BuildJSONString(int node, size_t depth);
|
||||
bool BuildJSONString(double node, size_t depth);
|
||||
|
@@ -178,7 +178,7 @@ std::unique_ptr<VlogInfo> VlogInfoFromCommandLine() {
|
||||
#endif // defined(LEAK_SANITIZER)
|
||||
return std::make_unique<VlogInfo>(
|
||||
command_line->GetSwitchValueASCII(switches::kV),
|
||||
command_line->GetSwitchValueASCII(switches::kVModule), &g_min_log_level);
|
||||
command_line->GetSwitchValueASCII(switches::kVModule), g_min_log_level);
|
||||
}
|
||||
|
||||
// If the commandline is initialized for the current process this will
|
||||
@@ -468,13 +468,13 @@ std::string BuildCrashString(const char* file,
|
||||
const char* message_without_prefix) {
|
||||
// Only log last path component.
|
||||
if (file) {
|
||||
const char* slash = strrchr(file,
|
||||
const char* slash = UNSAFE_TODO(strrchr(file,
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
'\\'
|
||||
'\\'
|
||||
#else
|
||||
'/'
|
||||
'/'
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
);
|
||||
));
|
||||
if (slash) {
|
||||
file = UNSAFE_TODO(slash + 1);
|
||||
}
|
||||
@@ -940,8 +940,8 @@ void LogMessage::Flush() {
|
||||
static_cast<DWORD>(str_newline.length()), &num_written,
|
||||
nullptr);
|
||||
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
|
||||
std::ignore =
|
||||
fwrite(str_newline.data(), str_newline.size(), 1, g_log_file);
|
||||
std::ignore = UNSAFE_TODO(
|
||||
fwrite(str_newline.data(), str_newline.size(), 1, g_log_file));
|
||||
fflush(g_log_file);
|
||||
#else
|
||||
#error Unsupported platform
|
||||
@@ -960,11 +960,20 @@ void LogMessage::Init(const char* file, int line) {
|
||||
// Don't let actions from this method affect the system error after returning.
|
||||
base::ScopedClearLastError scoped_clear_last_error;
|
||||
|
||||
std::string_view filename(file);
|
||||
size_t last_slash_pos = filename.find_last_of("\\/");
|
||||
if (last_slash_pos != std::string_view::npos) {
|
||||
filename.remove_prefix(last_slash_pos + 1);
|
||||
}
|
||||
// Most logging initializes `file` from __FILE__. Unfortunately, because we
|
||||
// build from out/Foo we get a `../../` (or \) prefix for all of our
|
||||
// __FILE__s. This isn't true for base::Location::Current() which already does
|
||||
// the stripping (and is used for some logging, especially CHECKs).
|
||||
//
|
||||
// Here we strip the first 6 (../../ or ..\..\) characters if `file` starts
|
||||
// with `.` but defensively clamp to strlen(file) just in case.
|
||||
//
|
||||
// TODO(pbos): Consider migrating LogMessage and the LOG() macros to use
|
||||
// base::Location directly. See base/check.h for inspiration.
|
||||
const std::string_view filename =
|
||||
file[0] == '.' ? std::string_view(file).substr(
|
||||
std::min(std::size_t{6}, strlen(file)))
|
||||
: file;
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS)
|
||||
if (g_log_format == LogFormat::LOG_FORMAT_SYSLOG) {
|
||||
@@ -1018,7 +1027,7 @@ void LogMessage::Init(const char* file, int line) {
|
||||
} else {
|
||||
stream_ << "VERBOSE" << -severity_;
|
||||
}
|
||||
stream_ << ":" << filename << "(" << line << ")] ";
|
||||
stream_ << ":" << filename << ":" << line << "] ";
|
||||
}
|
||||
message_start_ = stream_.str().length();
|
||||
}
|
||||
@@ -1026,7 +1035,8 @@ void LogMessage::Init(const char* file, int line) {
|
||||
void LogMessage::HandleFatal(size_t stack_start,
|
||||
const std::string& str_newline) const {
|
||||
char str_stack[1024];
|
||||
base::strlcpy(str_stack, str_newline.data(), std::size(str_stack));
|
||||
UNSAFE_TODO(
|
||||
base::strlcpy(str_stack, str_newline.data(), std::size(str_stack)));
|
||||
base::debug::Alias(&str_stack);
|
||||
|
||||
if (!GetLogAssertHandlerStack().empty()) {
|
||||
@@ -1316,7 +1326,7 @@ VlogInfo* ScopedVmoduleSwitches::CreateVlogInfoWithSwitches(
|
||||
VlogInfo* base_vlog_info = GetVlogInfo();
|
||||
if (!base_vlog_info) {
|
||||
// Base is |nullptr|, so just create it from scratch.
|
||||
return new VlogInfo(/*v_switch_=*/"", vmodule_switch, &g_min_log_level);
|
||||
return new VlogInfo(/*v_switch_=*/"", vmodule_switch, g_min_log_level);
|
||||
}
|
||||
return base_vlog_info->WithSwitches(vmodule_switch);
|
||||
}
|
||||
|
@@ -316,7 +316,15 @@ BASE_EXPORT int GetVlogLevelHelper(const char* file_start, size_t N);
|
||||
// Gets the current vlog level for the given file (usually taken from __FILE__).
|
||||
template <size_t N>
|
||||
int GetVlogLevel(const char (&file)[N]) {
|
||||
// Disable runtime VLOG()s in official non-DCHECK builds. This saves ~135k on
|
||||
// the android-binary-size bot in crrev.com/c/6344673. Parts of the code can,
|
||||
// and do, override ENABLED_VLOG_LEVEL to collect logs in the wild. The rest
|
||||
// is dead-code stripped.
|
||||
#if defined(OFFICIAL_BUILD) && !DCHECK_IS_ON() && BUILDFLAG(IS_ANDROID)
|
||||
return -1;
|
||||
#else
|
||||
return GetVlogLevelHelper(file, N);
|
||||
#endif // defined(OFFICIAL_BUILD) && !DCHECK_IS_ON() && BUILDFLAG(IS_ANDROID)
|
||||
}
|
||||
|
||||
// Sets the common items you want to be prepended to each log message.
|
||||
|
@@ -4,19 +4,33 @@
|
||||
|
||||
#include "base/logging/rust_log_integration.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/logging/log_severity.h"
|
||||
#include "base/logging/rust_logger.rs.h"
|
||||
#include "third_party/rust/cxx/v1/cxx.h"
|
||||
|
||||
namespace logging::internal {
|
||||
|
||||
BASE_EXPORT void print_rust_log(const char* msg,
|
||||
const char* file,
|
||||
int line,
|
||||
LogSeverity severity,
|
||||
bool verbose) {
|
||||
LogMessageRustWrapper::LogMessageRustWrapper(const char* file,
|
||||
int line,
|
||||
::logging::LogSeverity severity)
|
||||
: log_message(file, line, severity) {}
|
||||
|
||||
void LogMessageRustWrapper::write_to_stream(rust::Str str) {
|
||||
log_message.stream().write(str.data(),
|
||||
static_cast<std::streamsize>(str.size()));
|
||||
}
|
||||
|
||||
void print_rust_log(const RustFmtArguments& msg,
|
||||
const char* file,
|
||||
int32_t line,
|
||||
int32_t severity,
|
||||
bool verbose) {
|
||||
// TODO(danakj): If `verbose` make the log equivalent to VLOG instead of LOG.
|
||||
logging::LogMessage log_message(file, line, severity);
|
||||
log_message.stream() << msg;
|
||||
LogMessageRustWrapper wrapper(file, line, severity);
|
||||
msg.format(wrapper);
|
||||
}
|
||||
|
||||
} // namespace logging::internal
|
||||
|
@@ -5,23 +5,46 @@
|
||||
#ifndef BASE_LOGGING_RUST_LOG_INTEGRATION_H_
|
||||
#define BASE_LOGGING_RUST_LOG_INTEGRATION_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/logging/log_severity.h"
|
||||
#include "third_party/rust/cxx/v1/cxx.h"
|
||||
|
||||
namespace logging {
|
||||
namespace internal {
|
||||
|
||||
// Opaquely wraps a Rust std::fmt::Arguments object, which can be turned into a
|
||||
// string but must be done so from a Rust stack frame with the help of
|
||||
// LogMessageRustWrapper below.
|
||||
struct RustFmtArguments;
|
||||
|
||||
// Receives a log line from Rust and forwards it to base logging, because
|
||||
// logging::LogMessage is not accessible from Rust yet with our current interop
|
||||
// tools.
|
||||
//
|
||||
// TODO(danakj): Should this helper function be replaced with C-like apis next
|
||||
// to logging::LogMessage that Rust uses more directly?
|
||||
void BASE_EXPORT print_rust_log(const char* msg,
|
||||
const char* file,
|
||||
int line,
|
||||
LogSeverity severity,
|
||||
bool verbose);
|
||||
void print_rust_log(const RustFmtArguments& msg,
|
||||
const char* file,
|
||||
int32_t line,
|
||||
int32_t severity,
|
||||
bool verbose);
|
||||
|
||||
// Wraps a LogMessage object so that Rust code can write to its ostream.
|
||||
class LogMessageRustWrapper {
|
||||
public:
|
||||
LogMessageRustWrapper(const char* file,
|
||||
int line,
|
||||
::logging::LogSeverity severity);
|
||||
|
||||
void write_to_stream(rust::Str str);
|
||||
|
||||
private:
|
||||
::logging::LogMessage log_message;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace logging
|
||||
|
@@ -4,14 +4,13 @@
|
||||
|
||||
chromium::import! {
|
||||
"//base:logging_log_severity_bindgen" as log_severity;
|
||||
"//base:logging_rust_log_integration_bindgen" as rust_log_integration;
|
||||
}
|
||||
|
||||
use log::Level::{Debug, Error, Info, Trace, Warn};
|
||||
use log::{LevelFilter, Metadata, Record};
|
||||
use log_severity::logging::{LOGGING_ERROR, LOGGING_INFO, LOGGING_WARNING};
|
||||
use rust_log_integration::logging::internal::print_rust_log;
|
||||
use std::ffi::CString;
|
||||
use std::pin::Pin;
|
||||
|
||||
struct RustLogger;
|
||||
|
||||
@@ -26,16 +25,12 @@ impl log::Log for RustLogger {
|
||||
// TODO(thiruak1024@gmail.com): Rather than using heap allocation to pass |msg|
|
||||
// and |file|, we should return a pointer and size object to leverage the
|
||||
// string_view object in C++. https://crbug.com/371112531
|
||||
let msg = match record.args().as_str() {
|
||||
Some(s) => CString::new(s),
|
||||
None => CString::new(&*record.args().to_string()),
|
||||
}
|
||||
.expect("CString::new failed to create the log message!");
|
||||
let file = CString::new(record.file().unwrap())
|
||||
.expect("CString::new failed to create the log file name!");
|
||||
let wrapped_args = RustFmtArguments(*record.args());
|
||||
unsafe {
|
||||
print_rust_log(
|
||||
msg.as_ptr(),
|
||||
ffi::print_rust_log(
|
||||
&wrapped_args,
|
||||
file.as_ptr(),
|
||||
record.line().unwrap() as i32,
|
||||
match record.metadata().level() {
|
||||
@@ -56,11 +51,54 @@ impl log::Log for RustLogger {
|
||||
|
||||
static RUST_LOGGER: RustLogger = RustLogger;
|
||||
|
||||
/// Wrap a `std::fmt::Arguments` to pass to C++ code.
|
||||
struct RustFmtArguments<'a>(std::fmt::Arguments<'a>);
|
||||
|
||||
impl<'a> RustFmtArguments<'a> {
|
||||
/// Format `msg` to the C++-provided stream in `wrapper`.
|
||||
fn format(&self, mut wrapper: Pin<&mut ffi::LogMessageRustWrapper>) {
|
||||
// No error expected because our `Write` impl below is infallible.
|
||||
std::fmt::write(&mut wrapper, self.0).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Glue impl to use std::fmt tools with `ffi::LogMessageRustWrapper`.
|
||||
impl std::fmt::Write for Pin<&mut ffi::LogMessageRustWrapper> {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> {
|
||||
self.as_mut().write_to_stream(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cxx::bridge(namespace = "logging::internal")]
|
||||
mod ffi {
|
||||
extern "Rust" {
|
||||
type RustFmtArguments<'a>;
|
||||
|
||||
fn format(&self, wrapper: Pin<&mut LogMessageRustWrapper>);
|
||||
|
||||
fn init_rust_log_crate();
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("base/logging/rust_log_integration.h");
|
||||
|
||||
/// Wraps a C++ LogMessage object so we can write to its ostream.
|
||||
type LogMessageRustWrapper;
|
||||
|
||||
/// Write a block of characters to the stream.
|
||||
fn write_to_stream(self: Pin<&mut LogMessageRustWrapper>, s: &str);
|
||||
|
||||
/// Emit a log message to the C++-managed logger. `msg` is passed back
|
||||
/// to `format_to_wrapped_message` to be stringified.
|
||||
unsafe fn print_rust_log(
|
||||
msg: &RustFmtArguments,
|
||||
file: *const c_char,
|
||||
line: i32,
|
||||
severity: i32,
|
||||
verbose: bool,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_rust_log_crate() {
|
||||
|
@@ -86,7 +86,7 @@ void LogMessage::InitWithSyslogPrefix(std::string_view filename,
|
||||
stream_ << "]";
|
||||
}
|
||||
stream_ << ": ";
|
||||
stream_ << "[" << filename << "(" << line << ")] ";
|
||||
stream_ << "[" << filename << ":" << line << "] ";
|
||||
}
|
||||
|
||||
} // namespace logging
|
||||
|
@@ -4,11 +4,12 @@
|
||||
|
||||
#include "base/mac/code_signature.h"
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "base/apple/osstatus_logging.h"
|
||||
#include "base/apple/scoped_cftyperef.h"
|
||||
#include "base/mac/info_plist_data.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "third_party/abseil-cpp/absl/types/variant.h"
|
||||
|
||||
using base::apple::ScopedCFTypeRef;
|
||||
|
||||
@@ -19,22 +20,22 @@ namespace {
|
||||
// Return a dictionary of attributes suitable for looking up `process` with
|
||||
// `SecCodeCopyGuestWithAttributes`.
|
||||
ScopedCFTypeRef<CFDictionaryRef> AttributesForGuestValidation(
|
||||
absl::variant<audit_token_t, pid_t> process,
|
||||
std::variant<audit_token_t, pid_t> process,
|
||||
SignatureValidationType validation_type,
|
||||
std::string_view info_plist_xml) {
|
||||
ScopedCFTypeRef<CFMutableDictionaryRef> attributes(
|
||||
CFDictionaryCreateMutable(nullptr, 3, &kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks));
|
||||
|
||||
if (audit_token_t* token = absl::get_if<audit_token_t>(&process)) {
|
||||
if (audit_token_t* token = std::get_if<audit_token_t>(&process)) {
|
||||
ScopedCFTypeRef<CFDataRef> audit_token_cf(CFDataCreate(
|
||||
nullptr, reinterpret_cast<const UInt8*>(token), sizeof(audit_token_t)));
|
||||
CFDictionarySetValue(attributes.get(), kSecGuestAttributeAudit,
|
||||
audit_token_cf.get());
|
||||
} else {
|
||||
CHECK(absl::holds_alternative<pid_t>(process));
|
||||
CHECK(std::holds_alternative<pid_t>(process));
|
||||
ScopedCFTypeRef<CFNumberRef> pid_cf(
|
||||
CFNumberCreate(nullptr, kCFNumberIntType, &absl::get<pid_t>(process)));
|
||||
CFNumberCreate(nullptr, kCFNumberIntType, &std::get<pid_t>(process)));
|
||||
CFDictionarySetValue(attributes.get(), kSecGuestAttributePid, pid_cf.get());
|
||||
}
|
||||
|
||||
|
@@ -8,13 +8,13 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/functional/callback_forward.h"
|
||||
#include "third_party/abseil-cpp/absl/types/variant.h"
|
||||
|
||||
// Launches an application.
|
||||
//
|
||||
@@ -43,7 +43,7 @@ using LaunchApplicationCallback =
|
||||
base::OnceCallback<void(NSRunningApplication*, NSError*)>;
|
||||
|
||||
using CommandLineArgs =
|
||||
absl::variant<absl::monostate, CommandLine, std::vector<std::string>>;
|
||||
std::variant<std::monostate, CommandLine, std::vector<std::string>>;
|
||||
|
||||
// Launches the specified application.
|
||||
// - `app_bundle_path`: the location of the application to launch
|
||||
|
@@ -4,6 +4,8 @@
|
||||
|
||||
#import "base/mac/launch_application.h"
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "base/apple/bridging.h"
|
||||
#include "base/apple/foundation_util.h"
|
||||
#include "base/command_line.h"
|
||||
@@ -34,7 +36,7 @@ void LogLaunchResult(LaunchResult result) {
|
||||
|
||||
NSArray* CommandLineArgsToArgsArray(const CommandLineArgs& command_line_args) {
|
||||
if (const CommandLine* command_line =
|
||||
absl::get_if<CommandLine>(&command_line_args)) {
|
||||
std::get_if<CommandLine>(&command_line_args)) {
|
||||
const auto& argv = command_line->argv();
|
||||
size_t argc = argv.size();
|
||||
DCHECK_GT(argc, 0lu);
|
||||
@@ -50,7 +52,7 @@ NSArray* CommandLineArgsToArgsArray(const CommandLineArgs& command_line_args) {
|
||||
}
|
||||
|
||||
if (const std::vector<std::string>* string_vector =
|
||||
absl::get_if<std::vector<std::string>>(&command_line_args)) {
|
||||
std::get_if<std::vector<std::string>>(&command_line_args)) {
|
||||
NSMutableArray* args_array =
|
||||
[NSMutableArray arrayWithCapacity:string_vector->size()];
|
||||
for (const auto& arg : *string_vector) {
|
||||
|
@@ -29,6 +29,10 @@ DiscardableMemoryAllocator* DiscardableMemoryAllocator::GetInstance() {
|
||||
return g_discardable_allocator;
|
||||
}
|
||||
|
||||
bool DiscardableMemoryAllocator::HasInstance() {
|
||||
return g_discardable_allocator != nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<base::DiscardableMemory>
|
||||
DiscardableMemoryAllocator::AllocateLockedDiscardableMemoryWithRetryOrDie(
|
||||
size_t size,
|
||||
|
@@ -29,9 +29,12 @@ class BASE_EXPORT DiscardableMemoryAllocator {
|
||||
|
||||
virtual ~DiscardableMemoryAllocator() = default;
|
||||
|
||||
// Returns the allocator instance.
|
||||
// Returns the allocator instance. Asserts if not already set.
|
||||
static DiscardableMemoryAllocator* GetInstance();
|
||||
|
||||
// Returns true if the instance has been set.
|
||||
static bool HasInstance();
|
||||
|
||||
// Sets the allocator instance. Can only be called once, e.g. on startup.
|
||||
// Ownership of |instance| remains with the caller.
|
||||
static void SetInstance(DiscardableMemoryAllocator* allocator);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user