blob: d016f83fcdb9dfa5699becc7a4907953b5e545b4 [file] [log] [blame]
// Copyright (c) 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_STARSCAN_PCSCAN_INTERNAL_H_
#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_STARSCAN_PCSCAN_INTERNAL_H_
#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <utility>
#include <vector>
#include "base/allocator/partition_allocator/partition_alloc_base/memory/scoped_refptr.h"
#include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h"
#include "base/allocator/partition_allocator/starscan/metadata_allocator.h"
#include "base/allocator/partition_allocator/starscan/pcscan.h"
#include "base/allocator/partition_allocator/starscan/starscan_fwd.h"
#include "base/allocator/partition_allocator/starscan/write_protector.h"
namespace partition_alloc::internal {
class PCScanTask;
// Internal PCScan singleton. The separation between frontend and backend is
// needed to keep access to the hot data (quarantine) in the frontend fast,
// whereas the backend can hold cold data.
class PCScanInternal final {
public:
using Root = PCScan::Root;
using TaskHandle = scoped_refptr<PCScanTask>;
using SuperPages = std::vector<uintptr_t, MetadataAllocator<uintptr_t>>;
using RootsMap =
std::unordered_map<Root*,
SuperPages,
std::hash<Root*>,
std::equal_to<>,
MetadataAllocator<std::pair<Root* const, SuperPages>>>;
static PCScanInternal& Instance() {
// Since the data that PCScanInternal holds is cold, it's fine to have the
// runtime check for thread-safe local static initialization.
static internal::base::NoDestructor<PCScanInternal> instance;
return *instance;
}
PCScanInternal(const PCScanInternal&) = delete;
PCScanInternal& operator=(const PCScanInternal&) = delete;
~PCScanInternal();
void Initialize(PCScan::InitConfig);
bool is_initialized() const { return is_initialized_; }
void PerformScan(PCScan::InvocationMode);
void PerformScanIfNeeded(PCScan::InvocationMode);
void PerformDelayedScan(base::TimeDelta delay);
void JoinScan();
TaskHandle CurrentPCScanTask() const;
void SetCurrentPCScanTask(TaskHandle task);
void ResetCurrentPCScanTask();
void RegisterScannableRoot(Root*);
void RegisterNonScannableRoot(Root*);
RootsMap& scannable_roots() { return scannable_roots_; }
const RootsMap& scannable_roots() const { return scannable_roots_; }
RootsMap& nonscannable_roots() { return nonscannable_roots_; }
const RootsMap& nonscannable_roots() const { return nonscannable_roots_; }
void RegisterNewSuperPage(Root* root, uintptr_t super_page_base);
void SetProcessName(const char* name);
const char* process_name() const { return process_name_; }
// Get size of all committed pages from scannable and nonscannable roots.
size_t CalculateTotalHeapSize() const;
SimdSupport simd_support() const { return simd_support_; }
void EnableStackScanning();
void DisableStackScanning();
bool IsStackScanningEnabled() const;
void EnableImmediateFreeing() { immediate_freeing_enabled_ = true; }
bool IsImmediateFreeingEnabled() const { return immediate_freeing_enabled_; }
void NotifyThreadCreated(void* stack_top);
void NotifyThreadDestroyed();
void* GetCurrentThreadStackTop() const;
bool WriteProtectionEnabled() const;
void ProtectPages(uintptr_t begin, size_t size);
void UnprotectPages(uintptr_t begin, size_t size);
void ClearRootsForTesting(); // IN-TEST
void ReinitForTesting(PCScan::InitConfig); // IN-TEST
void FinishScanForTesting(); // IN-TEST
void RegisterStatsReporter(partition_alloc::StatsReporter* reporter);
partition_alloc::StatsReporter& GetReporter();
private:
friend internal::base::NoDestructor<PCScanInternal>;
friend class StarScanSnapshot;
using StackTops = std::unordered_map<
internal::base::PlatformThreadId,
void*,
std::hash<internal::base::PlatformThreadId>,
std::equal_to<>,
MetadataAllocator<
std::pair<const internal::base::PlatformThreadId, void*>>>;
PCScanInternal();
TaskHandle current_task_;
mutable std::mutex current_task_mutex_;
RootsMap scannable_roots_;
RootsMap nonscannable_roots_;
mutable std::mutex roots_mutex_;
bool stack_scanning_enabled_{false};
// TLS emulation of stack tops. Since this is guaranteed to go through
// non-quarantinable partition, using it from safepoints is safe.
StackTops stack_tops_;
mutable std::mutex stack_tops_mutex_;
bool immediate_freeing_enabled_{false};
const char* process_name_ = nullptr;
const SimdSupport simd_support_;
std::unique_ptr<WriteProtector> write_protector_;
partition_alloc::StatsReporter* stats_reporter_ = nullptr;
bool is_initialized_ = false;
};
} // namespace partition_alloc::internal
#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_STARSCAN_PCSCAN_INTERNAL_H_