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