1 | // Copyright (c) 2016, 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_ISOLATE_RELOAD_H_ |
6 | #define RUNTIME_VM_ISOLATE_RELOAD_H_ |
7 | |
8 | #include <functional> |
9 | #include <memory> |
10 | |
11 | #include "include/dart_tools_api.h" |
12 | |
13 | #include "vm/globals.h" |
14 | #include "vm/growable_array.h" |
15 | #include "vm/hash_map.h" |
16 | #include "vm/log.h" |
17 | #include "vm/object.h" |
18 | |
19 | DECLARE_FLAG(bool, trace_reload); |
20 | DECLARE_FLAG(bool, trace_reload_verbose); |
21 | |
22 | // 'Trace Isolate Reload' TIR_Print |
23 | #if defined(_MSC_VER) |
24 | #define TIR_Print(format, ...) \ |
25 | if (FLAG_trace_reload) Log::Current()->Print(format, __VA_ARGS__) |
26 | #else |
27 | #define TIR_Print(format, ...) \ |
28 | if (FLAG_trace_reload) Log::Current()->Print(format, ##__VA_ARGS__) |
29 | #endif |
30 | |
31 | // 'Verbose Trace Isolate Reload' VTIR_Print |
32 | #if defined(_MSC_VER) |
33 | #define VTIR_Print(format, ...) \ |
34 | if (FLAG_trace_reload_verbose) Log::Current()->Print(format, __VA_ARGS__) |
35 | #else |
36 | #define VTIR_Print(format, ...) \ |
37 | if (FLAG_trace_reload_verbose) Log::Current()->Print(format, ##__VA_ARGS__) |
38 | #endif |
39 | |
40 | #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) |
41 | |
42 | namespace dart { |
43 | |
44 | class BitVector; |
45 | class GrowableObjectArray; |
46 | class Isolate; |
47 | class Library; |
48 | class ObjectLocator; |
49 | class ObjectPointerVisitor; |
50 | class ObjectStore; |
51 | class Script; |
52 | class UpdateClassesVisitor; |
53 | |
54 | class InstanceMorpher : public ZoneAllocated { |
55 | public: |
56 | // Creates a new [InstanceMorpher] based on the [from]/[to] class |
57 | // descriptions. |
58 | static InstanceMorpher* CreateFromClassDescriptors( |
59 | Zone* zone, |
60 | SharedClassTable* shared_class_table, |
61 | const Class& from, |
62 | const Class& to); |
63 | |
64 | InstanceMorpher(Zone* zone, |
65 | classid_t cid, |
66 | SharedClassTable* shared_class_table, |
67 | ZoneGrowableArray<intptr_t>* mapping, |
68 | ZoneGrowableArray<intptr_t>* new_fields_offsets); |
69 | virtual ~InstanceMorpher() {} |
70 | |
71 | // Called on each instance that needs to be morphed. |
72 | InstancePtr Morph(const Instance& instance) const; |
73 | |
74 | // Adds an object to be morphed. |
75 | void AddObject(ObjectPtr object); |
76 | |
77 | // Create the morphed objects based on the before() list. |
78 | void CreateMorphedCopies(); |
79 | |
80 | // Append the morper info to JSON array. |
81 | void AppendTo(JSONArray* array); |
82 | |
83 | // Returns the list of objects that need to be morphed. |
84 | const GrowableArray<const Instance*>* before() const { return &before_; } |
85 | |
86 | // Returns the list of morphed objects (matches order in before()). |
87 | const GrowableArray<const Instance*>* after() const { return &after_; } |
88 | |
89 | // Returns the cid associated with the from_ and to_ class. |
90 | intptr_t cid() const { return cid_; } |
91 | |
92 | // Dumps the field mappings for the [cid()] class. |
93 | void Dump() const; |
94 | |
95 | private: |
96 | Zone* zone_; |
97 | classid_t cid_; |
98 | SharedClassTable* shared_class_table_; |
99 | ZoneGrowableArray<intptr_t>* mapping_; |
100 | ZoneGrowableArray<intptr_t>* new_fields_offsets_; |
101 | |
102 | GrowableArray<const Instance*> before_; |
103 | GrowableArray<const Instance*> after_; |
104 | }; |
105 | |
106 | class ReasonForCancelling : public ZoneAllocated { |
107 | public: |
108 | explicit ReasonForCancelling(Zone* zone) {} |
109 | virtual ~ReasonForCancelling() {} |
110 | |
111 | // Reports a reason for cancelling reload. |
112 | void Report(IsolateGroupReloadContext* context); |
113 | |
114 | // Conversion to a VM error object. |
115 | // Default implementation calls ToString. |
116 | virtual ErrorPtr ToError(); |
117 | |
118 | // Conversion to a string object. |
119 | // Default implementation calls ToError. |
120 | virtual StringPtr ToString(); |
121 | |
122 | // Append the reason to JSON array. |
123 | virtual void AppendTo(JSONArray* array); |
124 | |
125 | // Concrete subclasses must override either ToError or ToString. |
126 | }; |
127 | |
128 | // Abstract class for also capturing the from_ and to_ class. |
129 | class ClassReasonForCancelling : public ReasonForCancelling { |
130 | public: |
131 | ClassReasonForCancelling(Zone* zone, const Class& from, const Class& to); |
132 | void AppendTo(JSONArray* array); |
133 | |
134 | protected: |
135 | const Class& from_; |
136 | const Class& to_; |
137 | }; |
138 | |
139 | class IsolateGroupReloadContext { |
140 | public: |
141 | IsolateGroupReloadContext(IsolateGroup* isolate, |
142 | SharedClassTable* shared_class_table, |
143 | JSONStream* js); |
144 | ~IsolateGroupReloadContext(); |
145 | |
146 | // If kernel_buffer is provided, the VM takes ownership when Reload is called. |
147 | bool Reload(bool force_reload, |
148 | const char* root_script_url = NULL, |
149 | const char* packages_url = NULL, |
150 | const uint8_t* kernel_buffer = NULL, |
151 | intptr_t kernel_buffer_size = 0); |
152 | |
153 | // All zone allocated objects must be allocated from this zone. |
154 | Zone* zone() const { return zone_; } |
155 | |
156 | bool UseSavedSizeTableForGC() const { |
157 | return saved_size_table_.load(std::memory_order_relaxed) != nullptr; |
158 | } |
159 | |
160 | IsolateGroup* isolate_group() const { return isolate_group_; } |
161 | bool reload_aborted() const { return HasReasonsForCancelling(); } |
162 | bool reload_skipped() const { return reload_skipped_; } |
163 | ErrorPtr error() const; |
164 | int64_t start_time_micros() const { return start_time_micros_; } |
165 | int64_t reload_timestamp() const { return reload_timestamp_; } |
166 | |
167 | static Dart_FileModifiedCallback file_modified_callback() { |
168 | return file_modified_callback_; |
169 | } |
170 | static void SetFileModifiedCallback(Dart_FileModifiedCallback callback) { |
171 | file_modified_callback_ = callback; |
172 | } |
173 | |
174 | private: |
175 | intptr_t GetClassSizeForHeapWalkAt(classid_t cid); |
176 | void DiscardSavedClassTable(bool is_rollback); |
177 | |
178 | // Tells whether there are reasons for cancelling the reload. |
179 | bool HasReasonsForCancelling() const { |
180 | return !reasons_to_cancel_reload_.is_empty(); |
181 | } |
182 | |
183 | // Record problem for this reload. |
184 | void AddReasonForCancelling(ReasonForCancelling* reason); |
185 | |
186 | // Reports all reasons for cancelling reload. |
187 | void ReportReasonsForCancelling(); |
188 | |
189 | // Reports the details of a reload operation. |
190 | void ReportOnJSON(JSONStream* stream, intptr_t final_library_count); |
191 | |
192 | // Ensures there is a instance morpher for [cid], if not it will use |
193 | // [instance_morpher] |
194 | void EnsureHasInstanceMorpherFor(classid_t cid, |
195 | InstanceMorpher* instance_morpher); |
196 | |
197 | // Tells whether instance in the heap must be morphed. |
198 | bool HasInstanceMorphers() const { return !instance_morphers_.is_empty(); } |
199 | |
200 | // Called by both FinalizeLoading and FinalizeFailedLoad. |
201 | void CommonFinalizeTail(intptr_t final_library_count); |
202 | |
203 | // Report back through the observatory channels. |
204 | void ReportError(const Error& error); |
205 | void ReportSuccess(); |
206 | |
207 | void VisitObjectPointers(ObjectPointerVisitor* visitor); |
208 | |
209 | void GetRootLibUrl(const char* root_script_url); |
210 | char* CompileToKernel(bool force_reload, |
211 | const char* packages_url, |
212 | const uint8_t** kernel_buffer, |
213 | intptr_t* kernel_buffer_size); |
214 | void BuildModifiedLibrariesClosure(BitVector* modified_libs); |
215 | void FindModifiedSources(bool force_reload, |
216 | Dart_SourceFile** modified_sources, |
217 | intptr_t* count, |
218 | const char* packages_url); |
219 | bool ScriptModifiedSince(const Script& script, int64_t since); |
220 | |
221 | void CheckpointSharedClassTable(); |
222 | |
223 | void MorphInstancesPhase1Allocate(ObjectLocator* locator, |
224 | const Array& before, |
225 | const Array& after); |
226 | void MorphInstancesPhase2Become(const Array& before, const Array& after); |
227 | |
228 | void ForEachIsolate(std::function<void(Isolate*)> callback); |
229 | |
230 | // The zone used for all reload related allocations. |
231 | Zone* zone_; |
232 | |
233 | IsolateGroup* isolate_group_; |
234 | SharedClassTable* shared_class_table_; |
235 | |
236 | int64_t start_time_micros_ = -1; |
237 | int64_t reload_timestamp_ = -1; |
238 | Isolate* first_isolate_ = nullptr; |
239 | bool reload_skipped_ = false; |
240 | bool reload_finalized_ = false; |
241 | JSONStream* js_; |
242 | intptr_t num_old_libs_ = -1; |
243 | |
244 | intptr_t saved_num_cids_ = -1; |
245 | std::atomic<intptr_t*> saved_size_table_; |
246 | intptr_t num_received_libs_ = -1; |
247 | intptr_t bytes_received_libs_ = -1; |
248 | intptr_t num_received_classes_ = -1; |
249 | intptr_t num_received_procedures_ = -1; |
250 | intptr_t num_saved_libs_ = -1; |
251 | |
252 | // Required trait for the instance_morpher_by_cid_; |
253 | struct MorpherTrait { |
254 | typedef InstanceMorpher* Value; |
255 | typedef intptr_t Key; |
256 | typedef InstanceMorpher* Pair; |
257 | |
258 | static Key KeyOf(Pair kv) { return kv->cid(); } |
259 | static Value ValueOf(Pair kv) { return kv; } |
260 | static intptr_t Hashcode(Key key) { return key; } |
261 | static bool IsKeyEqual(Pair kv, Key key) { return kv->cid() == key; } |
262 | }; |
263 | |
264 | // Collect the necessary instance transformation for schema changes. |
265 | GrowableArray<InstanceMorpher*> instance_morphers_; |
266 | |
267 | // Collects the reasons for cancelling the reload. |
268 | GrowableArray<ReasonForCancelling*> reasons_to_cancel_reload_; |
269 | |
270 | // Hash map from cid to InstanceMorpher. |
271 | DirectChainedHashMap<MorpherTrait> instance_morpher_by_cid_; |
272 | |
273 | // A bit vector indicating which of the original libraries were modified. |
274 | BitVector* modified_libs_ = nullptr; |
275 | |
276 | // A bit vector indicating which of the original libraries were modified, |
277 | // or where a transitive dependency was modified. |
278 | BitVector* modified_libs_transitive_ = nullptr; |
279 | |
280 | // A bit vector indicating which of the saved libraries that transitively |
281 | // depend on a modified libary. |
282 | BitVector* saved_libs_transitive_updated_ = nullptr; |
283 | |
284 | String& root_lib_url_; |
285 | ObjectPtr* from() { return reinterpret_cast<ObjectPtr*>(&root_url_prefix_); } |
286 | StringPtr root_url_prefix_; |
287 | StringPtr old_root_url_prefix_; |
288 | ObjectPtr* to() { |
289 | return reinterpret_cast<ObjectPtr*>(&old_root_url_prefix_); |
290 | } |
291 | |
292 | friend class Isolate; |
293 | friend class Class; // AddStaticFieldMapping, AddEnumBecomeMapping. |
294 | friend class Library; |
295 | friend class ObjectLocator; |
296 | friend class MarkFunctionsForRecompilation; // IsDirty. |
297 | friend class ReasonForCancelling; |
298 | friend class IsolateReloadContext; |
299 | friend class IsolateGroup; // GetClassSizeForHeapWalkAt |
300 | friend class ObjectLayout; // GetClassSizeForHeapWalkAt |
301 | |
302 | static Dart_FileModifiedCallback file_modified_callback_; |
303 | }; |
304 | |
305 | class IsolateReloadContext { |
306 | public: |
307 | IsolateReloadContext( |
308 | std::shared_ptr<IsolateGroupReloadContext> group_reload_context, |
309 | Isolate* isolate); |
310 | ~IsolateReloadContext(); |
311 | |
312 | // All zone allocated objects must be allocated from this zone. |
313 | Zone* zone() const { return zone_; } |
314 | |
315 | IsolateGroupReloadContext* group_reload_context() { |
316 | return group_reload_context_.get(); |
317 | } |
318 | |
319 | static bool IsSameLibrary(const Library& a_lib, const Library& b_lib); |
320 | static bool IsSameClass(const Class& a, const Class& b); |
321 | |
322 | private: |
323 | bool IsDirty(const Library& lib); |
324 | |
325 | // Prefers old classes when we are in the middle of a reload. |
326 | ClassPtr GetClassForHeapWalkAt(intptr_t cid); |
327 | void DiscardSavedClassTable(bool is_rollback); |
328 | |
329 | void RegisterClass(const Class& new_cls); |
330 | |
331 | // Finds the library private key for |replacement_or_new| or return null |
332 | // if |replacement_or_new| is new. |
333 | StringPtr FindLibraryPrivateKey(const Library& replacement_or_new); |
334 | |
335 | void VisitObjectPointers(ObjectPointerVisitor* visitor); |
336 | |
337 | Isolate* isolate() { return isolate_; } |
338 | ObjectStore* object_store(); |
339 | |
340 | void EnsuredUnoptimizedCodeForStack(); |
341 | void DeoptimizeDependentCode(); |
342 | |
343 | void ReloadPhase1AllocateStorageMapsAndCheckpoint(); |
344 | void CheckpointClasses(); |
345 | ObjectPtr ReloadPhase2LoadKernel(kernel::Program* program, |
346 | const String& root_lib_url); |
347 | void ReloadPhase3FinalizeLoading(); |
348 | void ReloadPhase4CommitPrepare(); |
349 | void ReloadPhase4CommitFinish(); |
350 | void ReloadPhase4Rollback(); |
351 | |
352 | void CheckpointLibraries(); |
353 | |
354 | void RollbackClasses(); |
355 | void RollbackLibraries(); |
356 | |
357 | #ifdef DEBUG |
358 | void VerifyMaps(); |
359 | #endif |
360 | |
361 | void CommitBeforeInstanceMorphing(); |
362 | void CommitAfterInstanceMorphing(); |
363 | void PostCommit(); |
364 | |
365 | void RunInvalidationVisitors(); |
366 | void InvalidateKernelInfos( |
367 | Zone* zone, |
368 | const GrowableArray<const KernelProgramInfo*>& kernel_infos); |
369 | void InvalidateFunctions(Zone* zone, |
370 | const GrowableArray<const Function*>& functions); |
371 | void InvalidateFields(Zone* zone, |
372 | const GrowableArray<const Field*>& fields, |
373 | const GrowableArray<const Instance*>& instances); |
374 | void ResetUnoptimizedICsOnStack(); |
375 | void ResetMegamorphicCaches(); |
376 | void InvalidateWorld(); |
377 | |
378 | struct LibraryInfo { |
379 | bool dirty; |
380 | }; |
381 | |
382 | // The zone used for all reload related allocations. |
383 | Zone* zone_; |
384 | std::shared_ptr<IsolateGroupReloadContext> group_reload_context_; |
385 | Isolate* isolate_; |
386 | intptr_t saved_num_cids_ = -1; |
387 | intptr_t saved_num_tlc_cids_ = -1; |
388 | std::atomic<ClassPtr*> saved_class_table_; |
389 | std::atomic<ClassPtr*> saved_tlc_class_table_; |
390 | MallocGrowableArray<LibraryInfo> library_infos_; |
391 | |
392 | ClassPtr OldClassOrNull(const Class& replacement_or_new); |
393 | LibraryPtr OldLibraryOrNull(const Library& replacement_or_new); |
394 | LibraryPtr OldLibraryOrNullBaseMoved(const Library& replacement_or_new); |
395 | |
396 | void BuildLibraryMapping(); |
397 | void BuildRemovedClassesSet(); |
398 | void ValidateReload(); |
399 | |
400 | void AddClassMapping(const Class& replacement_or_new, const Class& original); |
401 | void AddLibraryMapping(const Library& replacement_or_new, |
402 | const Library& original); |
403 | void AddStaticFieldMapping(const Field& old_field, const Field& new_field); |
404 | void AddBecomeMapping(const Object& old, const Object& neu); |
405 | void AddEnumBecomeMapping(const Object& old, const Object& neu); |
406 | void RebuildDirectSubclasses(); |
407 | |
408 | ObjectPtr* from() { |
409 | return reinterpret_cast<ObjectPtr*>(&old_classes_set_storage_); |
410 | } |
411 | ArrayPtr old_classes_set_storage_; |
412 | ArrayPtr class_map_storage_; |
413 | ArrayPtr removed_class_set_storage_; |
414 | ArrayPtr old_libraries_set_storage_; |
415 | ArrayPtr library_map_storage_; |
416 | ArrayPtr become_map_storage_; |
417 | GrowableObjectArrayPtr become_enum_mappings_; |
418 | LibraryPtr saved_root_library_; |
419 | GrowableObjectArrayPtr saved_libraries_; |
420 | ObjectPtr* to() { return reinterpret_cast<ObjectPtr*>(&saved_libraries_); } |
421 | |
422 | friend class Isolate; |
423 | friend class Class; // AddStaticFieldMapping, AddEnumBecomeMapping. |
424 | friend class Library; |
425 | friend class ObjectLocator; |
426 | friend class MarkFunctionsForRecompilation; // IsDirty. |
427 | friend class ReasonForCancelling; |
428 | friend class IsolateGroupReloadContext; |
429 | }; |
430 | |
431 | class CallSiteResetter : public ValueObject { |
432 | public: |
433 | explicit CallSiteResetter(Zone* zone); |
434 | |
435 | void ZeroEdgeCounters(const Function& function); |
436 | void ResetCaches(const Code& code); |
437 | void ResetCaches(const ObjectPool& pool); |
438 | void RebindStaticTargets(const Bytecode& code); |
439 | void Reset(const ICData& ic); |
440 | void ResetSwitchableCalls(const Code& code); |
441 | |
442 | private: |
443 | Zone* zone_; |
444 | Instructions& instrs_; |
445 | ObjectPool& pool_; |
446 | Object& object_; |
447 | String& name_; |
448 | Class& new_cls_; |
449 | Library& new_lib_; |
450 | Function& new_function_; |
451 | Field& new_field_; |
452 | Array& entries_; |
453 | Function& old_target_; |
454 | Function& new_target_; |
455 | Function& caller_; |
456 | Array& args_desc_array_; |
457 | Array& ic_data_array_; |
458 | Array& edge_counters_; |
459 | PcDescriptors& descriptors_; |
460 | ICData& ic_data_; |
461 | }; |
462 | |
463 | } // namespace dart |
464 | |
465 | #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) |
466 | |
467 | #endif // RUNTIME_VM_ISOLATE_RELOAD_H_ |
468 | |