// Copyright (c) 2012, 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_CLASS_TABLE_H_ #define RUNTIME_VM_CLASS_TABLE_H_ #include "platform/assert.h" #include "vm/bitfield.h" #include "vm/globals.h" namespace dart { class Class; class ClassStats; class JSONArray; class JSONObject; class JSONStream; template class MallocGrowableArray; class ObjectPointerVisitor; class RawClass; #ifndef PRODUCT template class AllocStats { public: T new_count; T new_size; T old_count; T old_size; void ResetNew() { new_count = 0; new_size = 0; } void AddNew(T size) { new_count++; new_size += size; } void ResetOld() { old_count = 0; old_size = 0; } void AddOld(T size, T count = 1) { old_count += count; old_size += size; } void Reset() { new_count = 0; new_size = 0; old_count = 0; old_size = 0; } // For classes with fixed instance size we do not emit code to update // the size statistics. Update them by calling this method. void UpdateSize(intptr_t instance_size) { ASSERT(instance_size > 0); old_size = old_count * instance_size; new_size = new_count * instance_size; } void Verify() { ASSERT(new_count >= 0); ASSERT(new_size >= 0); ASSERT(old_count >= 0); ASSERT(old_size >= 0); } }; class ClassHeapStats { public: // Snapshot before GC. AllocStats pre_gc; // Live after GC. AllocStats post_gc; // Allocations since the last GC. AllocStats recent; // Accumulated (across GC) allocations . AllocStats accumulated; // Snapshot of recent at the time of the last reset. AllocStats last_reset; // Promoted from new to old by last new GC. intptr_t promoted_count; intptr_t promoted_size; static intptr_t allocated_since_gc_new_space_offset() { return OFFSET_OF(ClassHeapStats, recent) + OFFSET_OF(AllocStats, new_count); } static intptr_t allocated_since_gc_old_space_offset() { return OFFSET_OF(ClassHeapStats, recent) + OFFSET_OF(AllocStats, old_count); } static intptr_t allocated_size_since_gc_new_space_offset() { return OFFSET_OF(ClassHeapStats, recent) + OFFSET_OF(AllocStats, new_size); } static intptr_t allocated_size_since_gc_old_space_offset() { return OFFSET_OF(ClassHeapStats, recent) + OFFSET_OF(AllocStats, old_size); } static intptr_t state_offset() { return OFFSET_OF(ClassHeapStats, state_); } static intptr_t TraceAllocationMask() { return (1 << kTraceAllocationBit); } void Initialize(); void ResetAtNewGC(); void ResetAtOldGC(); void ResetAccumulator(); void UpdatePromotedAfterNewGC(); void UpdateSize(intptr_t instance_size); #ifndef PRODUCT void PrintToJSONObject(const Class& cls, JSONObject* obj) const; #endif void Verify(); bool trace_allocation() const { return TraceAllocationBit::decode(state_); } void set_trace_allocation(bool trace_allocation) { state_ = TraceAllocationBit::update(trace_allocation, state_); } private: enum StateBits { kTraceAllocationBit = 0, }; class TraceAllocationBit : public BitField {}; // Recent old at start of last new GC (used to compute promoted_*). intptr_t old_pre_new_gc_count_; intptr_t old_pre_new_gc_size_; intptr_t state_; intptr_t align_; // Make SIMARM and ARM agree on the size of ClassHeapStats. }; #endif // !PRODUCT class ClassTable { public: ClassTable(); // Creates a shallow copy of the original class table for some read-only // access, without support for stats data. explicit ClassTable(ClassTable* original); ~ClassTable(); // Thread-safe. RawClass* At(intptr_t index) const { ASSERT(IsValidIndex(index)); return table_[index]; } void SetAt(intptr_t index, RawClass* raw_cls) { table_[index] = raw_cls; } bool IsValidIndex(intptr_t index) const { return (index > 0) && (index < top_); } bool HasValidClassAt(intptr_t index) const { ASSERT(IsValidIndex(index)); return table_[index] != NULL; } intptr_t NumCids() const { return top_; } // Used to drop recently added classes. void SetNumCids(intptr_t num_cids) { ASSERT(num_cids <= top_); top_ = num_cids; } void Register(const Class& cls); void AllocateIndex(intptr_t index); void RegisterAt(intptr_t index, const Class& cls); #if defined(DEBUG) void Unregister(intptr_t index); #endif #if defined(DART_PRECOMPILER) void Remap(intptr_t* old_to_new_cids); #endif void VisitObjectPointers(ObjectPointerVisitor* visitor); void Validate(); void Print(); // Used by the generated code. static intptr_t table_offset() { return OFFSET_OF(ClassTable, table_); } // Used by the generated code. static intptr_t ClassOffsetFor(intptr_t cid); #ifndef PRODUCT // Called whenever a class is allocated in the runtime. void UpdateAllocatedNew(intptr_t cid, intptr_t size); void UpdateAllocatedOld(intptr_t cid, intptr_t size); // Called whenever a old GC occurs. void ResetCountersOld(); // Called whenever a new GC occurs. void ResetCountersNew(); // Called immediately after a new GC. void UpdatePromoted(); // Used by the generated code. ClassHeapStats** TableAddressFor(intptr_t cid); static intptr_t TableOffsetFor(intptr_t cid); // Used by the generated code. static intptr_t CounterOffsetFor(intptr_t cid, bool is_new_space); // Used by the generated code. static intptr_t StateOffsetFor(intptr_t cid); // Used by the generated code. static intptr_t SizeOffsetFor(intptr_t cid, bool is_new_space); ClassHeapStats* StatsWithUpdatedSize(intptr_t cid); void AllocationProfilePrintJSON(JSONStream* stream); void ResetAllocationAccumulators(); void PrintToJSONObject(JSONObject* object); #endif // !PRODUCT void AddOldTable(RawClass** old_table); // Deallocates table copies. Do not call during concurrent access to table. void FreeOldTables(); void SetTraceAllocationFor(intptr_t cid, bool trace); bool TraceAllocationFor(intptr_t cid); private: friend class GCMarker; friend class ScavengerVisitor; friend class ClassHeapStatsTestHelper; static const int initial_capacity_ = 512; static const int capacity_increment_ = 256; static bool ShouldUpdateSizeForClassId(intptr_t cid); intptr_t top_; intptr_t capacity_; // Copy-on-write is used for table_, with old copies stored in old_tables_. RawClass** table_; MallocGrowableArray* old_tables_; #ifndef PRODUCT ClassHeapStats* class_heap_stats_table_; ClassHeapStats* predefined_class_heap_stats_table_; // May not have updated size for variable size classes. ClassHeapStats* PreliminaryStatsAt(intptr_t cid); void UpdateLiveOld(intptr_t cid, intptr_t size, intptr_t count = 1); void UpdateLiveNew(intptr_t cid, intptr_t size); #endif // !PRODUCT DISALLOW_COPY_AND_ASSIGN(ClassTable); }; } // namespace dart #endif // RUNTIME_VM_CLASS_TABLE_H_