Update On Tue Apr 29 20:34:58 CEST 2025

This commit is contained in:
github-action[bot]
2025-04-29 20:34:58 +02:00
parent 21d70bf50e
commit 440b723146
2694 changed files with 151554 additions and 74170 deletions

View File

@@ -1 +1 @@
135.0.7049.38
136.0.7103.44

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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") {

View File

@@ -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

View File

@@ -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

View File

@@ -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_

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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) {}

View File

@@ -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) || \

View File

@@ -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);

View File

@@ -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))

View File

@@ -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() {

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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),

View File

@@ -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),

View File

@@ -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,
};

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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_;

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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" ||

View File

@@ -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(&regions, 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, &regions_)) {
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, &regions)) {
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);
}

View File

@@ -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());

View File

@@ -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;

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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));

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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());

View File

@@ -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) \

View File

@@ -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.

View File

@@ -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

View File

@@ -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_); }

View File

@@ -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(); }

View File

@@ -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});

View File

@@ -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_

View File

@@ -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"

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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",

View File

@@ -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.

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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));

View File

@@ -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:

View File

@@ -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"; },

View File

@@ -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

View File

@@ -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_

View File

@@ -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.

View File

@@ -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.

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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() {

View File

@@ -86,7 +86,7 @@ void LogMessage::InitWithSyslogPrefix(std::string_view filename,
stream_ << "]";
}
stream_ << ": ";
stream_ << "[" << filename << "(" << line << ")] ";
stream_ << "[" << filename << ":" << line << "] ";
}
} // namespace logging

View File

@@ -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());
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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