v8/test/cctest/test-allocation.cc
Thiabaud Engelbrecht 81cbf67f5f Reland "Reland "[v8] Use |AllocateAtLeast| for resizing v8 zones.""
This is a reland of commit 2a7f86edcb

Original change's description:
> Reland "[v8] Use |AllocateAtLeast| for resizing v8 zones."
>
> This is a reland of commit 4444874cdf
> This fixes a failure on the UBSan bots caused by assuming |Realloc| is always in-place if the new size is <= the value of |malloc_usable_size|.
>
> Original change's description:
> > [v8] Use |AllocateAtLeast| for resizing v8 zones.
> >
> > This is part of an ongoing effort to reduce fragmentation in Chrome.  Partition alloc shows v8 zones are a large user of memory in Renderer processes, and that there is fragmentation from these allocations. This CL will reduce this fragmentation by allowing v8 to use all allocated memory for its zones.
> >
> > Bug: v8:13193, chromium:1238858
> > Change-Id: Ibeac8bdba9d0e7ff66b14a3dde10e7c87d3cf953
> > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3889361
> > Reviewed-by: Adam Klein <adamk@chromium.org>
> > Commit-Queue: Thiabaud Engelbrecht <thiabaud@google.com>
> > Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
> > Cr-Commit-Position: refs/heads/main@{#83235}
>
> Bug: v8:13193, chromium:1238858
> Change-Id: I923bcbce8403dd7d84642340fd7202087b8a4440
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3910268
> Reviewed-by: Adam Klein <adamk@chromium.org>
> Commit-Queue: Thiabaud Engelbrecht <thiabaud@google.com>
> Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#83372}

Bug: v8:13193, chromium:1238858
Change-Id: Ie541f6d9cfe410e4f9c37bb5b2a5c44b7a71718e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3963703
Commit-Queue: Thiabaud Engelbrecht <thiabaud@google.com>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83795}
2022-10-19 11:55:27 +00:00

179 lines
6.8 KiB
C++

// 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>
#if V8_OS_POSIX
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
#endif
#include "src/init/v8.h"
#include "test/cctest/cctest.h"
using v8::internal::AccountingAllocator;
using v8::IdleTask;
using v8::Isolate;
using v8::Task;
#include "src/utils/allocation.h"
#include "src/zone/accounting-allocator.h"
// ASAN isn't configured to return nullptr, so skip all of these tests.
#if !defined(V8_USE_ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
!defined(THREAD_SANITIZER)
namespace {
// Implementation of v8::Platform that can register OOM callbacks.
class AllocationPlatform : public TestPlatform {
public:
AllocationPlatform() { current_platform = this; }
void OnCriticalMemoryPressure() override { oom_callback_called = true; }
static AllocationPlatform* current_platform;
bool oom_callback_called = false;
};
AllocationPlatform* AllocationPlatform::current_platform = nullptr;
bool DidCallOnCriticalMemoryPressure() {
return AllocationPlatform::current_platform &&
AllocationPlatform::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 |=
v8::base::bit_cast<size_t>(v8::internal::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_WITH_PLATFORM(AccountingAllocatorOOM, AllocationPlatform) {
v8::internal::AccountingAllocator allocator;
CHECK(!platform.oom_callback_called);
const bool support_compression = false;
v8::internal::Segment* result =
allocator.AllocateSegment(GetHugeMemoryAmount(), support_compression);
// On a few systems, allocation somehow succeeds.
CHECK_EQ(result == nullptr, platform.oom_callback_called);
}
// We use |AllocateAtLeast| in the accounting allocator, so we check only that
// we have _at least_ the expected amount of memory allocated.
TEST_WITH_PLATFORM(AccountingAllocatorCurrentAndMax, AllocationPlatform) {
v8::internal::AccountingAllocator allocator;
static constexpr size_t kAllocationSizes[] = {51, 231, 27};
std::vector<v8::internal::Segment*> segments;
const bool support_compression = false;
CHECK_EQ(0, allocator.GetCurrentMemoryUsage());
CHECK_EQ(0, allocator.GetMaxMemoryUsage());
size_t expected_current = 0;
size_t expected_max = 0;
for (size_t size : kAllocationSizes) {
segments.push_back(allocator.AllocateSegment(size, support_compression));
CHECK_NOT_NULL(segments.back());
CHECK_LE(size, segments.back()->total_size());
expected_current += segments.back()->total_size();
if (expected_current > expected_max) expected_max = expected_current;
CHECK_EQ(expected_current, allocator.GetCurrentMemoryUsage());
CHECK_EQ(expected_max, allocator.GetMaxMemoryUsage());
}
for (auto* segment : segments) {
expected_current -= segment->total_size();
allocator.ReturnSegment(segment, support_compression);
CHECK_EQ(expected_current, allocator.GetCurrentMemoryUsage());
}
CHECK_EQ(expected_max, allocator.GetMaxMemoryUsage());
CHECK_EQ(0, allocator.GetCurrentMemoryUsage());
CHECK(!platform.oom_callback_called);
}
TEST_WITH_PLATFORM(MallocedOperatorNewOOM, AllocationPlatform) {
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::operator new(GetHugeMemoryAmount());
// On a few systems, allocation somehow succeeds.
CHECK_EQ(result == nullptr, platform.oom_callback_called);
}
TEST_WITH_PLATFORM(NewArrayOOM, AllocationPlatform) {
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);
}
TEST_WITH_PLATFORM(AlignedAllocOOM, AllocationPlatform) {
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::AlignedAllocWithRetry(
GetHugeMemoryAmount(), v8::internal::AllocatePageSize());
// On a few systems, allocation somehow succeeds.
CHECK_EQ(result == nullptr, platform.oom_callback_called);
}
TEST_WITH_PLATFORM(AllocVirtualMemoryOOM, AllocationPlatform) {
CHECK(!platform.oom_callback_called);
v8::internal::VirtualMemory result(v8::internal::GetPlatformPageAllocator(),
GetHugeMemoryAmount(), nullptr);
// On a few systems, allocation somehow succeeds.
CHECK_IMPLIES(!result.IsReserved(), platform.oom_callback_called);
}
TEST_WITH_PLATFORM(AlignedAllocVirtualMemoryOOM, AllocationPlatform) {
CHECK(!platform.oom_callback_called);
v8::internal::VirtualMemory result(v8::internal::GetPlatformPageAllocator(),
GetHugeMemoryAmount(), nullptr,
v8::internal::AllocatePageSize());
// On a few systems, allocation somehow succeeds.
CHECK_IMPLIES(!result.IsReserved(), platform.oom_callback_called);
}
#endif // !defined(V8_USE_ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) &&
// !defined(THREAD_SANITIZER)