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