// Copyright 2011 Google Inc. All Rights Reserved. #include "space.h" #include #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(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(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(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 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(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 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(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(jni_stub_array)); Object* ame_stub_array = image_header.GetImageRoot(ImageHeader::kAbstractMethodErrorStubArray); runtime->SetAbstractMethodErrorStubArray(down_cast(ame_stub_array)); Object* resolution_stub_array = image_header.GetImageRoot(ImageHeader::kInstanceResolutionStubArray); runtime->SetResolutionStubArray( down_cast(resolution_stub_array), Runtime::kInstanceMethod); resolution_stub_array = image_header.GetImageRoot(ImageHeader::kStaticResolutionStubArray); runtime->SetResolutionStubArray( down_cast(resolution_stub_array), Runtime::kStaticMethod); resolution_stub_array = image_header.GetImageRoot(ImageHeader::kUnknownMethodResolutionStubArray); runtime->SetResolutionStubArray( down_cast(resolution_stub_array), Runtime::kUnknownMethod); Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod); runtime->SetCalleeSaveMethod(down_cast(callee_save_method), Runtime::kSaveAll); callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsOnlySaveMethod); runtime->SetCalleeSaveMethod(down_cast(callee_save_method), Runtime::kRefsOnly); callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsAndArgsSaveMethod); runtime->SetCalleeSaveMethod(down_cast(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(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(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::FreeList(size_t num_ptrs, void** ptrs) { DCHECK(mspace_ != NULL); DCHECK(ptrs != NULL); void* merged = ptrs[0]; size_t num_bytes = 0; for (size_t i = 1; i < num_ptrs; i++) { num_bytes += mspace_usable_size(mspace_, ptrs[i]); if (mspace_merge_objects(mspace_, merged, ptrs[i]) == NULL) { mspace_free(mspace_, merged); merged = ptrs[i]; } } CHECK(merged != NULL); mspace_free(mspace_, merged); 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(end) - reinterpret_cast(start); int result = madvise(start, length, MADV_DONTNEED); if (result == -1) { PLOG(WARNING) << "madvise failed"; } else { *reinterpret_cast(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