blob: 559e9434bb890f61bcecad1f9e87d403cdb3f3aa [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
#include "space.h"
#include <sys/mman.h>
#include "UniquePtr.h"
#include "file.h"
#include "image.h"
#include "logging.h"
#include "os.h"
#include "utils.h"
namespace art {
Space* Space::Create(const std::string& name, size_t initial_size, size_t maximum_size, size_t growth_size, byte* requested_base) {
UniquePtr<Space> space(new Space(name));
bool success = space->Init(initial_size, maximum_size, growth_size, requested_base);
if (!success) {
return NULL;
} else {
return space.release();
}
}
Space* Space::CreateFromImage(const std::string& image_file_name) {
CHECK(image_file_name != NULL);
UniquePtr<Space> space(new Space(image_file_name));
bool success = space->InitFromImage(image_file_name);
if (!success) {
return NULL;
} else {
return space.release();
}
}
Space::~Space() {}
void* Space::CreateMallocSpace(void* base,
size_t initial_size,
size_t maximum_size) {
errno = 0;
bool is_locked = false;
size_t commit_size = initial_size / 2;
void* msp = create_contiguous_mspace_with_base(commit_size, maximum_size,
is_locked, base);
if (msp != NULL) {
// Do not permit the heap grow past the starting size without our
// intervention.
mspace_set_max_allowed_footprint(msp, initial_size);
} else {
// There is no guarantee that errno has meaning when the call
// fails, but it often does.
PLOG(ERROR) << "create_contiguous_mspace_with_base failed";
}
return msp;
}
bool Space::Init(size_t initial_size, size_t maximum_size, size_t growth_size, byte* requested_base) {
const Runtime* runtime = Runtime::Current();
if (runtime->IsVerboseStartup()) {
LOG(INFO) << "Space::Init entering"
<< " initial_size=" << initial_size
<< " maximum_size=" << maximum_size
<< " growth_size=" << growth_size
<< " requested_base=" << reinterpret_cast<void*>(requested_base);
}
if (initial_size > growth_size) {
LOG(ERROR) << "Failed to create space with initial size > growth size ("
<< initial_size << ">" << growth_size << ")";
return false;
}
if (growth_size > maximum_size) {
LOG(ERROR) << "Failed to create space with growth size > maximum size ("
<< growth_size << ">" << maximum_size << ")";
return false;
}
size_t length = RoundUp(maximum_size, kPageSize);
int prot = PROT_READ | PROT_WRITE;
UniquePtr<MemMap> mem_map(MemMap::Map(requested_base, length, prot));
if (mem_map.get() == NULL) {
LOG(WARNING) << "Failed to allocate " << length << " bytes for space";
return false;
}
Init(mem_map.release());
maximum_size_ = maximum_size;
size_t growth_length = RoundUp(growth_size, kPageSize);
growth_size_ = growth_size;
growth_limit_ = base_ + growth_length;
mspace_ = CreateMallocSpace(base_, initial_size, maximum_size);
if (mspace_ == NULL) {
LOG(WARNING) << "Failed to create mspace for space";
return false;
}
if (runtime->IsVerboseStartup()) {
LOG(INFO) << "Space::Init exiting";
}
return true;
}
void Space::Init(MemMap* mem_map) {
mem_map_.reset(mem_map);
base_ = mem_map_->GetAddress();
limit_ = base_ + mem_map->GetLength();
}
bool Space::InitFromImage(const std::string& image_file_name) {
Runtime* runtime = Runtime::Current();
if (runtime->IsVerboseStartup()) {
LOG(INFO) << "Space::InitFromImage entering"
<< " image_file_name=" << image_file_name;
}
UniquePtr<File> file(OS::OpenFile(image_file_name.c_str(), false));
if (file.get() == NULL) {
LOG(WARNING) << "Failed to open " << image_file_name;
return false;
}
ImageHeader image_header;
bool success = file->ReadFully(&image_header, sizeof(image_header));
if (!success || !image_header.IsValid()) {
LOG(WARNING) << "Invalid image header " << image_file_name;
return false;
}
UniquePtr<MemMap> map(MemMap::Map(image_header.GetImageBaseAddr(),
file->Length(),
// TODO: selectively PROT_EXEC an image subset containing stubs
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_FIXED,
file->Fd(),
0));
if (map.get() == NULL) {
LOG(WARNING) << "Failed to map " << image_file_name;
return false;
}
CHECK_EQ(image_header.GetImageBaseAddr(), map->GetAddress());
image_header_ = reinterpret_cast<ImageHeader*>(map->GetAddress());
DCHECK_EQ(0, memcmp(&image_header, image_header_, sizeof(ImageHeader)));
Object* jni_stub_array = image_header.GetImageRoot(ImageHeader::kJniStubArray);
runtime->SetJniStubArray(down_cast<ByteArray*>(jni_stub_array));
Object* ame_stub_array = image_header.GetImageRoot(ImageHeader::kAbstractMethodErrorStubArray);
runtime->SetAbstractMethodErrorStubArray(down_cast<ByteArray*>(ame_stub_array));
Object* resolution_stub_array = image_header.GetImageRoot(ImageHeader::kInstanceResolutionStubArray);
runtime->SetResolutionStubArray(
down_cast<ByteArray*>(resolution_stub_array), Runtime::kInstanceMethod);
resolution_stub_array = image_header.GetImageRoot(ImageHeader::kStaticResolutionStubArray);
runtime->SetResolutionStubArray(
down_cast<ByteArray*>(resolution_stub_array), Runtime::kStaticMethod);
resolution_stub_array = image_header.GetImageRoot(ImageHeader::kUnknownMethodResolutionStubArray);
runtime->SetResolutionStubArray(
down_cast<ByteArray*>(resolution_stub_array), Runtime::kUnknownMethod);
Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod);
runtime->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method), Runtime::kSaveAll);
callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsOnlySaveMethod);
runtime->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method), Runtime::kRefsOnly);
callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsAndArgsSaveMethod);
runtime->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method), Runtime::kRefsAndArgs);
Init(map.release());
growth_limit_ = limit_;
if (runtime->IsVerboseStartup()) {
LOG(INFO) << "Space::InitFromImage exiting";
}
return true;
}
Object* Space::AllocWithoutGrowth(size_t num_bytes) {
DCHECK(mspace_ != NULL);
return reinterpret_cast<Object*>(mspace_calloc(mspace_, 1, num_bytes));
}
Object* Space::AllocWithGrowth(size_t num_bytes) {
DCHECK(mspace_ != NULL);
// Grow as much as possible within the mspace.
size_t max_allowed = growth_size_;
mspace_set_max_allowed_footprint(mspace_, max_allowed);
// Try the allocation.
void* ptr = AllocWithoutGrowth(num_bytes);
// Shrink back down as small as possible.
size_t footprint = mspace_footprint(mspace_);
mspace_set_max_allowed_footprint(mspace_, footprint);
// Return the new allocation or NULL.
return reinterpret_cast<Object*>(ptr);
}
size_t Space::Free(void* ptr) {
DCHECK(mspace_ != NULL);
DCHECK(ptr != NULL);
size_t num_bytes = mspace_usable_size(mspace_, ptr);
mspace_free(mspace_, ptr);
return num_bytes;
}
size_t Space::AllocationSize(const Object* obj) {
DCHECK(mspace_ != NULL);
return mspace_usable_size(mspace_, obj) + kChunkOverhead;
}
void Space::DontNeed(void* start, void* end, void* num_bytes) {
start = (void*)RoundUp((uintptr_t)start, kPageSize);
end = (void*)RoundDown((uintptr_t)end, kPageSize);
if (start >= end) {
return;
}
size_t length = reinterpret_cast<byte*>(end) - reinterpret_cast<byte*>(start);
int result = madvise(start, length, MADV_DONTNEED);
if (result == -1) {
PLOG(WARNING) << "madvise failed";
} else {
*reinterpret_cast<size_t*>(num_bytes) += length;
}
}
void Space::Trim() {
CHECK(mspace_ != NULL);
mspace_trim(mspace_, 0);
size_t num_bytes_released = 0;
mspace_walk_free_pages(mspace_, DontNeed, &num_bytes_released);
}
void Space::Walk(void(*callback)(const void*, size_t, const void*, size_t, void*), void* arg) {
if (mspace_ != NULL) {
mspace_walk_heap(mspace_, callback, arg);
}
}
size_t Space::GetMaxAllowedFootprint() {
DCHECK(mspace_ != NULL);
return mspace_max_allowed_footprint(mspace_);
}
void Space::SetMaxAllowedFootprint(size_t limit) {
DCHECK(mspace_ != NULL);
// Compare against the actual footprint, rather than the
// max_allowed, because the heap may not have grown all the
// way to the allowed size yet.
//
size_t current_space_size = mspace_footprint(mspace_);
if (limit < current_space_size) {
// Don't let the space grow any more.
mspace_set_max_allowed_footprint(mspace_, current_space_size);
} else {
// Let the heap grow to the requested limit.
mspace_set_max_allowed_footprint(mspace_, limit);
}
}
void Space::Grow(size_t new_size) {
UNIMPLEMENTED(FATAL);
}
} // namespace art