-// Copyright 2014 the V8 project authors. All rights reserved.\r
-// Use of this source code is governed by a BSD-style license that can be\r
-// found in the LICENSE file.\r
-\r
-#include <sstream>\r
-#include <string>\r
-\r
-#include "src/v8.h"\r
-\r
-#include "src/xdk-allocation.h"\r
-\r
-#include "frames-inl.h"\r
-#include "src/xdk-utils.h"\r
-\r
-namespace v8 {\r
-namespace internal {\r
-\r
-XDKAllocationTracker::XDKAllocationTracker(HeapProfiler* heap_profiler,\r
- HeapObjectsMap *ids,\r
- StringsStorage *names,\r
- int stackDepth,\r
- bool collectRetention,\r
- bool strict_collection)\r
- : heap_profiler_(heap_profiler),\r
- ids_(ids),\r
- names_(names),\r
- stackDepth_(stackDepth),\r
- collectRetention_(collectRetention),\r
- strict_collection_(strict_collection) {\r
- references_ = new References();\r
- aggregated_chunks_ = new AggregatedChunks();\r
- runtime_info_ = new RuntimeInfo(aggregated_chunks_);\r
- symbols_ = new SymbolsStorage(ids_->heap(), names_);\r
- collectedStacks_ = new ShadowStack();\r
- classNames_ = new ClassNames(names_);\r
-\r
- List<unsigned> stack_ooc;\r
- stack_ooc.Add(symbols_->registerSymInfo(1, "OutOfContext", "NoSource",\r
- 0, 0));\r
- outOfContextFrame_ = collectedStacks_->registerStack(stack_ooc);\r
-\r
- List<unsigned> stack_abc;\r
- stack_abc.Add(symbols_->registerSymInfo(2, "AllocatedBeforeCollection",\r
- "NoSource", 0, 0));\r
- AllocatedBeforeCollectionFrame_ = collectedStacks_->registerStack(stack_abc);\r
-\r
- runtime_info_->InitABCFrame(AllocatedBeforeCollectionFrame_);\r
-\r
- baseTime_ = v8::base::Time::Now();\r
- latest_delta_ = 0;\r
-}\r
-\r
-\r
-XDKAllocationTracker::~XDKAllocationTracker() {\r
- delete collectedStacks_;\r
- delete classNames_;\r
- delete aggregated_chunks_;\r
- delete runtime_info_;\r
- delete symbols_;\r
- delete references_;\r
-}\r
-\r
-\r
-// Heap profiler regularly takes time for storing when object was allocated,\r
-// deallocated, when object's retention snapshot was taken, etc\r
-unsigned int XDKAllocationTracker::GetTimeDelta() {\r
- v8::base::TimeDelta td = v8::base::Time::Now() - baseTime_;\r
- return static_cast<unsigned int>(td.InMilliseconds());\r
-}\r
-\r
-\r
-void XDKAllocationTracker::OnAlloc(Address addr, int size) {\r
- DisallowHeapAllocation no_alloc;\r
- Heap *heap = ids_->heap();\r
-\r
- // below call saves from the crash during StackTraceFrameIterator creation\r
- // Mark the new block as FreeSpace to make sure the heap is iterable\r
- // while we are capturing stack trace.\r
- FreeListNode::FromAddress(addr)->set_size(heap, size);\r
-\r
- Isolate *isolate = heap->isolate();\r
- StackTraceFrameIterator it(isolate);\r
- List<unsigned> stack;\r
-\r
- // TODO(amalyshe): checking of isolate->handle_scope_data()->level is quite\r
- // artificial. need to understand when we can have such behaviour\r
- // if level == 0 we will crash in getting of source info\r
- while (isolate->handle_scope_data()->level && !it.done() &&\r
- stack.length() < stackDepth_) {\r
- JavaScriptFrame *frame = it.frame();\r
- if (!frame->function())\r
- break;\r
- SharedFunctionInfo *shared = frame->function()->shared();\r
- if (!shared)\r
- break;\r
-\r
- stack.Add(symbols_->FindOrRegisterFrame(frame));\r
- it.Advance();\r
- }\r
-\r
- unsigned sid;\r
- if (!stack.is_empty()) {\r
- sid = collectedStacks_->registerStack(stack);\r
- } else {\r
- sid = outOfContextFrame_;\r
- }\r
-\r
- latest_delta_ = GetTimeDelta();\r
-\r
- PostCollectedInfo* info = runtime_info_->AddPostCollectedInfo(addr,\r
- latest_delta_);\r
- info->size_ = size;\r
- info->timeStamp_ = latest_delta_;\r
- info->stackId_ = sid;\r
- info->className_ = (unsigned int)-1;\r
- info->dirty_ = false;\r
-}\r
-\r
-\r
-void XDKAllocationTracker::OnMove(Address from, Address to, int size) {\r
- DisallowHeapAllocation no_alloc;\r
- // look for the prev address\r
- PostCollectedInfo* info_from = runtime_info_->FindPostCollectedInfo(from);\r
- if (info_from == NULL) {\r
- return;\r
- }\r
-\r
- runtime_info_->AddPostCollectedInfo(to, latest_delta_, info_from);\r
- runtime_info_->RemoveInfo(from);\r
-}\r
-\r
-\r
-HeapEventXDK* XDKAllocationTracker::stopTracking() {\r
- std::string symbols, types, frames, chunks, retentions;\r
- SerializeChunk(&symbols, &types, &frames, &chunks, &retentions);\r
- CollectFreedObjects(true);\r
- std::string symbolsA, typesA, framesA, chunksA, retentionsA;\r
- SerializeChunk(&symbolsA, &typesA, &framesA, &chunksA, &retentionsA, true);\r
-\r
- // TODO(amalyshe): check who releases this object - new HeapEventXDK\r
- return (new HeapEventXDK(GetTimeDelta(), symbols+symbolsA, types+typesA,\r
- frames+framesA, chunks+chunksA, ""));\r
-}\r
-\r
-\r
-void XDKAllocationTracker::CollectFreedObjects(bool bAll, bool initPreCollect) {\r
- clearIndividualReteiners();\r
- if (collectRetention_) {\r
- XDKSnapshotFiller filler(ids_, names_, this);\r
- HeapSnapshotGenerator generator(heap_profiler_, NULL, NULL, NULL,\r
- ids_->heap(), &filler);\r
- generator.GenerateSnapshot();\r
- }\r
-\r
- Heap *heap = ids_->heap();\r
- if (!heap) {\r
- return;\r
- }\r
-\r
- unsigned int ts = GetTimeDelta();\r
- if (bAll) {\r
- ts += RETAINED_DELTA;\r
- }\r
-\r
- // CDT heap profiler calls CollectAllGarbage twice because after the first\r
- // pass there are weak retained object not collected, but due to perf issues\r
- // and because we do garbage collection regularly, we leave here only one call\r
- // only for strict collection like in test where we need to make sure that\r
- // object is definitely collected, we collect twice\r
- heap->CollectAllGarbage(\r
- Heap::kMakeHeapIterableMask,\r
- "XDKAllocationTracker::CollectFreedObjects");\r
- if (strict_collection_) {\r
- heap->CollectAllGarbage(\r
- Heap::kMakeHeapIterableMask,\r
- "XDKAllocationTracker::CollectFreedObjects");\r
- }\r
- std::map<Address, RefSet> individualReteiners;\r
-\r
- // TODO(amalyshe): check what DisallowHeapAllocation no_alloc means because in\r
- // standalone v8 this part is crashed if DisallowHeapAllocation is defined\r
- // DisallowHeapAllocation no_alloc;\r
- if (!bAll) {\r
- HeapIterator iterator(heap);\r
- HeapObject* obj = iterator.next();\r
- for (;\r
- obj != NULL;\r
- obj = iterator.next()) {\r
- Address addr = obj->address();\r
-\r
- PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(addr);\r
- if (!info) {\r
- // if we don't find info, we consider it as pre collection allocated\r
- // object. need to add to the full picture for retentions\r
- if (initPreCollect) {\r
- info = runtime_info_->AddPreCollectionInfo(addr, obj->Size());\r
- }\r
- }\r
-\r
- if (info) {\r
- info->dirty_ = true;\r
- }\r
- // check of the class name and its initialization\r
- if ((info && info->className_ == (unsigned)-1) || !info) {\r
- InitClassName(addr, ts, obj->Size());\r
- }\r
- }\r
- }\r
-\r
- if (collectRetention_) {\r
- std::map<Address, RefSet>::const_iterator citir =\r
- individualReteiners_.begin();\r
- while (citir != individualReteiners_.end()) {\r
- PostCollectedInfo* infoChild =\r
- runtime_info_->FindPostCollectedInfo(citir->first);\r
- if (infoChild) {\r
- RefId rfId;\r
- rfId.stackId_ = infoChild->stackId_;\r
- rfId.classId_ = infoChild->className_;\r
-\r
- references_->addReference(rfId, citir->second, infoChild->timeStamp_);\r
- }\r
- citir++;\r
- }\r
- }\r
-\r
- runtime_info_->CollectGarbaged(ts);\r
-}\r
-\r
-\r
-void XDKAllocationTracker::SerializeChunk(std::string* symbols,\r
- std::string* types,\r
- std::string* frames,\r
- std::string* chunks,\r
- std::string* retentions,\r
- bool final) {\r
- if (final) {\r
- *symbols = symbols_->SerializeChunk();\r
- *types = classNames_->SerializeChunk();\r
- }\r
- *frames = collectedStacks_->SerializeChunk();\r
- *chunks = aggregated_chunks_->SerializeChunk();\r
-\r
- *retentions = references_->serialize();\r
- std::stringstream retentionsT;\r
- retentionsT << GetTimeDelta() << std::endl << retentions->c_str();\r
- *retentions = retentionsT.str();\r
- references_->clear();\r
-}\r
-\r
-\r
-OutputStream::WriteResult XDKAllocationTracker::SendChunk(\r
- OutputStream* stream) {\r
- // go over all aggregated_ and send data to the stream\r
- std::string symbols, types, frames, chunks, retentions;\r
- SerializeChunk(&symbols, &types, &frames, &chunks, &retentions);\r
-\r
- OutputStream::WriteResult ret = stream->WriteHeapXDKChunk(\r
- symbols.c_str(), symbols.length(),\r
- frames.c_str(), frames.length(),\r
- types.c_str(), types.length(),\r
- chunks.c_str(), chunks.length(),\r
- retentions.c_str(), retentions.length());\r
- return ret;\r
-}\r
-\r
-\r
-unsigned XDKAllocationTracker::GetTraceNodeId(Address address) {\r
- PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);\r
- if (info) {\r
- return info->stackId_;\r
- } else {\r
- return AllocatedBeforeCollectionFrame_;\r
- }\r
-}\r
-\r
-\r
-void XDKAllocationTracker::clearIndividualReteiners() {\r
- individualReteiners_.clear();\r
-}\r
-\r
-\r
-std::map<Address, RefSet>* XDKAllocationTracker::GetIndividualReteiners() {\r
- return &individualReteiners_;\r
-}\r
-\r
-\r
-unsigned XDKAllocationTracker::FindClassName(Address address) {\r
- PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);\r
- if (info) {\r
- return info->className_;\r
- } else {\r
- return (unsigned)-1;\r
- }\r
-}\r
-\r
-\r
-unsigned XDKAllocationTracker::InitClassName(Address address, unsigned ts,\r
- unsigned size) {\r
- unsigned id = -2;\r
- PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);\r
- if (!info) {\r
- info = runtime_info_->AddPostCollectedInfo(address, ts);\r
- info->className_ = -1;\r
- info->stackId_ = outOfContextFrame_;\r
- info->timeStamp_ = ts;\r
- info->size_ = size;\r
- }\r
- if (info->className_ == (unsigned)-1) {\r
- String* str = classNames_->GetConstructorName(address);\r
- if (str) {\r
- // get const char*, it's safe because pointer will be retained in the\r
- // name_ until it is destroyed\r
- id = classNames_->registerName(names_->GetName(str));\r
- }\r
- }\r
- info->className_ = id;\r
- return id;\r
-}\r
-\r
-\r
-unsigned XDKAllocationTracker::FindOrInitClassName(Address address,\r
- unsigned ts) {\r
- unsigned id = FindClassName(address);\r
- if (id == (unsigned)-1) {\r
- // TODO(amalyshe) check if 0 size here is appropriate\r
- id = InitClassName(address, ts, 0);\r
- }\r
- return id;\r
-}\r
-\r
-\r
-// -----------------------------------------------------------------------------\r
-// this is almost a copy and duplication of\r
-// V8HeapExplorer::AddEntry. refactoring is impossible because\r
-// heap-snapshot-generator rely on it's structure which is not fully suitable\r
-// for us.\r
-HeapEntry* XDKSnapshotFiller::AddEntry(HeapThing ptr,\r
- HeapEntriesAllocator* allocator) {\r
- HeapObject* object = reinterpret_cast<HeapObject*>(ptr);\r
- if (object->IsJSFunction()) {\r
- JSFunction* func = JSFunction::cast(object);\r
- SharedFunctionInfo* shared = func->shared();\r
- const char* name = shared->bound() ? "native_bind" :\r
- names_->GetName(String::cast(shared->name()));\r
- return AddEntry(ptr, object, HeapEntry::kClosure, name);\r
- } else if (object->IsJSRegExp()) {\r
- JSRegExp* re = JSRegExp::cast(object);\r
- return AddEntry(ptr, object,\r
- HeapEntry::kRegExp,\r
- names_->GetName(re->Pattern()));\r
- } else if (object->IsJSObject()) {\r
- return AddEntry(ptr, object, HeapEntry::kObject, "");\r
- } else if (object->IsString()) {\r
- String* string = String::cast(object);\r
- if (string->IsConsString())\r
- return AddEntry(ptr, object,\r
- HeapEntry::kConsString,\r
- "(concatenated string)");\r
- if (string->IsSlicedString())\r
- return AddEntry(ptr, object,\r
- HeapEntry::kSlicedString,\r
- "(sliced string)");\r
- return AddEntry(ptr, object,\r
- HeapEntry::kString,\r
- names_->GetName(String::cast(object)));\r
- } else if (object->IsSymbol()) {\r
- return AddEntry(ptr, object, HeapEntry::kSymbol, "symbol");\r
- } else if (object->IsCode()) {\r
- return AddEntry(ptr, object, HeapEntry::kCode, "");\r
- } else if (object->IsSharedFunctionInfo()) {\r
- String* name = String::cast(SharedFunctionInfo::cast(object)->name());\r
- return AddEntry(ptr, object,\r
- HeapEntry::kCode,\r
- names_->GetName(name));\r
- } else if (object->IsScript()) {\r
- Object* name = Script::cast(object)->name();\r
- return AddEntry(ptr, object,\r
- HeapEntry::kCode,\r
- name->IsString()\r
- ? names_->GetName(String::cast(name))\r
- : "");\r
- } else if (object->IsNativeContext()) {\r
- return AddEntry(ptr, object, HeapEntry::kHidden, "system / NativeContext");\r
- } else if (object->IsContext()) {\r
- return AddEntry(ptr, object, HeapEntry::kObject, "system / Context");\r
- } else if (object->IsFixedArray() ||\r
- object->IsFixedDoubleArray() ||\r
- object->IsByteArray() ||\r
- object->IsExternalArray()) {\r
- return AddEntry(ptr, object, HeapEntry::kArray, "");\r
- } else if (object->IsHeapNumber()) {\r
- return AddEntry(ptr, object, HeapEntry::kHeapNumber, "number");\r
- }\r
-\r
- return AddEntry(ptr, object, HeapEntry::kHidden, "system / NOT SUPORTED YET");\r
-}\r
-\r
-\r
-HeapEntry* XDKSnapshotFiller::AddEntry(HeapThing thing,\r
- HeapObject* object,\r
- HeapEntry::Type type,\r
- const char* name) {\r
- Address address = object->address();\r
- unsigned trace_node_id = 0;\r
- trace_node_id = allocation_tracker_->GetTraceNodeId(address);\r
-\r
- // cannot store pointer in the map because List reallcoates content regularly\r
- // and the only one way to find the entry - by index. so, index is cached in\r
- // the map\r
- // TODO(amalyshe): need to reuse type. it seems it is important\r
- HeapEntry entry(NULL, &heap_entries_list_, type, name, 0, 0,\r
- trace_node_id);\r
- heap_entries_list_.Add(entry);\r
- HeapEntry* pEntry = &heap_entries_list_.last();\r
-\r
- HashMap::Entry* cache_entry = heap_entries_.Lookup(thing, Hash(thing), true);\r
- DCHECK(cache_entry->value == NULL);\r
- int index = pEntry->index();\r
- cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(index));\r
-\r
- // TODO(amalyshe): it seems this storage might be optimized\r
- HashMap::Entry* address_entry = index_to_address_.Lookup(\r
- reinterpret_cast<void*>(index+1), HashInt(index+1), true);\r
- address_entry->value = reinterpret_cast<void*>(address);\r
-\r
- return pEntry;\r
-}\r
-\r
-\r
-HeapEntry* XDKSnapshotFiller::FindEntry(HeapThing thing) {\r
- HashMap::Entry* cache_entry = heap_entries_.Lookup(thing, Hash(thing), false);\r
- if (cache_entry == NULL) return NULL;\r
- return &heap_entries_list_[static_cast<int>(\r
- reinterpret_cast<intptr_t>(cache_entry->value))];\r
-}\r
-\r
-\r
-HeapEntry* XDKSnapshotFiller::FindOrAddEntry(HeapThing ptr,\r
- HeapEntriesAllocator* allocator) {\r
- HeapEntry* entry = FindEntry(ptr);\r
- return entry != NULL ? entry : AddEntry(ptr, allocator);\r
-}\r
-\r
-\r
-void XDKSnapshotFiller::SetIndexedReference(HeapGraphEdge::Type type,\r
- int parent,\r
- int index,\r
- HeapEntry* child_entry) {\r
- if (child_entry->trace_node_id() < 3) {\r
- return;\r
- }\r
- HashMap::Entry* address_entry_child = index_to_address_.Lookup(\r
- reinterpret_cast<void*>(child_entry->index()+1),\r
- HashInt(child_entry->index()+1), false);\r
- DCHECK(address_entry_child != NULL);\r
- if (!address_entry_child) {\r
- return;\r
- }\r
-\r
- Address child_addr = reinterpret_cast<Address>(address_entry_child->value);\r
-\r
- std::map<Address, RefSet>* individualReteiners =\r
- allocation_tracker_->GetIndividualReteiners();\r
- // get the parent's address, constructor name and form the RefId\r
- HashMap::Entry* address_entry = index_to_address_.Lookup(\r
- reinterpret_cast<void*>(parent+1), HashInt(parent+1), false);\r
- DCHECK(address_entry != NULL);\r
- if (!address_entry) {\r
- return;\r
- }\r
- HeapEntry* parent_entry = &(heap_entries_list_[parent]);\r
- Address parent_addr = reinterpret_cast<Address>(address_entry->value);\r
- RefId parent_ref_id;\r
- parent_ref_id.stackId_ = parent_entry->trace_node_id();\r
- parent_ref_id.classId_ =\r
- allocation_tracker_->FindOrInitClassName(parent_addr, 0);\r
-\r
- std::stringstream str;\r
- str << index << " element in Array";\r
- parent_ref_id.field_ = str.str();\r
-\r
- (*individualReteiners)[child_addr].references_.insert(parent_ref_id);\r
-}\r
-\r
-\r
-void XDKSnapshotFiller::SetIndexedAutoIndexReference(HeapGraphEdge::Type type,\r
- int parent,\r
- HeapEntry* child_entry) {\r
-}\r
-\r
-\r
-void XDKSnapshotFiller::SetNamedReference(HeapGraphEdge::Type type,\r
- int parent,\r
- const char* reference_name,\r
- HeapEntry* child_entry) {\r
- if (child_entry->trace_node_id() < 3) {\r
- return;\r
- }\r
-\r
- std::map<Address, RefSet>* individualReteiners =\r
- allocation_tracker_->GetIndividualReteiners();\r
- // get the parent's address, constructor name and form the RefId\r
- HashMap::Entry* address_entry = index_to_address_.Lookup(\r
- reinterpret_cast<void*>(parent+1), HashInt(parent+1), false);\r
- DCHECK(address_entry != NULL);\r
- if (!address_entry) {\r
- return;\r
- }\r
- HeapEntry* parent_entry = &(heap_entries_list_[parent]);\r
- Address parent_addr = reinterpret_cast<Address>(address_entry->value);\r
- RefId parent_ref_id;\r
- parent_ref_id.stackId_ = parent_entry->trace_node_id();\r
- // TODO(amalyshe): need to get access to classNames_\r
- parent_ref_id.classId_ =\r
- allocation_tracker_->FindOrInitClassName(parent_addr, 0);\r
- parent_ref_id.field_ = reference_name;\r
-\r
- HashMap::Entry* address_entry_child = index_to_address_.Lookup(\r
- reinterpret_cast<void*>(child_entry->index()+1),\r
- HashInt(child_entry->index()+1), false);\r
- DCHECK(address_entry_child != NULL);\r
- if (!address_entry_child) {\r
- return;\r
- }\r
- Address child_addr = reinterpret_cast<Address>(address_entry_child->value);\r
-\r
- (*individualReteiners)[child_addr].references_.insert(parent_ref_id);\r
-}\r
-\r
-\r
-void XDKSnapshotFiller::SetNamedAutoIndexReference(HeapGraphEdge::Type type,\r
- int parent,\r
- HeapEntry* child_entry) {\r
-}\r
-\r
-\r
-}\r
-} // namespace v8::internal\r
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sstream>
+#include <string>
+
+#include "src/v8.h"
+
+#include "src/xdk-allocation.h"
+
+#include "frames-inl.h"
+#include "src/xdk-utils.h"
+
+namespace v8 {
+namespace internal {
+
+XDKAllocationTracker::XDKAllocationTracker(HeapProfiler* heap_profiler,
+ HeapObjectsMap *ids,
+ StringsStorage *names,
+ int stackDepth,
+ bool collectRetention,
+ bool strict_collection)
+ : heap_profiler_(heap_profiler),
+ ids_(ids),
+ names_(names),
+ stackDepth_(stackDepth),
+ collectRetention_(collectRetention),
+ strict_collection_(strict_collection) {
+ references_ = new References();
+ aggregated_chunks_ = new AggregatedChunks();
+ runtime_info_ = new RuntimeInfo(aggregated_chunks_);
+ symbols_ = new SymbolsStorage(ids_->heap(), names_);
+ collectedStacks_ = new ShadowStack();
+ classNames_ = new ClassNames(names_);
+
+ List<unsigned> stack_ooc;
+ stack_ooc.Add(symbols_->registerSymInfo(1, "OutOfContext", "NoSource",
+ 0, 0));
+ outOfContextFrame_ = collectedStacks_->registerStack(stack_ooc);
+
+ List<unsigned> stack_abc;
+ stack_abc.Add(symbols_->registerSymInfo(2, "AllocatedBeforeCollection",
+ "NoSource", 0, 0));
+ AllocatedBeforeCollectionFrame_ = collectedStacks_->registerStack(stack_abc);
+
+ runtime_info_->InitABCFrame(AllocatedBeforeCollectionFrame_);
+
+ baseTime_ = v8::base::Time::Now();
+ latest_delta_ = 0;
+}
+
+
+XDKAllocationTracker::~XDKAllocationTracker() {
+ delete collectedStacks_;
+ delete classNames_;
+ delete aggregated_chunks_;
+ delete runtime_info_;
+ delete symbols_;
+ delete references_;
+}
+
+
+// Heap profiler regularly takes time for storing when object was allocated,
+// deallocated, when object's retention snapshot was taken, etc
+unsigned int XDKAllocationTracker::GetTimeDelta() {
+ v8::base::TimeDelta td = v8::base::Time::Now() - baseTime_;
+ return static_cast<unsigned int>(td.InMilliseconds());
+}
+
+
+void XDKAllocationTracker::OnAlloc(Address addr, int size) {
+ DisallowHeapAllocation no_alloc;
+ Heap *heap = ids_->heap();
+
+ // below call saves from the crash during StackTraceFrameIterator creation
+ // Mark the new block as FreeSpace to make sure the heap is iterable
+ // while we are capturing stack trace.
+ FreeListNode::FromAddress(addr)->set_size(heap, size);
+
+ Isolate *isolate = heap->isolate();
+ StackTraceFrameIterator it(isolate);
+ List<unsigned> stack;
+
+ // TODO(amalyshe): checking of isolate->handle_scope_data()->level is quite
+ // artificial. need to understand when we can have such behaviour
+ // if level == 0 we will crash in getting of source info
+ while (isolate->handle_scope_data()->level && !it.done() &&
+ stack.length() < stackDepth_) {
+ JavaScriptFrame *frame = it.frame();
+ if (!frame->function())
+ break;
+ SharedFunctionInfo *shared = frame->function()->shared();
+ if (!shared)
+ break;
+
+ stack.Add(symbols_->FindOrRegisterFrame(frame));
+ it.Advance();
+ }
+
+ unsigned sid;
+ if (!stack.is_empty()) {
+ sid = collectedStacks_->registerStack(stack);
+ } else {
+ sid = outOfContextFrame_;
+ }
+
+ latest_delta_ = GetTimeDelta();
+
+ PostCollectedInfo* info = runtime_info_->AddPostCollectedInfo(addr,
+ latest_delta_);
+ info->size_ = size;
+ info->timeStamp_ = latest_delta_;
+ info->stackId_ = sid;
+ info->className_ = (unsigned int)-1;
+ info->dirty_ = false;
+}
+
+
+void XDKAllocationTracker::OnMove(Address from, Address to, int size) {
+ DisallowHeapAllocation no_alloc;
+ // look for the prev address
+ PostCollectedInfo* info_from = runtime_info_->FindPostCollectedInfo(from);
+ if (info_from == NULL) {
+ return;
+ }
+
+ runtime_info_->AddPostCollectedInfo(to, latest_delta_, info_from);
+ runtime_info_->RemoveInfo(from);
+}
+
+
+HeapEventXDK* XDKAllocationTracker::stopTracking() {
+ std::string symbols, types, frames, chunks, retentions;
+ SerializeChunk(&symbols, &types, &frames, &chunks, &retentions);
+ CollectFreedObjects(true);
+ std::string symbolsA, typesA, framesA, chunksA, retentionsA;
+ SerializeChunk(&symbolsA, &typesA, &framesA, &chunksA, &retentionsA, true);
+
+ // TODO(amalyshe): check who releases this object - new HeapEventXDK
+ return (new HeapEventXDK(GetTimeDelta(), symbols+symbolsA, types+typesA,
+ frames+framesA, chunks+chunksA, ""));
+}
+
+
+void XDKAllocationTracker::CollectFreedObjects(bool bAll, bool initPreCollect) {
+ clearIndividualReteiners();
+ if (collectRetention_) {
+ XDKSnapshotFiller filler(ids_, names_, this);
+ HeapSnapshotGenerator generator(heap_profiler_, NULL, NULL, NULL,
+ ids_->heap(), &filler);
+ generator.GenerateSnapshot();
+ }
+
+ Heap *heap = ids_->heap();
+ if (!heap) {
+ return;
+ }
+
+ unsigned int ts = GetTimeDelta();
+ if (bAll) {
+ ts += RETAINED_DELTA;
+ }
+
+ // CDT heap profiler calls CollectAllGarbage twice because after the first
+ // pass there are weak retained object not collected, but due to perf issues
+ // and because we do garbage collection regularly, we leave here only one call
+ // only for strict collection like in test where we need to make sure that
+ // object is definitely collected, we collect twice
+ heap->CollectAllGarbage(
+ Heap::kMakeHeapIterableMask,
+ "XDKAllocationTracker::CollectFreedObjects");
+ if (strict_collection_) {
+ heap->CollectAllGarbage(
+ Heap::kMakeHeapIterableMask,
+ "XDKAllocationTracker::CollectFreedObjects");
+ }
+ std::map<Address, RefSet> individualReteiners;
+
+ // TODO(amalyshe): check what DisallowHeapAllocation no_alloc means because in
+ // standalone v8 this part is crashed if DisallowHeapAllocation is defined
+ // DisallowHeapAllocation no_alloc;
+ if (!bAll) {
+ HeapIterator iterator(heap);
+ HeapObject* obj = iterator.next();
+ for (;
+ obj != NULL;
+ obj = iterator.next()) {
+ Address addr = obj->address();
+
+ PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(addr);
+ if (!info) {
+ // if we don't find info, we consider it as pre collection allocated
+ // object. need to add to the full picture for retentions
+ if (initPreCollect) {
+ info = runtime_info_->AddPreCollectionInfo(addr, obj->Size());
+ }
+ }
+
+ if (info) {
+ info->dirty_ = true;
+ }
+ // check of the class name and its initialization
+ if ((info && info->className_ == (unsigned)-1) || !info) {
+ InitClassName(addr, ts, obj->Size());
+ }
+ }
+ }
+
+ if (collectRetention_) {
+ std::map<Address, RefSet>::const_iterator citir =
+ individualReteiners_.begin();
+ while (citir != individualReteiners_.end()) {
+ PostCollectedInfo* infoChild =
+ runtime_info_->FindPostCollectedInfo(citir->first);
+ if (infoChild) {
+ RefId rfId;
+ rfId.stackId_ = infoChild->stackId_;
+ rfId.classId_ = infoChild->className_;
+
+ references_->addReference(rfId, citir->second, infoChild->timeStamp_);
+ }
+ citir++;
+ }
+ }
+
+ runtime_info_->CollectGarbaged(ts);
+}
+
+
+void XDKAllocationTracker::SerializeChunk(std::string* symbols,
+ std::string* types,
+ std::string* frames,
+ std::string* chunks,
+ std::string* retentions,
+ bool final) {
+ if (final) {
+ *symbols = symbols_->SerializeChunk();
+ *types = classNames_->SerializeChunk();
+ }
+ *frames = collectedStacks_->SerializeChunk();
+ *chunks = aggregated_chunks_->SerializeChunk();
+
+ *retentions = references_->serialize();
+ std::stringstream retentionsT;
+ retentionsT << GetTimeDelta() << std::endl << retentions->c_str();
+ *retentions = retentionsT.str();
+ references_->clear();
+}
+
+
+OutputStream::WriteResult XDKAllocationTracker::SendChunk(
+ OutputStream* stream) {
+ // go over all aggregated_ and send data to the stream
+ std::string symbols, types, frames, chunks, retentions;
+ SerializeChunk(&symbols, &types, &frames, &chunks, &retentions);
+
+ OutputStream::WriteResult ret = stream->WriteHeapXDKChunk(
+ symbols.c_str(), symbols.length(),
+ frames.c_str(), frames.length(),
+ types.c_str(), types.length(),
+ chunks.c_str(), chunks.length(),
+ retentions.c_str(), retentions.length());
+ return ret;
+}
+
+
+unsigned XDKAllocationTracker::GetTraceNodeId(Address address) {
+ PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);
+ if (info) {
+ return info->stackId_;
+ } else {
+ return AllocatedBeforeCollectionFrame_;
+ }
+}
+
+
+void XDKAllocationTracker::clearIndividualReteiners() {
+ individualReteiners_.clear();
+}
+
+
+std::map<Address, RefSet>* XDKAllocationTracker::GetIndividualReteiners() {
+ return &individualReteiners_;
+}
+
+
+unsigned XDKAllocationTracker::FindClassName(Address address) {
+ PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);
+ if (info) {
+ return info->className_;
+ } else {
+ return (unsigned)-1;
+ }
+}
+
+
+unsigned XDKAllocationTracker::InitClassName(Address address, unsigned ts,
+ unsigned size) {
+ unsigned id = -2;
+ PostCollectedInfo* info = runtime_info_->FindPostCollectedInfo(address);
+ if (!info) {
+ info = runtime_info_->AddPostCollectedInfo(address, ts);
+ info->className_ = -1;
+ info->stackId_ = outOfContextFrame_;
+ info->timeStamp_ = ts;
+ info->size_ = size;
+ }
+ if (info->className_ == (unsigned)-1) {
+ String* str = classNames_->GetConstructorName(address);
+ if (str) {
+ // get const char*, it's safe because pointer will be retained in the
+ // name_ until it is destroyed
+ id = classNames_->registerName(names_->GetName(str));
+ }
+ }
+ info->className_ = id;
+ return id;
+}
+
+
+unsigned XDKAllocationTracker::FindOrInitClassName(Address address,
+ unsigned ts) {
+ unsigned id = FindClassName(address);
+ if (id == (unsigned)-1) {
+ // TODO(amalyshe) check if 0 size here is appropriate
+ id = InitClassName(address, ts, 0);
+ }
+ return id;
+}
+
+
+// -----------------------------------------------------------------------------
+// this is almost a copy and duplication of
+// V8HeapExplorer::AddEntry. refactoring is impossible because
+// heap-snapshot-generator rely on it's structure which is not fully suitable
+// for us.
+HeapEntry* XDKSnapshotFiller::AddEntry(HeapThing ptr,
+ HeapEntriesAllocator* allocator) {
+ HeapObject* object = reinterpret_cast<HeapObject*>(ptr);
+ if (object->IsJSFunction()) {
+ JSFunction* func = JSFunction::cast(object);
+ SharedFunctionInfo* shared = func->shared();
+ const char* name = shared->bound() ? "native_bind" :
+ names_->GetName(String::cast(shared->name()));
+ return AddEntry(ptr, object, HeapEntry::kClosure, name);
+ } else if (object->IsJSRegExp()) {
+ JSRegExp* re = JSRegExp::cast(object);
+ return AddEntry(ptr, object,
+ HeapEntry::kRegExp,
+ names_->GetName(re->Pattern()));
+ } else if (object->IsJSObject()) {
+ return AddEntry(ptr, object, HeapEntry::kObject, "");
+ } else if (object->IsString()) {
+ String* string = String::cast(object);
+ if (string->IsConsString())
+ return AddEntry(ptr, object,
+ HeapEntry::kConsString,
+ "(concatenated string)");
+ if (string->IsSlicedString())
+ return AddEntry(ptr, object,
+ HeapEntry::kSlicedString,
+ "(sliced string)");
+ return AddEntry(ptr, object,
+ HeapEntry::kString,
+ names_->GetName(String::cast(object)));
+ } else if (object->IsSymbol()) {
+ return AddEntry(ptr, object, HeapEntry::kSymbol, "symbol");
+ } else if (object->IsCode()) {
+ return AddEntry(ptr, object, HeapEntry::kCode, "");
+ } else if (object->IsSharedFunctionInfo()) {
+ String* name = String::cast(SharedFunctionInfo::cast(object)->name());
+ return AddEntry(ptr, object,
+ HeapEntry::kCode,
+ names_->GetName(name));
+ } else if (object->IsScript()) {
+ Object* name = Script::cast(object)->name();
+ return AddEntry(ptr, object,
+ HeapEntry::kCode,
+ name->IsString()
+ ? names_->GetName(String::cast(name))
+ : "");
+ } else if (object->IsNativeContext()) {
+ return AddEntry(ptr, object, HeapEntry::kHidden, "system / NativeContext");
+ } else if (object->IsContext()) {
+ return AddEntry(ptr, object, HeapEntry::kObject, "system / Context");
+ } else if (object->IsFixedArray() ||
+ object->IsFixedDoubleArray() ||
+ object->IsByteArray() ||
+ object->IsExternalArray()) {
+ return AddEntry(ptr, object, HeapEntry::kArray, "");
+ } else if (object->IsHeapNumber()) {
+ return AddEntry(ptr, object, HeapEntry::kHeapNumber, "number");
+ }
+
+ return AddEntry(ptr, object, HeapEntry::kHidden, "system / NOT SUPORTED YET");
+}
+
+
+HeapEntry* XDKSnapshotFiller::AddEntry(HeapThing thing,
+ HeapObject* object,
+ HeapEntry::Type type,
+ const char* name) {
+ Address address = object->address();
+ unsigned trace_node_id = 0;
+ trace_node_id = allocation_tracker_->GetTraceNodeId(address);
+
+ // cannot store pointer in the map because List reallcoates content regularly
+ // and the only one way to find the entry - by index. so, index is cached in
+ // the map
+ // TODO(amalyshe): need to reuse type. it seems it is important
+ HeapEntry entry(NULL, &heap_entries_list_, type, name, 0, 0,
+ trace_node_id);
+ heap_entries_list_.Add(entry);
+ HeapEntry* pEntry = &heap_entries_list_.last();
+
+ HashMap::Entry* cache_entry = heap_entries_.Lookup(thing, Hash(thing), true);
+ DCHECK(cache_entry->value == NULL);
+ int index = pEntry->index();
+ cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(index));
+
+ // TODO(amalyshe): it seems this storage might be optimized
+ HashMap::Entry* address_entry = index_to_address_.Lookup(
+ reinterpret_cast<void*>(index+1), HashInt(index+1), true);
+ address_entry->value = reinterpret_cast<void*>(address);
+
+ return pEntry;
+}
+
+
+HeapEntry* XDKSnapshotFiller::FindEntry(HeapThing thing) {
+ HashMap::Entry* cache_entry = heap_entries_.Lookup(thing, Hash(thing), false);
+ if (cache_entry == NULL) return NULL;
+ return &heap_entries_list_[static_cast<int>(
+ reinterpret_cast<intptr_t>(cache_entry->value))];
+}
+
+
+HeapEntry* XDKSnapshotFiller::FindOrAddEntry(HeapThing ptr,
+ HeapEntriesAllocator* allocator) {
+ HeapEntry* entry = FindEntry(ptr);
+ return entry != NULL ? entry : AddEntry(ptr, allocator);
+}
+
+
+void XDKSnapshotFiller::SetIndexedReference(HeapGraphEdge::Type type,
+ int parent,
+ int index,
+ HeapEntry* child_entry) {
+ if (child_entry->trace_node_id() < 3) {
+ return;
+ }
+ HashMap::Entry* address_entry_child = index_to_address_.Lookup(
+ reinterpret_cast<void*>(child_entry->index()+1),
+ HashInt(child_entry->index()+1), false);
+ DCHECK(address_entry_child != NULL);
+ if (!address_entry_child) {
+ return;
+ }
+
+ Address child_addr = reinterpret_cast<Address>(address_entry_child->value);
+
+ std::map<Address, RefSet>* individualReteiners =
+ allocation_tracker_->GetIndividualReteiners();
+ // get the parent's address, constructor name and form the RefId
+ HashMap::Entry* address_entry = index_to_address_.Lookup(
+ reinterpret_cast<void*>(parent+1), HashInt(parent+1), false);
+ DCHECK(address_entry != NULL);
+ if (!address_entry) {
+ return;
+ }
+ HeapEntry* parent_entry = &(heap_entries_list_[parent]);
+ Address parent_addr = reinterpret_cast<Address>(address_entry->value);
+ RefId parent_ref_id;
+ parent_ref_id.stackId_ = parent_entry->trace_node_id();
+ parent_ref_id.classId_ =
+ allocation_tracker_->FindOrInitClassName(parent_addr, 0);
+
+ std::stringstream str;
+ str << index << " element in Array";
+ parent_ref_id.field_ = str.str();
+
+ (*individualReteiners)[child_addr].references_.insert(parent_ref_id);
+}
+
+
+void XDKSnapshotFiller::SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent,
+ HeapEntry* child_entry) {
+}
+
+
+void XDKSnapshotFiller::SetNamedReference(HeapGraphEdge::Type type,
+ int parent,
+ const char* reference_name,
+ HeapEntry* child_entry) {
+ if (child_entry->trace_node_id() < 3) {
+ return;
+ }
+
+ std::map<Address, RefSet>* individualReteiners =
+ allocation_tracker_->GetIndividualReteiners();
+ // get the parent's address, constructor name and form the RefId
+ HashMap::Entry* address_entry = index_to_address_.Lookup(
+ reinterpret_cast<void*>(parent+1), HashInt(parent+1), false);
+ DCHECK(address_entry != NULL);
+ if (!address_entry) {
+ return;
+ }
+ HeapEntry* parent_entry = &(heap_entries_list_[parent]);
+ Address parent_addr = reinterpret_cast<Address>(address_entry->value);
+ RefId parent_ref_id;
+ parent_ref_id.stackId_ = parent_entry->trace_node_id();
+ // TODO(amalyshe): need to get access to classNames_
+ parent_ref_id.classId_ =
+ allocation_tracker_->FindOrInitClassName(parent_addr, 0);
+ parent_ref_id.field_ = reference_name;
+
+ HashMap::Entry* address_entry_child = index_to_address_.Lookup(
+ reinterpret_cast<void*>(child_entry->index()+1),
+ HashInt(child_entry->index()+1), false);
+ DCHECK(address_entry_child != NULL);
+ if (!address_entry_child) {
+ return;
+ }
+ Address child_addr = reinterpret_cast<Address>(address_entry_child->value);
+
+ (*individualReteiners)[child_addr].references_.insert(parent_ref_id);
+}
+
+
+void XDKSnapshotFiller::SetNamedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent,
+ HeapEntry* child_entry) {
+}
+
+
+}
+} // namespace v8::internal