1 // Copyright 2014 the V8 project authors. All rights reserved.
\r
2 // Use of this source code is governed by a BSD-style license that can be
\r
3 // found in the LICENSE file.
\r
10 #include "src/xdk-allocation.h"
\r
12 #include "frames-inl.h"
\r
13 #include "src/xdk-utils.h"
\r
16 namespace internal {
\r
18 XDKAllocationTracker::XDKAllocationTracker(HeapProfiler* heap_profiler,
\r
19 HeapObjectsMap *ids,
\r
20 StringsStorage *names,
\r
22 bool collectRetention,
\r
23 bool strict_collection)
\r
24 : heap_profiler_(heap_profiler),
\r
27 stackDepth_(stackDepth),
\r
28 collectRetention_(collectRetention),
\r
29 strict_collection_(strict_collection) {
\r
30 references_ = new References();
\r
31 aggregated_chunks_ = new AggregatedChunks();
\r
32 runtime_info_ = new RuntimeInfo(aggregated_chunks_);
\r
33 symbols_ = new SymbolsStorage(ids_->heap(), names_);
\r
34 collectedStacks_ = new ShadowStack();
\r
35 classNames_ = new ClassNames(names_);
\r
37 List<unsigned> stack_ooc;
\r
38 stack_ooc.Add(symbols_->registerSymInfo(1, "OutOfContext", "NoSource",
\r
40 outOfContextFrame_ = collectedStacks_->registerStack(stack_ooc);
\r
42 List<unsigned> stack_abc;
\r
43 stack_abc.Add(symbols_->registerSymInfo(2, "AllocatedBeforeCollection",
\r
45 AllocatedBeforeCollectionFrame_ = collectedStacks_->registerStack(stack_abc);
\r
47 runtime_info_->InitABCFrame(AllocatedBeforeCollectionFrame_);
\r
49 baseTime_ = v8::base::Time::Now();
\r
54 XDKAllocationTracker::~XDKAllocationTracker() {
\r
55 delete collectedStacks_;
\r
57 delete aggregated_chunks_;
\r
58 delete runtime_info_;
\r
64 // Heap profiler regularly takes time for storing when object was allocated,
\r
65 // deallocated, when object's retention snapshot was taken, etc
\r
66 unsigned int XDKAllocationTracker::GetTimeDelta() {
\r
67 v8::base::TimeDelta td = v8::base::Time::Now() - baseTime_;
\r
68 return static_cast<unsigned int>(td.InMilliseconds());
\r
72 void XDKAllocationTracker::OnAlloc(Address addr, int size) {
\r
73 DisallowHeapAllocation no_alloc;
\r
74 Heap *heap = ids_->heap();
\r
76 // below call saves from the crash during StackTraceFrameIterator creation
\r
77 // Mark the new block as FreeSpace to make sure the heap is iterable
\r
78 // while we are capturing stack trace.
\r
79 FreeListNode::FromAddress(addr)->set_size(heap, size);
\r
81 Isolate *isolate = heap->isolate();
\r
82 StackTraceFrameIterator it(isolate);
\r
83 List<unsigned> stack;
\r
85 // TODO(amalyshe): checking of isolate->handle_scope_data()->level is quite
\r
86 // artificial. need to understand when we can have such behaviour
\r
87 // if level == 0 we will crash in getting of source info
\r
88 while (isolate->handle_scope_data()->level && !it.done() &&
\r
89 stack.length() < stackDepth_) {
\r
90 JavaScriptFrame *frame = it.frame();
\r
91 if (!frame->function())
\r
93 SharedFunctionInfo *shared = frame->function()->shared();
\r
97 stack.Add(symbols_->FindOrRegisterFrame(frame));
\r
102 if (!stack.is_empty()) {
\r
103 sid = collectedStacks_->registerStack(stack);
\r
105 sid = outOfContextFrame_;
\r
108 latest_delta_ = GetTimeDelta();
\r
110 PostCollectedInfo* info = runtime_info_->AddPostCollectedInfo(addr,
\r
112 info->size_ = size;
\r
113 info->timeStamp_ = latest_delta_;
\r
114 info->stackId_ = sid;
\r
115 info->className_ = (unsigned int)-1;
\r
116 info->dirty_ = false;
\r
120 void XDKAllocationTracker::OnMove(Address from, Address to, int size) {
\r
121 DisallowHeapAllocation no_alloc;
\r
122 // look for the prev address
\r
123 PostCollectedInfo* info_from = runtime_info_->FindPostCollectedInfo(from);
\r
124 if (info_from == NULL) {
\r
128 runtime_info_->AddPostCollectedInfo(to, latest_delta_, info_from);
\r
129 runtime_info_->RemoveInfo(from);
\r
133 HeapEventXDK* XDKAllocationTracker::stopTracking() {
\r
134 std::string symbols, types, frames, chunks, retentions;
\r
135 SerializeChunk(&symbols, &types, &frames, &chunks, &retentions);
\r
136 CollectFreedObjects(true);
\r
137 std::string symbolsA, typesA, framesA, chunksA, retentionsA;
\r
138 SerializeChunk(&symbolsA, &typesA, &framesA, &chunksA, &retentionsA, true);
\r
140 // TODO(amalyshe): check who releases this object - new HeapEventXDK
\r
141 return (new HeapEventXDK(GetTimeDelta(), symbols+symbolsA, types+typesA,
\r
142 frames+framesA, chunks+chunksA, ""));
\r
146 void XDKAllocationTracker::CollectFreedObjects(bool bAll, bool initPreCollect) {
\r
147 clearIndividualReteiners();
\r
148 if (collectRetention_) {
\r
149 XDKSnapshotFiller filler(ids_, names_, this);
\r
150 HeapSnapshotGenerator generator(heap_profiler_, NULL, NULL, NULL,
\r
151 ids_->heap(), &filler);
\r
152 generator.GenerateSnapshot();
\r
155 Heap *heap = ids_->heap();
\r
160 unsigned int ts = GetTimeDelta();
\r
162 ts += RETAINED_DELTA;
\r
165 // CDT heap profiler calls CollectAllGarbage twice because after the first
\r
166 // pass there are weak retained object not collected, but due to perf issues
\r
167 // and because we do garbage collection regularly, we leave here only one call
\r
168 // only for strict collection like in test where we need to make sure that
\r
169 // object is definitely collected, we collect twice
\r
170 heap->CollectAllGarbage(
\r
171 Heap::kMakeHeapIterableMask,
\r
172 "XDKAllocationTracker::CollectFreedObjects");
\r
173 if (strict_collection_) {
\r
174 heap->CollectAllGarbage(
\r
175 Heap::kMakeHeapIterableMask,
\r
176 "XDKAllocationTracker::CollectFreedObjects");
\r
178 std::map<Address, RefSet> individualReteiners;
\r
180 // TODO(amalyshe): check what DisallowHeapAllocation no_alloc means because in
\r
181 // standalone v8 this part is crashed if DisallowHeapAllocation is defined
\r
182 // DisallowHeapAllocation no_alloc;
\r
184 HeapIterator iterator(heap);
\r
185 HeapObject* obj = iterator.next();
\r
188 obj = iterator.next()) {
\r
189 Address addr = obj->address();
\r
191 PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(addr);
\r
193 // if we don't find info, we consider it as pre collection allocated
\r
194 // object. need to add to the full picture for retentions
\r
195 if (initPreCollect) {
\r
196 info = runtime_info_->AddPreCollectionInfo(addr, obj->Size());
\r
201 info->dirty_ = true;
\r
203 // check of the class name and its initialization
\r
204 if ((info && info->className_ == (unsigned)-1) || !info) {
\r
205 InitClassName(addr, ts, obj->Size());
\r
210 if (collectRetention_) {
\r
211 std::map<Address, RefSet>::const_iterator citir =
\r
212 individualReteiners_.begin();
\r
213 while (citir != individualReteiners_.end()) {
\r
214 PostCollectedInfo* infoChild =
\r
215 runtime_info_->FindPostCollectedInfo(citir->first);
\r
218 rfId.stackId_ = infoChild->stackId_;
\r
219 rfId.classId_ = infoChild->className_;
\r
221 references_->addReference(rfId, citir->second, infoChild->timeStamp_);
\r
227 runtime_info_->CollectGarbaged(ts);
\r
231 void XDKAllocationTracker::SerializeChunk(std::string* symbols,
\r
232 std::string* types,
\r
233 std::string* frames,
\r
234 std::string* chunks,
\r
235 std::string* retentions,
\r
238 *symbols = symbols_->SerializeChunk();
\r
239 *types = classNames_->SerializeChunk();
\r
241 *frames = collectedStacks_->SerializeChunk();
\r
242 *chunks = aggregated_chunks_->SerializeChunk();
\r
244 *retentions = references_->serialize();
\r
245 std::stringstream retentionsT;
\r
246 retentionsT << GetTimeDelta() << std::endl << retentions->c_str();
\r
247 *retentions = retentionsT.str();
\r
248 references_->clear();
\r
252 OutputStream::WriteResult XDKAllocationTracker::SendChunk(
\r
253 OutputStream* stream) {
\r
254 // go over all aggregated_ and send data to the stream
\r
255 std::string symbols, types, frames, chunks, retentions;
\r
256 SerializeChunk(&symbols, &types, &frames, &chunks, &retentions);
\r
258 OutputStream::WriteResult ret = stream->WriteHeapXDKChunk(
\r
259 symbols.c_str(), symbols.length(),
\r
260 frames.c_str(), frames.length(),
\r
261 types.c_str(), types.length(),
\r
262 chunks.c_str(), chunks.length(),
\r
263 retentions.c_str(), retentions.length());
\r
268 unsigned XDKAllocationTracker::GetTraceNodeId(Address address) {
\r
269 PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);
\r
271 return info->stackId_;
\r
273 return AllocatedBeforeCollectionFrame_;
\r
278 void XDKAllocationTracker::clearIndividualReteiners() {
\r
279 individualReteiners_.clear();
\r
283 std::map<Address, RefSet>* XDKAllocationTracker::GetIndividualReteiners() {
\r
284 return &individualReteiners_;
\r
288 unsigned XDKAllocationTracker::FindClassName(Address address) {
\r
289 PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);
\r
291 return info->className_;
\r
293 return (unsigned)-1;
\r
298 unsigned XDKAllocationTracker::InitClassName(Address address, unsigned ts,
\r
301 PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);
\r
303 info = runtime_info_->AddPostCollectedInfo(address, ts);
\r
304 info->className_ = -1;
\r
305 info->stackId_ = outOfContextFrame_;
\r
306 info->timeStamp_ = ts;
\r
307 info->size_ = size;
\r
309 if (info->className_ == (unsigned)-1) {
\r
310 String* str = classNames_->GetConstructorName(address);
\r
312 // get const char*, it's safe because pointer will be retained in the
\r
313 // name_ until it is destroyed
\r
314 id = classNames_->registerName(names_->GetName(str));
\r
317 info->className_ = id;
\r
322 unsigned XDKAllocationTracker::FindOrInitClassName(Address address,
\r
324 unsigned id = FindClassName(address);
\r
325 if (id == (unsigned)-1) {
\r
326 // TODO(amalyshe) check if 0 size here is appropriate
\r
327 id = InitClassName(address, ts, 0);
\r
333 // -----------------------------------------------------------------------------
\r
334 // this is almost a copy and duplication of
\r
335 // V8HeapExplorer::AddEntry. refactoring is impossible because
\r
336 // heap-snapshot-generator rely on it's structure which is not fully suitable
\r
338 HeapEntry* XDKSnapshotFiller::AddEntry(HeapThing ptr,
\r
339 HeapEntriesAllocator* allocator) {
\r
340 HeapObject* object = reinterpret_cast<HeapObject*>(ptr);
\r
341 if (object->IsJSFunction()) {
\r
342 JSFunction* func = JSFunction::cast(object);
\r
343 SharedFunctionInfo* shared = func->shared();
\r
344 const char* name = shared->bound() ? "native_bind" :
\r
345 names_->GetName(String::cast(shared->name()));
\r
346 return AddEntry(ptr, object, HeapEntry::kClosure, name);
\r
347 } else if (object->IsJSRegExp()) {
\r
348 JSRegExp* re = JSRegExp::cast(object);
\r
349 return AddEntry(ptr, object,
\r
350 HeapEntry::kRegExp,
\r
351 names_->GetName(re->Pattern()));
\r
352 } else if (object->IsJSObject()) {
\r
353 return AddEntry(ptr, object, HeapEntry::kObject, "");
\r
354 } else if (object->IsString()) {
\r
355 String* string = String::cast(object);
\r
356 if (string->IsConsString())
\r
357 return AddEntry(ptr, object,
\r
358 HeapEntry::kConsString,
\r
359 "(concatenated string)");
\r
360 if (string->IsSlicedString())
\r
361 return AddEntry(ptr, object,
\r
362 HeapEntry::kSlicedString,
\r
363 "(sliced string)");
\r
364 return AddEntry(ptr, object,
\r
365 HeapEntry::kString,
\r
366 names_->GetName(String::cast(object)));
\r
367 } else if (object->IsSymbol()) {
\r
368 return AddEntry(ptr, object, HeapEntry::kSymbol, "symbol");
\r
369 } else if (object->IsCode()) {
\r
370 return AddEntry(ptr, object, HeapEntry::kCode, "");
\r
371 } else if (object->IsSharedFunctionInfo()) {
\r
372 String* name = String::cast(SharedFunctionInfo::cast(object)->name());
\r
373 return AddEntry(ptr, object,
\r
375 names_->GetName(name));
\r
376 } else if (object->IsScript()) {
\r
377 Object* name = Script::cast(object)->name();
\r
378 return AddEntry(ptr, object,
\r
381 ? names_->GetName(String::cast(name))
\r
383 } else if (object->IsNativeContext()) {
\r
384 return AddEntry(ptr, object, HeapEntry::kHidden, "system / NativeContext");
\r
385 } else if (object->IsContext()) {
\r
386 return AddEntry(ptr, object, HeapEntry::kObject, "system / Context");
\r
387 } else if (object->IsFixedArray() ||
\r
388 object->IsFixedDoubleArray() ||
\r
389 object->IsByteArray() ||
\r
390 object->IsExternalArray()) {
\r
391 return AddEntry(ptr, object, HeapEntry::kArray, "");
\r
392 } else if (object->IsHeapNumber()) {
\r
393 return AddEntry(ptr, object, HeapEntry::kHeapNumber, "number");
\r
396 return AddEntry(ptr, object, HeapEntry::kHidden, "system / NOT SUPORTED YET");
\r
400 HeapEntry* XDKSnapshotFiller::AddEntry(HeapThing thing,
\r
401 HeapObject* object,
\r
402 HeapEntry::Type type,
\r
403 const char* name) {
\r
404 Address address = object->address();
\r
405 unsigned trace_node_id = 0;
\r
406 trace_node_id = allocation_tracker_->GetTraceNodeId(address);
\r
408 // cannot store pointer in the map because List reallcoates content regularly
\r
409 // and the only one way to find the entry - by index. so, index is cached in
\r
411 // TODO(amalyshe): need to reuse type. it seems it is important
\r
412 HeapEntry entry(NULL, &heap_entries_list_, type, name, 0, 0,
\r
414 heap_entries_list_.Add(entry);
\r
415 HeapEntry* pEntry = &heap_entries_list_.last();
\r
417 HashMap::Entry* cache_entry = heap_entries_.Lookup(thing, Hash(thing), true);
\r
418 DCHECK(cache_entry->value == NULL);
\r
419 int index = pEntry->index();
\r
420 cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(index));
\r
422 // TODO(amalyshe): it seems this storage might be optimized
\r
423 HashMap::Entry* address_entry = index_to_address_.Lookup(
\r
424 reinterpret_cast<void*>(index+1), HashInt(index+1), true);
\r
425 address_entry->value = reinterpret_cast<void*>(address);
\r
431 HeapEntry* XDKSnapshotFiller::FindEntry(HeapThing thing) {
\r
432 HashMap::Entry* cache_entry = heap_entries_.Lookup(thing, Hash(thing), false);
\r
433 if (cache_entry == NULL) return NULL;
\r
434 return &heap_entries_list_[static_cast<int>(
\r
435 reinterpret_cast<intptr_t>(cache_entry->value))];
\r
439 HeapEntry* XDKSnapshotFiller::FindOrAddEntry(HeapThing ptr,
\r
440 HeapEntriesAllocator* allocator) {
\r
441 HeapEntry* entry = FindEntry(ptr);
\r
442 return entry != NULL ? entry : AddEntry(ptr, allocator);
\r
446 void XDKSnapshotFiller::SetIndexedReference(HeapGraphEdge::Type type,
\r
449 HeapEntry* child_entry) {
\r
450 if (child_entry->trace_node_id() < 3) {
\r
453 HashMap::Entry* address_entry_child = index_to_address_.Lookup(
\r
454 reinterpret_cast<void*>(child_entry->index()+1),
\r
455 HashInt(child_entry->index()+1), false);
\r
456 DCHECK(address_entry_child != NULL);
\r
457 if (!address_entry_child) {
\r
461 Address child_addr = reinterpret_cast<Address>(address_entry_child->value);
\r
463 std::map<Address, RefSet>* individualReteiners =
\r
464 allocation_tracker_->GetIndividualReteiners();
\r
465 // get the parent's address, constructor name and form the RefId
\r
466 HashMap::Entry* address_entry = index_to_address_.Lookup(
\r
467 reinterpret_cast<void*>(parent+1), HashInt(parent+1), false);
\r
468 DCHECK(address_entry != NULL);
\r
469 if (!address_entry) {
\r
472 HeapEntry* parent_entry = &(heap_entries_list_[parent]);
\r
473 Address parent_addr = reinterpret_cast<Address>(address_entry->value);
\r
474 RefId parent_ref_id;
\r
475 parent_ref_id.stackId_ = parent_entry->trace_node_id();
\r
476 parent_ref_id.classId_ =
\r
477 allocation_tracker_->FindOrInitClassName(parent_addr, 0);
\r
479 std::stringstream str;
\r
480 str << index << " element in Array";
\r
481 parent_ref_id.field_ = str.str();
\r
483 (*individualReteiners)[child_addr].references_.insert(parent_ref_id);
\r
487 void XDKSnapshotFiller::SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
\r
489 HeapEntry* child_entry) {
\r
493 void XDKSnapshotFiller::SetNamedReference(HeapGraphEdge::Type type,
\r
495 const char* reference_name,
\r
496 HeapEntry* child_entry) {
\r
497 if (child_entry->trace_node_id() < 3) {
\r
501 std::map<Address, RefSet>* individualReteiners =
\r
502 allocation_tracker_->GetIndividualReteiners();
\r
503 // get the parent's address, constructor name and form the RefId
\r
504 HashMap::Entry* address_entry = index_to_address_.Lookup(
\r
505 reinterpret_cast<void*>(parent+1), HashInt(parent+1), false);
\r
506 DCHECK(address_entry != NULL);
\r
507 if (!address_entry) {
\r
510 HeapEntry* parent_entry = &(heap_entries_list_[parent]);
\r
511 Address parent_addr = reinterpret_cast<Address>(address_entry->value);
\r
512 RefId parent_ref_id;
\r
513 parent_ref_id.stackId_ = parent_entry->trace_node_id();
\r
514 // TODO(amalyshe): need to get access to classNames_
\r
515 parent_ref_id.classId_ =
\r
516 allocation_tracker_->FindOrInitClassName(parent_addr, 0);
\r
517 parent_ref_id.field_ = reference_name;
\r
519 HashMap::Entry* address_entry_child = index_to_address_.Lookup(
\r
520 reinterpret_cast<void*>(child_entry->index()+1),
\r
521 HashInt(child_entry->index()+1), false);
\r
522 DCHECK(address_entry_child != NULL);
\r
523 if (!address_entry_child) {
\r
526 Address child_addr = reinterpret_cast<Address>(address_entry_child->value);
\r
528 (*individualReteiners)[child_addr].references_.insert(parent_ref_id);
\r
532 void XDKSnapshotFiller::SetNamedAutoIndexReference(HeapGraphEdge::Type type,
\r
534 HeapEntry* child_entry) {
\r
539 } // namespace v8::internal
\r