1 // Copyright 2009-2010 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/profiler/heap-profiler.h" 6 7 #include "src/api.h" 8 #include "src/debug/debug.h" 9 #include "src/heap/heap-inl.h" 10 #include "src/profiler/allocation-tracker.h" 11 #include "src/profiler/heap-snapshot-generator-inl.h" 12 #include "src/profiler/sampling-heap-profiler.h" 13 14 namespace v8 { 15 namespace internal { 16 17 HeapProfiler::HeapProfiler(Heap* heap) 18 : ids_(new HeapObjectsMap(heap)), 19 names_(new StringsStorage(heap)), 20 is_tracking_object_moves_(false), 21 get_retainer_infos_callback_(nullptr) {} 22 23 static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) { 24 delete *snapshot_ptr; 25 } 26 27 28 HeapProfiler::~HeapProfiler() { 29 snapshots_.Iterate(DeleteHeapSnapshot); 30 snapshots_.Clear(); 31 } 32 33 34 void HeapProfiler::DeleteAllSnapshots() { 35 snapshots_.Iterate(DeleteHeapSnapshot); 36 snapshots_.Clear(); 37 names_.reset(new StringsStorage(heap())); 38 } 39 40 41 void HeapProfiler::RemoveSnapshot(HeapSnapshot* snapshot) { 42 snapshots_.RemoveElement(snapshot); 43 } 44 45 46 void HeapProfiler::DefineWrapperClass( 47 uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) { 48 DCHECK(class_id != v8::HeapProfiler::kPersistentHandleNoClassId); 49 if (wrapper_callbacks_.length() <= class_id) { 50 wrapper_callbacks_.AddBlock( 51 NULL, class_id - wrapper_callbacks_.length() + 1); 52 } 53 wrapper_callbacks_[class_id] = callback; 54 } 55 56 57 v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback( 58 uint16_t class_id, Object** wrapper) { 59 if (wrapper_callbacks_.length() <= class_id) return NULL; 60 return wrapper_callbacks_[class_id]( 61 class_id, Utils::ToLocal(Handle<Object>(wrapper))); 62 } 63 64 void HeapProfiler::SetGetRetainerInfosCallback( 65 v8::HeapProfiler::GetRetainerInfosCallback callback) { 66 get_retainer_infos_callback_ = callback; 67 } 68 69 v8::HeapProfiler::RetainerInfos HeapProfiler::GetRetainerInfos( 70 Isolate* isolate) { 71 v8::HeapProfiler::RetainerInfos infos; 72 if (get_retainer_infos_callback_ != nullptr) 73 infos = 74 get_retainer_infos_callback_(reinterpret_cast<v8::Isolate*>(isolate)); 75 return infos; 76 } 77 78 HeapSnapshot* HeapProfiler::TakeSnapshot( 79 v8::ActivityControl* control, 80 v8::HeapProfiler::ObjectNameResolver* resolver) { 81 HeapSnapshot* result = new HeapSnapshot(this); 82 { 83 HeapSnapshotGenerator generator(result, control, resolver, heap()); 84 if (!generator.GenerateSnapshot()) { 85 delete result; 86 result = NULL; 87 } else { 88 snapshots_.Add(result); 89 } 90 } 91 ids_->RemoveDeadEntries(); 92 is_tracking_object_moves_ = true; 93 94 heap()->isolate()->debug()->feature_tracker()->Track( 95 DebugFeatureTracker::kHeapSnapshot); 96 97 return result; 98 } 99 100 bool HeapProfiler::StartSamplingHeapProfiler( 101 uint64_t sample_interval, int stack_depth, 102 v8::HeapProfiler::SamplingFlags flags) { 103 if (sampling_heap_profiler_.get()) { 104 return false; 105 } 106 sampling_heap_profiler_.reset(new SamplingHeapProfiler( 107 heap(), names_.get(), sample_interval, stack_depth, flags)); 108 return true; 109 } 110 111 112 void HeapProfiler::StopSamplingHeapProfiler() { 113 sampling_heap_profiler_.reset(); 114 } 115 116 117 v8::AllocationProfile* HeapProfiler::GetAllocationProfile() { 118 if (sampling_heap_profiler_.get()) { 119 return sampling_heap_profiler_->GetAllocationProfile(); 120 } else { 121 return nullptr; 122 } 123 } 124 125 126 void HeapProfiler::StartHeapObjectsTracking(bool track_allocations) { 127 ids_->UpdateHeapObjectsMap(); 128 is_tracking_object_moves_ = true; 129 DCHECK(!is_tracking_allocations()); 130 if (track_allocations) { 131 allocation_tracker_.reset(new AllocationTracker(ids_.get(), names_.get())); 132 heap()->DisableInlineAllocation(); 133 heap()->isolate()->debug()->feature_tracker()->Track( 134 DebugFeatureTracker::kAllocationTracking); 135 } 136 } 137 138 139 SnapshotObjectId HeapProfiler::PushHeapObjectsStats(OutputStream* stream, 140 int64_t* timestamp_us) { 141 return ids_->PushHeapObjectsStats(stream, timestamp_us); 142 } 143 144 145 void HeapProfiler::StopHeapObjectsTracking() { 146 ids_->StopHeapObjectsTracking(); 147 if (is_tracking_allocations()) { 148 allocation_tracker_.reset(); 149 heap()->EnableInlineAllocation(); 150 } 151 } 152 153 154 size_t HeapProfiler::GetMemorySizeUsedByProfiler() { 155 size_t size = sizeof(*this); 156 size += names_->GetUsedMemorySize(); 157 size += ids_->GetUsedMemorySize(); 158 size += GetMemoryUsedByList(snapshots_); 159 for (int i = 0; i < snapshots_.length(); ++i) { 160 size += snapshots_[i]->RawSnapshotSize(); 161 } 162 return size; 163 } 164 165 166 int HeapProfiler::GetSnapshotsCount() { 167 return snapshots_.length(); 168 } 169 170 171 HeapSnapshot* HeapProfiler::GetSnapshot(int index) { 172 return snapshots_.at(index); 173 } 174 175 176 SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) { 177 if (!obj->IsHeapObject()) 178 return v8::HeapProfiler::kUnknownObjectId; 179 return ids_->FindEntry(HeapObject::cast(*obj)->address()); 180 } 181 182 183 void HeapProfiler::ObjectMoveEvent(Address from, Address to, int size) { 184 base::LockGuard<base::Mutex> guard(&profiler_mutex_); 185 bool known_object = ids_->MoveObject(from, to, size); 186 if (!known_object && allocation_tracker_) { 187 allocation_tracker_->address_to_trace()->MoveObject(from, to, size); 188 } 189 } 190 191 192 void HeapProfiler::AllocationEvent(Address addr, int size) { 193 DisallowHeapAllocation no_allocation; 194 if (allocation_tracker_) { 195 allocation_tracker_->AllocationEvent(addr, size); 196 } 197 } 198 199 200 void HeapProfiler::UpdateObjectSizeEvent(Address addr, int size) { 201 ids_->UpdateObjectSize(addr, size); 202 } 203 204 205 void HeapProfiler::SetRetainedObjectInfo(UniqueId id, 206 RetainedObjectInfo* info) { 207 // TODO(yurus, marja): Don't route this information through GlobalHandles. 208 heap()->isolate()->global_handles()->SetRetainedObjectInfo(id, info); 209 } 210 211 212 Handle<HeapObject> HeapProfiler::FindHeapObjectById(SnapshotObjectId id) { 213 HeapObject* object = NULL; 214 HeapIterator iterator(heap(), HeapIterator::kFilterUnreachable); 215 // Make sure that object with the given id is still reachable. 216 for (HeapObject* obj = iterator.next(); 217 obj != NULL; 218 obj = iterator.next()) { 219 if (ids_->FindEntry(obj->address()) == id) { 220 DCHECK(object == NULL); 221 object = obj; 222 // Can't break -- kFilterUnreachable requires full heap traversal. 223 } 224 } 225 return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>(); 226 } 227 228 229 void HeapProfiler::ClearHeapObjectMap() { 230 ids_.reset(new HeapObjectsMap(heap())); 231 if (!is_tracking_allocations()) is_tracking_object_moves_ = false; 232 } 233 234 235 Heap* HeapProfiler::heap() const { return ids_->heap(); } 236 237 238 } // namespace internal 239 } // namespace v8 240