Line data Source code
1 : // Copyright 2012 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 :
5 : #include "src/zone/zone.h"
6 :
7 : #include <cstring>
8 :
9 : #include "src/utils.h"
10 : #include "src/v8.h"
11 :
12 : #ifdef V8_USE_ADDRESS_SANITIZER
13 : #include <sanitizer/asan_interface.h>
14 : #endif // V8_USE_ADDRESS_SANITIZER
15 :
16 : namespace v8 {
17 : namespace internal {
18 :
19 : namespace {
20 :
21 : #if V8_USE_ADDRESS_SANITIZER
22 :
23 : const size_t kASanRedzoneBytes = 24; // Must be a multiple of 8.
24 :
25 : #else
26 :
27 : #define ASAN_POISON_MEMORY_REGION(start, size) \
28 : do { \
29 : USE(start); \
30 : USE(size); \
31 : } while (false)
32 :
33 : #define ASAN_UNPOISON_MEMORY_REGION(start, size) \
34 : do { \
35 : USE(start); \
36 : USE(size); \
37 : } while (false)
38 :
39 : const size_t kASanRedzoneBytes = 0;
40 :
41 : #endif // V8_USE_ADDRESS_SANITIZER
42 :
43 : } // namespace
44 :
45 57654547 : Zone::Zone(AccountingAllocator* allocator, const char* name)
46 : : allocation_size_(0),
47 : segment_bytes_allocated_(0),
48 : position_(0),
49 : limit_(0),
50 : allocator_(allocator),
51 : segment_head_(nullptr),
52 : name_(name),
53 57654547 : sealed_(false) {
54 57654547 : allocator_->ZoneCreation(this);
55 57654244 : }
56 :
57 57635177 : Zone::~Zone() {
58 57635177 : allocator_->ZoneDestruction(this);
59 :
60 57634948 : DeleteAll();
61 :
62 : DCHECK(segment_bytes_allocated_ == 0);
63 57635711 : }
64 :
65 3146963287 : void* Zone::New(size_t size) {
66 3146963287 : CHECK(!sealed_);
67 :
68 : // Round up the requested size to fit the alignment.
69 : size = RoundUp(size, kAlignmentInBytes);
70 :
71 : // Check if the requested size is available without expanding.
72 3146963287 : Address result = position_;
73 :
74 : const size_t size_with_redzone = size + kASanRedzoneBytes;
75 3146963287 : const uintptr_t limit = reinterpret_cast<uintptr_t>(limit_);
76 3146963287 : const uintptr_t position = reinterpret_cast<uintptr_t>(position_);
77 : // position_ > limit_ can be true after the alignment correction above.
78 3146963287 : if (limit < position || size_with_redzone > limit - position) {
79 44694957 : result = NewExpand(size_with_redzone);
80 : } else {
81 3102268330 : position_ += size_with_redzone;
82 : }
83 :
84 : Address redzone_position = result + size;
85 : DCHECK(redzone_position + kASanRedzoneBytes == position_);
86 : ASAN_POISON_MEMORY_REGION(redzone_position, kASanRedzoneBytes);
87 :
88 : // Check that the result has the proper alignment and return it.
89 : DCHECK(IsAddressAligned(result, kAlignmentInBytes, 0));
90 3147252482 : allocation_size_ += size;
91 3147252482 : return reinterpret_cast<void*>(result);
92 : }
93 :
94 57634805 : void Zone::DeleteAll() {
95 : // Traverse the chained list of segments and return them all to the allocator.
96 160235368 : for (Segment* current = segment_head_; current;) {
97 : Segment* next = current->next();
98 : size_t size = current->size();
99 :
100 : // Un-poison the segment content so we can re-use or zap it later.
101 : ASAN_UNPOISON_MEMORY_REGION(current->start(), current->capacity());
102 :
103 44964568 : segment_bytes_allocated_ -= size;
104 44964568 : allocator_->ReturnSegment(current);
105 : current = next;
106 : }
107 :
108 57635995 : position_ = limit_ = 0;
109 57635995 : allocation_size_ = 0;
110 57635995 : segment_head_ = nullptr;
111 57635995 : }
112 :
113 : // Creates a new segment, sets it size, and pushes it to the front
114 : // of the segment chain. Returns the new segment.
115 44982068 : Segment* Zone::NewSegment(size_t requested_size) {
116 89966251 : Segment* result = allocator_->GetSegment(requested_size);
117 44984186 : if (result != nullptr) {
118 : DCHECK_GE(result->size(), requested_size);
119 44984183 : segment_bytes_allocated_ += result->size();
120 : result->set_zone(this);
121 44984183 : result->set_next(segment_head_);
122 44984183 : segment_head_ = result;
123 : }
124 44984186 : return result;
125 : }
126 :
127 44982070 : Address Zone::NewExpand(size_t size) {
128 : // Make sure the requested size is already properly aligned and that
129 : // there isn't enough room in the Zone to satisfy the request.
130 : DCHECK_EQ(size, RoundDown(size, kAlignmentInBytes));
131 : DCHECK(limit_ < position_ ||
132 : reinterpret_cast<uintptr_t>(limit_) -
133 : reinterpret_cast<uintptr_t>(position_) <
134 : size);
135 :
136 : // Compute the new segment size. We use a 'high water mark'
137 : // strategy, where we increase the segment size every time we expand
138 : // except that we employ a maximum segment size when we delete. This
139 : // is to avoid excessive malloc() and free() overhead.
140 50858158 : Segment* head = segment_head_;
141 44982070 : const size_t old_size = (head == nullptr) ? 0 : head->size();
142 : static const size_t kSegmentOverhead = sizeof(Segment) + kAlignmentInBytes;
143 44982070 : const size_t new_size_no_overhead = size + (old_size << 1);
144 44982070 : size_t new_size = kSegmentOverhead + new_size_no_overhead;
145 44982070 : const size_t min_new_size = kSegmentOverhead + size;
146 : // Guard against integer overflow.
147 44982070 : if (new_size_no_overhead < size || new_size < kSegmentOverhead) {
148 0 : V8::FatalProcessOutOfMemory("Zone");
149 0 : return nullptr;
150 : }
151 44982070 : if (new_size < kMinimumSegmentSize) {
152 : new_size = kMinimumSegmentSize;
153 5972945 : } else if (new_size > kMaximumSegmentSize) {
154 : // Limit the size of new segments to avoid growing the segment size
155 : // exponentially, thus putting pressure on contiguous virtual address space.
156 : // All the while making sure to allocate a segment large enough to hold the
157 : // requested size.
158 : new_size = Max(min_new_size, kMaximumSegmentSize);
159 : }
160 44982070 : if (new_size > INT_MAX) {
161 0 : V8::FatalProcessOutOfMemory("Zone");
162 0 : return nullptr;
163 : }
164 44982070 : Segment* segment = NewSegment(new_size);
165 44984155 : if (segment == nullptr) {
166 0 : V8::FatalProcessOutOfMemory("Zone");
167 0 : return nullptr;
168 : }
169 :
170 : // Recompute 'top' and 'limit' based on the new segment.
171 : Address result = RoundUp(segment->start(), kAlignmentInBytes);
172 44984155 : position_ = result + size;
173 : // Check for address overflow.
174 : // (Should not happen since the segment is guaranteed to accomodate
175 : // size bytes + header and alignment padding)
176 : DCHECK(reinterpret_cast<uintptr_t>(position_) >=
177 : reinterpret_cast<uintptr_t>(result));
178 44984155 : limit_ = segment->end();
179 : DCHECK(position_ <= limit_);
180 44984155 : return result;
181 : }
182 :
183 : } // namespace internal
184 : } // namespace v8
|