Commit 79a07d91 authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

cppgc-js: Allow custom OOM handling and redirect to V8 handler

Sets up custom OOM handling in cppgc and installs a handler that
redirects to V8's handler when running with unified heap.

Bug: chromium:1242180
Change-Id: I68b7038a3736cc0aa92207db2c3d129a9ff68091
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3116253
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarAnton Bikineev <bikineev@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76467}
parent 56090f1b
...@@ -2676,6 +2676,7 @@ filegroup( ...@@ -2676,6 +2676,7 @@ filegroup(
"src/heap/cppgc/page-memory.h", "src/heap/cppgc/page-memory.h",
"src/heap/cppgc/persistent-node.cc", "src/heap/cppgc/persistent-node.cc",
"src/heap/cppgc/platform.cc", "src/heap/cppgc/platform.cc",
"src/heap/cppgc/platform.h",
"src/heap/cppgc/pointer-policies.cc", "src/heap/cppgc/pointer-policies.cc",
"src/heap/cppgc/prefinalizer-handler.cc", "src/heap/cppgc/prefinalizer-handler.cc",
"src/heap/cppgc/prefinalizer-handler.h", "src/heap/cppgc/prefinalizer-handler.h",
......
...@@ -5291,6 +5291,7 @@ v8_source_set("cppgc_base") { ...@@ -5291,6 +5291,7 @@ v8_source_set("cppgc_base") {
"src/heap/cppgc/page-memory.h", "src/heap/cppgc/page-memory.h",
"src/heap/cppgc/persistent-node.cc", "src/heap/cppgc/persistent-node.cc",
"src/heap/cppgc/platform.cc", "src/heap/cppgc/platform.cc",
"src/heap/cppgc/platform.h",
"src/heap/cppgc/pointer-policies.cc", "src/heap/cppgc/pointer-policies.cc",
"src/heap/cppgc/prefinalizer-handler.cc", "src/heap/cppgc/prefinalizer-handler.cc",
"src/heap/cppgc/prefinalizer-handler.h", "src/heap/cppgc/prefinalizer-handler.h",
......
...@@ -217,6 +217,14 @@ void UnifiedHeapMarker::AddObject(void* object) { ...@@ -217,6 +217,14 @@ void UnifiedHeapMarker::AddObject(void* object) {
cppgc::internal::HeapObjectHeader::FromObject(object)); cppgc::internal::HeapObjectHeader::FromObject(object));
} }
void FatalOutOfMemoryHandlerImpl(const std::string& reason,
const SourceLocation&, HeapBase* heap) {
FatalProcessOutOfMemory(
reinterpret_cast<v8::internal::Isolate*>(
static_cast<v8::internal::CppHeap*>(heap)->isolate()),
reason.c_str());
}
} // namespace } // namespace
void CppHeap::MetricRecorderAdapter::AddMainThreadEvent( void CppHeap::MetricRecorderAdapter::AddMainThreadEvent(
...@@ -355,6 +363,7 @@ void CppHeap::AttachIsolate(Isolate* isolate) { ...@@ -355,6 +363,7 @@ void CppHeap::AttachIsolate(Isolate* isolate) {
wrapper_descriptor_); wrapper_descriptor_);
SetMetricRecorder(std::make_unique<MetricRecorderAdapter>(*this)); SetMetricRecorder(std::make_unique<MetricRecorderAdapter>(*this));
SetStackStart(base::Stack::GetStackStart()); SetStackStart(base::Stack::GetStackStart());
oom_handler().SetCustomHandler(&FatalOutOfMemoryHandlerImpl);
no_gc_scope_--; no_gc_scope_--;
} }
...@@ -376,6 +385,7 @@ void CppHeap::DetachIsolate() { ...@@ -376,6 +385,7 @@ void CppHeap::DetachIsolate() {
isolate_ = nullptr; isolate_ = nullptr;
// Any future garbage collections will ignore the V8->C++ references. // Any future garbage collections will ignore the V8->C++ references.
isolate()->SetEmbedderHeapTracer(nullptr); isolate()->SetEmbedderHeapTracer(nullptr);
oom_handler().SetCustomHandler(nullptr);
// Enter no GC scope. // Enter no GC scope.
no_gc_scope_++; no_gc_scope_++;
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "src/heap/cppgc/marking-verifier.h" #include "src/heap/cppgc/marking-verifier.h"
#include "src/heap/cppgc/object-view.h" #include "src/heap/cppgc/object-view.h"
#include "src/heap/cppgc/page-memory.h" #include "src/heap/cppgc/page-memory.h"
#include "src/heap/cppgc/platform.h"
#include "src/heap/cppgc/prefinalizer-handler.h" #include "src/heap/cppgc/prefinalizer-handler.h"
#include "src/heap/cppgc/stats-collector.h" #include "src/heap/cppgc/stats-collector.h"
...@@ -56,15 +57,18 @@ HeapBase::HeapBase( ...@@ -56,15 +57,18 @@ HeapBase::HeapBase(
StackSupport stack_support) StackSupport stack_support)
: raw_heap_(this, custom_spaces), : raw_heap_(this, custom_spaces),
platform_(std::move(platform)), platform_(std::move(platform)),
oom_handler_(std::make_unique<FatalOutOfMemoryHandler>(this)),
#if defined(LEAK_SANITIZER) #if defined(LEAK_SANITIZER)
lsan_page_allocator_(std::make_unique<v8::base::LsanPageAllocator>( lsan_page_allocator_(std::make_unique<v8::base::LsanPageAllocator>(
platform_->GetPageAllocator())), platform_->GetPageAllocator())),
#endif // LEAK_SANITIZER #endif // LEAK_SANITIZER
#if defined(CPPGC_CAGED_HEAP) #if defined(CPPGC_CAGED_HEAP)
caged_heap_(this, page_allocator()), caged_heap_(this, page_allocator()),
page_backend_(std::make_unique<PageBackend>(caged_heap_.allocator())), page_backend_(std::make_unique<PageBackend>(caged_heap_.allocator(),
*oom_handler_.get())),
#else // !CPPGC_CAGED_HEAP #else // !CPPGC_CAGED_HEAP
page_backend_(std::make_unique<PageBackend>(*page_allocator())), page_backend_(std::make_unique<PageBackend>(*page_allocator(),
*oom_handler_.get())),
#endif // !CPPGC_CAGED_HEAP #endif // !CPPGC_CAGED_HEAP
stats_collector_(std::make_unique<StatsCollector>(platform_.get())), stats_collector_(std::make_unique<StatsCollector>(platform_.get())),
stack_(std::make_unique<heap::base::Stack>( stack_(std::make_unique<heap::base::Stack>(
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "src/heap/cppgc/marker.h" #include "src/heap/cppgc/marker.h"
#include "src/heap/cppgc/metric-recorder.h" #include "src/heap/cppgc/metric-recorder.h"
#include "src/heap/cppgc/object-allocator.h" #include "src/heap/cppgc/object-allocator.h"
#include "src/heap/cppgc/platform.h"
#include "src/heap/cppgc/process-heap-statistics.h" #include "src/heap/cppgc/process-heap-statistics.h"
#include "src/heap/cppgc/process-heap.h" #include "src/heap/cppgc/process-heap.h"
#include "src/heap/cppgc/raw-heap.h" #include "src/heap/cppgc/raw-heap.h"
...@@ -65,6 +66,7 @@ namespace testing { ...@@ -65,6 +66,7 @@ namespace testing {
class TestWithHeap; class TestWithHeap;
} // namespace testing } // namespace testing
class FatalOutOfMemoryHandler;
class PageBackend; class PageBackend;
class PreFinalizerHandler; class PreFinalizerHandler;
class StatsCollector; class StatsCollector;
...@@ -95,6 +97,11 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle { ...@@ -95,6 +97,11 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
cppgc::Platform* platform() { return platform_.get(); } cppgc::Platform* platform() { return platform_.get(); }
const cppgc::Platform* platform() const { return platform_.get(); } const cppgc::Platform* platform() const { return platform_.get(); }
FatalOutOfMemoryHandler& oom_handler() { return *oom_handler_.get(); }
const FatalOutOfMemoryHandler& oom_handler() const {
return *oom_handler_.get();
}
PageBackend* page_backend() { return page_backend_.get(); } PageBackend* page_backend() { return page_backend_.get(); }
const PageBackend* page_backend() const { return page_backend_.get(); } const PageBackend* page_backend() const { return page_backend_.get(); }
...@@ -214,6 +221,7 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle { ...@@ -214,6 +221,7 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
RawHeap raw_heap_; RawHeap raw_heap_;
std::shared_ptr<cppgc::Platform> platform_; std::shared_ptr<cppgc::Platform> platform_;
std::unique_ptr<FatalOutOfMemoryHandler> oom_handler_;
#if defined(LEAK_SANITIZER) #if defined(LEAK_SANITIZER)
std::unique_ptr<v8::base::LsanPageAllocator> lsan_page_allocator_; std::unique_ptr<v8::base::LsanPageAllocator> lsan_page_allocator_;
......
...@@ -6,17 +6,21 @@ ...@@ -6,17 +6,21 @@
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/base/sanitizer/asan.h" #include "src/base/sanitizer/asan.h"
#include "src/heap/cppgc/platform.h"
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
namespace { namespace {
void Unprotect(PageAllocator& allocator, const PageMemory& page_memory) { void Unprotect(PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler,
const PageMemory& page_memory) {
if (SupportsCommittingGuardPages(allocator)) { if (SupportsCommittingGuardPages(allocator)) {
CHECK(allocator.SetPermissions(page_memory.writeable_region().base(), if (!allocator.SetPermissions(page_memory.writeable_region().base(),
page_memory.writeable_region().size(), page_memory.writeable_region().size(),
PageAllocator::Permission::kReadWrite)); PageAllocator::Permission::kReadWrite)) {
oom_handler("Oilpan: Unprotecting memory.");
}
} else { } else {
// No protection in case the allocator cannot commit at the required // No protection in case the allocator cannot commit at the required
// granularity. Only protect if the allocator supports committing at that // granularity. Only protect if the allocator supports committing at that
...@@ -25,34 +29,45 @@ void Unprotect(PageAllocator& allocator, const PageMemory& page_memory) { ...@@ -25,34 +29,45 @@ void Unprotect(PageAllocator& allocator, const PageMemory& page_memory) {
// The allocator needs to support committing the overall range. // The allocator needs to support committing the overall range.
CHECK_EQ(0u, CHECK_EQ(0u,
page_memory.overall_region().size() % allocator.CommitPageSize()); page_memory.overall_region().size() % allocator.CommitPageSize());
CHECK(allocator.SetPermissions(page_memory.overall_region().base(), if (!allocator.SetPermissions(page_memory.overall_region().base(),
page_memory.overall_region().size(), page_memory.overall_region().size(),
PageAllocator::Permission::kReadWrite)); PageAllocator::Permission::kReadWrite)) {
oom_handler("Oilpan: Unprotecting memory.");
}
} }
} }
void Protect(PageAllocator& allocator, const PageMemory& page_memory) { void Protect(PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler,
const PageMemory& page_memory) {
if (SupportsCommittingGuardPages(allocator)) { if (SupportsCommittingGuardPages(allocator)) {
// Swap the same region, providing the OS with a chance for fast lookup and // Swap the same region, providing the OS with a chance for fast lookup and
// change. // change.
CHECK(allocator.SetPermissions(page_memory.writeable_region().base(), if (!allocator.SetPermissions(page_memory.writeable_region().base(),
page_memory.writeable_region().size(), page_memory.writeable_region().size(),
PageAllocator::Permission::kNoAccess)); PageAllocator::Permission::kNoAccess)) {
oom_handler("Oilpan: Protecting memory.");
}
} else { } else {
// See Unprotect(). // See Unprotect().
CHECK_EQ(0u, CHECK_EQ(0u,
page_memory.overall_region().size() % allocator.CommitPageSize()); page_memory.overall_region().size() % allocator.CommitPageSize());
CHECK(allocator.SetPermissions(page_memory.overall_region().base(), if (!allocator.SetPermissions(page_memory.overall_region().base(),
page_memory.overall_region().size(), page_memory.overall_region().size(),
PageAllocator::Permission::kNoAccess)); PageAllocator::Permission::kNoAccess)) {
oom_handler("Oilpan: Protecting memory.");
}
} }
} }
MemoryRegion ReserveMemoryRegion(PageAllocator& allocator, MemoryRegion ReserveMemoryRegion(PageAllocator& allocator,
FatalOutOfMemoryHandler& oom_handler,
size_t allocation_size) { size_t allocation_size) {
void* region_memory = void* region_memory =
allocator.AllocatePages(nullptr, allocation_size, kPageSize, allocator.AllocatePages(nullptr, allocation_size, kPageSize,
PageAllocator::Permission::kNoAccess); PageAllocator::Permission::kNoAccess);
if (!region_memory) {
oom_handler("Oilpan: Reserving memory.");
}
const MemoryRegion reserved_region(static_cast<Address>(region_memory), const MemoryRegion reserved_region(static_cast<Address>(region_memory),
allocation_size); allocation_size);
DCHECK_EQ(reserved_region.base() + allocation_size, reserved_region.end()); DCHECK_EQ(reserved_region.base() + allocation_size, reserved_region.end());
...@@ -69,8 +84,10 @@ void FreeMemoryRegion(PageAllocator& allocator, ...@@ -69,8 +84,10 @@ void FreeMemoryRegion(PageAllocator& allocator,
} // namespace } // namespace
PageMemoryRegion::PageMemoryRegion(PageAllocator& allocator, PageMemoryRegion::PageMemoryRegion(PageAllocator& allocator,
FatalOutOfMemoryHandler& oom_handler,
MemoryRegion reserved_region, bool is_large) MemoryRegion reserved_region, bool is_large)
: allocator_(allocator), : allocator_(allocator),
oom_handler_(oom_handler),
reserved_region_(reserved_region), reserved_region_(reserved_region),
is_large_(is_large) {} is_large_(is_large) {}
...@@ -81,11 +98,13 @@ PageMemoryRegion::~PageMemoryRegion() { ...@@ -81,11 +98,13 @@ PageMemoryRegion::~PageMemoryRegion() {
// static // static
constexpr size_t NormalPageMemoryRegion::kNumPageRegions; constexpr size_t NormalPageMemoryRegion::kNumPageRegions;
NormalPageMemoryRegion::NormalPageMemoryRegion(PageAllocator& allocator) NormalPageMemoryRegion::NormalPageMemoryRegion(
PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler)
: PageMemoryRegion( : PageMemoryRegion(
allocator, allocator, oom_handler,
ReserveMemoryRegion(allocator, RoundUp(kPageSize * kNumPageRegions, ReserveMemoryRegion(allocator, oom_handler,
allocator.AllocatePageSize())), RoundUp(kPageSize * kNumPageRegions,
allocator.AllocatePageSize())),
false) { false) {
#ifdef DEBUG #ifdef DEBUG
for (size_t i = 0; i < kNumPageRegions; ++i) { for (size_t i = 0; i < kNumPageRegions; ++i) {
...@@ -99,33 +118,35 @@ NormalPageMemoryRegion::~NormalPageMemoryRegion() = default; ...@@ -99,33 +118,35 @@ NormalPageMemoryRegion::~NormalPageMemoryRegion() = default;
void NormalPageMemoryRegion::Allocate(Address writeable_base) { void NormalPageMemoryRegion::Allocate(Address writeable_base) {
const size_t index = GetIndex(writeable_base); const size_t index = GetIndex(writeable_base);
ChangeUsed(index, true); ChangeUsed(index, true);
Unprotect(allocator_, GetPageMemory(index)); Unprotect(allocator_, oom_handler_, GetPageMemory(index));
} }
void NormalPageMemoryRegion::Free(Address writeable_base) { void NormalPageMemoryRegion::Free(Address writeable_base) {
const size_t index = GetIndex(writeable_base); const size_t index = GetIndex(writeable_base);
ChangeUsed(index, false); ChangeUsed(index, false);
Protect(allocator_, GetPageMemory(index)); Protect(allocator_, oom_handler_, GetPageMemory(index));
} }
void NormalPageMemoryRegion::UnprotectForTesting() { void NormalPageMemoryRegion::UnprotectForTesting() {
for (size_t i = 0; i < kNumPageRegions; ++i) { for (size_t i = 0; i < kNumPageRegions; ++i) {
Unprotect(allocator_, GetPageMemory(i)); Unprotect(allocator_, oom_handler_, GetPageMemory(i));
} }
} }
LargePageMemoryRegion::LargePageMemoryRegion(PageAllocator& allocator, LargePageMemoryRegion::LargePageMemoryRegion(
size_t length) PageAllocator& allocator, FatalOutOfMemoryHandler& oom_handler,
size_t length)
: PageMemoryRegion( : PageMemoryRegion(
allocator, allocator, oom_handler,
ReserveMemoryRegion(allocator, RoundUp(length + 2 * kGuardPageSize, ReserveMemoryRegion(allocator, oom_handler,
allocator.AllocatePageSize())), RoundUp(length + 2 * kGuardPageSize,
allocator.AllocatePageSize())),
true) {} true) {}
LargePageMemoryRegion::~LargePageMemoryRegion() = default; LargePageMemoryRegion::~LargePageMemoryRegion() = default;
void LargePageMemoryRegion::UnprotectForTesting() { void LargePageMemoryRegion::UnprotectForTesting() {
Unprotect(allocator_, GetPageMemory()); Unprotect(allocator_, oom_handler_, GetPageMemory());
} }
PageMemoryRegionTree::PageMemoryRegionTree() = default; PageMemoryRegionTree::PageMemoryRegionTree() = default;
...@@ -165,14 +186,17 @@ std::pair<NormalPageMemoryRegion*, Address> NormalPageMemoryPool::Take( ...@@ -165,14 +186,17 @@ std::pair<NormalPageMemoryRegion*, Address> NormalPageMemoryPool::Take(
return pair; return pair;
} }
PageBackend::PageBackend(PageAllocator& allocator) : allocator_(allocator) {} PageBackend::PageBackend(PageAllocator& allocator,
FatalOutOfMemoryHandler& oom_handler)
: allocator_(allocator), oom_handler_(oom_handler) {}
PageBackend::~PageBackend() = default; PageBackend::~PageBackend() = default;
Address PageBackend::AllocateNormalPageMemory(size_t bucket) { Address PageBackend::AllocateNormalPageMemory(size_t bucket) {
std::pair<NormalPageMemoryRegion*, Address> result = page_pool_.Take(bucket); std::pair<NormalPageMemoryRegion*, Address> result = page_pool_.Take(bucket);
if (!result.first) { if (!result.first) {
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator_); auto pmr =
std::make_unique<NormalPageMemoryRegion>(allocator_, oom_handler_);
for (size_t i = 0; i < NormalPageMemoryRegion::kNumPageRegions; ++i) { for (size_t i = 0; i < NormalPageMemoryRegion::kNumPageRegions; ++i) {
page_pool_.Add(bucket, pmr.get(), page_pool_.Add(bucket, pmr.get(),
pmr->GetPageMemory(i).writeable_region().base()); pmr->GetPageMemory(i).writeable_region().base());
...@@ -193,9 +217,10 @@ void PageBackend::FreeNormalPageMemory(size_t bucket, Address writeable_base) { ...@@ -193,9 +217,10 @@ void PageBackend::FreeNormalPageMemory(size_t bucket, Address writeable_base) {
} }
Address PageBackend::AllocateLargePageMemory(size_t size) { Address PageBackend::AllocateLargePageMemory(size_t size) {
auto pmr = std::make_unique<LargePageMemoryRegion>(allocator_, size); auto pmr =
std::make_unique<LargePageMemoryRegion>(allocator_, oom_handler_, size);
const PageMemory pm = pmr->GetPageMemory(); const PageMemory pm = pmr->GetPageMemory();
Unprotect(allocator_, pm); Unprotect(allocator_, oom_handler_, pm);
page_memory_region_tree_.Add(pmr.get()); page_memory_region_tree_.Add(pmr.get());
large_page_memory_regions_.insert(std::make_pair(pmr.get(), std::move(pmr))); large_page_memory_regions_.insert(std::make_pair(pmr.get(), std::move(pmr)));
return pm.writeable_region().base(); return pm.writeable_region().base();
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
class FatalOutOfMemoryHandler;
class V8_EXPORT_PRIVATE MemoryRegion final { class V8_EXPORT_PRIVATE MemoryRegion final {
public: public:
MemoryRegion() = default; MemoryRegion() = default;
...@@ -79,9 +81,11 @@ class V8_EXPORT_PRIVATE PageMemoryRegion { ...@@ -79,9 +81,11 @@ class V8_EXPORT_PRIVATE PageMemoryRegion {
virtual void UnprotectForTesting() = 0; virtual void UnprotectForTesting() = 0;
protected: protected:
PageMemoryRegion(PageAllocator&, MemoryRegion, bool); PageMemoryRegion(PageAllocator&, FatalOutOfMemoryHandler&, MemoryRegion,
bool);
PageAllocator& allocator_; PageAllocator& allocator_;
FatalOutOfMemoryHandler& oom_handler_;
const MemoryRegion reserved_region_; const MemoryRegion reserved_region_;
const bool is_large_; const bool is_large_;
}; };
...@@ -91,7 +95,7 @@ class V8_EXPORT_PRIVATE NormalPageMemoryRegion final : public PageMemoryRegion { ...@@ -91,7 +95,7 @@ class V8_EXPORT_PRIVATE NormalPageMemoryRegion final : public PageMemoryRegion {
public: public:
static constexpr size_t kNumPageRegions = 10; static constexpr size_t kNumPageRegions = 10;
explicit NormalPageMemoryRegion(PageAllocator&); NormalPageMemoryRegion(PageAllocator&, FatalOutOfMemoryHandler&);
~NormalPageMemoryRegion() override; ~NormalPageMemoryRegion() override;
const PageMemory GetPageMemory(size_t index) const { const PageMemory GetPageMemory(size_t index) const {
...@@ -133,7 +137,7 @@ class V8_EXPORT_PRIVATE NormalPageMemoryRegion final : public PageMemoryRegion { ...@@ -133,7 +137,7 @@ class V8_EXPORT_PRIVATE NormalPageMemoryRegion final : public PageMemoryRegion {
// LargePageMemoryRegion serves a single large PageMemory object. // LargePageMemoryRegion serves a single large PageMemory object.
class V8_EXPORT_PRIVATE LargePageMemoryRegion final : public PageMemoryRegion { class V8_EXPORT_PRIVATE LargePageMemoryRegion final : public PageMemoryRegion {
public: public:
LargePageMemoryRegion(PageAllocator&, size_t); LargePageMemoryRegion(PageAllocator&, FatalOutOfMemoryHandler&, size_t);
~LargePageMemoryRegion() override; ~LargePageMemoryRegion() override;
const PageMemory GetPageMemory() const { const PageMemory GetPageMemory() const {
...@@ -193,7 +197,7 @@ class V8_EXPORT_PRIVATE NormalPageMemoryPool final { ...@@ -193,7 +197,7 @@ class V8_EXPORT_PRIVATE NormalPageMemoryPool final {
// regions alive. // regions alive.
class V8_EXPORT_PRIVATE PageBackend final { class V8_EXPORT_PRIVATE PageBackend final {
public: public:
explicit PageBackend(PageAllocator&); PageBackend(PageAllocator&, FatalOutOfMemoryHandler&);
~PageBackend(); ~PageBackend();
// Allocates a normal page from the backend. // Allocates a normal page from the backend.
...@@ -224,6 +228,7 @@ class V8_EXPORT_PRIVATE PageBackend final { ...@@ -224,6 +228,7 @@ class V8_EXPORT_PRIVATE PageBackend final {
private: private:
PageAllocator& allocator_; PageAllocator& allocator_;
FatalOutOfMemoryHandler& oom_handler_;
NormalPageMemoryPool page_pool_; NormalPageMemoryPool page_pool_;
PageMemoryRegionTree page_memory_region_tree_; PageMemoryRegionTree page_memory_region_tree_;
std::vector<std::unique_ptr<PageMemoryRegion>> normal_page_memory_regions_; std::vector<std::unique_ptr<PageMemoryRegion>> normal_page_memory_regions_;
......
...@@ -5,10 +5,35 @@ ...@@ -5,10 +5,35 @@
#include "include/cppgc/platform.h" #include "include/cppgc/platform.h"
#include "src/base/lazy-instance.h" #include "src/base/lazy-instance.h"
#include "src/base/logging.h"
#include "src/base/platform/platform.h" #include "src/base/platform/platform.h"
#include "src/heap/cppgc/gc-info-table.h" #include "src/heap/cppgc/gc-info-table.h"
#include "src/heap/cppgc/platform.h"
namespace cppgc { namespace cppgc {
namespace internal {
void Abort() { v8::base::OS::Abort(); }
void FatalOutOfMemoryHandler::operator()(const std::string& reason,
const SourceLocation& loc) const {
if (custom_handler_) {
(*custom_handler_)(reason, loc, heap_);
FATAL("Custom out of memory handler should not have returned");
}
#ifdef DEBUG
V8_Fatal(loc.FileName(), static_cast<int>(loc.Line()),
"Oilpan: Out of memory (%s)", reason.c_str());
#else // !DEBUG
V8_Fatal("Oilpan: Out of memory");
#endif // !DEBUG
}
void FatalOutOfMemoryHandler::SetCustomHandler(Callback* callback) {
custom_handler_ = callback;
}
} // namespace internal
namespace { namespace {
PageAllocator* g_page_allocator = nullptr; PageAllocator* g_page_allocator = nullptr;
...@@ -27,9 +52,4 @@ void InitializeProcess(PageAllocator* page_allocator) { ...@@ -27,9 +52,4 @@ void InitializeProcess(PageAllocator* page_allocator) {
void ShutdownProcess() { g_page_allocator = nullptr; } void ShutdownProcess() { g_page_allocator = nullptr; }
namespace internal {
void Abort() { v8::base::OS::Abort(); }
} // namespace internal
} // namespace cppgc } // namespace cppgc
// Copyright 2021 the V8 project 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 V8_HEAP_CPPGC_PLATFORM_H_
#define V8_HEAP_CPPGC_PLATFORM_H_
#include <string>
#include "include/cppgc/source-location.h"
#include "src/base/macros.h"
namespace cppgc {
namespace internal {
class HeapBase;
class V8_EXPORT_PRIVATE FatalOutOfMemoryHandler final {
public:
using Callback = void(const std::string&, const SourceLocation&, HeapBase*);
FatalOutOfMemoryHandler() = default;
explicit FatalOutOfMemoryHandler(HeapBase* heap) : heap_(heap) {}
[[noreturn]] void operator()(
const std::string& reason = std::string(),
const SourceLocation& = SourceLocation::Current()) const;
void SetCustomHandler(Callback*);
// Disallow copy/move.
FatalOutOfMemoryHandler(const FatalOutOfMemoryHandler&) = delete;
FatalOutOfMemoryHandler& operator=(const FatalOutOfMemoryHandler&) = delete;
private:
HeapBase* heap_ = nullptr;
Callback* custom_handler_ = nullptr;
};
} // namespace internal
} // namespace cppgc
#endif // V8_HEAP_CPPGC_PLATFORM_H_
...@@ -119,6 +119,7 @@ v8_source_set("cppgc_unittests_sources") { ...@@ -119,6 +119,7 @@ v8_source_set("cppgc_unittests_sources") {
"heap/cppgc/object-start-bitmap-unittest.cc", "heap/cppgc/object-start-bitmap-unittest.cc",
"heap/cppgc/page-memory-unittest.cc", "heap/cppgc/page-memory-unittest.cc",
"heap/cppgc/persistent-family-unittest.cc", "heap/cppgc/persistent-family-unittest.cc",
"heap/cppgc/platform-unittest.cc",
"heap/cppgc/prefinalizer-unittest.cc", "heap/cppgc/prefinalizer-unittest.cc",
"heap/cppgc/sanitizer-unittest.cc", "heap/cppgc/sanitizer-unittest.cc",
"heap/cppgc/source-location-unittest.cc", "heap/cppgc/source-location-unittest.cc",
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "src/heap/cppgc/page-memory.h" #include "src/heap/cppgc/page-memory.h"
#include "src/base/page-allocator.h" #include "src/base/page-allocator.h"
#include "src/heap/cppgc/platform.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace cppgc { namespace cppgc {
...@@ -77,7 +78,8 @@ TEST(PageMemoryDeathTest, ConstructNonContainedRegions) { ...@@ -77,7 +78,8 @@ TEST(PageMemoryDeathTest, ConstructNonContainedRegions) {
TEST(PageMemoryRegionTest, NormalPageMemoryRegion) { TEST(PageMemoryRegionTest, NormalPageMemoryRegion) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator); FatalOutOfMemoryHandler oom_handler;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator, oom_handler);
pmr->UnprotectForTesting(); pmr->UnprotectForTesting();
MemoryRegion prev_overall; MemoryRegion prev_overall;
for (size_t i = 0; i < NormalPageMemoryRegion::kNumPageRegions; ++i) { for (size_t i = 0; i < NormalPageMemoryRegion::kNumPageRegions; ++i) {
...@@ -103,7 +105,9 @@ TEST(PageMemoryRegionTest, NormalPageMemoryRegion) { ...@@ -103,7 +105,9 @@ TEST(PageMemoryRegionTest, NormalPageMemoryRegion) {
TEST(PageMemoryRegionTest, LargePageMemoryRegion) { TEST(PageMemoryRegionTest, LargePageMemoryRegion) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
auto pmr = std::make_unique<LargePageMemoryRegion>(allocator, 1024); FatalOutOfMemoryHandler oom_handler;
auto pmr =
std::make_unique<LargePageMemoryRegion>(allocator, oom_handler, 1024);
pmr->UnprotectForTesting(); pmr->UnprotectForTesting();
const PageMemory pm = pmr->GetPageMemory(); const PageMemory pm = pmr->GetPageMemory();
EXPECT_LE(1024u, pm.writeable_region().size()); EXPECT_LE(1024u, pm.writeable_region().size());
...@@ -140,8 +144,10 @@ TEST(PageMemoryRegionDeathTest, ReservationIsFreed) { ...@@ -140,8 +144,10 @@ TEST(PageMemoryRegionDeathTest, ReservationIsFreed) {
// may expand to statements that re-purpose the previously freed memory // may expand to statements that re-purpose the previously freed memory
// and thus not crash. // and thus not crash.
EXPECT_DEATH_IF_SUPPORTED( EXPECT_DEATH_IF_SUPPORTED(
v8::base::PageAllocator allocator; Address base; { v8::base::PageAllocator allocator; FatalOutOfMemoryHandler oom_handler;
auto pmr = std::make_unique<LargePageMemoryRegion>(allocator, 1024); Address base; {
auto pmr = std::make_unique<LargePageMemoryRegion>(allocator,
oom_handler, 1024);
base = pmr->reserved_region().base(); base = pmr->reserved_region().base();
} access(base[0]); } access(base[0]);
, ""); , "");
...@@ -149,7 +155,8 @@ TEST(PageMemoryRegionDeathTest, ReservationIsFreed) { ...@@ -149,7 +155,8 @@ TEST(PageMemoryRegionDeathTest, ReservationIsFreed) {
TEST(PageMemoryRegionDeathTest, FrontGuardPageAccessCrashes) { TEST(PageMemoryRegionDeathTest, FrontGuardPageAccessCrashes) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator); FatalOutOfMemoryHandler oom_handler;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator, oom_handler);
if (SupportsCommittingGuardPages(allocator)) { if (SupportsCommittingGuardPages(allocator)) {
EXPECT_DEATH_IF_SUPPORTED( EXPECT_DEATH_IF_SUPPORTED(
access(pmr->GetPageMemory(0).overall_region().base()[0]), ""); access(pmr->GetPageMemory(0).overall_region().base()[0]), "");
...@@ -158,7 +165,8 @@ TEST(PageMemoryRegionDeathTest, FrontGuardPageAccessCrashes) { ...@@ -158,7 +165,8 @@ TEST(PageMemoryRegionDeathTest, FrontGuardPageAccessCrashes) {
TEST(PageMemoryRegionDeathTest, BackGuardPageAccessCrashes) { TEST(PageMemoryRegionDeathTest, BackGuardPageAccessCrashes) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator); FatalOutOfMemoryHandler oom_handler;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator, oom_handler);
if (SupportsCommittingGuardPages(allocator)) { if (SupportsCommittingGuardPages(allocator)) {
EXPECT_DEATH_IF_SUPPORTED( EXPECT_DEATH_IF_SUPPORTED(
access(pmr->GetPageMemory(0).writeable_region().end()[0]), ""); access(pmr->GetPageMemory(0).writeable_region().end()[0]), "");
...@@ -167,7 +175,8 @@ TEST(PageMemoryRegionDeathTest, BackGuardPageAccessCrashes) { ...@@ -167,7 +175,8 @@ TEST(PageMemoryRegionDeathTest, BackGuardPageAccessCrashes) {
TEST(PageMemoryRegionTreeTest, AddNormalLookupRemove) { TEST(PageMemoryRegionTreeTest, AddNormalLookupRemove) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator); FatalOutOfMemoryHandler oom_handler;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator, oom_handler);
PageMemoryRegionTree tree; PageMemoryRegionTree tree;
tree.Add(pmr.get()); tree.Add(pmr.get());
ASSERT_EQ(pmr.get(), tree.Lookup(pmr->reserved_region().base())); ASSERT_EQ(pmr.get(), tree.Lookup(pmr->reserved_region().base()));
...@@ -181,8 +190,10 @@ TEST(PageMemoryRegionTreeTest, AddNormalLookupRemove) { ...@@ -181,8 +190,10 @@ TEST(PageMemoryRegionTreeTest, AddNormalLookupRemove) {
TEST(PageMemoryRegionTreeTest, AddLargeLookupRemove) { TEST(PageMemoryRegionTreeTest, AddLargeLookupRemove) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
FatalOutOfMemoryHandler oom_handler;
constexpr size_t kLargeSize = 5012; constexpr size_t kLargeSize = 5012;
auto pmr = std::make_unique<LargePageMemoryRegion>(allocator, kLargeSize); auto pmr = std::make_unique<LargePageMemoryRegion>(allocator, oom_handler,
kLargeSize);
PageMemoryRegionTree tree; PageMemoryRegionTree tree;
tree.Add(pmr.get()); tree.Add(pmr.get());
ASSERT_EQ(pmr.get(), tree.Lookup(pmr->reserved_region().base())); ASSERT_EQ(pmr.get(), tree.Lookup(pmr->reserved_region().base()));
...@@ -196,9 +207,11 @@ TEST(PageMemoryRegionTreeTest, AddLargeLookupRemove) { ...@@ -196,9 +207,11 @@ TEST(PageMemoryRegionTreeTest, AddLargeLookupRemove) {
TEST(PageMemoryRegionTreeTest, AddLookupRemoveMultiple) { TEST(PageMemoryRegionTreeTest, AddLookupRemoveMultiple) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
auto pmr1 = std::make_unique<NormalPageMemoryRegion>(allocator); FatalOutOfMemoryHandler oom_handler;
auto pmr1 = std::make_unique<NormalPageMemoryRegion>(allocator, oom_handler);
constexpr size_t kLargeSize = 3127; constexpr size_t kLargeSize = 3127;
auto pmr2 = std::make_unique<LargePageMemoryRegion>(allocator, kLargeSize); auto pmr2 = std::make_unique<LargePageMemoryRegion>(allocator, oom_handler,
kLargeSize);
PageMemoryRegionTree tree; PageMemoryRegionTree tree;
tree.Add(pmr1.get()); tree.Add(pmr1.get());
tree.Add(pmr2.get()); tree.Add(pmr2.get());
...@@ -223,7 +236,8 @@ TEST(NormalPageMemoryPool, ConstructorEmpty) { ...@@ -223,7 +236,8 @@ TEST(NormalPageMemoryPool, ConstructorEmpty) {
TEST(NormalPageMemoryPool, AddTakeSameBucket) { TEST(NormalPageMemoryPool, AddTakeSameBucket) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator); FatalOutOfMemoryHandler oom_handler;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator, oom_handler);
const PageMemory pm = pmr->GetPageMemory(0); const PageMemory pm = pmr->GetPageMemory(0);
NormalPageMemoryPool pool; NormalPageMemoryPool pool;
constexpr size_t kBucket = 0; constexpr size_t kBucket = 0;
...@@ -235,7 +249,8 @@ TEST(NormalPageMemoryPool, AddTakeSameBucket) { ...@@ -235,7 +249,8 @@ TEST(NormalPageMemoryPool, AddTakeSameBucket) {
TEST(NormalPageMemoryPool, AddTakeNotFoundDifferentBucket) { TEST(NormalPageMemoryPool, AddTakeNotFoundDifferentBucket) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator); FatalOutOfMemoryHandler oom_handler;
auto pmr = std::make_unique<NormalPageMemoryRegion>(allocator, oom_handler);
const PageMemory pm = pmr->GetPageMemory(0); const PageMemory pm = pmr->GetPageMemory(0);
NormalPageMemoryPool pool; NormalPageMemoryPool pool;
constexpr size_t kFirstBucket = 0; constexpr size_t kFirstBucket = 0;
...@@ -250,7 +265,8 @@ TEST(NormalPageMemoryPool, AddTakeNotFoundDifferentBucket) { ...@@ -250,7 +265,8 @@ TEST(NormalPageMemoryPool, AddTakeNotFoundDifferentBucket) {
TEST(PageBackendTest, AllocateNormalUsesPool) { TEST(PageBackendTest, AllocateNormalUsesPool) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
PageBackend backend(allocator); FatalOutOfMemoryHandler oom_handler;
PageBackend backend(allocator, oom_handler);
constexpr size_t kBucket = 0; constexpr size_t kBucket = 0;
Address writeable_base1 = backend.AllocateNormalPageMemory(kBucket); Address writeable_base1 = backend.AllocateNormalPageMemory(kBucket);
EXPECT_NE(nullptr, writeable_base1); EXPECT_NE(nullptr, writeable_base1);
...@@ -262,7 +278,8 @@ TEST(PageBackendTest, AllocateNormalUsesPool) { ...@@ -262,7 +278,8 @@ TEST(PageBackendTest, AllocateNormalUsesPool) {
TEST(PageBackendTest, AllocateLarge) { TEST(PageBackendTest, AllocateLarge) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
PageBackend backend(allocator); FatalOutOfMemoryHandler oom_handler;
PageBackend backend(allocator, oom_handler);
Address writeable_base1 = backend.AllocateLargePageMemory(13731); Address writeable_base1 = backend.AllocateLargePageMemory(13731);
EXPECT_NE(nullptr, writeable_base1); EXPECT_NE(nullptr, writeable_base1);
Address writeable_base2 = backend.AllocateLargePageMemory(9478); Address writeable_base2 = backend.AllocateLargePageMemory(9478);
...@@ -274,7 +291,8 @@ TEST(PageBackendTest, AllocateLarge) { ...@@ -274,7 +291,8 @@ TEST(PageBackendTest, AllocateLarge) {
TEST(PageBackendTest, LookupNormal) { TEST(PageBackendTest, LookupNormal) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
PageBackend backend(allocator); FatalOutOfMemoryHandler oom_handler;
PageBackend backend(allocator, oom_handler);
constexpr size_t kBucket = 0; constexpr size_t kBucket = 0;
Address writeable_base = backend.AllocateNormalPageMemory(kBucket); Address writeable_base = backend.AllocateNormalPageMemory(kBucket);
EXPECT_EQ(nullptr, backend.Lookup(writeable_base - kGuardPageSize)); EXPECT_EQ(nullptr, backend.Lookup(writeable_base - kGuardPageSize));
...@@ -290,7 +308,8 @@ TEST(PageBackendTest, LookupNormal) { ...@@ -290,7 +308,8 @@ TEST(PageBackendTest, LookupNormal) {
TEST(PageBackendTest, LookupLarge) { TEST(PageBackendTest, LookupLarge) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
PageBackend backend(allocator); FatalOutOfMemoryHandler oom_handler;
PageBackend backend(allocator, oom_handler);
constexpr size_t kSize = 7934; constexpr size_t kSize = 7934;
Address writeable_base = backend.AllocateLargePageMemory(kSize); Address writeable_base = backend.AllocateLargePageMemory(kSize);
EXPECT_EQ(nullptr, backend.Lookup(writeable_base - kGuardPageSize)); EXPECT_EQ(nullptr, backend.Lookup(writeable_base - kGuardPageSize));
...@@ -301,9 +320,10 @@ TEST(PageBackendTest, LookupLarge) { ...@@ -301,9 +320,10 @@ TEST(PageBackendTest, LookupLarge) {
TEST(PageBackendDeathTest, DestructingBackendDestroysPageMemory) { TEST(PageBackendDeathTest, DestructingBackendDestroysPageMemory) {
v8::base::PageAllocator allocator; v8::base::PageAllocator allocator;
FatalOutOfMemoryHandler oom_handler;
Address base; Address base;
{ {
PageBackend backend(allocator); PageBackend backend(allocator, oom_handler);
constexpr size_t kBucket = 0; constexpr size_t kBucket = 0;
base = backend.AllocateNormalPageMemory(kBucket); base = backend.AllocateNormalPageMemory(kBucket);
} }
......
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/heap/cppgc/platform.h"
#include "src/base/logging.h"
#include "src/base/page-allocator.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
TEST(FatalOutOfMemoryHandlerDeathTest, DefaultHandlerCrashes) {
FatalOutOfMemoryHandler handler;
EXPECT_DEATH_IF_SUPPORTED(handler(), "");
}
namespace {
constexpr uintptr_t kHeapNeedle = 0x14;
[[noreturn]] void CustomHandler(const std::string&, const SourceLocation&,
HeapBase* heap) {
if (heap == reinterpret_cast<HeapBase*>(kHeapNeedle)) {
FATAL("cust0m h4ndl3r with matching heap");
}
FATAL("cust0m h4ndl3r");
}
} // namespace
TEST(FatalOutOfMemoryHandlerDeathTest, CustomHandlerCrashes) {
FatalOutOfMemoryHandler handler;
handler.SetCustomHandler(&CustomHandler);
EXPECT_DEATH_IF_SUPPORTED(handler(), "cust0m h4ndl3r");
}
TEST(FatalOutOfMemoryHandlerDeathTest, CustomHandlerWithHeapState) {
FatalOutOfMemoryHandler handler(reinterpret_cast<HeapBase*>(kHeapNeedle));
handler.SetCustomHandler(&CustomHandler);
EXPECT_DEATH_IF_SUPPORTED(handler(), "cust0m h4ndl3r with matching heap");
}
} // namespace internal
} // namespace cppgc
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment