blob: a37a443c25f52a41be4c12401caaeee4eb1a20cd [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/zone.h"
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/flags.h"
#include "vm/heap.h"
#include "vm/isolate.h"
#include "vm/os.h"
namespace dart {
DEFINE_DEBUG_FLAG(bool, trace_zones,
false, "Traces allocation sizes in the zone.");
// Zone segments represent chunks of memory: They have starting
// address encoded in the this pointer and a size in bytes. They are
// chained together to form the backing storage for an expanding zone.
class Zone::Segment {
public:
Segment* next() const { return next_; }
intptr_t size() const { return size_; }
uword start() { return address(sizeof(Segment)); }
uword end() { return address(size_); }
// Allocate or delete individual segments.
static Segment* New(intptr_t size, Segment* next);
static void DeleteSegmentList(Segment* segment);
private:
Segment* next_;
intptr_t size_;
// Computes the address of the nth byte in this segment.
uword address(int n) { return reinterpret_cast<uword>(this) + n; }
static void Delete(Segment* segment) { delete[] segment; }
DISALLOW_IMPLICIT_CONSTRUCTORS(Segment);
};
void Zone::Segment::DeleteSegmentList(Segment* head) {
Segment* current = head;
while (current != NULL) {
Segment* next = current->next();
#ifdef DEBUG
// Zap the entire current segment (including the header).
memset(current, kZapDeletedByte, current->size());
#endif
Segment::Delete(current);
current = next;
}
}
Zone::Segment* Zone::Segment::New(intptr_t size, Zone::Segment* next) {
ASSERT(size >= 0);
Segment* result = reinterpret_cast<Segment*>(new uint8_t[size]);
if (result != NULL) {
#ifdef DEBUG
// Zap the entire allocated segment (including the header).
memset(result, kZapUninitializedByte, size);
#endif
result->next_ = next;
result->size_ = size;
}
return result;
}
Zone::Zone()
: initial_buffer_(buffer_, kInitialChunkSize),
position_(initial_buffer_.start()),
limit_(initial_buffer_.end()),
head_(NULL),
large_segments_(NULL),
handles_(),
previous_(NULL) {
#ifdef DEBUG
// Zap the entire initial buffer.
memset(initial_buffer_.pointer(), kZapUninitializedByte,
initial_buffer_.size());
#endif
}
Zone::~Zone() {
#if defined(DEBUG)
if (FLAG_trace_zones) {
DumpZoneSizes();
}
#endif
DeleteAll();
}
void Zone::DeleteAll() {
// Traverse the chained list of segments, zapping (in debug mode)
// and freeing every zone segment.
Segment::DeleteSegmentList(head_);
Segment::DeleteSegmentList(large_segments_);
// Reset zone state.
#ifdef DEBUG
memset(initial_buffer_.pointer(), kZapDeletedByte, initial_buffer_.size());
#endif
position_ = initial_buffer_.start();
limit_ = initial_buffer_.end();
head_ = NULL;
large_segments_ = NULL;
}
intptr_t Zone::SizeInBytes() const {
intptr_t size = 0;
for (Segment* s = large_segments_; s != NULL; s = s->next()) {
size += s->size();
}
if (head_ == NULL) {
return size + (position_ - initial_buffer_.start());
}
size += initial_buffer_.size();
for (Segment* s = head_->next(); s != NULL; s = s->next()) {
size += s->size();
}
return size + (position_ - head_->start());
}
uword Zone::AllocateExpand(intptr_t size) {
#if defined(DEBUG)
ASSERT(size >= 0);
if (FLAG_trace_zones) {
OS::PrintErr("*** Expanding zone 0x%"Px"\n",
reinterpret_cast<intptr_t>(this));
DumpZoneSizes();
}
// Make sure the requested size is already properly aligned and that
// there isn't enough room in the Zone to satisfy the request.
ASSERT(Utils::IsAligned(size, kAlignment));
intptr_t free_size = (limit_ - position_);
ASSERT(free_size < size);
#endif
// First check to see if we should just chain it as a large segment.
intptr_t max_size = Utils::RoundDown(kSegmentSize - sizeof(Segment),
kAlignment);
ASSERT(max_size > 0);
if (size > max_size) {
return AllocateLargeSegment(size);
}
// Allocate another segment and chain it up.
head_ = Segment::New(kSegmentSize, head_);
// Recompute 'position' and 'limit' based on the new head segment.
uword result = Utils::RoundUp(head_->start(), kAlignment);
position_ = result + size;
limit_ = head_->end();
ASSERT(position_ <= limit_);
return result;
}
uword Zone::AllocateLargeSegment(intptr_t size) {
#if defined(DEBUG)
ASSERT(size >= 0);
// Make sure the requested size is already properly aligned and that
// there isn't enough room in the Zone to satisfy the request.
ASSERT(Utils::IsAligned(size, kAlignment));
intptr_t free_size = (limit_ - position_);
ASSERT(free_size < size);
#endif
// Create a new large segment and chain it up.
ASSERT(Utils::IsAligned(sizeof(Segment), kAlignment));
size += sizeof(Segment); // Account for book keeping fields in size.
large_segments_ = Segment::New(size, large_segments_);
uword result = Utils::RoundUp(large_segments_->start(), kAlignment);
return result;
}
char* Zone::MakeCopyOfString(const char* str) {
intptr_t len = strlen(str) + 1; // '\0'-terminated.
char* copy = Alloc<char>(len);
strncpy(copy, str, len);
return copy;
}
#if defined(DEBUG)
void Zone::DumpZoneSizes() {
intptr_t size = 0;
for (Segment* s = large_segments_; s != NULL; s = s->next()) {
size += s->size();
}
OS::PrintErr("*** Zone(0x%"Px") size in bytes,"
" Total = %"Pd" Large Segments = %"Pd"\n",
reinterpret_cast<intptr_t>(this), SizeInBytes(), size);
}
#endif
StackZone::StackZone(BaseIsolate* isolate)
: StackResource(isolate),
zone_() {
#ifdef DEBUG
if (FLAG_trace_zones) {
OS::PrintErr("*** Starting a new Stack zone 0x%"Px"(0x%"Px")\n",
reinterpret_cast<intptr_t>(this),
reinterpret_cast<intptr_t>(&zone_));
}
#endif
zone_.Link(isolate->current_zone());
isolate->set_current_zone(&zone_);
}
StackZone::~StackZone() {
ASSERT(isolate()->current_zone() == &zone_);
isolate()->set_current_zone(zone_.previous_);
#ifdef DEBUG
if (FLAG_trace_zones) {
OS::PrintErr("*** Deleting Stack zone 0x%"Px"(0x%"Px")\n",
reinterpret_cast<intptr_t>(this),
reinterpret_cast<intptr_t>(&zone_));
}
#endif
}
void Zone::VisitObjectPointers(ObjectPointerVisitor* visitor) {
Zone* zone = this;
while (zone != NULL) {
zone->handles()->VisitObjectPointers(visitor);
zone = zone->previous_;
}
}
char* Zone::PrintToString(const char* format, ...) {
va_list args;
va_start(args, format);
intptr_t len = OS::VSNPrint(NULL, 0, format, args);
va_end(args);
char* buffer = Alloc<char>(len + 1);
va_list args2;
va_start(args2, format);
OS::VSNPrint(buffer, (len + 1), format, args2);
va_end(args2);
return buffer;
}
} // namespace dart