LCOV - code coverage report
Current view: top level - test/cctest - test-allocation.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 81 90 90.0 %
Date: 2019-04-17 Functions: 16 18 88.9 %

          Line data    Source code
       1             : // Copyright 2017 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : #include <stdlib.h>
       5             : #include <string.h>
       6             : 
       7             : #if V8_OS_POSIX
       8             : #include <setjmp.h>
       9             : #include <signal.h>
      10             : #include <unistd.h>  // NOLINT
      11             : #endif
      12             : 
      13             : #include "src/v8.h"
      14             : 
      15             : #include "test/cctest/cctest.h"
      16             : 
      17             : using v8::internal::AccountingAllocator;
      18             : 
      19             : using v8::IdleTask;
      20             : using v8::Isolate;
      21             : using v8::Task;
      22             : 
      23             : #include "src/allocation.h"
      24             : #include "src/zone/accounting-allocator.h"
      25             : 
      26             : // ASAN isn't configured to return nullptr, so skip all of these tests.
      27             : #if !defined(V8_USE_ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \
      28             :     !defined(THREAD_SANITIZER)
      29             : 
      30             : namespace {
      31             : 
      32             : // Implementation of v8::Platform that can register OOM callbacks.
      33             : class AllocationPlatform : public TestPlatform {
      34             :  public:
      35          70 :   AllocationPlatform() {
      36          35 :     current_platform = this;
      37             :     // Now that it's completely constructed, make this the current platform.
      38          35 :     i::V8::SetPlatformForTesting(this);
      39          35 :   }
      40          40 :   ~AllocationPlatform() override = default;
      41             : 
      42           5 :   void OnCriticalMemoryPressure() override { oom_callback_called = true; }
      43             : 
      44          50 :   bool OnCriticalMemoryPressure(size_t length) override {
      45          50 :     oom_callback_called = true;
      46          50 :     return true;
      47             :   }
      48             : 
      49             :   static AllocationPlatform* current_platform;
      50             :   bool oom_callback_called = false;
      51             : };
      52             : 
      53             : AllocationPlatform* AllocationPlatform::current_platform = nullptr;
      54             : 
      55             : bool DidCallOnCriticalMemoryPressure() {
      56          30 :   return AllocationPlatform::current_platform &&
      57          15 :          AllocationPlatform::current_platform->oom_callback_called;
      58             : }
      59             : 
      60             : // No OS should be able to malloc/new this number of bytes. Generate enough
      61             : // random values in the address space to get a very large fraction of it. Using
      62             : // even larger values is that overflow from rounding or padding can cause the
      63             : // allocations to succeed somehow.
      64          30 : size_t GetHugeMemoryAmount() {
      65             :   static size_t huge_memory = 0;
      66          30 :   if (!huge_memory) {
      67        6030 :     for (int i = 0; i < 100; i++) {
      68        3000 :       huge_memory |= bit_cast<size_t>(v8::internal::GetRandomMmapAddr());
      69             :     }
      70             :     // Make it larger than the available address space.
      71          30 :     huge_memory *= 2;
      72          30 :     CHECK_NE(0, huge_memory);
      73             :   }
      74          30 :   return huge_memory;
      75             : }
      76             : 
      77           5 : void OnMallocedOperatorNewOOM(const char* location, const char* message) {
      78             :   // exit(0) if the OOM callback was called and location matches expectation.
      79           5 :   if (DidCallOnCriticalMemoryPressure())
      80           5 :     exit(strcmp(location, "Malloced operator new"));
      81           0 :   exit(1);
      82             : }
      83             : 
      84           5 : void OnNewArrayOOM(const char* location, const char* message) {
      85             :   // exit(0) if the OOM callback was called and location matches expectation.
      86           5 :   if (DidCallOnCriticalMemoryPressure()) exit(strcmp(location, "NewArray"));
      87           0 :   exit(1);
      88             : }
      89             : 
      90           5 : void OnAlignedAllocOOM(const char* location, const char* message) {
      91             :   // exit(0) if the OOM callback was called and location matches expectation.
      92           5 :   if (DidCallOnCriticalMemoryPressure()) exit(strcmp(location, "AlignedAlloc"));
      93           0 :   exit(1);
      94             : }
      95             : 
      96             : }  // namespace
      97             : 
      98       26644 : TEST(AccountingAllocatorOOM) {
      99           5 :   AllocationPlatform platform;
     100          10 :   v8::internal::AccountingAllocator allocator;
     101           5 :   CHECK(!platform.oom_callback_called);
     102             :   v8::internal::Segment* result =
     103           5 :       allocator.AllocateSegment(GetHugeMemoryAmount());
     104             :   // On a few systems, allocation somehow succeeds.
     105           5 :   CHECK_EQ(result == nullptr, platform.oom_callback_called);
     106           5 : }
     107             : 
     108       26644 : TEST(AccountingAllocatorCurrentAndMax) {
     109           5 :   AllocationPlatform platform;
     110          10 :   v8::internal::AccountingAllocator allocator;
     111             :   static constexpr size_t kAllocationSizes[] = {51, 231, 27};
     112             :   std::vector<v8::internal::Segment*> segments;
     113           5 :   CHECK_EQ(0, allocator.GetCurrentMemoryUsage());
     114           5 :   CHECK_EQ(0, allocator.GetMaxMemoryUsage());
     115             :   size_t expected_current = 0;
     116             :   size_t expected_max = 0;
     117          35 :   for (size_t size : kAllocationSizes) {
     118          30 :     segments.push_back(allocator.AllocateSegment(size));
     119          15 :     CHECK_NOT_NULL(segments.back());
     120          15 :     CHECK_EQ(size, segments.back()->total_size());
     121          15 :     expected_current += size;
     122          15 :     if (expected_current > expected_max) expected_max = expected_current;
     123          15 :     CHECK_EQ(expected_current, allocator.GetCurrentMemoryUsage());
     124          15 :     CHECK_EQ(expected_max, allocator.GetMaxMemoryUsage());
     125             :   }
     126          20 :   for (auto* segment : segments) {
     127          15 :     expected_current -= segment->total_size();
     128          15 :     allocator.ReturnSegment(segment);
     129          15 :     CHECK_EQ(expected_current, allocator.GetCurrentMemoryUsage());
     130             :   }
     131           5 :   CHECK_EQ(expected_max, allocator.GetMaxMemoryUsage());
     132           5 :   CHECK_EQ(0, allocator.GetCurrentMemoryUsage());
     133           5 :   CHECK(!platform.oom_callback_called);
     134           5 : }
     135             : 
     136       26644 : TEST(MallocedOperatorNewOOM) {
     137           5 :   AllocationPlatform platform;
     138           5 :   CHECK(!platform.oom_callback_called);
     139           5 :   CcTest::isolate()->SetFatalErrorHandler(OnMallocedOperatorNewOOM);
     140             :   // On failure, this won't return, since a Malloced::New failure is fatal.
     141             :   // In that case, behavior is checked in OnMallocedOperatorNewOOM before exit.
     142           5 :   void* result = v8::internal::Malloced::New(GetHugeMemoryAmount());
     143             :   // On a few systems, allocation somehow succeeds.
     144           0 :   CHECK_EQ(result == nullptr, platform.oom_callback_called);
     145           0 : }
     146             : 
     147       26644 : TEST(NewArrayOOM) {
     148           5 :   AllocationPlatform platform;
     149           5 :   CHECK(!platform.oom_callback_called);
     150           5 :   CcTest::isolate()->SetFatalErrorHandler(OnNewArrayOOM);
     151             :   // On failure, this won't return, since a NewArray failure is fatal.
     152             :   // In that case, behavior is checked in OnNewArrayOOM before exit.
     153           5 :   int8_t* result = v8::internal::NewArray<int8_t>(GetHugeMemoryAmount());
     154             :   // On a few systems, allocation somehow succeeds.
     155           0 :   CHECK_EQ(result == nullptr, platform.oom_callback_called);
     156           0 : }
     157             : 
     158       26644 : TEST(AlignedAllocOOM) {
     159           5 :   AllocationPlatform platform;
     160           5 :   CHECK(!platform.oom_callback_called);
     161           5 :   CcTest::isolate()->SetFatalErrorHandler(OnAlignedAllocOOM);
     162             :   // On failure, this won't return, since an AlignedAlloc failure is fatal.
     163             :   // In that case, behavior is checked in OnAlignedAllocOOM before exit.
     164           5 :   void* result = v8::internal::AlignedAlloc(GetHugeMemoryAmount(),
     165           5 :                                             v8::internal::AllocatePageSize());
     166             :   // On a few systems, allocation somehow succeeds.
     167           0 :   CHECK_EQ(result == nullptr, platform.oom_callback_called);
     168           0 : }
     169             : 
     170       26644 : TEST(AllocVirtualMemoryOOM) {
     171           5 :   AllocationPlatform platform;
     172           5 :   CHECK(!platform.oom_callback_called);
     173             :   v8::internal::VirtualMemory result(v8::internal::GetPlatformPageAllocator(),
     174          10 :                                      GetHugeMemoryAmount(), nullptr);
     175             :   // On a few systems, allocation somehow succeeds.
     176           5 :   CHECK_IMPLIES(!result.IsReserved(), platform.oom_callback_called);
     177           5 : }
     178             : 
     179       26644 : TEST(AlignedAllocVirtualMemoryOOM) {
     180           5 :   AllocationPlatform platform;
     181           5 :   CHECK(!platform.oom_callback_called);
     182             :   v8::internal::VirtualMemory result(v8::internal::GetPlatformPageAllocator(),
     183             :                                      GetHugeMemoryAmount(), nullptr,
     184          10 :                                      v8::internal::AllocatePageSize());
     185             :   // On a few systems, allocation somehow succeeds.
     186           5 :   CHECK_IMPLIES(!result.IsReserved(), platform.oom_callback_called);
     187       79922 : }
     188             : 
     189             : #endif  // !defined(V8_USE_ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) &&
     190             :         // !defined(THREAD_SANITIZER)

Generated by: LCOV version 1.10