| 1 | // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #ifndef RUNTIME_VM_IMAGE_SNAPSHOT_H_ |
| 6 | #define RUNTIME_VM_IMAGE_SNAPSHOT_H_ |
| 7 | |
| 8 | #include <memory> |
| 9 | #include <utility> |
| 10 | |
| 11 | #include "platform/assert.h" |
| 12 | #include "platform/utils.h" |
| 13 | #include "vm/allocation.h" |
| 14 | #include "vm/compiler/runtime_api.h" |
| 15 | #include "vm/datastream.h" |
| 16 | #include "vm/globals.h" |
| 17 | #include "vm/growable_array.h" |
| 18 | #include "vm/hash_map.h" |
| 19 | #include "vm/object.h" |
| 20 | #include "vm/reusable_handles.h" |
| 21 | #include "vm/type_testing_stubs.h" |
| 22 | #include "vm/v8_snapshot_writer.h" |
| 23 | |
| 24 | namespace dart { |
| 25 | |
| 26 | // Forward declarations. |
| 27 | class Code; |
| 28 | class Dwarf; |
| 29 | class Elf; |
| 30 | class Instructions; |
| 31 | class Object; |
| 32 | |
| 33 | class Image : ValueObject { |
| 34 | public: |
| 35 | explicit Image(const void* raw_memory) : raw_memory_(raw_memory) { |
| 36 | ASSERT(Utils::IsAligned(raw_memory, kMaxObjectAlignment)); |
| 37 | } |
| 38 | |
| 39 | void* object_start() const { |
| 40 | return reinterpret_cast<void*>(reinterpret_cast<uword>(raw_memory_) + |
| 41 | kHeaderSize); |
| 42 | } |
| 43 | |
| 44 | uword object_size() const { |
| 45 | uword snapshot_size = *reinterpret_cast<const uword*>(raw_memory_); |
| 46 | return snapshot_size - kHeaderSize; |
| 47 | } |
| 48 | |
| 49 | bool contains(uword address) const { |
| 50 | uword start = reinterpret_cast<uword>(object_start()); |
| 51 | return address >= start && (address - start < object_size()); |
| 52 | } |
| 53 | |
| 54 | // Returns the offset of the BSS section from this image. Only has meaning for |
| 55 | // instructions images. |
| 56 | word bss_offset() const { |
| 57 | auto const raw_value = *(reinterpret_cast<const word*>(raw_memory_) + 1); |
| 58 | return Utils::RoundDown(raw_value, kBssAlignment); |
| 59 | } |
| 60 | |
| 61 | // Returns true if the image was compiled directly to ELF. Only has meaning |
| 62 | // for instructions images. |
| 63 | bool compiled_to_elf() const { |
| 64 | auto const raw_value = *(reinterpret_cast<const word*>(raw_memory_) + 1); |
| 65 | return (raw_value & 0x1) == 0x1; |
| 66 | } |
| 67 | |
| 68 | private: |
| 69 | static constexpr intptr_t = 2; |
| 70 | static constexpr intptr_t = kMaxObjectAlignment; |
| 71 | // Explicitly double-checking kHeaderSize is never changed. Increasing the |
| 72 | // Image header size would mean objects would not start at a place expected |
| 73 | // by parts of the VM (like the GC) that use Image pages as HeapPages. |
| 74 | static_assert(kHeaderSize == kMaxObjectAlignment, |
| 75 | "Image page cannot be used as HeapPage" ); |
| 76 | |
| 77 | // Determines how many bits we have for encoding any extra information in |
| 78 | // the BSS offset. |
| 79 | static constexpr intptr_t kBssAlignment = compiler::target::kWordSize; |
| 80 | |
| 81 | const void* raw_memory_; // The symbol kInstructionsSnapshot. |
| 82 | |
| 83 | // For access to private constants. |
| 84 | friend class AssemblyImageWriter; |
| 85 | friend class BlobImageWriter; |
| 86 | friend class ImageWriter; |
| 87 | friend class Elf; |
| 88 | |
| 89 | DISALLOW_COPY_AND_ASSIGN(Image); |
| 90 | }; |
| 91 | |
| 92 | class ImageReader : public ZoneAllocated { |
| 93 | public: |
| 94 | ImageReader(const uint8_t* data_image, const uint8_t* instructions_image); |
| 95 | |
| 96 | ApiErrorPtr VerifyAlignment() const; |
| 97 | |
| 98 | ONLY_IN_PRECOMPILED(uword GetBareInstructionsAt(uint32_t offset) const); |
| 99 | ONLY_IN_PRECOMPILED(uword GetBareInstructionsEnd() const); |
| 100 | InstructionsPtr GetInstructionsAt(uint32_t offset) const; |
| 101 | ObjectPtr GetObjectAt(uint32_t offset) const; |
| 102 | |
| 103 | private: |
| 104 | const uint8_t* data_image_; |
| 105 | const uint8_t* instructions_image_; |
| 106 | |
| 107 | DISALLOW_COPY_AND_ASSIGN(ImageReader); |
| 108 | }; |
| 109 | |
| 110 | struct ObjectOffsetPair { |
| 111 | public: |
| 112 | ObjectOffsetPair() : ObjectOffsetPair(NULL, 0) {} |
| 113 | ObjectOffsetPair(ObjectPtr obj, int32_t off) : object(obj), offset(off) {} |
| 114 | |
| 115 | ObjectPtr object; |
| 116 | int32_t offset; |
| 117 | }; |
| 118 | |
| 119 | class ObjectOffsetTrait { |
| 120 | public: |
| 121 | // Typedefs needed for the DirectChainedHashMap template. |
| 122 | typedef ObjectPtr Key; |
| 123 | typedef int32_t Value; |
| 124 | typedef ObjectOffsetPair Pair; |
| 125 | |
| 126 | static Key KeyOf(Pair kv) { return kv.object; } |
| 127 | static Value ValueOf(Pair kv) { return kv.offset; } |
| 128 | static intptr_t Hashcode(Key key); |
| 129 | static inline bool IsKeyEqual(Pair pair, Key key); |
| 130 | }; |
| 131 | |
| 132 | typedef DirectChainedHashMap<ObjectOffsetTrait> ObjectOffsetMap; |
| 133 | |
| 134 | // A command which instructs the image writer to emit something into the ".text" |
| 135 | // segment. |
| 136 | // |
| 137 | // For now this supports |
| 138 | // |
| 139 | // * emitting the instructions of a [Code] object |
| 140 | // * emitting a trampoline of a certain size |
| 141 | // |
| 142 | struct ImageWriterCommand { |
| 143 | enum Opcode { |
| 144 | InsertInstructionOfCode, |
| 145 | InsertBytesOfTrampoline, |
| 146 | }; |
| 147 | |
| 148 | ImageWriterCommand(intptr_t expected_offset, CodePtr code) |
| 149 | : expected_offset(expected_offset), |
| 150 | op(ImageWriterCommand::InsertInstructionOfCode), |
| 151 | insert_instruction_of_code({code}) {} |
| 152 | |
| 153 | ImageWriterCommand(intptr_t expected_offset, |
| 154 | uint8_t* trampoline_bytes, |
| 155 | intptr_t trampoine_length) |
| 156 | : expected_offset(expected_offset), |
| 157 | op(ImageWriterCommand::InsertBytesOfTrampoline), |
| 158 | insert_trampoline_bytes({trampoline_bytes, trampoine_length}) {} |
| 159 | |
| 160 | // The offset (relative to the very first [ImageWriterCommand]) we expect |
| 161 | // this [ImageWriterCommand] to have. |
| 162 | intptr_t expected_offset; |
| 163 | |
| 164 | Opcode op; |
| 165 | union { |
| 166 | struct { |
| 167 | CodePtr code; |
| 168 | } insert_instruction_of_code; |
| 169 | struct { |
| 170 | uint8_t* buffer; |
| 171 | intptr_t buffer_length; |
| 172 | } insert_trampoline_bytes; |
| 173 | }; |
| 174 | }; |
| 175 | |
| 176 | class ImageWriter : public ValueObject { |
| 177 | public: |
| 178 | explicit ImageWriter(Thread* thread); |
| 179 | virtual ~ImageWriter() {} |
| 180 | |
| 181 | void ResetOffsets() { |
| 182 | next_data_offset_ = Image::kHeaderSize; |
| 183 | next_text_offset_ = Image::kHeaderSize; |
| 184 | if (FLAG_use_bare_instructions && FLAG_precompiled_mode) { |
| 185 | next_text_offset_ += compiler::target::InstructionsSection::HeaderSize(); |
| 186 | } |
| 187 | objects_.Clear(); |
| 188 | instructions_.Clear(); |
| 189 | } |
| 190 | |
| 191 | // Will start preparing the ".text" segment by interpreting the provided |
| 192 | // [ImageWriterCommand]s. |
| 193 | void PrepareForSerialization(GrowableArray<ImageWriterCommand>* commands); |
| 194 | |
| 195 | bool IsROSpace() const { |
| 196 | return offset_space_ == V8SnapshotProfileWriter::kVmData || |
| 197 | offset_space_ == V8SnapshotProfileWriter::kVmText || |
| 198 | offset_space_ == V8SnapshotProfileWriter::kIsolateData || |
| 199 | offset_space_ == V8SnapshotProfileWriter::kIsolateText; |
| 200 | } |
| 201 | int32_t GetTextOffsetFor(InstructionsPtr instructions, CodePtr code); |
| 202 | uint32_t GetDataOffsetFor(ObjectPtr raw_object); |
| 203 | |
| 204 | void Write(WriteStream* clustered_stream, bool vm); |
| 205 | intptr_t data_size() const { return next_data_offset_; } |
| 206 | intptr_t text_size() const { return next_text_offset_; } |
| 207 | intptr_t GetTextObjectCount() const; |
| 208 | void GetTrampolineInfo(intptr_t* count, intptr_t* size) const; |
| 209 | |
| 210 | void DumpStatistics(); |
| 211 | |
| 212 | void SetProfileWriter(V8SnapshotProfileWriter* profile_writer) { |
| 213 | profile_writer_ = profile_writer; |
| 214 | } |
| 215 | |
| 216 | void ClearProfileWriter() { profile_writer_ = nullptr; } |
| 217 | |
| 218 | void TraceInstructions(const Instructions& instructions); |
| 219 | |
| 220 | static intptr_t SizeInSnapshot(ObjectPtr object); |
| 221 | static const intptr_t kBareInstructionsAlignment = 4; |
| 222 | |
| 223 | static_assert( |
| 224 | (kObjectAlignmentLog2 - |
| 225 | compiler::target::ObjectAlignment::kObjectAlignmentLog2) >= 0, |
| 226 | "Target object alignment is larger than the host object alignment" ); |
| 227 | |
| 228 | // Converts the target object size (in bytes) to an appropriate argument for |
| 229 | // ObjectLayout::SizeTag methods on the host machine. |
| 230 | // |
| 231 | // ObjectLayout::SizeTag expects a size divisible by kObjectAlignment and |
| 232 | // checks this in debug mode, but the size on the target machine may not be |
| 233 | // divisible by the host machine's object alignment if they differ. |
| 234 | // |
| 235 | // If target_size = n, we convert it to n * m, where m is the host alignment |
| 236 | // divided by the target alignment. This means AdjustObjectSizeForTarget(n) |
| 237 | // encodes on the host machine to the same bits that decode to n on the target |
| 238 | // machine. That is: |
| 239 | // n * (host align / target align) / host align => n / target align |
| 240 | static constexpr intptr_t AdjustObjectSizeForTarget(intptr_t target_size) { |
| 241 | return target_size |
| 242 | << (kObjectAlignmentLog2 - |
| 243 | compiler::target::ObjectAlignment::kObjectAlignmentLog2); |
| 244 | } |
| 245 | |
| 246 | static UNLESS_DEBUG(constexpr) compiler::target::uword |
| 247 | UpdateObjectSizeForTarget(intptr_t size, uword marked_tags) { |
| 248 | return ObjectLayout::SizeTag::update(AdjustObjectSizeForTarget(size), |
| 249 | marked_tags); |
| 250 | } |
| 251 | |
| 252 | // Returns nullptr if there is no profile writer. |
| 253 | const char* ObjectTypeForProfile(const Object& object) const; |
| 254 | static const char* TagObjectTypeAsReadOnly(Zone* zone, const char* type); |
| 255 | |
| 256 | protected: |
| 257 | void WriteROData(WriteStream* stream); |
| 258 | virtual void WriteText(WriteStream* clustered_stream, bool vm) = 0; |
| 259 | |
| 260 | void DumpInstructionStats(); |
| 261 | void DumpInstructionsSizes(); |
| 262 | |
| 263 | struct InstructionsData { |
| 264 | InstructionsData(InstructionsPtr insns, CodePtr code, intptr_t text_offset) |
| 265 | : raw_insns_(insns), |
| 266 | raw_code_(code), |
| 267 | text_offset_(text_offset), |
| 268 | trampoline_bytes(nullptr), |
| 269 | trampoline_length(0) {} |
| 270 | |
| 271 | InstructionsData(uint8_t* trampoline_bytes, |
| 272 | intptr_t trampoline_length, |
| 273 | intptr_t text_offset) |
| 274 | : raw_insns_(nullptr), |
| 275 | raw_code_(nullptr), |
| 276 | text_offset_(text_offset), |
| 277 | trampoline_bytes(trampoline_bytes), |
| 278 | trampoline_length(trampoline_length) {} |
| 279 | |
| 280 | union { |
| 281 | InstructionsPtr raw_insns_; |
| 282 | const Instructions* insns_; |
| 283 | }; |
| 284 | union { |
| 285 | CodePtr raw_code_; |
| 286 | const Code* code_; |
| 287 | }; |
| 288 | intptr_t text_offset_; |
| 289 | |
| 290 | uint8_t* trampoline_bytes; |
| 291 | intptr_t trampoline_length; |
| 292 | }; |
| 293 | |
| 294 | struct ObjectData { |
| 295 | explicit ObjectData(ObjectPtr raw_obj) : raw_obj_(raw_obj) {} |
| 296 | |
| 297 | union { |
| 298 | ObjectPtr raw_obj_; |
| 299 | const Object* obj_; |
| 300 | }; |
| 301 | }; |
| 302 | |
| 303 | Heap* heap_; // Used for mapping RawInstructiosn to object ids. |
| 304 | intptr_t next_data_offset_; |
| 305 | intptr_t next_text_offset_; |
| 306 | GrowableArray<ObjectData> objects_; |
| 307 | GrowableArray<InstructionsData> instructions_; |
| 308 | |
| 309 | V8SnapshotProfileWriter::IdSpace offset_space_ = |
| 310 | V8SnapshotProfileWriter::kSnapshot; |
| 311 | V8SnapshotProfileWriter* profile_writer_ = nullptr; |
| 312 | const char* const instructions_section_type_; |
| 313 | const char* const instructions_type_; |
| 314 | const char* const trampoline_type_; |
| 315 | |
| 316 | template <class T> |
| 317 | friend class TraceImageObjectScope; |
| 318 | friend class SnapshotTextObjectNamer; // For InstructionsData. |
| 319 | |
| 320 | private: |
| 321 | DISALLOW_COPY_AND_ASSIGN(ImageWriter); |
| 322 | }; |
| 323 | |
| 324 | #define AutoTraceImage(object, section_offset, stream) \ |
| 325 | auto AutoTraceImagObjectScopeVar##__COUNTER__ = \ |
| 326 | TraceImageObjectScope<std::remove_pointer<decltype(stream)>::type>( \ |
| 327 | this, section_offset, stream, object); |
| 328 | |
| 329 | template <typename T> |
| 330 | class TraceImageObjectScope { |
| 331 | public: |
| 332 | TraceImageObjectScope(ImageWriter* writer, |
| 333 | intptr_t section_offset, |
| 334 | const T* stream, |
| 335 | const Object& object) |
| 336 | : writer_(ASSERT_NOTNULL(writer)), |
| 337 | stream_(ASSERT_NOTNULL(stream)), |
| 338 | section_offset_(section_offset), |
| 339 | start_offset_(stream_->Position() - section_offset), |
| 340 | object_(object) {} |
| 341 | |
| 342 | ~TraceImageObjectScope() { |
| 343 | if (writer_->profile_writer_ == nullptr) return; |
| 344 | ASSERT(writer_->IsROSpace()); |
| 345 | writer_->profile_writer_->SetObjectTypeAndName( |
| 346 | {writer_->offset_space_, start_offset_}, |
| 347 | writer_->ObjectTypeForProfile(object_), nullptr); |
| 348 | writer_->profile_writer_->AttributeBytesTo( |
| 349 | {writer_->offset_space_, start_offset_}, |
| 350 | stream_->Position() - section_offset_ - start_offset_); |
| 351 | } |
| 352 | |
| 353 | private: |
| 354 | ImageWriter* const writer_; |
| 355 | const T* const stream_; |
| 356 | const intptr_t section_offset_; |
| 357 | const intptr_t start_offset_; |
| 358 | const Object& object_; |
| 359 | }; |
| 360 | |
| 361 | class SnapshotTextObjectNamer { |
| 362 | public: |
| 363 | explicit SnapshotTextObjectNamer(Zone* zone) |
| 364 | : zone_(zone), |
| 365 | owner_(Object::Handle(zone)), |
| 366 | string_(String::Handle(zone)), |
| 367 | insns_(Instructions::Handle(zone)), |
| 368 | store_(Isolate::Current()->object_store()) {} |
| 369 | |
| 370 | const char* StubNameForType(const AbstractType& type) const; |
| 371 | |
| 372 | const char* SnapshotNameFor(intptr_t code_index, const Code& code); |
| 373 | const char* SnapshotNameFor(intptr_t index, |
| 374 | const ImageWriter::InstructionsData& data); |
| 375 | |
| 376 | private: |
| 377 | Zone* const zone_; |
| 378 | Object& owner_; |
| 379 | String& string_; |
| 380 | Instructions& insns_; |
| 381 | ObjectStore* const store_; |
| 382 | TypeTestingStubNamer namer_; |
| 383 | }; |
| 384 | |
| 385 | class AssemblyImageWriter : public ImageWriter { |
| 386 | public: |
| 387 | AssemblyImageWriter(Thread* thread, |
| 388 | Dart_StreamingWriteCallback callback, |
| 389 | void* callback_data, |
| 390 | bool strip = false, |
| 391 | Elf* debug_elf = nullptr); |
| 392 | void Finalize(); |
| 393 | |
| 394 | virtual void WriteText(WriteStream* clustered_stream, bool vm); |
| 395 | |
| 396 | private: |
| 397 | void FrameUnwindPrologue(); |
| 398 | void FrameUnwindEpilogue(); |
| 399 | intptr_t WriteByteSequence(uword start, uword end); |
| 400 | intptr_t Align(intptr_t alignment, uword position = 0); |
| 401 | |
| 402 | #if defined(TARGET_ARCH_IS_64_BIT) |
| 403 | const char* kLiteralPrefix = ".quad" ; |
| 404 | #else |
| 405 | const char* kLiteralPrefix = ".long" ; |
| 406 | #endif |
| 407 | |
| 408 | intptr_t WriteWordLiteralText(compiler::target::uword value) { |
| 409 | // Padding is helpful for comparing the .S with --disassemble. |
| 410 | #if defined(TARGET_ARCH_IS_64_BIT) |
| 411 | assembly_stream_.Print(".quad 0x%0.16" Px "\n" , value); |
| 412 | #else |
| 413 | assembly_stream_.Print(".long 0x%0.8" Px "\n" , value); |
| 414 | #endif |
| 415 | return compiler::target::kWordSize; |
| 416 | } |
| 417 | |
| 418 | StreamingWriteStream assembly_stream_; |
| 419 | Dwarf* assembly_dwarf_; |
| 420 | Elf* debug_elf_; |
| 421 | |
| 422 | DISALLOW_COPY_AND_ASSIGN(AssemblyImageWriter); |
| 423 | }; |
| 424 | |
| 425 | class BlobImageWriter : public ImageWriter { |
| 426 | public: |
| 427 | BlobImageWriter(Thread* thread, |
| 428 | uint8_t** instructions_blob_buffer, |
| 429 | ReAlloc alloc, |
| 430 | intptr_t initial_size, |
| 431 | Elf* debug_elf = nullptr, |
| 432 | Elf* elf = nullptr); |
| 433 | |
| 434 | virtual void WriteText(WriteStream* clustered_stream, bool vm); |
| 435 | |
| 436 | intptr_t InstructionsBlobSize() const { |
| 437 | return instructions_blob_stream_.bytes_written(); |
| 438 | } |
| 439 | |
| 440 | private: |
| 441 | intptr_t WriteByteSequence(uword start, uword end); |
| 442 | |
| 443 | WriteStream instructions_blob_stream_; |
| 444 | Elf* const elf_; |
| 445 | Elf* const debug_elf_; |
| 446 | |
| 447 | DISALLOW_COPY_AND_ASSIGN(BlobImageWriter); |
| 448 | }; |
| 449 | |
| 450 | } // namespace dart |
| 451 | |
| 452 | #endif // RUNTIME_VM_IMAGE_SNAPSHOT_H_ |
| 453 | |