Commit 16b58424 authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

[cppgc] Use global OOM handler for GCInfoTable

GCInfoTable is a process-global table storing Oilpan type information.
Table operations may fail in OOM scenarios which were previously just
caught in regular CHECKs. Change to use a global OOM handler that is set
up to use V8's handler.

Bug: chromium:1283199
Change-Id: Id33263ef7cd4028d60a071f5ab3b165e59ac9593
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3745368Reviewed-by: 's avatarAnton Bikineev <bikineev@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81540}
parent 32e6b48f
...@@ -240,6 +240,11 @@ void FatalOutOfMemoryHandlerImpl(const std::string& reason, ...@@ -240,6 +240,11 @@ void FatalOutOfMemoryHandlerImpl(const std::string& reason,
static_cast<v8::internal::CppHeap*>(heap)->isolate(), reason.c_str()); static_cast<v8::internal::CppHeap*>(heap)->isolate(), reason.c_str());
} }
void GlobalFatalOutOfMemoryHandlerImpl(const std::string& reason,
const SourceLocation&, HeapBase* heap) {
V8::FatalProcessOutOfMemory(nullptr, reason.c_str());
}
} // namespace } // namespace
class UnifiedHeapMarker final : public cppgc::internal::MarkerBase { class UnifiedHeapMarker final : public cppgc::internal::MarkerBase {
...@@ -428,6 +433,12 @@ v8::metrics::Recorder::ContextId CppHeap::MetricRecorderAdapter::GetContextId() ...@@ -428,6 +433,12 @@ v8::metrics::Recorder::ContextId CppHeap::MetricRecorderAdapter::GetContextId()
GetIsolate()->native_context()); GetIsolate()->native_context());
} }
// static
void CppHeap::InitializeOncePerProcess() {
cppgc::internal::GetGlobalOOMHandler().SetCustomHandler(
&GlobalFatalOutOfMemoryHandlerImpl);
}
CppHeap::CppHeap( CppHeap::CppHeap(
v8::Platform* platform, v8::Platform* platform,
const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces, const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces,
......
...@@ -101,6 +101,8 @@ class V8_EXPORT_PRIVATE CppHeap final ...@@ -101,6 +101,8 @@ class V8_EXPORT_PRIVATE CppHeap final
pause_scope_; pause_scope_;
}; };
static void InitializeOncePerProcess();
static CppHeap* From(v8::CppHeap* heap) { static CppHeap* From(v8::CppHeap* heap) {
return static_cast<CppHeap*>(heap); return static_cast<CppHeap*>(heap);
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <map> #include <map>
#include "src/heap/cppgc/platform.h"
#include "v8config.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory)
#if !defined(CPPGC_CAGED_HEAP) #if !defined(CPPGC_CAGED_HEAP)
...@@ -73,8 +74,7 @@ VirtualMemory ReserveCagedHeap(PageAllocator& platform_allocator) { ...@@ -73,8 +74,7 @@ VirtualMemory ReserveCagedHeap(PageAllocator& platform_allocator) {
if (memory.IsReserved()) return memory; if (memory.IsReserved()) return memory;
} }
FATAL("Fatal process out of memory: Failed to reserve memory for caged heap"); GetGlobalOOMHandler()("Oilpan: CagedHeap reservation.");
UNREACHABLE();
} }
} // namespace } // namespace
...@@ -111,12 +111,13 @@ CagedHeap::CagedHeap(PageAllocator& platform_allocator) ...@@ -111,12 +111,13 @@ CagedHeap::CagedHeap(PageAllocator& platform_allocator)
CageBaseGlobalUpdater::UpdateCageBase(CagedHeapBase::g_heap_base_); CageBaseGlobalUpdater::UpdateCageBase(CagedHeapBase::g_heap_base_);
#endif // defined(CPPGC_POINTER_COMPRESSION) #endif // defined(CPPGC_POINTER_COMPRESSION)
const bool is_not_oom = platform_allocator.SetPermissions( if (!platform_allocator.SetPermissions(
cage_start, cage_start,
RoundUp(sizeof(CagedHeapLocalData), platform_allocator.CommitPageSize()), RoundUp(sizeof(CagedHeapLocalData),
PageAllocator::kReadWrite); platform_allocator.CommitPageSize()),
// Failing to commit the reservation means that we are out of memory. PageAllocator::kReadWrite)) {
CHECK(is_not_oom); GetGlobalOOMHandler()("Oilpan: CagedHeap commit CageHeapLocalData.");
}
const CagedAddress caged_heap_start = RoundUp( const CagedAddress caged_heap_start = RoundUp(
reinterpret_cast<CagedAddress>(cage_start) + sizeof(CagedHeapLocalData), reinterpret_cast<CagedAddress>(cage_start) + sizeof(CagedHeapLocalData),
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "src/base/bits.h" #include "src/base/bits.h"
#include "src/base/lazy-instance.h" #include "src/base/lazy-instance.h"
#include "src/base/page-allocator.h" #include "src/base/page-allocator.h"
#include "src/heap/cppgc/platform.h"
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
...@@ -32,38 +33,43 @@ static_assert(v8::base::bits::IsPowerOfTwo(kEntrySize), ...@@ -32,38 +33,43 @@ static_assert(v8::base::bits::IsPowerOfTwo(kEntrySize),
} // namespace } // namespace
GCInfoTable* GlobalGCInfoTable::global_table_ = nullptr; GCInfoTable* GlobalGCInfoTable::global_table_ = nullptr;
constexpr GCInfoIndex GCInfoTable::kMaxIndex; constexpr GCInfoIndex GCInfoTable::kMaxIndex;
constexpr GCInfoIndex GCInfoTable::kMinIndex; constexpr GCInfoIndex GCInfoTable::kMinIndex;
constexpr GCInfoIndex GCInfoTable::kInitialWantedLimit; constexpr GCInfoIndex GCInfoTable::kInitialWantedLimit;
// static // static
void GlobalGCInfoTable::Initialize(PageAllocator* page_allocator) { void GlobalGCInfoTable::Initialize(PageAllocator& page_allocator) {
DCHECK_NOT_NULL(page_allocator); static v8::base::LeakyObject<GCInfoTable> table(page_allocator,
static v8::base::LeakyObject<GCInfoTable> table(page_allocator); GetGlobalOOMHandler());
if (!global_table_) { if (!global_table_) {
global_table_ = table.get(); global_table_ = table.get();
} else { } else {
CHECK_EQ(page_allocator, global_table_->allocator()); CHECK_EQ(&page_allocator, &global_table_->allocator());
} }
} }
GCInfoTable::GCInfoTable(PageAllocator* page_allocator) GCInfoTable::GCInfoTable(PageAllocator& page_allocator,
FatalOutOfMemoryHandler& oom_handler)
: page_allocator_(page_allocator), : page_allocator_(page_allocator),
table_(static_cast<decltype(table_)>(page_allocator_->AllocatePages( oom_handler_(oom_handler),
nullptr, MaxTableSize(), page_allocator_->AllocatePageSize(), table_(static_cast<decltype(table_)>(page_allocator_.AllocatePages(
nullptr, MaxTableSize(), page_allocator_.AllocatePageSize(),
PageAllocator::kNoAccess))), PageAllocator::kNoAccess))),
read_only_table_end_(reinterpret_cast<uint8_t*>(table_)) { read_only_table_end_(reinterpret_cast<uint8_t*>(table_)) {
CHECK(table_); if (!table_) {
oom_handler_("Oilpan: GCInfoTable initial reservation.");
}
Resize(); Resize();
} }
GCInfoTable::~GCInfoTable() { GCInfoTable::~GCInfoTable() {
page_allocator_->ReleasePages(const_cast<GCInfo*>(table_), MaxTableSize(), 0); page_allocator_.ReleasePages(const_cast<GCInfo*>(table_), MaxTableSize(), 0);
} }
size_t GCInfoTable::MaxTableSize() const { size_t GCInfoTable::MaxTableSize() const {
return RoundUp(GCInfoTable::kMaxIndex * kEntrySize, return RoundUp(GCInfoTable::kMaxIndex * kEntrySize,
page_allocator_->AllocatePageSize()); page_allocator_.AllocatePageSize());
} }
GCInfoIndex GCInfoTable::InitialTableLimit() const { GCInfoIndex GCInfoTable::InitialTableLimit() const {
...@@ -71,7 +77,7 @@ GCInfoIndex GCInfoTable::InitialTableLimit() const { ...@@ -71,7 +77,7 @@ GCInfoIndex GCInfoTable::InitialTableLimit() const {
// of memory wanted and OS page size. // of memory wanted and OS page size.
constexpr size_t memory_wanted = kInitialWantedLimit * kEntrySize; constexpr size_t memory_wanted = kInitialWantedLimit * kEntrySize;
const size_t initial_limit = const size_t initial_limit =
RoundUp(memory_wanted, page_allocator_->AllocatePageSize()) / kEntrySize; RoundUp(memory_wanted, page_allocator_.AllocatePageSize()) / kEntrySize;
CHECK_GT(std::numeric_limits<GCInfoIndex>::max(), initial_limit); CHECK_GT(std::numeric_limits<GCInfoIndex>::max(), initial_limit);
return static_cast<GCInfoIndex>( return static_cast<GCInfoIndex>(
std::min(static_cast<size_t>(kMaxIndex), initial_limit)); std::min(static_cast<size_t>(kMaxIndex), initial_limit));
...@@ -83,20 +89,23 @@ void GCInfoTable::Resize() { ...@@ -83,20 +89,23 @@ void GCInfoTable::Resize() {
const size_t old_committed_size = limit_ * kEntrySize; const size_t old_committed_size = limit_ * kEntrySize;
const size_t new_committed_size = new_limit * kEntrySize; const size_t new_committed_size = new_limit * kEntrySize;
CHECK(table_); CHECK(table_);
CHECK_EQ(0u, new_committed_size % page_allocator_->AllocatePageSize()); CHECK_EQ(0u, new_committed_size % page_allocator_.AllocatePageSize());
CHECK_GE(MaxTableSize(), new_committed_size); CHECK_GE(MaxTableSize(), new_committed_size);
// Recommit new area as read/write. // Recommit new area as read/write.
uint8_t* current_table_end = uint8_t* current_table_end =
reinterpret_cast<uint8_t*>(table_) + old_committed_size; reinterpret_cast<uint8_t*>(table_) + old_committed_size;
const size_t table_size_delta = new_committed_size - old_committed_size; const size_t table_size_delta = new_committed_size - old_committed_size;
CHECK(page_allocator_->SetPermissions(current_table_end, table_size_delta, if (!page_allocator_.SetPermissions(current_table_end, table_size_delta,
PageAllocator::kReadWrite)); PageAllocator::kReadWrite)) {
oom_handler_("Oilpan: GCInfoTable resize.");
}
// Recommit old area as read-only. // Recommit old area as read-only.
if (read_only_table_end_ != current_table_end) { if (read_only_table_end_ != current_table_end) {
DCHECK_GT(current_table_end, read_only_table_end_); DCHECK_GT(current_table_end, read_only_table_end_);
const size_t read_only_delta = current_table_end - read_only_table_end_; const size_t read_only_delta = current_table_end - read_only_table_end_;
CHECK(page_allocator_->SetPermissions(read_only_table_end_, read_only_delta, CHECK(page_allocator_.SetPermissions(read_only_table_end_, read_only_delta,
PageAllocator::kRead)); PageAllocator::kRead));
read_only_table_end_ += read_only_delta; read_only_table_end_ += read_only_delta;
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/base/platform/mutex.h" #include "src/base/platform/mutex.h"
#include "src/base/platform/platform.h" #include "src/base/platform/platform.h"
#include "src/heap/cppgc/platform.h"
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
...@@ -49,7 +50,8 @@ class V8_EXPORT GCInfoTable final { ...@@ -49,7 +50,8 @@ class V8_EXPORT GCInfoTable final {
// Refer through GlobalGCInfoTable for retrieving the global table outside // Refer through GlobalGCInfoTable for retrieving the global table outside
// of testing code. // of testing code.
explicit GCInfoTable(PageAllocator* page_allocator); GCInfoTable(PageAllocator& page_allocator,
FatalOutOfMemoryHandler& oom_handler);
~GCInfoTable(); ~GCInfoTable();
GCInfoTable(const GCInfoTable&) = delete; GCInfoTable(const GCInfoTable&) = delete;
GCInfoTable& operator=(const GCInfoTable&) = delete; GCInfoTable& operator=(const GCInfoTable&) = delete;
...@@ -68,7 +70,7 @@ class V8_EXPORT GCInfoTable final { ...@@ -68,7 +70,7 @@ class V8_EXPORT GCInfoTable final {
GCInfoIndex LimitForTesting() const { return limit_; } GCInfoIndex LimitForTesting() const { return limit_; }
GCInfo& TableSlotForTesting(GCInfoIndex index) { return table_[index]; } GCInfo& TableSlotForTesting(GCInfoIndex index) { return table_[index]; }
PageAllocator* allocator() const { return page_allocator_; } PageAllocator& allocator() const { return page_allocator_; }
private: private:
void Resize(); void Resize();
...@@ -78,7 +80,8 @@ class V8_EXPORT GCInfoTable final { ...@@ -78,7 +80,8 @@ class V8_EXPORT GCInfoTable final {
void CheckMemoryIsZeroed(uintptr_t* base, size_t len); void CheckMemoryIsZeroed(uintptr_t* base, size_t len);
PageAllocator* page_allocator_; PageAllocator& page_allocator_;
FatalOutOfMemoryHandler& oom_handler_;
// Holds the per-class GCInfo descriptors; each HeapObjectHeader keeps an // Holds the per-class GCInfo descriptors; each HeapObjectHeader keeps an
// index into this table. // index into this table.
GCInfo* table_; GCInfo* table_;
...@@ -99,7 +102,7 @@ class V8_EXPORT GlobalGCInfoTable final { ...@@ -99,7 +102,7 @@ class V8_EXPORT GlobalGCInfoTable final {
// Sets up the table with the provided `page_allocator`. Will use an internal // Sets up the table with the provided `page_allocator`. Will use an internal
// allocator in case no PageAllocator is provided. May be called multiple // allocator in case no PageAllocator is provided. May be called multiple
// times with the same `page_allocator` argument. // times with the same `page_allocator` argument.
static void Initialize(PageAllocator* page_allocator); static void Initialize(PageAllocator& page_allocator);
// Accessors for the singleton table. // Accessors for the singleton table.
static GCInfoTable& GetMutable() { return *global_table_; } static GCInfoTable& GetMutable() { return *global_table_; }
......
...@@ -48,6 +48,11 @@ void FatalOutOfMemoryHandler::SetCustomHandler(Callback* callback) { ...@@ -48,6 +48,11 @@ void FatalOutOfMemoryHandler::SetCustomHandler(Callback* callback) {
custom_handler_ = callback; custom_handler_ = callback;
} }
FatalOutOfMemoryHandler& GetGlobalOOMHandler() {
static FatalOutOfMemoryHandler oom_handler;
return oom_handler;
}
} // namespace internal } // namespace internal
namespace { namespace {
...@@ -91,7 +96,7 @@ void InitializeProcess(PageAllocator* page_allocator) { ...@@ -91,7 +96,7 @@ void InitializeProcess(PageAllocator* page_allocator) {
auto& allocator = GetAllocator(page_allocator); auto& allocator = GetAllocator(page_allocator);
CHECK(!g_page_allocator); CHECK(!g_page_allocator);
internal::GlobalGCInfoTable::Initialize(&allocator); internal::GlobalGCInfoTable::Initialize(allocator);
#if defined(CPPGC_CAGED_HEAP) #if defined(CPPGC_CAGED_HEAP)
internal::CagedHeap::InitializeIfNeeded(allocator); internal::CagedHeap::InitializeIfNeeded(allocator);
#endif // defined(CPPGC_CAGED_HEAP) #endif // defined(CPPGC_CAGED_HEAP)
......
...@@ -37,6 +37,9 @@ class V8_EXPORT_PRIVATE FatalOutOfMemoryHandler final { ...@@ -37,6 +37,9 @@ class V8_EXPORT_PRIVATE FatalOutOfMemoryHandler final {
Callback* custom_handler_ = nullptr; Callback* custom_handler_ = nullptr;
}; };
// Gets the global OOM handler that is not bound to any specific Heap instance.
FatalOutOfMemoryHandler& GetGlobalOOMHandler();
} // namespace internal } // namespace internal
} // namespace cppgc } // namespace cppgc
......
...@@ -103,6 +103,12 @@ void V8::InitializePlatform(v8::Platform* platform) { ...@@ -103,6 +103,12 @@ void V8::InitializePlatform(v8::Platform* platform) {
v8::internal::ETWJITInterface::Register(); v8::internal::ETWJITInterface::Register();
} }
#endif #endif
// Initialization needs to happen on platform-level, as this sets up some
// cppgc internals that are needed to allow gracefully failing during cppgc
// platform setup.
CppHeap::InitializeOncePerProcess();
AdvanceStartupState(V8StartupState::kPlatformInitialized); AdvanceStartupState(V8StartupState::kPlatformInitialized);
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "src/base/page-allocator.h" #include "src/base/page-allocator.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"
#include "test/unittests/heap/cppgc/tests.h" #include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -22,7 +23,8 @@ constexpr GCInfo GetEmptyGCInfo() { return {nullptr, nullptr, nullptr, false}; } ...@@ -22,7 +23,8 @@ constexpr GCInfo GetEmptyGCInfo() { return {nullptr, nullptr, nullptr, false}; }
class GCInfoTableTest : public ::testing::Test { class GCInfoTableTest : public ::testing::Test {
public: public:
GCInfoTableTest() : table_(std::make_unique<GCInfoTable>(&page_allocator_)) {} GCInfoTableTest()
: table_(std::make_unique<GCInfoTable>(page_allocator_, oom_handler_)) {}
GCInfoIndex RegisterNewGCInfoForTesting(const GCInfo& info) { GCInfoIndex RegisterNewGCInfoForTesting(const GCInfo& info) {
// Unused registered index will result in registering a new index. // Unused registered index will result in registering a new index.
...@@ -35,6 +37,7 @@ class GCInfoTableTest : public ::testing::Test { ...@@ -35,6 +37,7 @@ class GCInfoTableTest : public ::testing::Test {
private: private:
v8::base::PageAllocator page_allocator_; v8::base::PageAllocator page_allocator_;
FatalOutOfMemoryHandler oom_handler_;
std::unique_ptr<GCInfoTable> table_; std::unique_ptr<GCInfoTable> table_;
}; };
......
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