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