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 | |