wasm-memory.h 6.04 KB
Newer Older
1 2 3 4
// 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.

5 6
#ifndef V8_WASM_WASM_MEMORY_H_
#define V8_WASM_WASM_MEMORY_H_
7

Marja Hölttä's avatar
Marja Hölttä committed
8
#include <atomic>
9 10 11
#include <unordered_map>

#include "src/base/platform/mutex.h"
12 13
#include "src/flags.h"
#include "src/handles.h"
14
#include "src/objects/js-array-buffer.h"
15 16 17 18 19

namespace v8 {
namespace internal {
namespace wasm {

20 21 22 23 24
// The {WasmMemoryTracker} tracks reservations and allocations for wasm memory
// and wasm code. There is an upper limit on the total reserved memory which is
// checked by this class. Allocations are stored so we can look them up when an
// array buffer dies and figure out the reservation and allocation bounds for
// that buffer.
25
class WasmMemoryTracker {
26
 public:
27
  WasmMemoryTracker() = default;
28
  V8_EXPORT_PRIVATE ~WasmMemoryTracker();
29 30

  // ReserveAddressSpace attempts to increase the reserved address space counter
31 32
  // by {num_bytes}. Returns true if successful (meaning it is okay to go ahead
  // and reserve {num_bytes} bytes), false otherwise.
33 34 35 36
  // Use {kSoftLimit} if you can implement a fallback which needs less reserved
  // memory.
  enum ReservationLimit { kSoftLimit, kHardLimit };
  bool ReserveAddressSpace(size_t num_bytes, ReservationLimit limit);
37

38 39 40
  void RegisterAllocation(Isolate* isolate, void* allocation_base,
                          size_t allocation_length, void* buffer_start,
                          size_t buffer_length);
41 42

  struct AllocationData {
43 44 45 46
    void* allocation_base = nullptr;
    size_t allocation_length = 0;
    void* buffer_start = nullptr;
    size_t buffer_length = 0;
47 48

   private:
49
    AllocationData() = default;
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    AllocationData(void* allocation_base, size_t allocation_length,
                   void* buffer_start, size_t buffer_length)
        : allocation_base(allocation_base),
          allocation_length(allocation_length),
          buffer_start(buffer_start),
          buffer_length(buffer_length) {
      DCHECK_LE(reinterpret_cast<uintptr_t>(allocation_base),
                reinterpret_cast<uintptr_t>(buffer_start));
      DCHECK_GE(
          reinterpret_cast<uintptr_t>(allocation_base) + allocation_length,
          reinterpret_cast<uintptr_t>(buffer_start));
      DCHECK_GE(
          reinterpret_cast<uintptr_t>(allocation_base) + allocation_length,
          reinterpret_cast<uintptr_t>(buffer_start) + buffer_length);
    }

    friend WasmMemoryTracker;
  };

69 70 71 72 73 74 75 76 77 78 79 80
  // Allow tests to allocate a backing store the same way as we do it for
  // WebAssembly memory. This is used in unit tests for trap handler to
  // generate the same signals/exceptions for invalid memory accesses as
  // we would get with WebAssembly memory.
  V8_EXPORT_PRIVATE void* TryAllocateBackingStoreForTesting(
      Heap* heap, size_t size, void** allocation_base,
      size_t* allocation_length);

  // Free memory allocated with TryAllocateBackingStoreForTesting.
  V8_EXPORT_PRIVATE void FreeBackingStoreForTesting(base::AddressRegion memory,
                                                    void* buffer_start);

81
  // Decreases the amount of reserved address space.
82 83
  void ReleaseReservation(size_t num_bytes);

84
  // Removes an allocation from the tracker.
85
  AllocationData ReleaseAllocation(Isolate* isolate, const void* buffer_start);
86 87 88

  bool IsWasmMemory(const void* buffer_start);

89 90 91 92
  // Returns whether the given buffer is a Wasm memory with guard regions large
  // enough to safely use trap handlers.
  bool HasFullGuardRegions(const void* buffer_start);

93 94 95
  // Returns a pointer to a Wasm buffer's allocation data, or nullptr if the
  // buffer is not tracked.
  const AllocationData* FindAllocationData(const void* buffer_start);
96

97 98 99
  // Checks if a buffer points to a Wasm memory and if so does any necessary
  // work to reclaim the buffer. If this function returns false, the caller must
  // free the buffer manually.
100
  bool FreeMemoryIfIsWasmMemory(Isolate* isolate, const void* buffer_start);
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

  // Allocation results are reported to UMA
  //
  // See wasm_memory_allocation_result in counters.h
  enum class AllocationStatus {
    kSuccess,  // Succeeded on the first try

    kSuccessAfterRetry,  // Succeeded after garbage collection

    kAddressSpaceLimitReachedFailure,  // Failed because Wasm is at its address
                                       // space limit

    kOtherFailure  // Failed for an unknown reason
  };

116
 private:
117
  void AddAddressSpaceSample(Isolate* isolate);
118

119 120 121 122 123 124 125
  // Clients use a two-part process. First they "reserve" the address space,
  // which signifies an intent to actually allocate it. This determines whether
  // doing the allocation would put us over our limit. Once there is a
  // reservation, clients can do the allocation and register the result.
  //
  // We should always have:
  // allocated_address_space_ <= reserved_address_space_ <= kAddressSpaceLimit
126
  std::atomic<size_t> reserved_address_space_{0};
127 128 129 130 131

  // Used to protect access to the allocated address space counter and
  // allocation map. This is needed because Wasm memories can be freed on
  // another thread by the ArrayBufferTracker.
  base::Mutex mutex_;
132

133
  size_t allocated_address_space_ = 0;
134 135 136 137 138 139

  // Track Wasm memory allocation information. This is keyed by the start of the
  // buffer, rather than by the start of the allocation.
  std::unordered_map<const void*, AllocationData> allocations_;

  DISALLOW_COPY_AND_ASSIGN(WasmMemoryTracker);
140 141
};

142 143 144
// Attempts to allocate an array buffer with guard regions suitable for trap
// handling. If address space is not available, it will return a buffer with
// mini-guards that will require bounds checks.
145
MaybeHandle<JSArrayBuffer> NewArrayBuffer(
146
    Isolate*, size_t size, SharedFlag shared = SharedFlag::kNotShared);
147 148

Handle<JSArrayBuffer> SetupArrayBuffer(
149 150
    Isolate*, void* backing_store, size_t size, bool is_external,
    SharedFlag shared = SharedFlag::kNotShared);
151 152 153 154 155 156 157 158

void DetachMemoryBuffer(Isolate* isolate, Handle<JSArrayBuffer> buffer,
                        bool free_memory);

}  // namespace wasm
}  // namespace internal
}  // namespace v8

159
#endif  // V8_WASM_WASM_MEMORY_H_