OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <string.h> | 5 #include "src/zone.h" |
6 | 6 |
7 #include "src/v8.h" | 7 #include <cstring> |
8 #include "src/zone-inl.h" | 8 |
| 9 #ifdef V8_USE_ADDRESS_SANITIZER |
| 10 #include <sanitizer/asan_interface.h> |
| 11 #endif // V8_USE_ADDRESS_SANITIZER |
9 | 12 |
10 namespace v8 { | 13 namespace v8 { |
11 namespace internal { | 14 namespace internal { |
12 | 15 |
| 16 namespace { |
| 17 |
| 18 #if V8_USE_ADDRESS_SANITIZER |
| 19 |
| 20 const int kASanRedzoneBytes = 24; // Must be a multiple of 8. |
| 21 |
| 22 #else |
| 23 |
| 24 #define ASAN_POISON_MEMORY_REGION(start, size) \ |
| 25 do { \ |
| 26 USE(start); \ |
| 27 USE(size); \ |
| 28 } while (false) |
| 29 |
| 30 #define ASAN_UNPOISON_MEMORY_REGION(start, size) \ |
| 31 do { \ |
| 32 USE(start); \ |
| 33 USE(size); \ |
| 34 } while (false) |
| 35 |
| 36 const int kASanRedzoneBytes = 0; |
| 37 |
| 38 #endif // V8_USE_ADDRESS_SANITIZER |
| 39 |
| 40 } // namespace |
| 41 |
13 | 42 |
14 // Segments represent chunks of memory: They have starting address | 43 // Segments represent chunks of memory: They have starting address |
15 // (encoded in the this pointer) and a size in bytes. Segments are | 44 // (encoded in the this pointer) and a size in bytes. Segments are |
16 // chained together forming a LIFO structure with the newest segment | 45 // chained together forming a LIFO structure with the newest segment |
17 // available as segment_head_. Segments are allocated using malloc() | 46 // available as segment_head_. Segments are allocated using malloc() |
18 // and de-allocated using free(). | 47 // and de-allocated using free(). |
19 | 48 |
20 class Segment { | 49 class Segment { |
21 public: | 50 public: |
22 void Initialize(Segment* next, int size) { | 51 void Initialize(Segment* next, int size) { |
23 next_ = next; | 52 next_ = next; |
24 size_ = size; | 53 size_ = size; |
25 } | 54 } |
26 | 55 |
27 Segment* next() const { return next_; } | 56 Segment* next() const { return next_; } |
28 void clear_next() { next_ = NULL; } | 57 void clear_next() { next_ = nullptr; } |
29 | 58 |
30 int size() const { return size_; } | 59 int size() const { return size_; } |
31 int capacity() const { return size_ - sizeof(Segment); } | 60 int capacity() const { return size_ - sizeof(Segment); } |
32 | 61 |
33 Address start() const { return address(sizeof(Segment)); } | 62 Address start() const { return address(sizeof(Segment)); } |
34 Address end() const { return address(size_); } | 63 Address end() const { return address(size_); } |
35 | 64 |
36 private: | 65 private: |
37 // Computes the address of the nth byte in this segment. | 66 // Computes the address of the nth byte in this segment. |
38 Address address(int n) const { | 67 Address address(int n) const { |
39 return Address(this) + n; | 68 return Address(this) + n; |
40 } | 69 } |
41 | 70 |
42 Segment* next_; | 71 Segment* next_; |
43 int size_; | 72 int size_; |
44 }; | 73 }; |
45 | 74 |
46 | 75 |
47 Zone::Zone() | 76 Zone::Zone() |
48 : allocation_size_(0), | 77 : allocation_size_(0), |
49 segment_bytes_allocated_(0), | 78 segment_bytes_allocated_(0), |
50 position_(0), | 79 position_(0), |
51 limit_(0), | 80 limit_(0), |
52 segment_head_(NULL) {} | 81 segment_head_(nullptr) {} |
53 | 82 |
54 | 83 |
55 Zone::~Zone() { | 84 Zone::~Zone() { |
56 DeleteAll(); | 85 DeleteAll(); |
57 DeleteKeptSegment(); | 86 DeleteKeptSegment(); |
58 | 87 |
59 DCHECK(segment_bytes_allocated_ == 0); | 88 DCHECK(segment_bytes_allocated_ == 0); |
60 } | 89 } |
61 | 90 |
62 | 91 |
63 void* Zone::New(int size) { | 92 void* Zone::New(int size) { |
64 // Round up the requested size to fit the alignment. | 93 // Round up the requested size to fit the alignment. |
65 size = RoundUp(size, kAlignment); | 94 size = RoundUp(size, kAlignment); |
66 | 95 |
67 // If the allocation size is divisible by 8 then we return an 8-byte aligned | 96 // If the allocation size is divisible by 8 then we return an 8-byte aligned |
68 // address. | 97 // address. |
69 if (kPointerSize == 4 && kAlignment == 4) { | 98 if (kPointerSize == 4 && kAlignment == 4) { |
70 position_ += ((~size) & 4) & (reinterpret_cast<intptr_t>(position_) & 4); | 99 position_ += ((~size) & 4) & (reinterpret_cast<intptr_t>(position_) & 4); |
71 } else { | 100 } else { |
72 DCHECK(kAlignment >= kPointerSize); | 101 DCHECK(kAlignment >= kPointerSize); |
73 } | 102 } |
74 | 103 |
75 // Check if the requested size is available without expanding. | 104 // Check if the requested size is available without expanding. |
76 Address result = position_; | 105 Address result = position_; |
77 | 106 |
78 int size_with_redzone = | 107 const int size_with_redzone = size + kASanRedzoneBytes; |
79 #ifdef V8_USE_ADDRESS_SANITIZER | |
80 size + kASanRedzoneBytes; | |
81 #else | |
82 size; | |
83 #endif | |
84 | |
85 if (size_with_redzone > limit_ - position_) { | 108 if (size_with_redzone > limit_ - position_) { |
86 result = NewExpand(size_with_redzone); | 109 result = NewExpand(size_with_redzone); |
87 } else { | 110 } else { |
88 position_ += size_with_redzone; | 111 position_ += size_with_redzone; |
89 } | 112 } |
90 | 113 |
91 #ifdef V8_USE_ADDRESS_SANITIZER | |
92 Address redzone_position = result + size; | 114 Address redzone_position = result + size; |
93 DCHECK(redzone_position + kASanRedzoneBytes == position_); | 115 DCHECK(redzone_position + kASanRedzoneBytes == position_); |
94 ASAN_POISON_MEMORY_REGION(redzone_position, kASanRedzoneBytes); | 116 ASAN_POISON_MEMORY_REGION(redzone_position, kASanRedzoneBytes); |
95 #endif | |
96 | 117 |
97 // Check that the result has the proper alignment and return it. | 118 // Check that the result has the proper alignment and return it. |
98 DCHECK(IsAddressAligned(result, kAlignment, 0)); | 119 DCHECK(IsAddressAligned(result, kAlignment, 0)); |
99 allocation_size_ += size; | 120 allocation_size_ += size; |
100 return reinterpret_cast<void*>(result); | 121 return reinterpret_cast<void*>(result); |
101 } | 122 } |
102 | 123 |
103 | 124 |
104 void Zone::DeleteAll() { | 125 void Zone::DeleteAll() { |
105 #ifdef DEBUG | 126 #ifdef DEBUG |
106 // Constant byte value used for zapping dead memory in debug mode. | 127 // Constant byte value used for zapping dead memory in debug mode. |
107 static const unsigned char kZapDeadByte = 0xcd; | 128 static const unsigned char kZapDeadByte = 0xcd; |
108 #endif | 129 #endif |
109 | 130 |
110 // Find a segment with a suitable size to keep around. | 131 // Find a segment with a suitable size to keep around. |
111 Segment* keep = NULL; | 132 Segment* keep = nullptr; |
112 // Traverse the chained list of segments, zapping (in debug mode) | 133 // Traverse the chained list of segments, zapping (in debug mode) |
113 // and freeing every segment except the one we wish to keep. | 134 // and freeing every segment except the one we wish to keep. |
114 for (Segment* current = segment_head_; current != NULL; ) { | 135 for (Segment* current = segment_head_; current;) { |
115 Segment* next = current->next(); | 136 Segment* next = current->next(); |
116 if (keep == NULL && current->size() <= kMaximumKeptSegmentSize) { | 137 if (!keep && current->size() <= kMaximumKeptSegmentSize) { |
117 // Unlink the segment we wish to keep from the list. | 138 // Unlink the segment we wish to keep from the list. |
118 keep = current; | 139 keep = current; |
119 keep->clear_next(); | 140 keep->clear_next(); |
120 } else { | 141 } else { |
121 int size = current->size(); | 142 int size = current->size(); |
122 #ifdef DEBUG | 143 #ifdef DEBUG |
123 // Un-poison first so the zapping doesn't trigger ASan complaints. | 144 // Un-poison first so the zapping doesn't trigger ASan complaints. |
124 ASAN_UNPOISON_MEMORY_REGION(current, size); | 145 ASAN_UNPOISON_MEMORY_REGION(current, size); |
125 // Zap the entire current segment (including the header). | 146 // Zap the entire current segment (including the header). |
126 memset(current, kZapDeadByte, size); | 147 memset(current, kZapDeadByte, size); |
127 #endif | 148 #endif |
128 DeleteSegment(current, size); | 149 DeleteSegment(current, size); |
129 } | 150 } |
130 current = next; | 151 current = next; |
131 } | 152 } |
132 | 153 |
133 // If we have found a segment we want to keep, we must recompute the | 154 // If we have found a segment we want to keep, we must recompute the |
134 // variables 'position' and 'limit' to prepare for future allocate | 155 // variables 'position' and 'limit' to prepare for future allocate |
135 // attempts. Otherwise, we must clear the position and limit to | 156 // attempts. Otherwise, we must clear the position and limit to |
136 // force a new segment to be allocated on demand. | 157 // force a new segment to be allocated on demand. |
137 if (keep != NULL) { | 158 if (keep) { |
138 Address start = keep->start(); | 159 Address start = keep->start(); |
139 position_ = RoundUp(start, kAlignment); | 160 position_ = RoundUp(start, kAlignment); |
140 limit_ = keep->end(); | 161 limit_ = keep->end(); |
141 // Un-poison so we can re-use the segment later. | 162 // Un-poison so we can re-use the segment later. |
142 ASAN_UNPOISON_MEMORY_REGION(start, keep->capacity()); | 163 ASAN_UNPOISON_MEMORY_REGION(start, keep->capacity()); |
143 #ifdef DEBUG | 164 #ifdef DEBUG |
144 // Zap the contents of the kept segment (but not the header). | 165 // Zap the contents of the kept segment (but not the header). |
145 memset(start, kZapDeadByte, keep->capacity()); | 166 memset(start, kZapDeadByte, keep->capacity()); |
146 #endif | 167 #endif |
147 } else { | 168 } else { |
148 position_ = limit_ = 0; | 169 position_ = limit_ = 0; |
149 } | 170 } |
150 | 171 |
151 allocation_size_ = 0; | 172 allocation_size_ = 0; |
152 // Update the head segment to be the kept segment (if any). | 173 // Update the head segment to be the kept segment (if any). |
153 segment_head_ = keep; | 174 segment_head_ = keep; |
154 } | 175 } |
155 | 176 |
156 | 177 |
157 void Zone::DeleteKeptSegment() { | 178 void Zone::DeleteKeptSegment() { |
158 #ifdef DEBUG | 179 #ifdef DEBUG |
159 // Constant byte value used for zapping dead memory in debug mode. | 180 // Constant byte value used for zapping dead memory in debug mode. |
160 static const unsigned char kZapDeadByte = 0xcd; | 181 static const unsigned char kZapDeadByte = 0xcd; |
161 #endif | 182 #endif |
162 | 183 |
163 DCHECK(segment_head_ == NULL || segment_head_->next() == NULL); | 184 DCHECK(segment_head_ == nullptr || segment_head_->next() == nullptr); |
164 if (segment_head_ != NULL) { | 185 if (segment_head_ != nullptr) { |
165 int size = segment_head_->size(); | 186 int size = segment_head_->size(); |
166 #ifdef DEBUG | 187 #ifdef DEBUG |
167 // Un-poison first so the zapping doesn't trigger ASan complaints. | 188 // Un-poison first so the zapping doesn't trigger ASan complaints. |
168 ASAN_UNPOISON_MEMORY_REGION(segment_head_, size); | 189 ASAN_UNPOISON_MEMORY_REGION(segment_head_, size); |
169 // Zap the entire kept segment (including the header). | 190 // Zap the entire kept segment (including the header). |
170 memset(segment_head_, kZapDeadByte, size); | 191 memset(segment_head_, kZapDeadByte, size); |
171 #endif | 192 #endif |
172 DeleteSegment(segment_head_, size); | 193 DeleteSegment(segment_head_, size); |
173 segment_head_ = NULL; | 194 segment_head_ = nullptr; |
174 } | 195 } |
175 | 196 |
176 DCHECK(segment_bytes_allocated_ == 0); | 197 DCHECK(segment_bytes_allocated_ == 0); |
177 } | 198 } |
178 | 199 |
179 | 200 |
180 // Creates a new segment, sets it size, and pushes it to the front | 201 // Creates a new segment, sets it size, and pushes it to the front |
181 // of the segment chain. Returns the new segment. | 202 // of the segment chain. Returns the new segment. |
182 Segment* Zone::NewSegment(int size) { | 203 Segment* Zone::NewSegment(int size) { |
183 Segment* result = reinterpret_cast<Segment*>(Malloced::New(size)); | 204 Segment* result = reinterpret_cast<Segment*>(Malloced::New(size)); |
184 adjust_segment_bytes_allocated(size); | 205 segment_bytes_allocated_ += size; |
185 if (result != NULL) { | 206 if (result != nullptr) { |
186 result->Initialize(segment_head_, size); | 207 result->Initialize(segment_head_, size); |
187 segment_head_ = result; | 208 segment_head_ = result; |
188 } | 209 } |
189 return result; | 210 return result; |
190 } | 211 } |
191 | 212 |
192 | 213 |
193 // Deletes the given segment. Does not touch the segment chain. | 214 // Deletes the given segment. Does not touch the segment chain. |
194 void Zone::DeleteSegment(Segment* segment, int size) { | 215 void Zone::DeleteSegment(Segment* segment, int size) { |
195 adjust_segment_bytes_allocated(-size); | 216 segment_bytes_allocated_ -= size; |
196 Malloced::Delete(segment); | 217 Malloced::Delete(segment); |
197 } | 218 } |
198 | 219 |
199 | 220 |
200 Address Zone::NewExpand(int size) { | 221 Address Zone::NewExpand(int size) { |
201 // Make sure the requested size is already properly aligned and that | 222 // Make sure the requested size is already properly aligned and that |
202 // there isn't enough room in the Zone to satisfy the request. | 223 // there isn't enough room in the Zone to satisfy the request. |
203 DCHECK(size == RoundDown(size, kAlignment)); | 224 DCHECK(size == RoundDown(size, kAlignment)); |
204 DCHECK(size > limit_ - position_); | 225 DCHECK(size > limit_ - position_); |
205 | 226 |
206 // Compute the new segment size. We use a 'high water mark' | 227 // Compute the new segment size. We use a 'high water mark' |
207 // strategy, where we increase the segment size every time we expand | 228 // strategy, where we increase the segment size every time we expand |
208 // except that we employ a maximum segment size when we delete. This | 229 // except that we employ a maximum segment size when we delete. This |
209 // is to avoid excessive malloc() and free() overhead. | 230 // is to avoid excessive malloc() and free() overhead. |
210 Segment* head = segment_head_; | 231 Segment* head = segment_head_; |
211 const size_t old_size = (head == NULL) ? 0 : head->size(); | 232 const size_t old_size = (head == nullptr) ? 0 : head->size(); |
212 static const size_t kSegmentOverhead = sizeof(Segment) + kAlignment; | 233 static const size_t kSegmentOverhead = sizeof(Segment) + kAlignment; |
213 const size_t new_size_no_overhead = size + (old_size << 1); | 234 const size_t new_size_no_overhead = size + (old_size << 1); |
214 size_t new_size = kSegmentOverhead + new_size_no_overhead; | 235 size_t new_size = kSegmentOverhead + new_size_no_overhead; |
215 const size_t min_new_size = kSegmentOverhead + static_cast<size_t>(size); | 236 const size_t min_new_size = kSegmentOverhead + static_cast<size_t>(size); |
216 // Guard against integer overflow. | 237 // Guard against integer overflow. |
217 if (new_size_no_overhead < static_cast<size_t>(size) || | 238 if (new_size_no_overhead < static_cast<size_t>(size) || |
218 new_size < static_cast<size_t>(kSegmentOverhead)) { | 239 new_size < static_cast<size_t>(kSegmentOverhead)) { |
219 V8::FatalProcessOutOfMemory("Zone"); | 240 FatalProcessOutOfMemory("Zone"); |
220 return NULL; | 241 return nullptr; |
221 } | 242 } |
222 if (new_size < static_cast<size_t>(kMinimumSegmentSize)) { | 243 if (new_size < static_cast<size_t>(kMinimumSegmentSize)) { |
223 new_size = kMinimumSegmentSize; | 244 new_size = kMinimumSegmentSize; |
224 } else if (new_size > static_cast<size_t>(kMaximumSegmentSize)) { | 245 } else if (new_size > static_cast<size_t>(kMaximumSegmentSize)) { |
225 // Limit the size of new segments to avoid growing the segment size | 246 // Limit the size of new segments to avoid growing the segment size |
226 // exponentially, thus putting pressure on contiguous virtual address space. | 247 // exponentially, thus putting pressure on contiguous virtual address space. |
227 // All the while making sure to allocate a segment large enough to hold the | 248 // All the while making sure to allocate a segment large enough to hold the |
228 // requested size. | 249 // requested size. |
229 new_size = Max(min_new_size, static_cast<size_t>(kMaximumSegmentSize)); | 250 new_size = Max(min_new_size, static_cast<size_t>(kMaximumSegmentSize)); |
230 } | 251 } |
231 if (new_size > INT_MAX) { | 252 if (new_size > INT_MAX) { |
232 V8::FatalProcessOutOfMemory("Zone"); | 253 FatalProcessOutOfMemory("Zone"); |
233 return NULL; | 254 return nullptr; |
234 } | 255 } |
235 Segment* segment = NewSegment(static_cast<int>(new_size)); | 256 Segment* segment = NewSegment(static_cast<int>(new_size)); |
236 if (segment == NULL) { | 257 if (segment == nullptr) { |
237 V8::FatalProcessOutOfMemory("Zone"); | 258 FatalProcessOutOfMemory("Zone"); |
238 return NULL; | 259 return nullptr; |
239 } | 260 } |
240 | 261 |
241 // Recompute 'top' and 'limit' based on the new segment. | 262 // Recompute 'top' and 'limit' based on the new segment. |
242 Address result = RoundUp(segment->start(), kAlignment); | 263 Address result = RoundUp(segment->start(), kAlignment); |
243 position_ = result + size; | 264 position_ = result + size; |
244 // Check for address overflow. | 265 // Check for address overflow. |
245 // (Should not happen since the segment is guaranteed to accomodate | 266 // (Should not happen since the segment is guaranteed to accomodate |
246 // size bytes + header and alignment padding) | 267 // size bytes + header and alignment padding) |
247 if (reinterpret_cast<uintptr_t>(position_) | 268 DCHECK_GE(reinterpret_cast<uintptr_t>(position_), |
248 < reinterpret_cast<uintptr_t>(result)) { | 269 reinterpret_cast<uintptr_t>(result)); |
249 V8::FatalProcessOutOfMemory("Zone"); | |
250 return NULL; | |
251 } | |
252 limit_ = segment->end(); | 270 limit_ = segment->end(); |
253 DCHECK(position_ <= limit_); | 271 DCHECK(position_ <= limit_); |
254 return result; | 272 return result; |
255 } | 273 } |
256 | 274 |
257 | 275 } // namespace internal |
258 } } // namespace v8::internal | 276 } // namespace v8 |
OLD | NEW |