blob: a09f604df2981d3d859f41bb747eef1747fe9a44 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_ISOLATE_RELOAD_H_
#define RUNTIME_VM_ISOLATE_RELOAD_H_
#include <functional>
#include <memory>
#include "include/dart_tools_api.h"
#include "vm/globals.h"
#include "vm/growable_array.h"
#include "vm/hash_map.h"
#include "vm/log.h"
#include "vm/object.h"
DECLARE_FLAG(bool, trace_reload);
DECLARE_FLAG(bool, trace_reload_verbose);
// 'Trace Isolate Reload' TIR_Print
#if defined(_MSC_VER)
#define TIR_Print(format, ...) \
if (FLAG_trace_reload) Log::Current()->Print(format, __VA_ARGS__)
#else
#define TIR_Print(format, ...) \
if (FLAG_trace_reload) Log::Current()->Print(format, ##__VA_ARGS__)
#endif
// 'Verbose Trace Isolate Reload' VTIR_Print
#if defined(_MSC_VER)
#define VTIR_Print(format, ...) \
if (FLAG_trace_reload_verbose) Log::Current()->Print(format, __VA_ARGS__)
#else
#define VTIR_Print(format, ...) \
if (FLAG_trace_reload_verbose) Log::Current()->Print(format, ##__VA_ARGS__)
#endif
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
class BitVector;
class GrowableObjectArray;
class Isolate;
class Library;
class ObjectLocator;
class ObjectPointerVisitor;
class ObjectStore;
class Script;
class UpdateClassesVisitor;
class InstanceMorpher : public ZoneAllocated {
public:
// Creates a new [InstanceMorpher] based on the [from]/[to] class
// descriptions.
static InstanceMorpher* CreateFromClassDescriptors(
Zone* zone,
SharedClassTable* shared_class_table,
const Class& from,
const Class& to);
InstanceMorpher(Zone* zone,
classid_t cid,
SharedClassTable* shared_class_table,
ZoneGrowableArray<intptr_t>* mapping,
ZoneGrowableArray<intptr_t>* new_fields_offsets);
virtual ~InstanceMorpher() {}
// Called on each instance that needs to be morphed.
InstancePtr Morph(const Instance& instance) const;
// Adds an object to be morphed.
void AddObject(ObjectPtr object);
// Create the morphed objects based on the before() list.
void CreateMorphedCopies();
// Append the morper info to JSON array.
void AppendTo(JSONArray* array);
// Returns the list of objects that need to be morphed.
const GrowableArray<const Instance*>* before() const { return &before_; }
// Returns the list of morphed objects (matches order in before()).
const GrowableArray<const Instance*>* after() const { return &after_; }
// Returns the cid associated with the from_ and to_ class.
intptr_t cid() const { return cid_; }
// Dumps the field mappings for the [cid()] class.
void Dump() const;
private:
Zone* zone_;
classid_t cid_;
SharedClassTable* shared_class_table_;
ZoneGrowableArray<intptr_t>* mapping_;
ZoneGrowableArray<intptr_t>* new_fields_offsets_;
GrowableArray<const Instance*> before_;
GrowableArray<const Instance*> after_;
};
class ReasonForCancelling : public ZoneAllocated {
public:
explicit ReasonForCancelling(Zone* zone) {}
virtual ~ReasonForCancelling() {}
// Reports a reason for cancelling reload.
void Report(IsolateGroupReloadContext* context);
// Conversion to a VM error object.
// Default implementation calls ToString.
virtual ErrorPtr ToError();
// Conversion to a string object.
// Default implementation calls ToError.
virtual StringPtr ToString();
// Append the reason to JSON array.
virtual void AppendTo(JSONArray* array);
// Concrete subclasses must override either ToError or ToString.
};
// Abstract class for also capturing the from_ and to_ class.
class ClassReasonForCancelling : public ReasonForCancelling {
public:
ClassReasonForCancelling(Zone* zone, const Class& from, const Class& to);
void AppendTo(JSONArray* array);
protected:
const Class& from_;
const Class& to_;
};
class IsolateGroupReloadContext {
public:
IsolateGroupReloadContext(IsolateGroup* isolate,
SharedClassTable* shared_class_table,
JSONStream* js);
~IsolateGroupReloadContext();
// If kernel_buffer is provided, the VM takes ownership when Reload is called.
bool Reload(bool force_reload,
const char* root_script_url = NULL,
const char* packages_url = NULL,
const uint8_t* kernel_buffer = NULL,
intptr_t kernel_buffer_size = 0);
// All zone allocated objects must be allocated from this zone.
Zone* zone() const { return zone_; }
bool UseSavedSizeTableForGC() const {
return saved_size_table_.load(std::memory_order_relaxed) != nullptr;
}
IsolateGroup* isolate_group() const { return isolate_group_; }
bool reload_aborted() const { return HasReasonsForCancelling(); }
bool reload_skipped() const { return reload_skipped_; }
ErrorPtr error() const;
int64_t start_time_micros() const { return start_time_micros_; }
int64_t reload_timestamp() const { return reload_timestamp_; }
static Dart_FileModifiedCallback file_modified_callback() {
return file_modified_callback_;
}
static void SetFileModifiedCallback(Dart_FileModifiedCallback callback) {
file_modified_callback_ = callback;
}
private:
intptr_t GetClassSizeForHeapWalkAt(classid_t cid);
void DiscardSavedClassTable(bool is_rollback);
// Tells whether there are reasons for cancelling the reload.
bool HasReasonsForCancelling() const {
return !reasons_to_cancel_reload_.is_empty();
}
// Record problem for this reload.
void AddReasonForCancelling(ReasonForCancelling* reason);
// Reports all reasons for cancelling reload.
void ReportReasonsForCancelling();
// Reports the details of a reload operation.
void ReportOnJSON(JSONStream* stream, intptr_t final_library_count);
// Ensures there is a instance morpher for [cid], if not it will use
// [instance_morpher]
void EnsureHasInstanceMorpherFor(classid_t cid,
InstanceMorpher* instance_morpher);
// Tells whether instance in the heap must be morphed.
bool HasInstanceMorphers() const { return !instance_morphers_.is_empty(); }
// Called by both FinalizeLoading and FinalizeFailedLoad.
void CommonFinalizeTail(intptr_t final_library_count);
// Report back through the observatory channels.
void ReportError(const Error& error);
void ReportSuccess();
void VisitObjectPointers(ObjectPointerVisitor* visitor);
void GetRootLibUrl(const char* root_script_url);
char* CompileToKernel(bool force_reload,
const char* packages_url,
const uint8_t** kernel_buffer,
intptr_t* kernel_buffer_size);
void BuildModifiedLibrariesClosure(BitVector* modified_libs);
void FindModifiedSources(bool force_reload,
Dart_SourceFile** modified_sources,
intptr_t* count,
const char* packages_url);
bool ScriptModifiedSince(const Script& script, int64_t since);
void CheckpointSharedClassTable();
void MorphInstancesPhase1Allocate(ObjectLocator* locator,
const Array& before,
const Array& after);
void MorphInstancesPhase2Become(const Array& before, const Array& after);
void ForEachIsolate(std::function<void(Isolate*)> callback);
// The zone used for all reload related allocations.
Zone* zone_;
IsolateGroup* isolate_group_;
SharedClassTable* shared_class_table_;
int64_t start_time_micros_ = -1;
int64_t reload_timestamp_ = -1;
bool reload_skipped_ = false;
bool reload_finalized_ = false;
JSONStream* js_;
intptr_t num_old_libs_ = -1;
intptr_t saved_num_cids_ = -1;
std::atomic<intptr_t*> saved_size_table_;
intptr_t num_received_libs_ = -1;
intptr_t bytes_received_libs_ = -1;
intptr_t num_received_classes_ = -1;
intptr_t num_received_procedures_ = -1;
intptr_t num_saved_libs_ = -1;
// Required trait for the instance_morpher_by_cid_;
struct MorpherTrait {
typedef InstanceMorpher* Value;
typedef intptr_t Key;
typedef InstanceMorpher* Pair;
static Key KeyOf(Pair kv) { return kv->cid(); }
static Value ValueOf(Pair kv) { return kv; }
static intptr_t Hashcode(Key key) { return key; }
static bool IsKeyEqual(Pair kv, Key key) { return kv->cid() == key; }
};
// Collect the necessary instance transformation for schema changes.
GrowableArray<InstanceMorpher*> instance_morphers_;
// Collects the reasons for cancelling the reload.
GrowableArray<ReasonForCancelling*> reasons_to_cancel_reload_;
// Hash map from cid to InstanceMorpher.
DirectChainedHashMap<MorpherTrait> instance_morpher_by_cid_;
// A bit vector indicating which of the original libraries were modified.
BitVector* modified_libs_ = nullptr;
// A bit vector indicating which of the original libraries were modified,
// or where a transitive dependency was modified.
BitVector* modified_libs_transitive_ = nullptr;
// A bit vector indicating which of the saved libraries that transitively
// depend on a modified libary.
BitVector* saved_libs_transitive_updated_ = nullptr;
String& root_lib_url_;
ObjectPtr* from() { return reinterpret_cast<ObjectPtr*>(&root_url_prefix_); }
StringPtr root_url_prefix_;
StringPtr old_root_url_prefix_;
ObjectPtr* to() {
return reinterpret_cast<ObjectPtr*>(&old_root_url_prefix_);
}
friend class Isolate;
friend class Class; // AddStaticFieldMapping, AddEnumBecomeMapping.
friend class Library;
friend class ObjectLocator;
friend class MarkFunctionsForRecompilation; // IsDirty.
friend class ReasonForCancelling;
friend class ProgramReloadContext;
friend class IsolateGroup; // GetClassSizeForHeapWalkAt
friend class UntaggedObject; // GetClassSizeForHeapWalkAt
static Dart_FileModifiedCallback file_modified_callback_;
};
class ProgramReloadContext {
public:
ProgramReloadContext(
std::shared_ptr<IsolateGroupReloadContext> group_reload_context,
IsolateGroup* isolate_group);
~ProgramReloadContext();
// All zone allocated objects must be allocated from this zone.
Zone* zone() const { return zone_; }
IsolateGroupReloadContext* group_reload_context() {
return group_reload_context_.get();
}
static bool IsSameLibrary(const Library& a_lib, const Library& b_lib);
static bool IsSameClass(const Class& a, const Class& b);
private:
bool IsDirty(const Library& lib);
// Prefers old classes when we are in the middle of a reload.
ClassPtr GetClassForHeapWalkAt(intptr_t cid);
void DiscardSavedClassTable(bool is_rollback);
void RegisterClass(const Class& new_cls);
// Finds the library private key for |replacement_or_new| or return null
// if |replacement_or_new| is new.
StringPtr FindLibraryPrivateKey(const Library& replacement_or_new);
void VisitObjectPointers(ObjectPointerVisitor* visitor);
IsolateGroup* isolate_group() { return isolate_group_; }
ObjectStore* object_store();
void EnsuredUnoptimizedCodeForStack();
void DeoptimizeDependentCode();
void ReloadPhase1AllocateStorageMapsAndCheckpoint();
void CheckpointClasses();
ObjectPtr ReloadPhase2LoadKernel(kernel::Program* program,
const String& root_lib_url);
void ReloadPhase3FinalizeLoading();
void ReloadPhase4CommitPrepare();
void ReloadPhase4CommitFinish();
void ReloadPhase4Rollback();
void CheckpointLibraries();
void RollbackClasses();
void RollbackLibraries();
#ifdef DEBUG
void VerifyMaps();
#endif
void CommitBeforeInstanceMorphing();
void CommitAfterInstanceMorphing();
void PostCommit();
void RunInvalidationVisitors();
void InvalidateKernelInfos(
Zone* zone,
const GrowableArray<const KernelProgramInfo*>& kernel_infos);
void InvalidateFunctions(Zone* zone,
const GrowableArray<const Function*>& functions);
void InvalidateFields(Zone* zone,
const GrowableArray<const Field*>& fields,
const GrowableArray<const Instance*>& instances);
void ResetUnoptimizedICsOnStack();
void ResetMegamorphicCaches();
void InvalidateWorld();
struct LibraryInfo {
bool dirty;
};
// The zone used for all reload related allocations.
Zone* zone_;
std::shared_ptr<IsolateGroupReloadContext> group_reload_context_;
IsolateGroup* isolate_group_;
intptr_t saved_num_cids_ = -1;
intptr_t saved_num_tlc_cids_ = -1;
std::atomic<ClassPtr*> saved_class_table_;
std::atomic<ClassPtr*> saved_tlc_class_table_;
MallocGrowableArray<LibraryInfo> library_infos_;
ClassPtr OldClassOrNull(const Class& replacement_or_new);
LibraryPtr OldLibraryOrNull(const Library& replacement_or_new);
LibraryPtr OldLibraryOrNullBaseMoved(const Library& replacement_or_new);
void BuildLibraryMapping();
void BuildRemovedClassesSet();
void ValidateReload();
void AddClassMapping(const Class& replacement_or_new, const Class& original);
void AddLibraryMapping(const Library& replacement_or_new,
const Library& original);
void AddStaticFieldMapping(const Field& old_field, const Field& new_field);
void AddBecomeMapping(const Object& old, const Object& neu);
void AddEnumBecomeMapping(const Object& old, const Object& neu);
void RebuildDirectSubclasses();
ObjectPtr* from() {
return reinterpret_cast<ObjectPtr*>(&old_classes_set_storage_);
}
ArrayPtr old_classes_set_storage_;
ArrayPtr class_map_storage_;
ArrayPtr removed_class_set_storage_;
ArrayPtr old_libraries_set_storage_;
ArrayPtr library_map_storage_;
ArrayPtr become_map_storage_;
GrowableObjectArrayPtr become_enum_mappings_;
LibraryPtr saved_root_library_;
GrowableObjectArrayPtr saved_libraries_;
ObjectPtr* to() { return reinterpret_cast<ObjectPtr*>(&saved_libraries_); }
friend class Isolate;
friend class IsolateGroup;
friend class Class; // AddStaticFieldMapping, AddEnumBecomeMapping.
friend class Library;
friend class ObjectLocator;
friend class MarkFunctionsForRecompilation; // IsDirty.
friend class ReasonForCancelling;
friend class IsolateGroupReloadContext;
};
class CallSiteResetter : public ValueObject {
public:
explicit CallSiteResetter(Zone* zone);
void ZeroEdgeCounters(const Function& function);
void ResetCaches(const Code& code);
void ResetCaches(const ObjectPool& pool);
void Reset(const ICData& ic);
void ResetSwitchableCalls(const Code& code);
private:
Zone* zone_;
Instructions& instrs_;
ObjectPool& pool_;
Object& object_;
String& name_;
Class& new_cls_;
Library& new_lib_;
Function& new_function_;
Field& new_field_;
Array& entries_;
Function& old_target_;
Function& new_target_;
Function& caller_;
Array& args_desc_array_;
Array& ic_data_array_;
Array& edge_counters_;
PcDescriptors& descriptors_;
ICData& ic_data_;
};
class ReloadHandler {
public:
ReloadHandler() {}
~ReloadHandler() {}
void RegisterIsolate();
void UnregisterIsolate();
void CheckForReload();
private:
friend class ReloadOperationScope;
void PauseIsolatesForReloadLocked();
void ResumeIsolatesLocked();
void ParticipateIfReloadRequested(SafepointMonitorLocker* ml,
bool is_registered,
bool allow_later_retry);
intptr_t registered_isolate_count_ = 0;
Monitor monitor_;
Thread* reloading_thread_ = nullptr;
Monitor checkin_monitor_;
intptr_t isolates_checked_in_ = 0;
};
class ReloadOperationScope : public ThreadStackResource {
public:
explicit ReloadOperationScope(Thread* thread)
: ThreadStackResource(thread), isolate_group_(thread->isolate_group()) {
isolate_group_->reload_handler()->PauseIsolatesForReloadLocked();
}
~ReloadOperationScope() {
isolate_group_->reload_handler()->ResumeIsolatesLocked();
}
private:
IsolateGroup* isolate_group_;
};
} // namespace dart
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
#endif // RUNTIME_VM_ISOLATE_RELOAD_H_