// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/snapshot/serializer-allocator.h" #include "src/heap/heap-inl.h" // crbug.com/v8/8499 #include "src/snapshot/references.h" #include "src/snapshot/serializer.h" #include "src/snapshot/snapshot-source-sink.h" namespace v8 { namespace internal { SerializerAllocator::SerializerAllocator(Serializer* serializer) : serializer_(serializer) { for (int i = 0; i < kNumberOfPreallocatedSpaces; i++) { pending_chunk_[i] = 0; } } void SerializerAllocator::UseCustomChunkSize(uint32_t chunk_size) { custom_chunk_size_ = chunk_size; } static uint32_t PageSizeOfSpace(SnapshotSpace space) { return static_cast<uint32_t>( MemoryChunkLayout::AllocatableMemoryInMemoryChunk( static_cast<AllocationSpace>(space))); } uint32_t SerializerAllocator::TargetChunkSize(SnapshotSpace space) { if (custom_chunk_size_ == 0) return PageSizeOfSpace(space); DCHECK_LE(custom_chunk_size_, PageSizeOfSpace(space)); return custom_chunk_size_; } SerializerReference SerializerAllocator::Allocate(SnapshotSpace space, uint32_t size) { const int space_number = static_cast<int>(space); DCHECK(IsPreAllocatedSpace(space)); DCHECK(size > 0 && size <= PageSizeOfSpace(space)); // Maps are allocated through AllocateMap. DCHECK_NE(SnapshotSpace::kMap, space); uint32_t old_chunk_size = pending_chunk_[space_number]; uint32_t new_chunk_size = old_chunk_size + size; // Start a new chunk if the new size exceeds the target chunk size. // We may exceed the target chunk size if the single object size does. if (new_chunk_size > TargetChunkSize(space) && old_chunk_size != 0) { serializer_->PutNextChunk(space); completed_chunks_[space_number].push_back(pending_chunk_[space_number]); pending_chunk_[space_number] = 0; new_chunk_size = size; } uint32_t offset = pending_chunk_[space_number]; pending_chunk_[space_number] = new_chunk_size; return SerializerReference::BackReference( space, static_cast<uint32_t>(completed_chunks_[space_number].size()), offset); } SerializerReference SerializerAllocator::AllocateMap() { // Maps are allocated one-by-one when deserializing. return SerializerReference::MapReference(num_maps_++); } SerializerReference SerializerAllocator::AllocateLargeObject(uint32_t size) { // Large objects are allocated one-by-one when deserializing. We do not // have to keep track of multiple chunks. large_objects_total_size_ += size; return SerializerReference::LargeObjectReference(seen_large_objects_index_++); } SerializerReference SerializerAllocator::AllocateOffHeapBackingStore() { DCHECK_NE(0, seen_backing_stores_index_); return SerializerReference::OffHeapBackingStoreReference( seen_backing_stores_index_++); } #ifdef DEBUG bool SerializerAllocator::BackReferenceIsAlreadyAllocated( SerializerReference reference) const { DCHECK(reference.is_back_reference()); SnapshotSpace space = reference.space(); if (space == SnapshotSpace::kLargeObject) { return reference.large_object_index() < seen_large_objects_index_; } else if (space == SnapshotSpace::kMap) { return reference.map_index() < num_maps_; } else if (space == SnapshotSpace::kReadOnlyHeap && serializer_->isolate()->heap()->deserialization_complete()) { // If not deserializing the isolate itself, then we create BackReferences // for all read-only heap objects without ever allocating. return true; } else { const int space_number = static_cast<int>(space); size_t chunk_index = reference.chunk_index(); if (chunk_index == completed_chunks_[space_number].size()) { return reference.chunk_offset() < pending_chunk_[space_number]; } else { return chunk_index < completed_chunks_[space_number].size() && reference.chunk_offset() < completed_chunks_[space_number][chunk_index]; } } } #endif std::vector<SerializedData::Reservation> SerializerAllocator::EncodeReservations() const { std::vector<SerializedData::Reservation> out; for (int i = 0; i < kNumberOfPreallocatedSpaces; i++) { for (size_t j = 0; j < completed_chunks_[i].size(); j++) { out.emplace_back(completed_chunks_[i][j]); } if (pending_chunk_[i] > 0 || completed_chunks_[i].size() == 0) { out.emplace_back(pending_chunk_[i]); } out.back().mark_as_last(); } STATIC_ASSERT(SnapshotSpace::kMap == SnapshotSpace::kNumberOfPreallocatedSpaces); out.emplace_back(num_maps_ * Map::kSize); out.back().mark_as_last(); STATIC_ASSERT(static_cast<int>(SnapshotSpace::kLargeObject) == static_cast<int>(SnapshotSpace::kNumberOfPreallocatedSpaces) + 1); out.emplace_back(large_objects_total_size_); out.back().mark_as_last(); return out; } void SerializerAllocator::OutputStatistics() { DCHECK(FLAG_serialization_statistics); PrintF(" Spaces (bytes):\n"); for (int space = 0; space < kNumberOfSpaces; space++) { PrintF("%16s", Heap::GetSpaceName(static_cast<AllocationSpace>(space))); } PrintF("\n"); for (int space = 0; space < kNumberOfPreallocatedSpaces; space++) { size_t s = pending_chunk_[space]; for (uint32_t chunk_size : completed_chunks_[space]) s += chunk_size; PrintF("%16zu", s); } STATIC_ASSERT(SnapshotSpace::kMap == SnapshotSpace::kNumberOfPreallocatedSpaces); PrintF("%16d", num_maps_ * Map::kSize); STATIC_ASSERT(static_cast<int>(SnapshotSpace::kLargeObject) == static_cast<int>(SnapshotSpace::kNumberOfPreallocatedSpaces) + 1); PrintF("%16d\n", large_objects_total_size_); } } // namespace internal } // namespace v8