Commit 53db0584 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

Revert "[Memory] Add an OnCriticalMemoryPressure method to V8::Platform."

This reverts commit 3f90d9f9.

Reason for revert: https://build.chromium.org/p/client.v8/builders/V8%20Linux64%20TSAN/builds/16510

Original change's description:
> [Memory] Add an OnCriticalMemoryPressure method to V8::Platform.
> 
> Adds virtual V8::Platform::OnCriticalMemoryPressure method, default
> implementation does nothing.
> 
> Calls this method on first allocation failures in NewArray, Malloced,
> and zone AccountingAllocator and adds retry logic.
> 
> Adds utility functions for allocating base::VirtualMemory to functions
> in allocation.h, which call this method and add retry logic.
> 
> Calls these utility functions in heap CodeRange, Spaces, StoreBuffer
> and SequentialMarkingDeque.
> 
> Bug: v8:6635
> Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
> Change-Id: I38afd394f3be556aca037d16675e9884658158cb
> Reviewed-on: https://chromium-review.googlesource.com/583543
> Commit-Queue: Bill Budge <bbudge@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#46988}

TBR=bbudge@chromium.org,ulan@chromium.org,mlippautz@chromium.org

Change-Id: I79afea5982e62db1462cc5a5585a226f0ddbe752
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: v8:6635
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Reviewed-on: https://chromium-review.googlesource.com/592887Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46989}
parent 3f90d9f9
...@@ -132,15 +132,6 @@ class Platform { ...@@ -132,15 +132,6 @@ class Platform {
virtual ~Platform() = default; virtual ~Platform() = default;
/**
* Enables the embedder to respond in cases where V8 can't allocate large
* blocks of memory. V8 retries the failed allocation once after calling this
* method. On success, execution continues; otherwise V8 exits with a fatal
* error.
* Embedder overrides of this function must NOT call back into V8.
*/
virtual void OnCriticalMemoryPressure() {}
/** /**
* Gets the number of threads that are used to execute background tasks. Is * Gets the number of threads that are used to execute background tasks. Is
* used to estimate the number of tasks a work package should be split into. * used to estimate the number of tasks a work package should be split into.
......
...@@ -18,33 +18,11 @@ ...@@ -18,33 +18,11 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
namespace {
void* AlignedAllocInternal(size_t size, size_t alignment) {
void* ptr;
#if V8_OS_WIN
ptr = _aligned_malloc(size, alignment);
#elif V8_LIBC_BIONIC
// posix_memalign is not exposed in some Android versions, so we fall back to
// memalign. See http://code.google.com/p/android/issues/detail?id=35391.
ptr = memalign(alignment, size);
#else
if (posix_memalign(&ptr, alignment, size)) ptr = nullptr;
#endif
return ptr;
}
} // namespace
void* Malloced::New(size_t size) { void* Malloced::New(size_t size) {
void* result = malloc(size); void* result = malloc(size);
if (result == nullptr) { if (result == NULL) {
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
result = malloc(size);
if (result == nullptr) {
V8::FatalProcessOutOfMemory("Malloced operator new"); V8::FatalProcessOutOfMemory("Malloced operator new");
} }
}
return result; return result;
} }
...@@ -76,14 +54,17 @@ char* StrNDup(const char* str, int n) { ...@@ -76,14 +54,17 @@ char* StrNDup(const char* str, int n) {
void* AlignedAlloc(size_t size, size_t alignment) { void* AlignedAlloc(size_t size, size_t alignment) {
DCHECK_LE(V8_ALIGNOF(void*), alignment); DCHECK_LE(V8_ALIGNOF(void*), alignment);
DCHECK(base::bits::IsPowerOfTwo(alignment)); DCHECK(base::bits::IsPowerOfTwo(alignment));
void* ptr = AlignedAllocInternal(size, alignment); void* ptr;
if (ptr == nullptr) { #if V8_OS_WIN
V8::GetCurrentPlatform()->OnCriticalMemoryPressure(); ptr = _aligned_malloc(size, alignment);
ptr = AlignedAllocInternal(size, alignment); #elif V8_LIBC_BIONIC
if (ptr == nullptr) { // posix_memalign is not exposed in some Android versions, so we fall back to
V8::FatalProcessOutOfMemory("AlignedAlloc"); // memalign. See http://code.google.com/p/android/issues/detail?id=35391.
} ptr = memalign(alignment, size);
} #else
if (posix_memalign(&ptr, alignment, size)) ptr = NULL;
#endif
if (ptr == NULL) V8::FatalProcessOutOfMemory("AlignedAlloc");
return ptr; return ptr;
} }
...@@ -99,32 +80,5 @@ void AlignedFree(void *ptr) { ...@@ -99,32 +80,5 @@ void AlignedFree(void *ptr) {
#endif #endif
} }
bool AllocVirtualMemory(size_t size, void* hint, base::VirtualMemory* result) {
base::VirtualMemory first_try(size, hint);
if (first_try.IsReserved()) {
result->TakeControl(&first_try);
return true;
}
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
base::VirtualMemory second_try(size, hint);
result->TakeControl(&second_try);
return result->IsReserved();
}
bool AlignedAllocVirtualMemory(size_t size, size_t alignment, void* hint,
base::VirtualMemory* result) {
base::VirtualMemory first_try(size, alignment, hint);
if (first_try.IsReserved()) {
result->TakeControl(&first_try);
return true;
}
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
base::VirtualMemory second_try(size, alignment, hint);
result->TakeControl(&second_try);
return result->IsReserved();
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -5,21 +5,15 @@ ...@@ -5,21 +5,15 @@
#ifndef V8_ALLOCATION_H_ #ifndef V8_ALLOCATION_H_
#define V8_ALLOCATION_H_ #define V8_ALLOCATION_H_
#include "include/v8-platform.h"
#include "src/base/compiler-specific.h" #include "src/base/compiler-specific.h"
#include "src/base/platform/platform.h"
#include "src/globals.h" #include "src/globals.h"
#include "src/v8.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
// This file defines memory allocation functions. If a first attempt at an // Called when allocation routines fail to allocate.
// allocation fails, these functions call back into the embedder, then attempt // This function should not return, but should terminate the current
// the allocation a second time. The embedder callback must not reenter V8. // processing.
// Called when allocation routines fail to allocate, even with a possible retry.
// This function should not return, but should terminate the current processing.
V8_EXPORT_PRIVATE void FatalProcessOutOfMemory(const char* message); V8_EXPORT_PRIVATE void FatalProcessOutOfMemory(const char* message);
// Superclass for classes managed with new & delete. // Superclass for classes managed with new & delete.
...@@ -32,17 +26,29 @@ class V8_EXPORT_PRIVATE Malloced { ...@@ -32,17 +26,29 @@ class V8_EXPORT_PRIVATE Malloced {
static void Delete(void* p); static void Delete(void* p);
}; };
// DEPRECATED
// TODO(leszeks): Delete this during a quiet period
#define BASE_EMBEDDED
// Superclass for classes only using static method functions.
// The subclass of AllStatic cannot be instantiated at all.
class AllStatic {
#ifdef DEBUG
public:
AllStatic() = delete;
#endif
};
template <typename T> template <typename T>
T* NewArray(size_t size) { T* NewArray(size_t size) {
T* result = new (std::nothrow) T[size]; T* result = new T[size];
if (result == nullptr) { if (result == NULL) FatalProcessOutOfMemory("NewArray");
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
result = new (std::nothrow) T[size];
if (result == nullptr) FatalProcessOutOfMemory("NewArray");
}
return result; return result;
} }
template <typename T> template <typename T>
void DeleteArray(T* array) { void DeleteArray(T* array) {
delete[] array; delete[] array;
...@@ -68,10 +74,6 @@ class FreeStoreAllocationPolicy { ...@@ -68,10 +74,6 @@ class FreeStoreAllocationPolicy {
void* AlignedAlloc(size_t size, size_t alignment); void* AlignedAlloc(size_t size, size_t alignment);
void AlignedFree(void *ptr); void AlignedFree(void *ptr);
bool AllocVirtualMemory(size_t size, void* hint, base::VirtualMemory* result);
bool AlignedAllocVirtualMemory(size_t size, size_t alignment, void* hint,
base::VirtualMemory* result);
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
include_rules = [ include_rules = [
"-include", "-include",
"+include/v8config.h", "+include/v8config.h",
"+include/v8stdint.h",
"-src", "-src",
"+src/base", "+src/base",
] ]
...@@ -125,19 +125,6 @@ const int kStackSpaceRequiredForCompilation = 40; ...@@ -125,19 +125,6 @@ const int kStackSpaceRequiredForCompilation = 40;
#define V8_SFI_HAS_UNIQUE_ID 1 #define V8_SFI_HAS_UNIQUE_ID 1
#endif #endif
// Superclass for classes only using static method functions.
// The subclass of AllStatic cannot be instantiated at all.
class AllStatic {
#ifdef DEBUG
public:
AllStatic() = delete;
#endif
};
// DEPRECATED
// TODO(leszeks): Delete this during a quiet period
#define BASE_EMBEDDED
typedef uint8_t byte; typedef uint8_t byte;
typedef byte* Address; typedef byte* Address;
......
...@@ -13,8 +13,8 @@ namespace v8 { ...@@ -13,8 +13,8 @@ namespace v8 {
namespace internal { namespace internal {
void SequentialMarkingDeque::SetUp() { void SequentialMarkingDeque::SetUp() {
base::VirtualMemory reservation; base::VirtualMemory reservation(kMaxSize, heap_->GetRandomMmapAddr());
if (!AllocVirtualMemory(kMaxSize, heap_->GetRandomMmapAddr(), &reservation)) { if (!reservation.IsReserved()) {
V8::FatalProcessOutOfMemory("SequentialMarkingDeque::SetUp"); V8::FatalProcessOutOfMemory("SequentialMarkingDeque::SetUp");
} }
backing_store_committed_size_ = 0; backing_store_committed_size_ = 0;
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// HeapObjectIterator // HeapObjectIterator
...@@ -118,14 +119,12 @@ bool CodeRange::SetUp(size_t requested) { ...@@ -118,14 +119,12 @@ bool CodeRange::SetUp(size_t requested) {
DCHECK(!kRequiresCodeRange || requested <= kMaximalCodeRangeSize); DCHECK(!kRequiresCodeRange || requested <= kMaximalCodeRangeSize);
base::VirtualMemory reservation; base::VirtualMemory reservation(
if (!AlignedAllocVirtualMemory(
requested, requested,
Max(kCodeRangeAreaAlignment, Max(kCodeRangeAreaAlignment,
static_cast<size_t>(base::OS::AllocateAlignment())), static_cast<size_t>(base::OS::AllocateAlignment())),
base::OS::GetRandomMmapAddr(), &reservation)) { base::OS::GetRandomMmapAddr());
return false; if (!reservation.IsReserved()) return false;
}
// We are sure that we have mapped a block of requested addresses. // We are sure that we have mapped a block of requested addresses.
DCHECK(reservation.size() == requested); DCHECK(reservation.size() == requested);
...@@ -454,10 +453,9 @@ void MemoryAllocator::FreeMemory(Address base, size_t size, ...@@ -454,10 +453,9 @@ void MemoryAllocator::FreeMemory(Address base, size_t size,
Address MemoryAllocator::ReserveAlignedMemory(size_t size, size_t alignment, Address MemoryAllocator::ReserveAlignedMemory(size_t size, size_t alignment,
void* hint, void* hint,
base::VirtualMemory* controller) { base::VirtualMemory* controller) {
base::VirtualMemory reservation; base::VirtualMemory reservation(size, alignment, hint);
if (!AlignedAllocVirtualMemory(size, alignment, hint, &reservation))
return nullptr;
if (!reservation.IsReserved()) return nullptr;
const Address base = const Address base =
RoundUp(static_cast<Address>(reservation.address()), alignment); RoundUp(static_cast<Address>(reservation.address()), alignment);
if (base + size != reservation.end()) { if (base + size != reservation.end()) {
......
...@@ -31,11 +31,8 @@ void StoreBuffer::SetUp() { ...@@ -31,11 +31,8 @@ void StoreBuffer::SetUp() {
// Allocate 3x the buffer size, so that we can start the new store buffer // Allocate 3x the buffer size, so that we can start the new store buffer
// aligned to 2x the size. This lets us use a bit test to detect the end of // aligned to 2x the size. This lets us use a bit test to detect the end of
// the area. // the area.
base::VirtualMemory reservation; base::VirtualMemory reservation(kStoreBufferSize * 3,
if (!AllocVirtualMemory(kStoreBufferSize * 3, heap_->GetRandomMmapAddr(), heap_->GetRandomMmapAddr());
&reservation)) {
V8::FatalProcessOutOfMemory("StoreBuffer::SetUp");
}
uintptr_t start_as_int = reinterpret_cast<uintptr_t>(reservation.address()); uintptr_t start_as_int = reinterpret_cast<uintptr_t>(reservation.address());
start_[0] = start_[0] =
reinterpret_cast<Address*>(RoundUp(start_as_int, kStoreBufferSize)); reinterpret_cast<Address*>(RoundUp(start_as_int, kStoreBufferSize));
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define V8_V8_H_ #define V8_V8_H_
#include "include/v8.h" #include "include/v8.h"
#include "src/allocation.h"
#include "src/globals.h" #include "src/globals.h"
namespace v8 { namespace v8 {
......
...@@ -83,11 +83,7 @@ Segment* AccountingAllocator::GetSegment(size_t bytes) { ...@@ -83,11 +83,7 @@ Segment* AccountingAllocator::GetSegment(size_t bytes) {
Segment* AccountingAllocator::AllocateSegment(size_t bytes) { Segment* AccountingAllocator::AllocateSegment(size_t bytes) {
void* memory = malloc(bytes); void* memory = malloc(bytes);
if (memory == nullptr) { if (memory) {
V8::GetCurrentPlatform()->OnCriticalMemoryPressure();
memory = malloc(bytes);
}
if (memory != nullptr) {
base::AtomicWord current = base::AtomicWord current =
base::Relaxed_AtomicIncrement(&current_memory_usage_, bytes); base::Relaxed_AtomicIncrement(&current_memory_usage_, bytes);
base::AtomicWord max = base::Relaxed_Load(&max_memory_usage_); base::AtomicWord max = base::Relaxed_Load(&max_memory_usage_);
......
...@@ -108,7 +108,6 @@ v8_executable("cctest") { ...@@ -108,7 +108,6 @@ v8_executable("cctest") {
"test-access-checks.cc", "test-access-checks.cc",
"test-accessor-assembler.cc", "test-accessor-assembler.cc",
"test-accessors.cc", "test-accessors.cc",
"test-allocation.cc",
"test-api-accessors.cc", "test-api-accessors.cc",
"test-api-interceptors.cc", "test-api-interceptors.cc",
"test-api.cc", "test-api.cc",
......
...@@ -126,7 +126,6 @@ ...@@ -126,7 +126,6 @@
'test-access-checks.cc', 'test-access-checks.cc',
'test-accessor-assembler.cc', 'test-accessor-assembler.cc',
'test-accessors.cc', 'test-accessors.cc',
'test-allocation.cc',
'test-api.cc', 'test-api.cc',
'test-api.h', 'test-api.h',
'test-api-accessors.cc', 'test-api-accessors.cc',
......
// Copyright 2017 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 <stdlib.h>
#include <string.h>
#include "src/v8.h"
#include "test/cctest/cctest.h"
using v8::internal::AccountingAllocator;
using v8::IdleTask;
using v8::Isolate;
using v8::Task;
#include "src/allocation.h"
#include "src/zone/accounting-allocator.h"
// ASAN isn't configured to return NULL, so skip all of these tests.
#ifndef V8_USE_ADDRESS_SANITIZER
namespace {
// Minimal implementation of platform that can receive OOM callbacks.
class MockAllocationPlatform : public v8::Platform {
public:
MockAllocationPlatform() { current_platform = this; }
virtual ~MockAllocationPlatform() {}
void OnCriticalMemoryPressure() override { oom_callback_called = true; }
void CallOnBackgroundThread(Task* task,
ExpectedRuntime expected_runtime) override {}
void CallOnForegroundThread(Isolate* isolate, Task* task) override {}
void CallDelayedOnForegroundThread(Isolate* isolate, Task* task,
double delay_in_seconds) override {}
double MonotonicallyIncreasingTime() override { return 0.0; }
void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {}
bool IdleTasksEnabled(Isolate* isolate) override { return false; }
v8::TracingController* GetTracingController() override {
return &tracing_controller_;
}
bool PendingIdleTask() { return false; }
void PerformIdleTask(double idle_time_in_seconds) {}
bool PendingDelayedTask() { return false; }
void PerformDelayedTask() {}
static MockAllocationPlatform* current_platform;
bool oom_callback_called = false;
private:
v8::TracingController tracing_controller_;
DISALLOW_COPY_AND_ASSIGN(MockAllocationPlatform);
};
MockAllocationPlatform* MockAllocationPlatform::current_platform = nullptr;
bool DidCallOnCriticalMemoryPressure() {
return MockAllocationPlatform::current_platform &&
MockAllocationPlatform::current_platform->oom_callback_called;
}
// No OS should be able to malloc/new this number of bytes. Generate enough
// random values in the address space to get a very large fraction of it. Using
// even larger values is that overflow from rounding or padding can cause the
// allocations to succeed somehow.
size_t GetHugeMemoryAmount() {
static size_t huge_memory = 0;
if (!huge_memory) {
for (int i = 0; i < 100; i++) {
huge_memory |= bit_cast<size_t>(v8::base::OS::GetRandomMmapAddr());
}
// Make it larger than the available address space.
huge_memory *= 2;
CHECK_NE(0, huge_memory);
}
return huge_memory;
}
void OnMallocedOperatorNewOOM(const char* location, const char* message) {
// exit(0) if the OOM callback was called and location matches expectation.
if (DidCallOnCriticalMemoryPressure())
exit(strcmp(location, "Malloced operator new"));
exit(1);
}
void OnNewArrayOOM(const char* location, const char* message) {
// exit(0) if the OOM callback was called and location matches expectation.
if (DidCallOnCriticalMemoryPressure()) exit(strcmp(location, "NewArray"));
exit(1);
}
void OnAlignedAllocOOM(const char* location, const char* message) {
// exit(0) if the OOM callback was called and location matches expectation.
if (DidCallOnCriticalMemoryPressure()) exit(strcmp(location, "AlignedAlloc"));
exit(1);
}
} // namespace
TEST(AccountingAllocatorOOM) {
// TODO(bbudge) Implement a TemporaryPlatformScope to simplify test code.
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
MockAllocationPlatform platform;
i::V8::SetPlatformForTesting(&platform);
v8::internal::AccountingAllocator allocator;
CHECK(!platform.oom_callback_called);
v8::internal::Segment* result = allocator.GetSegment(GetHugeMemoryAmount());
// On a few systems, allocation somehow succeeds.
CHECK_EQ(result == nullptr, platform.oom_callback_called);
i::V8::SetPlatformForTesting(old_platform);
}
TEST(MallocedOperatorNewOOM) {
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
MockAllocationPlatform platform;
i::V8::SetPlatformForTesting(&platform);
CHECK(!platform.oom_callback_called);
CcTest::isolate()->SetFatalErrorHandler(OnMallocedOperatorNewOOM);
// On failure, this won't return, since a Malloced::New failure is fatal.
// In that case, behavior is checked in OnMallocedOperatorNewOOM before exit.
void* result = v8::internal::Malloced::New(GetHugeMemoryAmount());
// On a few systems, allocation somehow succeeds.
CHECK_EQ(result == nullptr, platform.oom_callback_called);
i::V8::SetPlatformForTesting(old_platform);
}
TEST(NewArrayOOM) {
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
MockAllocationPlatform platform;
i::V8::SetPlatformForTesting(&platform);
CHECK(!platform.oom_callback_called);
CcTest::isolate()->SetFatalErrorHandler(OnNewArrayOOM);
// On failure, this won't return, since a NewArray failure is fatal.
// In that case, behavior is checked in OnNewArrayOOM before exit.
int8_t* result = v8::internal::NewArray<int8_t>(GetHugeMemoryAmount());
// On a few systems, allocation somehow succeeds.
CHECK_EQ(result == nullptr, platform.oom_callback_called);
i::V8::SetPlatformForTesting(old_platform);
}
TEST(AlignedAllocOOM) {
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
MockAllocationPlatform platform;
i::V8::SetPlatformForTesting(&platform);
CHECK(!platform.oom_callback_called);
CcTest::isolate()->SetFatalErrorHandler(OnAlignedAllocOOM);
// On failure, this won't return, since an AlignedAlloc failure is fatal.
// In that case, behavior is checked in OnAlignedAllocOOM before exit.
void* result = v8::internal::AlignedAlloc(GetHugeMemoryAmount(),
v8::base::OS::AllocateAlignment());
// On a few systems, allocation somehow succeeds.
CHECK_EQ(result == nullptr, platform.oom_callback_called);
i::V8::SetPlatformForTesting(old_platform);
}
TEST(AllocVirtualMemoryOOM) {
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
MockAllocationPlatform platform;
i::V8::SetPlatformForTesting(&platform);
CHECK(!platform.oom_callback_called);
v8::base::VirtualMemory result;
bool success =
v8::internal::AllocVirtualMemory(GetHugeMemoryAmount(), nullptr, &result);
// On a few systems, allocation somehow succeeds.
CHECK_IMPLIES(success, result.IsReserved());
CHECK_IMPLIES(!success, !result.IsReserved() && platform.oom_callback_called);
i::V8::SetPlatformForTesting(old_platform);
}
TEST(AlignedAllocVirtualMemoryOOM) {
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
MockAllocationPlatform platform;
i::V8::SetPlatformForTesting(&platform);
CHECK(!platform.oom_callback_called);
v8::base::VirtualMemory result;
bool success = v8::internal::AlignedAllocVirtualMemory(
GetHugeMemoryAmount(), v8::base::OS::AllocateAlignment(), nullptr,
&result);
// On a few systems, allocation somehow succeeds.
CHECK_IMPLIES(success, result.IsReserved());
CHECK_IMPLIES(!success, !result.IsReserved() && platform.oom_callback_called);
i::V8::SetPlatformForTesting(old_platform);
}
#endif // V8_USE_ADDRESS_SANITIZER
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