%define _binary_payload w3.gzdio
Name: crosswalk
-Version: 11.39.258.0
+Version: 11.39.260.0
Release: 0
Summary: Chromium-based app runtime
License: (BSD-3-Clause and LGPL-2.1+)
// This convoluted DCHECK is needed to work around a gcc problem that
// improperly detects an array bounds overflow in optimized debug builds
// when using a plain DCHECK.
- if (n >= arraysize(registers_) {
+ if (n >= arraysize(registers_)) {
DCHECK(false);
return 0;
}
-// 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
-// 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
-#ifndef V8_XDK_ALLOCATION_H_\r
-#define V8_XDK_ALLOCATION_H_\r
-\r
-#include <map>\r
-#include <string>\r
-#include "src/base/platform/time.h"\r
-\r
-namespace v8 {\r
-namespace internal {\r
-\r
-class HeapObjectsMap;\r
-class HeapEventXDK;\r
-class ClassNames;\r
-class ShadowStack;\r
-class SymbolsStorage;\r
-class AggregatedChunks;\r
-class RuntimeInfo;\r
-class References;\r
-class RefSet;\r
-\r
-\r
-class XDKSnapshotFiller: public SnapshotFiller {\r
- public:\r
- explicit XDKSnapshotFiller(HeapObjectsMap* heap_object_map,\r
- StringsStorage* names,\r
- XDKAllocationTracker* allocation_tracker)\r
- : names_(names),\r
- heap_object_map_(heap_object_map),\r
- allocation_tracker_(allocation_tracker),\r
- heap_entries_(HashMap::PointersMatch),\r
- index_to_address_(HashMap::PointersMatch) {}\r
- virtual ~XDKSnapshotFiller() {}\r
-\r
- HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator);\r
- HeapEntry* FindEntry(HeapThing thing);\r
- HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator);\r
- void SetIndexedReference(HeapGraphEdge::Type type,\r
- int parent,\r
- int index,\r
- HeapEntry* child_entry);\r
- void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,\r
- int parent,\r
- HeapEntry* child_entry);\r
- void SetNamedReference(HeapGraphEdge::Type type,\r
- int parent,\r
- const char* reference_name,\r
- HeapEntry* child_entry);\r
- void SetNamedAutoIndexReference(HeapGraphEdge::Type type,\r
- int parent,\r
- HeapEntry* child_entry);\r
-\r
- private:\r
- StringsStorage* names_;\r
- HeapObjectsMap* heap_object_map_;\r
- XDKAllocationTracker* allocation_tracker_;\r
- HashMap heap_entries_;\r
- HashMap index_to_address_;\r
-\r
-\r
- List<HeapEntry> heap_entries_list_;\r
-\r
- HeapEntry* AddEntry(HeapThing thing,\r
- HeapObject* object,\r
- HeapEntry::Type type,\r
- const char* name);\r
-\r
- static uint32_t Hash(HeapThing thing) {\r
- return ComputeIntegerHash(\r
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(thing)),\r
- v8::internal::kZeroHashSeed);\r
- }\r
- static uint32_t HashInt(int key) {\r
- return ComputeIntegerHash(key, v8::internal::kZeroHashSeed);\r
- }\r
-};\r
-\r
-\r
-class XDKAllocationTracker {\r
- public:\r
- XDKAllocationTracker(HeapProfiler* heap_profiler,\r
- HeapObjectsMap* ids,\r
- StringsStorage* names,\r
- int stackDepth,\r
- bool collectRetention,\r
- bool strict_collection);\r
- ~XDKAllocationTracker();\r
-\r
- void OnAlloc(Address addr, int size);\r
- void OnMove(Address from, Address to, int size);\r
- void CollectFreedObjects(bool bAll = false, bool initPreCollect = false);\r
- HeapEventXDK* stopTracking();\r
- OutputStream::WriteResult SendChunk(OutputStream* stream);\r
- unsigned GetTraceNodeId(Address address);\r
- void clearIndividualReteiners();\r
- std::map<Address, RefSet>* GetIndividualReteiners();\r
-\r
- unsigned FindOrInitClassName(Address address, unsigned ts);\r
-\r
- private:\r
- static const int RETAINED_DELTA = 1000;\r
-\r
- // external object\r
- HeapProfiler* heap_profiler_;\r
- HeapObjectsMap* ids_;\r
- StringsStorage* names_;\r
-\r
- AggregatedChunks* aggregated_chunks_;\r
- RuntimeInfo* runtime_info_;\r
- void SerializeChunk(std::string* symbols, std::string* types,\r
- std::string* frames, std::string* chunks,\r
- std::string* retentions, bool final = false);\r
-\r
- unsigned FindClassName(Address address);\r
- unsigned InitClassName(Address address, unsigned ts, unsigned size);\r
-\r
- SymbolsStorage* symbols_;\r
- ShadowStack* collectedStacks_;\r
- ClassNames* classNames_;\r
-\r
- unsigned outOfContextFrame_;\r
- unsigned AllocatedBeforeCollectionFrame_;\r
-\r
- v8::base::Time baseTime_;\r
- unsigned latest_delta_;\r
- unsigned int GetTimeDelta();\r
-\r
- int stackDepth_;\r
- bool collectRetention_;\r
- bool strict_collection_;\r
- References* references_;\r
- std::map<Address, RefSet> individualReteiners_;\r
-};\r
-\r
-\r
-class HeapEventXDK {\r
- public:\r
- HeapEventXDK(unsigned int duration,\r
- const std::string& symbols, const std::string& types,\r
- const std::string& frames, const std::string& chunks,\r
- const std::string& retentions) :\r
- symbols_(symbols), types_(types), frames_(frames), chunks_(chunks),\r
- duration_(duration), retentions_(retentions) {\r
- }\r
-\r
- unsigned int duration() const {return duration_; }\r
- const char* symbols() const { return symbols_.c_str(); }\r
- const char* types() const { return types_.c_str(); }\r
- const char* frames() const { return frames_.c_str(); }\r
- const char* chunks() const { return chunks_.c_str(); }\r
- const char* retentions() const { return retentions_.c_str(); }\r
-\r
- private:\r
- std::string symbols_;\r
- std::string types_;\r
- std::string frames_;\r
- std::string chunks_;\r
- unsigned int duration_;\r
- std::string retentions_;\r
- DISALLOW_COPY_AND_ASSIGN(HeapEventXDK);\r
-};\r
-\r
-} } // namespace v8::internal\r
-\r
-#endif // V8_XDK_ALLOCATION_H_\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.
+
+#ifndef V8_XDK_ALLOCATION_H_
+#define V8_XDK_ALLOCATION_H_
+
+#include <map>
+#include <string>
+#include "src/base/platform/time.h"
+
+namespace v8 {
+namespace internal {
+
+class HeapObjectsMap;
+class HeapEventXDK;
+class ClassNames;
+class ShadowStack;
+class SymbolsStorage;
+class AggregatedChunks;
+class RuntimeInfo;
+class References;
+class RefSet;
+
+
+class XDKSnapshotFiller: public SnapshotFiller {
+ public:
+ explicit XDKSnapshotFiller(HeapObjectsMap* heap_object_map,
+ StringsStorage* names,
+ XDKAllocationTracker* allocation_tracker)
+ : names_(names),
+ heap_object_map_(heap_object_map),
+ allocation_tracker_(allocation_tracker),
+ heap_entries_(HashMap::PointersMatch),
+ index_to_address_(HashMap::PointersMatch) {}
+ virtual ~XDKSnapshotFiller() {}
+
+ HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator);
+ HeapEntry* FindEntry(HeapThing thing);
+ HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator);
+ void SetIndexedReference(HeapGraphEdge::Type type,
+ int parent,
+ int index,
+ HeapEntry* child_entry);
+ void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent,
+ HeapEntry* child_entry);
+ void SetNamedReference(HeapGraphEdge::Type type,
+ int parent,
+ const char* reference_name,
+ HeapEntry* child_entry);
+ void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent,
+ HeapEntry* child_entry);
+
+ private:
+ StringsStorage* names_;
+ HeapObjectsMap* heap_object_map_;
+ XDKAllocationTracker* allocation_tracker_;
+ HashMap heap_entries_;
+ HashMap index_to_address_;
+
+
+ List<HeapEntry> heap_entries_list_;
+
+ HeapEntry* AddEntry(HeapThing thing,
+ HeapObject* object,
+ HeapEntry::Type type,
+ const char* name);
+
+ static uint32_t Hash(HeapThing thing) {
+ return ComputeIntegerHash(
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(thing)),
+ v8::internal::kZeroHashSeed);
+ }
+ static uint32_t HashInt(int key) {
+ return ComputeIntegerHash(key, v8::internal::kZeroHashSeed);
+ }
+};
+
+
+class XDKAllocationTracker {
+ public:
+ XDKAllocationTracker(HeapProfiler* heap_profiler,
+ HeapObjectsMap* ids,
+ StringsStorage* names,
+ int stackDepth,
+ bool collectRetention,
+ bool strict_collection);
+ ~XDKAllocationTracker();
+
+ void OnAlloc(Address addr, int size);
+ void OnMove(Address from, Address to, int size);
+ void CollectFreedObjects(bool bAll = false, bool initPreCollect = false);
+ HeapEventXDK* stopTracking();
+ OutputStream::WriteResult SendChunk(OutputStream* stream);
+ unsigned GetTraceNodeId(Address address);
+ void clearIndividualReteiners();
+ std::map<Address, RefSet>* GetIndividualReteiners();
+
+ unsigned FindOrInitClassName(Address address, unsigned ts);
+
+ private:
+ static const int RETAINED_DELTA = 1000;
+
+ // external object
+ HeapProfiler* heap_profiler_;
+ HeapObjectsMap* ids_;
+ StringsStorage* names_;
+
+ AggregatedChunks* aggregated_chunks_;
+ RuntimeInfo* runtime_info_;
+ void SerializeChunk(std::string* symbols, std::string* types,
+ std::string* frames, std::string* chunks,
+ std::string* retentions, bool final = false);
+
+ unsigned FindClassName(Address address);
+ unsigned InitClassName(Address address, unsigned ts, unsigned size);
+
+ SymbolsStorage* symbols_;
+ ShadowStack* collectedStacks_;
+ ClassNames* classNames_;
+
+ unsigned outOfContextFrame_;
+ unsigned AllocatedBeforeCollectionFrame_;
+
+ v8::base::Time baseTime_;
+ unsigned latest_delta_;
+ unsigned int GetTimeDelta();
+
+ int stackDepth_;
+ bool collectRetention_;
+ bool strict_collection_;
+ References* references_;
+ std::map<Address, RefSet> individualReteiners_;
+};
+
+
+class HeapEventXDK {
+ public:
+ HeapEventXDK(unsigned int duration,
+ const std::string& symbols, const std::string& types,
+ const std::string& frames, const std::string& chunks,
+ const std::string& retentions) :
+ symbols_(symbols), types_(types), frames_(frames), chunks_(chunks),
+ duration_(duration), retentions_(retentions) {
+ }
+
+ unsigned int duration() const {return duration_; }
+ const char* symbols() const { return symbols_.c_str(); }
+ const char* types() const { return types_.c_str(); }
+ const char* frames() const { return frames_.c_str(); }
+ const char* chunks() const { return chunks_.c_str(); }
+ const char* retentions() const { return retentions_.c_str(); }
+
+ private:
+ std::string symbols_;
+ std::string types_;
+ std::string frames_;
+ std::string chunks_;
+ unsigned int duration_;
+ std::string retentions_;
+ DISALLOW_COPY_AND_ASSIGN(HeapEventXDK);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_XDK_ALLOCATION_H_
-// 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 "src/v8.h"\r
-\r
-#include "src/frames-inl.h"\r
-#include "src/xdk-utils.h"\r
-\r
-namespace v8 {\r
-namespace internal {\r
-\r
-static bool AddressesMatch(void* key1, void* key2) {\r
- return key1 == key2;\r
-}\r
-\r
-\r
-static uint32_t CharAddressHash(char* addr) {\r
- return ComputeIntegerHash(static_cast<uint32_t>(\r
- reinterpret_cast<uintptr_t>(addr)),\r
- v8::internal::kZeroHashSeed);\r
-}\r
-\r
-\r
-static uint32_t AddressHash(Address addr) {\r
- return ComputeIntegerHash(static_cast<uint32_t>(\r
- reinterpret_cast<uintptr_t>(addr)),\r
- v8::internal::kZeroHashSeed);\r
-}\r
-\r
-\r
-ClassNames::ClassNames(StringsStorage* names)\r
- : counter_(0),\r
- char_to_idx_(AddressesMatch),\r
- names_(names) {\r
-}\r
-\r
-\r
-unsigned ClassNames::registerName(const char* name) {\r
- // since const char is retained outside and cannot be moved, we rely on this\r
- // and just compare the pointers. It should be enough for the strings from the\r
- // only one StringStorage\r
- unsigned counter;\r
- HashMap::Entry* entry = char_to_idx_.Lookup(const_cast<char*>(name),\r
- CharAddressHash(const_cast<char*>(name)),\r
- true);\r
- if (entry->value == NULL) {\r
- counter = ++counter_;\r
- entry->value = reinterpret_cast<void*>(counter);\r
- } else {\r
- counter = static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value));\r
- }\r
- return counter;\r
-}\r
-\r
-\r
-std::string ClassNames::SerializeChunk() {\r
- std::stringstream serialized;\r
- for (HashMap::Entry* p = char_to_idx_.Start(); p != NULL;\r
- p = char_to_idx_.Next(p)) {\r
- serialized << static_cast<unsigned>(\r
- reinterpret_cast<uintptr_t>(p->value)) << "," <<\r
- reinterpret_cast<char*>(p->key) << std::endl;\r
- }\r
-\r
- return serialized.str();\r
-}\r
-\r
-\r
-String* ClassNames::GetConstructorName(Address address) {\r
- HeapObject *heap_object = HeapObject::FromAddress(address);\r
- bool is_js_object = heap_object->IsJSObject();\r
- if (!is_js_object) {\r
- // TODO(amalyshe): look for another function for taking the class name\r
- // String* constructor_name = object2->constructor_name();\r
- return NULL;\r
- }\r
- JSObject* object = JSObject::cast(heap_object);\r
- Heap* heap = object->GetHeap();\r
- if (object->IsJSFunction()) return heap->closure_string();\r
- return object->constructor_name();\r
-}\r
-\r
-\r
-// -----------------------------------------------------------------------------\r
-ShadowStack::ShadowStack() {\r
- last_index_ = 1;\r
- serializedCounter_ = last_index_;\r
- root_.index_ = 0;\r
- root_.parent_ = NULL;\r
- root_.callsite_ = 0;\r
-}\r
-\r
-\r
-ShadowStack::~ShadowStack() {\r
- // erasing all objects from the current container\r
- std::map<unsigned, CallTree*>::iterator eit = allNodes_.begin();\r
- while (eit != allNodes_.end()) {\r
- delete eit->second;\r
- eit++;\r
- }\r
-}\r
-\r
-\r
-unsigned ShadowStack::registerStack(const List<unsigned>& shadow_stack_) {\r
- // look for the latest node\r
- CallTree* pNode = &root_;\r
- // go over all entries and add them to the tree if they are not in the map\r
- int i, j;\r
- for (i = shadow_stack_.length()-1; i != -1; i--) {\r
- std::map<unsigned, CallTree*>::iterator it =\r
- pNode->children_.find(shadow_stack_[i]);\r
- if (it == pNode->children_.end())\r
- break;\r
- pNode = it->second;\r
- }\r
- // verification if we need to add something or not\r
- for (j = i; j != -1; j--) {\r
- CallTree* pNodeTmp = new CallTree;\r
- pNodeTmp->index_ = last_index_++;\r
- pNodeTmp->parent_ = pNode;\r
- pNodeTmp->callsite_ = shadow_stack_[j];\r
- pNode->children_[shadow_stack_[j]] = pNodeTmp;\r
- allNodes_[pNodeTmp->index_] = pNodeTmp;\r
- pNode = pNodeTmp;\r
- }\r
- return pNode->index_;\r
-}\r
-\r
-\r
-std::string ShadowStack::SerializeChunk() {\r
- std::stringstream str;\r
- std::map<unsigned, CallTree*>::iterator it =\r
- allNodes_.find(serializedCounter_);\r
- while (it!= allNodes_.end()) {\r
- str << it->first << "," << it->second->callsite_ << "," <<\r
- it->second->parent_->index_ << std::endl;\r
- it++;\r
- }\r
-\r
- serializedCounter_ = last_index_;\r
- return str.str();\r
-}\r
-\r
-\r
-// -----------------------------------------------------------------------------\r
-static bool SymInfoMatch(void* key1, void* key2) {\r
- SymInfoKey* key_c1 = reinterpret_cast<SymInfoKey*>(key1);\r
- SymInfoKey* key_c2 = reinterpret_cast<SymInfoKey*>(key2);\r
- return *key_c1 == *key_c2;\r
-}\r
-\r
-\r
-static uint32_t SymInfoHash(const SymInfoKey& key) {\r
- uint32_t hash = 0;\r
- // take the low 16 bits of function_id_\r
- hash |= (key.function_id_ & 0xffff);\r
- // take the low 8 bits of line_ and column_ and init highest bits\r
- hash |= ((key.line_ & 0xff) << 16);\r
- hash |= ((key.column_ & 0xff) << 14);\r
-\r
- return hash;\r
-}\r
-\r
-\r
-struct SymbolCached {\r
- unsigned int symbol_id_;\r
- uintptr_t function_;\r
-};\r
-\r
-\r
-SymbolsStorage::SymbolsStorage(Heap* heap, StringsStorage* names) :\r
- symbols_(SymInfoMatch),\r
- curSym_(1),\r
- sym_info_hash_(AddressesMatch),\r
- heap_(heap),\r
- names_(names) {\r
- reserved_key_ = new SymInfoKey();\r
-}\r
-\r
-\r
-SymbolsStorage::~SymbolsStorage() {\r
- // go over map and delete all keys and values\r
- for (HashMap::Entry* p = symbols_.Start(); p != NULL; p = symbols_.Next(p)) {\r
- delete reinterpret_cast<SymInfoValue*>(p->value);\r
- delete reinterpret_cast<SymInfoKey*>(p->key);\r
- }\r
- delete reserved_key_;\r
-}\r
-\r
-\r
-unsigned SymbolsStorage::registerSymInfo(size_t functionId,\r
- std::string functionName,\r
- std::string sourceName,\r
- unsigned line,\r
- unsigned column) {\r
- if (sourceName.empty()) {\r
- sourceName = "unknown";\r
- }\r
-\r
- reserved_key_->function_id_ = functionId;\r
- reserved_key_->line_ = line;\r
- reserved_key_->column_ = column;\r
-\r
- HashMap::Entry* entry = symbols_.Lookup(reserved_key_,\r
- SymInfoHash(*reserved_key_), true);\r
- if (entry->value) {\r
- return reinterpret_cast<SymInfoValue*>(entry->value)->symId_;\r
- }\r
-\r
- // else initialize by new one\r
- SymInfoValue* value = new SymInfoValue;\r
- value->symId_ = curSym_++;\r
- value->funcName_ = functionName;\r
- value->sourceFile_ = sourceName;\r
- entry->value = value;\r
-\r
- // compensation for registered one\r
- reserved_key_ = new SymInfoKey();\r
-\r
- return value->symId_;\r
-}\r
-\r
-\r
-std::string SymbolsStorage::SerializeChunk() {\r
- std::stringstream serialized;\r
- for (HashMap::Entry* p = symbols_.Start(); p != NULL; p = symbols_.Next(p)) {\r
- SymInfoValue* v = reinterpret_cast<SymInfoValue*>(p->value);\r
- SymInfoKey* k = reinterpret_cast<SymInfoKey*>(p->key);\r
- serialized << v->symId_ << "," << k->function_id_ << "," <<\r
- v->funcName_ << "," << v->sourceFile_ << "," <<\r
- k->line_ << "," << k->column_ << std::endl;\r
- }\r
-\r
- return serialized.str();\r
-}\r
-\r
-\r
-unsigned SymbolsStorage::FindOrRegisterFrame(JavaScriptFrame* frame) {\r
- SharedFunctionInfo *shared = frame->function()->shared();\r
- DCHECK(shared);\r
- Isolate *isolate = heap_->isolate();\r
-\r
- Address pc = frame->pc();\r
- unsigned int symbolId = 0;\r
-\r
- // We don't rely on the address only. Since this is JIT based language,\r
- // the address might be occupied by other function\r
- // thus we are verifying if the same function takes this place\r
- // before we take symbol info from the cache\r
- HashMap::Entry* sym_entry = sym_info_hash_.Lookup(\r
- reinterpret_cast<void*>(pc), AddressHash(pc), true);\r
- if (sym_entry->value == NULL ||\r
- (reinterpret_cast<SymbolCached*>(sym_entry->value)->function_ !=\r
- reinterpret_cast<uintptr_t>(frame->function()))) {\r
- if (sym_entry->value) {\r
- delete reinterpret_cast<SymbolCached*>(sym_entry->value);\r
- }\r
-\r
- const char *s = names_->GetFunctionName(shared->DebugName());\r
- // trying to get the source name and line#\r
- Code *code = Code::cast(isolate->FindCodeObject(pc));\r
- if (code) {\r
- int source_pos = code->SourcePosition(pc);\r
- Object *maybe_script = shared->script();\r
- if (maybe_script && maybe_script->IsScript()) {\r
- Handle<Script> script(Script::cast(maybe_script));\r
- if (!script.is_null()) {\r
- int line = script->GetLineNumber(source_pos) + 1;\r
- // TODO(amalyshe): check if this can be used:\r
- // int line = GetScriptLineNumberSafe(script, source_pos) + 1;\r
- // TODO(amalyshe): add column number getting\r
- int column = 0; // GetScriptColumnNumber(script, source_pos);\r
- Object *script_name_raw = script->name();\r
- if (script_name_raw->IsString()) {\r
- String *script_name = String::cast(script->name());\r
- SmartArrayPointer<char> c_script_name =\r
- script_name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);\r
- symbolId = registerSymInfo((size_t)frame->function(), s,\r
- c_script_name.get(), line, column);\r
- }\r
- }\r
- }\r
- }\r
- if (symbolId == 0) {\r
- symbolId = registerSymInfo((size_t)frame->function(), s, "", 0, 0);\r
- }\r
-\r
- SymbolCached* symCached = new SymbolCached;\r
- symCached->function_ = reinterpret_cast<uintptr_t>(frame->function());\r
- symCached->symbol_id_ = symbolId;\r
- sym_entry->value = symCached;\r
- } else {\r
- symbolId = reinterpret_cast<SymbolCached*>(sym_entry->value)->symbol_id_;\r
- }\r
- return symbolId;\r
-}\r
-\r
-\r
-// -----------------------------------------------------------------------------\r
-RuntimeInfo::RuntimeInfo(AggregatedChunks* aggregated_chunks):\r
- working_set_hash_(AddressesMatch),\r
- aggregated_chunks_(aggregated_chunks),\r
- AllocatedBeforeCollectionFrame_(0) {\r
-}\r
-\r
-\r
-PostCollectedInfo* RuntimeInfo::FindPostCollectedInfo(Address addr) {\r
- HashMap::Entry* entry = working_set_hash_.Lookup(\r
- reinterpret_cast<void*>(addr), AddressHash(addr), false);\r
- if (entry && entry->value) {\r
- PostCollectedInfo* info =\r
- reinterpret_cast<PostCollectedInfo*>(entry->value);\r
- return info;\r
- }\r
- return NULL;\r
-}\r
-\r
-\r
-PostCollectedInfo* RuntimeInfo::AddPostCollectedInfo(Address addr,\r
- unsigned time_delta,\r
- PostCollectedInfo* info) {\r
- PostCollectedInfo* info_new = NULL;\r
- if (!info) {\r
- info_new = new PostCollectedInfo;\r
- } else {\r
- info_new = info;\r
- }\r
-\r
- HashMap::Entry* entry = working_set_hash_.Lookup(\r
- reinterpret_cast<void*>(addr), AddressHash(addr), true);\r
- DCHECK(entry);\r
- if (entry->value != NULL) {\r
- // compensation of the wrong deallocation place\r
- // we were not able to work the GC epilogue callback because GC is not\r
- // iteratable in the prologue\r
- // thus we need to mark the object as freed\r
- PostCollectedInfo* info_old =\r
- static_cast<PostCollectedInfo*>(entry->value);\r
- aggregated_chunks_->addObjectToAggregated(info_old, time_delta);\r
- delete info_old;\r
- }\r
-\r
- entry->value = info_new;\r
- return info_new;\r
-}\r
-\r
-\r
-PostCollectedInfo* RuntimeInfo::AddPreCollectionInfo(Address addr,\r
- unsigned size) {\r
- PostCollectedInfo* info = AddPostCollectedInfo(addr);\r
- info->size_ = size;\r
- info->timeStamp_ = 0;\r
- info->stackId_ = AllocatedBeforeCollectionFrame_;\r
- info->className_ = (unsigned)-1;\r
- return info;\r
-}\r
-\r
-\r
-void RuntimeInfo::RemoveInfo(Address addr) {\r
- working_set_hash_.Remove(reinterpret_cast<void*>(addr), AddressHash(addr));\r
-}\r
-\r
-\r
-void RuntimeInfo::InitABCFrame(unsigned abc_frame) {\r
- AllocatedBeforeCollectionFrame_ = abc_frame;\r
-}\r
-\r
-\r
-void RuntimeInfo::CollectGarbaged(unsigned ts) {\r
- // iteration over the working_set_hash_\r
- for (HashMap::Entry* p = working_set_hash_.Start(); p != NULL;\r
- p = working_set_hash_.Next(p)) {\r
- if (p->value) {\r
- PostCollectedInfo* info = static_cast<PostCollectedInfo*>(p->value);\r
- if (info->dirty_ == false) {\r
- // need to care of allocated during collection.\r
- // if timeStamp_ == 0 this object was allocated before collection\r
- // and we don't care of it\r
- aggregated_chunks_->addObjectToAggregated(info, ts);\r
- delete info;\r
- p->value = NULL;\r
- } else {\r
- info->dirty_ = false;\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-//------------------------------------------------------------------------------\r
-static bool AggregatedMatch(void* key1, void* key2) {\r
- // cast to the AggregatedKey\r
- AggregatedKey* key_c1 = reinterpret_cast<AggregatedKey*>(key1);\r
- AggregatedKey* key_c2 = reinterpret_cast<AggregatedKey*>(key2);\r
- return *key_c1 == *key_c2;\r
-}\r
-\r
-\r
-static uint32_t AggregatedHash(const AggregatedKey& key) {\r
- uint32_t hash = 0;\r
- // take the low 8 bits of stackId_\r
- hash |= (key.stackId_ & 0xff);\r
- // take the low 8 bits of classId_ and init hash from 8th to 15th bits\r
- hash |= ((key.classId_ & 0xff) << 8);\r
- // since times are well graduated it's no sense take the lowest 8 bit\r
- // instead this we will move to 3 bits and only then take 8 bits\r
- hash |= (((key.tsBegin_ >> 3) & 0xff) << 16);\r
- hash |= (((key.tsBegin_ >> 3) & 0xff) << 24);\r
- return hash;\r
-}\r
-\r
-\r
-AggregatedChunks::AggregatedChunks() :\r
- aggregated_map_(AggregatedMatch),\r
- bucketSize_(500) {\r
- reserved_key_ = new AggregatedKey();\r
-}\r
-\r
-\r
-AggregatedChunks::~AggregatedChunks() {\r
- delete reserved_key_;\r
-}\r
-\r
-\r
-void AggregatedChunks::addObjectToAggregated(PostCollectedInfo* info,\r
- unsigned td) {\r
- reserved_key_->stackId_ = info->stackId_;\r
- reserved_key_->classId_ = info->className_;\r
- // get the bucket for the first time\r
- reserved_key_->tsBegin_ = info->timeStamp_ - (info->timeStamp_ % bucketSize_);\r
- reserved_key_->tsEnd_ = td - (td % bucketSize_);\r
-\r
- HashMap::Entry* aggregated_entry = aggregated_map_.Lookup(reserved_key_,\r
- AggregatedHash(*reserved_key_),\r
- true);\r
- if (aggregated_entry->value) {\r
- // no need to store the latest record in the aggregated_keys_list_\r
- AggregatedValue* value =\r
- reinterpret_cast<AggregatedValue*>(aggregated_entry->value);\r
- value->objects_++;\r
- value->size_ += info->size_;\r
- } else {\r
- reserved_key_ = new AggregatedKey;\r
- AggregatedValue* value = new AggregatedValue;\r
- value->objects_ = 1;\r
- value->size_ = info->size_;\r
- aggregated_entry->value = value;\r
- }\r
-}\r
-\r
-\r
-std::string AggregatedChunks::SerializeChunk() {\r
- std::stringstream schunks;\r
- for (HashMap::Entry* p = aggregated_map_.Start(); p != NULL;\r
- p = aggregated_map_.Next(p)) {\r
- if (p->key && p->value) {\r
- AggregatedKey* key = reinterpret_cast<AggregatedKey*>(p->key);\r
- AggregatedValue* value = reinterpret_cast<AggregatedValue*>(p->value);\r
- schunks <<\r
- key->tsBegin_ << "," << key->tsEnd_ << "," <<\r
- key->stackId_ << "," << key->classId_ << "," <<\r
- value->size_ << "," << value->objects_ << std::endl;\r
- delete key;\r
- delete value;\r
- }\r
- }\r
-\r
- aggregated_map_.Clear();\r
-\r
- return schunks.str();\r
-}\r
-\r
-\r
-// -----------------------------------------------------------------------------\r
-void References::addReference(const RefId& parent, const RefSet& refSet,\r
- int parentTime) {\r
- // looking for the parent in the refMap_\r
- PARENTREFMAP::iterator cit = refMap_.find(parent);\r
- if (cit != refMap_.end()) {\r
- REFERENCESETS& sets = cit->second;\r
- REFERENCESETS::iterator it = sets.find(refSet);\r
- if (it != sets.end()) {\r
- // look for the time\r
- TIMETOCOUNT::iterator cittc = it->second.find(parentTime);\r
- if (cittc != it->second.end()) {\r
- cittc->second++;\r
- } else {\r
- it->second[parentTime] = 1;\r
- }\r
- } else {\r
- TIMETOCOUNT tc;\r
- tc[parentTime] = 1;\r
- sets[refSet] = tc;\r
- }\r
- } else {\r
- // adding new parent, new sets\r
- REFERENCESETS sets;\r
- TIMETOCOUNT tc;\r
- tc[parentTime] = 1;\r
- sets[refSet] = tc;\r
- refMap_[parent] = sets;\r
- }\r
-}\r
-\r
-\r
-void References::clear() {\r
- refMap_.clear();\r
-}\r
-\r
-\r
-std::string References::serialize() const {\r
- std::stringstream str;\r
- PARENTREFMAP::const_iterator citrefs = refMap_.begin();\r
- while (citrefs != refMap_.end()) {\r
- REFERENCESETS::const_iterator citsets = citrefs->second.begin();\r
- while (citsets != citrefs->second.end()) {\r
- str << citrefs->first.stackId_ << "," << citrefs->first.classId_;\r
- // output of length, and content of TIMETOCOUNT\r
- str << "," << citsets->second.size();\r
- TIMETOCOUNT::const_iterator cittc = citsets->second.begin();\r
- while (cittc != citsets->second.end()) {\r
- str << "," << cittc->first << "," << cittc->second;\r
- cittc++;\r
- }\r
- REFERENCESET::const_iterator citset = citsets->first.references_.begin();\r
- while (citset != citsets->first.references_.end()) {\r
- str << "," << citset->stackId_ << "," << citset->classId_<< "," <<\r
- citset->field_;\r
- citset++;\r
- }\r
- str << std::endl;\r
- citsets++;\r
- }\r
- citrefs++;\r
- }\r
- return str.str();\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 "src/v8.h"
+
+#include "src/frames-inl.h"
+#include "src/xdk-utils.h"
+
+namespace v8 {
+namespace internal {
+
+static bool AddressesMatch(void* key1, void* key2) {
+ return key1 == key2;
+}
+
+
+static uint32_t CharAddressHash(char* addr) {
+ return ComputeIntegerHash(static_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(addr)),
+ v8::internal::kZeroHashSeed);
+}
+
+
+static uint32_t AddressHash(Address addr) {
+ return ComputeIntegerHash(static_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(addr)),
+ v8::internal::kZeroHashSeed);
+}
+
+
+ClassNames::ClassNames(StringsStorage* names)
+ : counter_(0),
+ char_to_idx_(AddressesMatch),
+ names_(names) {
+}
+
+
+unsigned ClassNames::registerName(const char* name) {
+ // since const char is retained outside and cannot be moved, we rely on this
+ // and just compare the pointers. It should be enough for the strings from the
+ // only one StringStorage
+ unsigned counter;
+ HashMap::Entry* entry = char_to_idx_.Lookup(const_cast<char*>(name),
+ CharAddressHash(const_cast<char*>(name)),
+ true);
+ if (entry->value == NULL) {
+ counter = ++counter_;
+ entry->value = reinterpret_cast<void*>(counter);
+ } else {
+ counter = static_cast<unsigned>(reinterpret_cast<uintptr_t>(entry->value));
+ }
+ return counter;
+}
+
+
+std::string ClassNames::SerializeChunk() {
+ std::stringstream serialized;
+ for (HashMap::Entry* p = char_to_idx_.Start(); p != NULL;
+ p = char_to_idx_.Next(p)) {
+ serialized << static_cast<unsigned>(
+ reinterpret_cast<uintptr_t>(p->value)) << "," <<
+ reinterpret_cast<char*>(p->key) << std::endl;
+ }
+
+ return serialized.str();
+}
+
+
+String* ClassNames::GetConstructorName(Address address) {
+ HeapObject *heap_object = HeapObject::FromAddress(address);
+ bool is_js_object = heap_object->IsJSObject();
+ if (!is_js_object) {
+ // TODO(amalyshe): look for another function for taking the class name
+ // String* constructor_name = object2->constructor_name();
+ return NULL;
+ }
+ JSObject* object = JSObject::cast(heap_object);
+ Heap* heap = object->GetHeap();
+ if (object->IsJSFunction()) return heap->closure_string();
+ return object->constructor_name();
+}
+
+
+// -----------------------------------------------------------------------------
+ShadowStack::ShadowStack() {
+ last_index_ = 1;
+ serializedCounter_ = last_index_;
+ root_.index_ = 0;
+ root_.parent_ = NULL;
+ root_.callsite_ = 0;
+}
+
+
+ShadowStack::~ShadowStack() {
+ // erasing all objects from the current container
+ std::map<unsigned, CallTree*>::iterator eit = allNodes_.begin();
+ while (eit != allNodes_.end()) {
+ delete eit->second;
+ eit++;
+ }
+}
+
+
+unsigned ShadowStack::registerStack(const List<unsigned>& shadow_stack_) {
+ // look for the latest node
+ CallTree* pNode = &root_;
+ // go over all entries and add them to the tree if they are not in the map
+ int i, j;
+ for (i = shadow_stack_.length()-1; i != -1; i--) {
+ std::map<unsigned, CallTree*>::iterator it =
+ pNode->children_.find(shadow_stack_[i]);
+ if (it == pNode->children_.end())
+ break;
+ pNode = it->second;
+ }
+ // verification if we need to add something or not
+ for (j = i; j != -1; j--) {
+ CallTree* pNodeTmp = new CallTree;
+ pNodeTmp->index_ = last_index_++;
+ pNodeTmp->parent_ = pNode;
+ pNodeTmp->callsite_ = shadow_stack_[j];
+ pNode->children_[shadow_stack_[j]] = pNodeTmp;
+ allNodes_[pNodeTmp->index_] = pNodeTmp;
+ pNode = pNodeTmp;
+ }
+ return pNode->index_;
+}
+
+
+std::string ShadowStack::SerializeChunk() {
+ std::stringstream str;
+ std::map<unsigned, CallTree*>::iterator it =
+ allNodes_.find(serializedCounter_);
+ while (it!= allNodes_.end()) {
+ str << it->first << "," << it->second->callsite_ << "," <<
+ it->second->parent_->index_ << std::endl;
+ it++;
+ }
+
+ serializedCounter_ = last_index_;
+ return str.str();
+}
+
+
+// -----------------------------------------------------------------------------
+static bool SymInfoMatch(void* key1, void* key2) {
+ SymInfoKey* key_c1 = reinterpret_cast<SymInfoKey*>(key1);
+ SymInfoKey* key_c2 = reinterpret_cast<SymInfoKey*>(key2);
+ return *key_c1 == *key_c2;
+}
+
+
+static uint32_t SymInfoHash(const SymInfoKey& key) {
+ uint32_t hash = 0;
+ // take the low 16 bits of function_id_
+ hash |= (key.function_id_ & 0xffff);
+ // take the low 8 bits of line_ and column_ and init highest bits
+ hash |= ((key.line_ & 0xff) << 16);
+ hash |= ((key.column_ & 0xff) << 14);
+
+ return hash;
+}
+
+
+struct SymbolCached {
+ unsigned int symbol_id_;
+ uintptr_t function_;
+};
+
+
+SymbolsStorage::SymbolsStorage(Heap* heap, StringsStorage* names) :
+ symbols_(SymInfoMatch),
+ curSym_(1),
+ sym_info_hash_(AddressesMatch),
+ heap_(heap),
+ names_(names) {
+ reserved_key_ = new SymInfoKey();
+}
+
+
+SymbolsStorage::~SymbolsStorage() {
+ // go over map and delete all keys and values
+ for (HashMap::Entry* p = symbols_.Start(); p != NULL; p = symbols_.Next(p)) {
+ delete reinterpret_cast<SymInfoValue*>(p->value);
+ delete reinterpret_cast<SymInfoKey*>(p->key);
+ }
+ delete reserved_key_;
+}
+
+
+unsigned SymbolsStorage::registerSymInfo(size_t functionId,
+ std::string functionName,
+ std::string sourceName,
+ unsigned line,
+ unsigned column) {
+ if (sourceName.empty()) {
+ sourceName = "unknown";
+ }
+
+ reserved_key_->function_id_ = functionId;
+ reserved_key_->line_ = line;
+ reserved_key_->column_ = column;
+
+ HashMap::Entry* entry = symbols_.Lookup(reserved_key_,
+ SymInfoHash(*reserved_key_), true);
+ if (entry->value) {
+ return reinterpret_cast<SymInfoValue*>(entry->value)->symId_;
+ }
+
+ // else initialize by new one
+ SymInfoValue* value = new SymInfoValue;
+ value->symId_ = curSym_++;
+ value->funcName_ = functionName;
+ value->sourceFile_ = sourceName;
+ entry->value = value;
+
+ // compensation for registered one
+ reserved_key_ = new SymInfoKey();
+
+ return value->symId_;
+}
+
+
+std::string SymbolsStorage::SerializeChunk() {
+ std::stringstream serialized;
+ for (HashMap::Entry* p = symbols_.Start(); p != NULL; p = symbols_.Next(p)) {
+ SymInfoValue* v = reinterpret_cast<SymInfoValue*>(p->value);
+ SymInfoKey* k = reinterpret_cast<SymInfoKey*>(p->key);
+ serialized << v->symId_ << "," << k->function_id_ << "," <<
+ v->funcName_ << "," << v->sourceFile_ << "," <<
+ k->line_ << "," << k->column_ << std::endl;
+ }
+
+ return serialized.str();
+}
+
+
+unsigned SymbolsStorage::FindOrRegisterFrame(JavaScriptFrame* frame) {
+ SharedFunctionInfo *shared = frame->function()->shared();
+ DCHECK(shared);
+ Isolate *isolate = heap_->isolate();
+
+ Address pc = frame->pc();
+ unsigned int symbolId = 0;
+
+ // We don't rely on the address only. Since this is JIT based language,
+ // the address might be occupied by other function
+ // thus we are verifying if the same function takes this place
+ // before we take symbol info from the cache
+ HashMap::Entry* sym_entry = sym_info_hash_.Lookup(
+ reinterpret_cast<void*>(pc), AddressHash(pc), true);
+ if (sym_entry->value == NULL ||
+ (reinterpret_cast<SymbolCached*>(sym_entry->value)->function_ !=
+ reinterpret_cast<uintptr_t>(frame->function()))) {
+ if (sym_entry->value) {
+ delete reinterpret_cast<SymbolCached*>(sym_entry->value);
+ }
+
+ const char *s = names_->GetFunctionName(shared->DebugName());
+ // trying to get the source name and line#
+ Code *code = Code::cast(isolate->FindCodeObject(pc));
+ if (code) {
+ int source_pos = code->SourcePosition(pc);
+ Object *maybe_script = shared->script();
+ if (maybe_script && maybe_script->IsScript()) {
+ Handle<Script> script(Script::cast(maybe_script));
+ if (!script.is_null()) {
+ int line = script->GetLineNumber(source_pos) + 1;
+ // TODO(amalyshe): check if this can be used:
+ // int line = GetScriptLineNumberSafe(script, source_pos) + 1;
+ // TODO(amalyshe): add column number getting
+ int column = 0; // GetScriptColumnNumber(script, source_pos);
+ Object *script_name_raw = script->name();
+ if (script_name_raw->IsString()) {
+ String *script_name = String::cast(script->name());
+ SmartArrayPointer<char> c_script_name =
+ script_name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ symbolId = registerSymInfo((size_t)frame->function(), s,
+ c_script_name.get(), line, column);
+ }
+ }
+ }
+ }
+ if (symbolId == 0) {
+ symbolId = registerSymInfo((size_t)frame->function(), s, "", 0, 0);
+ }
+
+ SymbolCached* symCached = new SymbolCached;
+ symCached->function_ = reinterpret_cast<uintptr_t>(frame->function());
+ symCached->symbol_id_ = symbolId;
+ sym_entry->value = symCached;
+ } else {
+ symbolId = reinterpret_cast<SymbolCached*>(sym_entry->value)->symbol_id_;
+ }
+ return symbolId;
+}
+
+
+// -----------------------------------------------------------------------------
+RuntimeInfo::RuntimeInfo(AggregatedChunks* aggregated_chunks):
+ working_set_hash_(AddressesMatch),
+ aggregated_chunks_(aggregated_chunks),
+ AllocatedBeforeCollectionFrame_(0) {
+}
+
+
+PostCollectedInfo* RuntimeInfo::FindPostCollectedInfo(Address addr) {
+ HashMap::Entry* entry = working_set_hash_.Lookup(
+ reinterpret_cast<void*>(addr), AddressHash(addr), false);
+ if (entry && entry->value) {
+ PostCollectedInfo* info =
+ reinterpret_cast<PostCollectedInfo*>(entry->value);
+ return info;
+ }
+ return NULL;
+}
+
+
+PostCollectedInfo* RuntimeInfo::AddPostCollectedInfo(Address addr,
+ unsigned time_delta,
+ PostCollectedInfo* info) {
+ PostCollectedInfo* info_new = NULL;
+ if (!info) {
+ info_new = new PostCollectedInfo;
+ } else {
+ info_new = info;
+ }
+
+ HashMap::Entry* entry = working_set_hash_.Lookup(
+ reinterpret_cast<void*>(addr), AddressHash(addr), true);
+ DCHECK(entry);
+ if (entry->value != NULL) {
+ // compensation of the wrong deallocation place
+ // we were not able to work the GC epilogue callback because GC is not
+ // iteratable in the prologue
+ // thus we need to mark the object as freed
+ PostCollectedInfo* info_old =
+ static_cast<PostCollectedInfo*>(entry->value);
+ aggregated_chunks_->addObjectToAggregated(info_old, time_delta);
+ delete info_old;
+ }
+
+ entry->value = info_new;
+ return info_new;
+}
+
+
+PostCollectedInfo* RuntimeInfo::AddPreCollectionInfo(Address addr,
+ unsigned size) {
+ PostCollectedInfo* info = AddPostCollectedInfo(addr);
+ info->size_ = size;
+ info->timeStamp_ = 0;
+ info->stackId_ = AllocatedBeforeCollectionFrame_;
+ info->className_ = (unsigned)-1;
+ return info;
+}
+
+
+void RuntimeInfo::RemoveInfo(Address addr) {
+ working_set_hash_.Remove(reinterpret_cast<void*>(addr), AddressHash(addr));
+}
+
+
+void RuntimeInfo::InitABCFrame(unsigned abc_frame) {
+ AllocatedBeforeCollectionFrame_ = abc_frame;
+}
+
+
+void RuntimeInfo::CollectGarbaged(unsigned ts) {
+ // iteration over the working_set_hash_
+ for (HashMap::Entry* p = working_set_hash_.Start(); p != NULL;
+ p = working_set_hash_.Next(p)) {
+ if (p->value) {
+ PostCollectedInfo* info = static_cast<PostCollectedInfo*>(p->value);
+ if (info->dirty_ == false) {
+ // need to care of allocated during collection.
+ // if timeStamp_ == 0 this object was allocated before collection
+ // and we don't care of it
+ aggregated_chunks_->addObjectToAggregated(info, ts);
+ delete info;
+ p->value = NULL;
+ } else {
+ info->dirty_ = false;
+ }
+ }
+ }
+}
+
+
+//------------------------------------------------------------------------------
+static bool AggregatedMatch(void* key1, void* key2) {
+ // cast to the AggregatedKey
+ AggregatedKey* key_c1 = reinterpret_cast<AggregatedKey*>(key1);
+ AggregatedKey* key_c2 = reinterpret_cast<AggregatedKey*>(key2);
+ return *key_c1 == *key_c2;
+}
+
+
+static uint32_t AggregatedHash(const AggregatedKey& key) {
+ uint32_t hash = 0;
+ // take the low 8 bits of stackId_
+ hash |= (key.stackId_ & 0xff);
+ // take the low 8 bits of classId_ and init hash from 8th to 15th bits
+ hash |= ((key.classId_ & 0xff) << 8);
+ // since times are well graduated it's no sense take the lowest 8 bit
+ // instead this we will move to 3 bits and only then take 8 bits
+ hash |= (((key.tsBegin_ >> 3) & 0xff) << 16);
+ hash |= (((key.tsBegin_ >> 3) & 0xff) << 24);
+ return hash;
+}
+
+
+AggregatedChunks::AggregatedChunks() :
+ aggregated_map_(AggregatedMatch),
+ bucketSize_(500) {
+ reserved_key_ = new AggregatedKey();
+}
+
+
+AggregatedChunks::~AggregatedChunks() {
+ delete reserved_key_;
+}
+
+
+void AggregatedChunks::addObjectToAggregated(PostCollectedInfo* info,
+ unsigned td) {
+ reserved_key_->stackId_ = info->stackId_;
+ reserved_key_->classId_ = info->className_;
+ // get the bucket for the first time
+ reserved_key_->tsBegin_ = info->timeStamp_ - (info->timeStamp_ % bucketSize_);
+ reserved_key_->tsEnd_ = td - (td % bucketSize_);
+
+ HashMap::Entry* aggregated_entry = aggregated_map_.Lookup(reserved_key_,
+ AggregatedHash(*reserved_key_),
+ true);
+ if (aggregated_entry->value) {
+ // no need to store the latest record in the aggregated_keys_list_
+ AggregatedValue* value =
+ reinterpret_cast<AggregatedValue*>(aggregated_entry->value);
+ value->objects_++;
+ value->size_ += info->size_;
+ } else {
+ reserved_key_ = new AggregatedKey;
+ AggregatedValue* value = new AggregatedValue;
+ value->objects_ = 1;
+ value->size_ = info->size_;
+ aggregated_entry->value = value;
+ }
+}
+
+
+std::string AggregatedChunks::SerializeChunk() {
+ std::stringstream schunks;
+ for (HashMap::Entry* p = aggregated_map_.Start(); p != NULL;
+ p = aggregated_map_.Next(p)) {
+ if (p->key && p->value) {
+ AggregatedKey* key = reinterpret_cast<AggregatedKey*>(p->key);
+ AggregatedValue* value = reinterpret_cast<AggregatedValue*>(p->value);
+ schunks <<
+ key->tsBegin_ << "," << key->tsEnd_ << "," <<
+ key->stackId_ << "," << key->classId_ << "," <<
+ value->size_ << "," << value->objects_ << std::endl;
+ delete key;
+ delete value;
+ }
+ }
+
+ aggregated_map_.Clear();
+
+ return schunks.str();
+}
+
+
+// -----------------------------------------------------------------------------
+void References::addReference(const RefId& parent, const RefSet& refSet,
+ int parentTime) {
+ // looking for the parent in the refMap_
+ PARENTREFMAP::iterator cit = refMap_.find(parent);
+ if (cit != refMap_.end()) {
+ REFERENCESETS& sets = cit->second;
+ REFERENCESETS::iterator it = sets.find(refSet);
+ if (it != sets.end()) {
+ // look for the time
+ TIMETOCOUNT::iterator cittc = it->second.find(parentTime);
+ if (cittc != it->second.end()) {
+ cittc->second++;
+ } else {
+ it->second[parentTime] = 1;
+ }
+ } else {
+ TIMETOCOUNT tc;
+ tc[parentTime] = 1;
+ sets[refSet] = tc;
+ }
+ } else {
+ // adding new parent, new sets
+ REFERENCESETS sets;
+ TIMETOCOUNT tc;
+ tc[parentTime] = 1;
+ sets[refSet] = tc;
+ refMap_[parent] = sets;
+ }
+}
+
+
+void References::clear() {
+ refMap_.clear();
+}
+
+
+std::string References::serialize() const {
+ std::stringstream str;
+ PARENTREFMAP::const_iterator citrefs = refMap_.begin();
+ while (citrefs != refMap_.end()) {
+ REFERENCESETS::const_iterator citsets = citrefs->second.begin();
+ while (citsets != citrefs->second.end()) {
+ str << citrefs->first.stackId_ << "," << citrefs->first.classId_;
+ // output of length, and content of TIMETOCOUNT
+ str << "," << citsets->second.size();
+ TIMETOCOUNT::const_iterator cittc = citsets->second.begin();
+ while (cittc != citsets->second.end()) {
+ str << "," << cittc->first << "," << cittc->second;
+ cittc++;
+ }
+ REFERENCESET::const_iterator citset = citsets->first.references_.begin();
+ while (citset != citsets->first.references_.end()) {
+ str << "," << citset->stackId_ << "," << citset->classId_<< "," <<
+ citset->field_;
+ citset++;
+ }
+ str << std::endl;
+ citsets++;
+ }
+ citrefs++;
+ }
+ return str.str();
+}
+
+
+} } // namespace v8::internal
-// 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
-#ifndef __xdk_utils_h__\r
-#define __xdk_utils_h__\r
-\r
-#include <map>\r
-#include <set>\r
-#include <sstream>\r
-#include <string>\r
-#include "src/hashmap.h"\r
-\r
-namespace v8 {\r
-namespace internal {\r
-\r
-class AggregatedChunks;\r
-class StringsStorage;\r
-class JavaScriptFrame;\r
-\r
-// --- ClassNames\r
-class ClassNames {\r
- public:\r
- explicit ClassNames(StringsStorage* names);\r
-\r
- unsigned registerName(const char* className);\r
- std::string SerializeChunk();\r
- String* GetConstructorName(Address address);\r
-\r
- private:\r
- unsigned counter_;\r
- HashMap char_to_idx_;\r
- StringsStorage* names_;\r
-};\r
-\r
-\r
-// --- ShadowStack\r
-class CallTree {\r
- public:\r
- // For quick search we use below member. it is not reasnable to use here\r
- // map because it occupies a lot of space even in empty state and such nodes\r
- // will be many. In opposite to map, std::map uses binary tree search and\r
- // don't store buffer, but allocates it dinamically\r
- std::map<unsigned, CallTree*> children_;\r
-\r
- // This is _not_ the same as index in the children_. This index is\r
- // incremental value from list of all nodes, but the key in the children_ is\r
- // callsite\r
- unsigned index_;\r
- CallTree* parent_;\r
- // the only one field which characterize the call point\r
- unsigned callsite_;\r
-};\r
-\r
-\r
-class ShadowStack {\r
- CallTree root_;\r
-\r
- // unsigned here is ok, size_t is not required because even 10 millions\r
- // objects in this class will lead to the significant memory consumption\r
- unsigned last_index_;\r
-\r
- // TODO(amalyshe): rewrite using List, storing nodes and use index in the list\r
- // instead pointer to CallTree in the children_\r
- std::map<unsigned, CallTree*> allNodes_;\r
- unsigned serializedCounter_;\r
- public:\r
- ShadowStack();\r
- ~ShadowStack();\r
- // Returns unique stack id. This method can work with incremental stacks when\r
- // we have old stack id, new tail and number of functions that we need to\r
- // unroll.\r
- unsigned registerStack(const List<unsigned>& shadow_stack_);\r
- std::string SerializeChunk();\r
-};\r
-\r
-\r
-// --- SymbolsStorage\r
-struct SymInfoKey {\r
- size_t function_id_;\r
- unsigned line_;\r
- unsigned column_;\r
-};\r
-\r
-bool inline operator == (const SymInfoKey& key1, const SymInfoKey& key2) {\r
- return key1.function_id_ == key2.function_id_ &&\r
- key1.line_ == key2.line_ &&\r
- key1.column_ == key2.column_;\r
-}\r
-\r
-\r
-struct SymInfoValue {\r
- unsigned symId_;\r
- std::string funcName_;\r
- std::string sourceFile_;\r
-};\r
-\r
-\r
-class SymbolsStorage {\r
- public:\r
- unsigned registerSymInfo(size_t functionId,\r
- std::string functionName,\r
- std::string sourceName, unsigned line,\r
- unsigned column);\r
- unsigned FindOrRegisterFrame(JavaScriptFrame* frame);\r
- SymbolsStorage(Heap* heap, StringsStorage* names);\r
- ~SymbolsStorage();\r
- std::string SerializeChunk();\r
-\r
- private:\r
- HashMap symbols_;\r
- unsigned curSym_;\r
- // fast living storage which duplicate info but is cleaned regularly\r
- SymInfoKey* reserved_key_;\r
- HashMap sym_info_hash_;\r
- Heap* heap_;\r
- StringsStorage* names_;\r
-};\r
-\r
-\r
-struct PostCollectedInfo {\r
- int size_;\r
- int timeStamp_;\r
- int stackId_;\r
- unsigned className_;\r
- bool dirty_;\r
-};\r
-\r
-\r
-class RuntimeInfo {\r
- public:\r
- explicit RuntimeInfo(AggregatedChunks* aggregated_chunks);\r
- PostCollectedInfo* FindPostCollectedInfo(Address addr);\r
- PostCollectedInfo* AddPostCollectedInfo(Address addr,\r
- unsigned time_delta = 0,\r
- PostCollectedInfo* info = NULL);\r
- PostCollectedInfo* AddPreCollectionInfo(Address addr, unsigned size);\r
- void RemoveInfo(Address addr);\r
- void InitABCFrame(unsigned abc_frame);\r
- void CollectGarbaged(unsigned ts);\r
-\r
- private:\r
- HashMap working_set_hash_;\r
- AggregatedChunks* aggregated_chunks_;\r
- unsigned AllocatedBeforeCollectionFrame_;\r
-};\r
-\r
-\r
-struct AggregatedKey {\r
- int stackId_;\r
- // do we need class here? is not it defined by the stack id?\r
- unsigned classId_;\r
- unsigned tsBegin_;\r
- unsigned tsEnd_;\r
-};\r
-\r
-bool inline operator == (const AggregatedKey& key1, const AggregatedKey& key2) {\r
- return key1.stackId_ == key2.stackId_ &&\r
- key1.classId_ == key2.classId_ &&\r
- key1.tsBegin_ == key2.tsBegin_ &&\r
- key1.tsEnd_ == key2.tsEnd_;\r
-}\r
-\r
-\r
-struct AggregatedValue {\r
- unsigned size_;\r
- unsigned objects_;\r
-};\r
-\r
-\r
-class AggregatedChunks {\r
- public:\r
- AggregatedChunks();\r
- ~AggregatedChunks();\r
- void addObjectToAggregated(PostCollectedInfo* info, unsigned td);\r
- std::string SerializeChunk();\r
-\r
- private:\r
- HashMap aggregated_map_;\r
- int bucketSize_;\r
- AggregatedKey* reserved_key_;\r
-};\r
-\r
-\r
-struct RefId {\r
- int stackId_;\r
- int classId_;\r
- std::string field_;\r
-};\r
-\r
-inline bool operator < (const RefId& first, const RefId& second ) {\r
- if (first.stackId_ < second.stackId_ )\r
- return true;\r
- else if (first.stackId_ > second.stackId_ )\r
- return false;\r
- if (first.classId_ < second.classId_ )\r
- return true;\r
- if (first.classId_ > second.classId_ )\r
- return false;\r
- if (first.field_.compare(second.field_) < 0 )\r
- return true;\r
-\r
- return false;\r
-}\r
-\r
-typedef std::set<RefId> REFERENCESET;\r
-\r
-\r
-struct RefSet {\r
- REFERENCESET references_;\r
-};\r
-\r
-inline bool operator < (const RefSet& first, const RefSet& second) {\r
- // compare the sizes first of all\r
- if (first.references_.size() != second.references_.size() )\r
- return first.references_.size() < second.references_.size();\r
- // iterating by the first\r
- REFERENCESET::const_iterator cit1 = first.references_.begin();\r
- REFERENCESET::const_iterator cit2 = second.references_.begin();\r
- while (cit1 != first.references_.end()) {\r
- if (*cit1 < *cit2 )\r
- return true;\r
- if (*cit2 < *cit1 )\r
- return false;\r
- cit1++;\r
- cit2++;\r
- }\r
- return false;\r
-}\r
-typedef std::map<unsigned int, int> TIMETOCOUNT;\r
-typedef std::map<RefSet, TIMETOCOUNT> REFERENCESETS;\r
-typedef std::map<RefId, REFERENCESETS> PARENTREFMAP;\r
-\r
-\r
-class References {\r
- public:\r
- void addReference(const RefId& parent,\r
- const RefSet& refSet,\r
- int parentTime);\r
- void clear();\r
- std::string serialize() const;\r
-\r
- private:\r
- PARENTREFMAP refMap_;\r
-};\r
-\r
-\r
-} } // namespace v8::internal\r
-#endif // __xdk_utils_h__\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.
+
+#ifndef __xdk_utils_h__
+#define __xdk_utils_h__
+
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include "src/hashmap.h"
+
+namespace v8 {
+namespace internal {
+
+class AggregatedChunks;
+class StringsStorage;
+class JavaScriptFrame;
+
+// --- ClassNames
+class ClassNames {
+ public:
+ explicit ClassNames(StringsStorage* names);
+
+ unsigned registerName(const char* className);
+ std::string SerializeChunk();
+ String* GetConstructorName(Address address);
+
+ private:
+ unsigned counter_;
+ HashMap char_to_idx_;
+ StringsStorage* names_;
+};
+
+
+// --- ShadowStack
+class CallTree {
+ public:
+ // For quick search we use below member. it is not reasnable to use here
+ // map because it occupies a lot of space even in empty state and such nodes
+ // will be many. In opposite to map, std::map uses binary tree search and
+ // don't store buffer, but allocates it dinamically
+ std::map<unsigned, CallTree*> children_;
+
+ // This is _not_ the same as index in the children_. This index is
+ // incremental value from list of all nodes, but the key in the children_ is
+ // callsite
+ unsigned index_;
+ CallTree* parent_;
+ // the only one field which characterize the call point
+ unsigned callsite_;
+};
+
+
+class ShadowStack {
+ CallTree root_;
+
+ // unsigned here is ok, size_t is not required because even 10 millions
+ // objects in this class will lead to the significant memory consumption
+ unsigned last_index_;
+
+ // TODO(amalyshe): rewrite using List, storing nodes and use index in the list
+ // instead pointer to CallTree in the children_
+ std::map<unsigned, CallTree*> allNodes_;
+ unsigned serializedCounter_;
+ public:
+ ShadowStack();
+ ~ShadowStack();
+ // Returns unique stack id. This method can work with incremental stacks when
+ // we have old stack id, new tail and number of functions that we need to
+ // unroll.
+ unsigned registerStack(const List<unsigned>& shadow_stack_);
+ std::string SerializeChunk();
+};
+
+
+// --- SymbolsStorage
+struct SymInfoKey {
+ size_t function_id_;
+ unsigned line_;
+ unsigned column_;
+};
+
+bool inline operator == (const SymInfoKey& key1, const SymInfoKey& key2) {
+ return key1.function_id_ == key2.function_id_ &&
+ key1.line_ == key2.line_ &&
+ key1.column_ == key2.column_;
+}
+
+
+struct SymInfoValue {
+ unsigned symId_;
+ std::string funcName_;
+ std::string sourceFile_;
+};
+
+
+class SymbolsStorage {
+ public:
+ unsigned registerSymInfo(size_t functionId,
+ std::string functionName,
+ std::string sourceName, unsigned line,
+ unsigned column);
+ unsigned FindOrRegisterFrame(JavaScriptFrame* frame);
+ SymbolsStorage(Heap* heap, StringsStorage* names);
+ ~SymbolsStorage();
+ std::string SerializeChunk();
+
+ private:
+ HashMap symbols_;
+ unsigned curSym_;
+ // fast living storage which duplicate info but is cleaned regularly
+ SymInfoKey* reserved_key_;
+ HashMap sym_info_hash_;
+ Heap* heap_;
+ StringsStorage* names_;
+};
+
+
+struct PostCollectedInfo {
+ int size_;
+ int timeStamp_;
+ int stackId_;
+ unsigned className_;
+ bool dirty_;
+};
+
+
+class RuntimeInfo {
+ public:
+ explicit RuntimeInfo(AggregatedChunks* aggregated_chunks);
+ PostCollectedInfo* FindPostCollectedInfo(Address addr);
+ PostCollectedInfo* AddPostCollectedInfo(Address addr,
+ unsigned time_delta = 0,
+ PostCollectedInfo* info = NULL);
+ PostCollectedInfo* AddPreCollectionInfo(Address addr, unsigned size);
+ void RemoveInfo(Address addr);
+ void InitABCFrame(unsigned abc_frame);
+ void CollectGarbaged(unsigned ts);
+
+ private:
+ HashMap working_set_hash_;
+ AggregatedChunks* aggregated_chunks_;
+ unsigned AllocatedBeforeCollectionFrame_;
+};
+
+
+struct AggregatedKey {
+ int stackId_;
+ // do we need class here? is not it defined by the stack id?
+ unsigned classId_;
+ unsigned tsBegin_;
+ unsigned tsEnd_;
+};
+
+bool inline operator == (const AggregatedKey& key1, const AggregatedKey& key2) {
+ return key1.stackId_ == key2.stackId_ &&
+ key1.classId_ == key2.classId_ &&
+ key1.tsBegin_ == key2.tsBegin_ &&
+ key1.tsEnd_ == key2.tsEnd_;
+}
+
+
+struct AggregatedValue {
+ unsigned size_;
+ unsigned objects_;
+};
+
+
+class AggregatedChunks {
+ public:
+ AggregatedChunks();
+ ~AggregatedChunks();
+ void addObjectToAggregated(PostCollectedInfo* info, unsigned td);
+ std::string SerializeChunk();
+
+ private:
+ HashMap aggregated_map_;
+ int bucketSize_;
+ AggregatedKey* reserved_key_;
+};
+
+
+struct RefId {
+ int stackId_;
+ int classId_;
+ std::string field_;
+};
+
+inline bool operator < (const RefId& first, const RefId& second ) {
+ if (first.stackId_ < second.stackId_ )
+ return true;
+ else if (first.stackId_ > second.stackId_ )
+ return false;
+ if (first.classId_ < second.classId_ )
+ return true;
+ if (first.classId_ > second.classId_ )
+ return false;
+ if (first.field_.compare(second.field_) < 0 )
+ return true;
+
+ return false;
+}
+
+typedef std::set<RefId> REFERENCESET;
+
+
+struct RefSet {
+ REFERENCESET references_;
+};
+
+inline bool operator < (const RefSet& first, const RefSet& second) {
+ // compare the sizes first of all
+ if (first.references_.size() != second.references_.size() )
+ return first.references_.size() < second.references_.size();
+ // iterating by the first
+ REFERENCESET::const_iterator cit1 = first.references_.begin();
+ REFERENCESET::const_iterator cit2 = second.references_.begin();
+ while (cit1 != first.references_.end()) {
+ if (*cit1 < *cit2 )
+ return true;
+ if (*cit2 < *cit1 )
+ return false;
+ cit1++;
+ cit2++;
+ }
+ return false;
+}
+typedef std::map<unsigned int, int> TIMETOCOUNT;
+typedef std::map<RefSet, TIMETOCOUNT> REFERENCESETS;
+typedef std::map<RefId, REFERENCESETS> PARENTREFMAP;
+
+
+class References {
+ public:
+ void addReference(const RefId& parent,
+ const RefSet& refSet,
+ int parentTime);
+ void clear();
+ std::string serialize() const;
+
+ private:
+ PARENTREFMAP refMap_;
+};
+
+
+} } // namespace v8::internal
+#endif // __xdk_utils_h__
# -----------------------------------
chromium_crosswalk_rev = 'ea46c0b6279a60b0173a092f0e9f0403a2a047a9'
-v8_crosswalk_rev = '825a59c274c95ce90ceab7234c8fa07ad52f7ae5'
+v8_crosswalk_rev = '05d1cf41ef1b6e73079f69492d9e942ec19cc61a'
ozone_wayland_rev = '6379cd118da098b55a5934ce1a90b377a177ed40'
# |blink_crosswalk_rev| specifies the SHA1 hash of the blink-crosswalk commit
MAJOR=11
MINOR=39
-BUILD=258
+BUILD=260
PATCH=0
auto site = content::SiteInstance::CreateForURL(browser_context_, url);
Runtime* runtime = Runtime::Create(browser_context_, site);
runtime->set_observer(this);
+ runtime->set_remote_debugging_enabled(remote_debugging_enabled_);
runtimes_.push_back(runtime);
render_process_host_ = runtime->GetRenderProcessHost();
render_process_host_->AddObserver(this);
if (manifest_->HasPath(widget_keys::kContentNamespace)) {
std::string ns;
if (manifest_->GetString(widget_keys::kContentNamespace, &ns) &&
- ns == kTizenNamespacePrefix)
+ ns == widget_keys::kTizenNamespacePrefix)
hosted = true;
}
#endif
current_value->GetString(kNamespaceKey, ¤t_namespace);
sub_value->GetString(kNamespaceKey, &new_namespace);
if (current_namespace != new_namespace &&
- new_namespace == kTizenNamespacePrefix)
+ new_namespace == widget_keys::kTizenNamespacePrefix)
value->Set(sub_node_name, sub_value);
continue;
#endif
const char kPreferencesValueKey[] = "@value";
const char kPreferencesReadonlyKey[] = "@readonly";
+const char kWidgetNamespaceKey[] = "widget.@namespace";
+const char kWidgetNamespacePrefix[] = "http://www.w3.org/ns/widgets";
+
// Child keys inside 'kAccessKey'.
const char kAccessOriginKey[] = "@origin";
const char kAccessSubdomainsKey[] = "@subdomains";
const char kTizenApplicationAppControlUriKey[] = "uri";
const char kTizenApplicationAppControlMimeKey[] = "mime";
const char kTizenApplicationAppControlChildNameAttrKey[] = "@name";
+const char kTizenNamespacePrefix[] = "http://tizen.org/ns/widgets";
#endif
} // namespace application_widget_keys
-const char kW3CNamespaceKey[] = "widget.@namespace";
-const char kW3CNamespacePrefix[] = "http://www.w3.org/ns/widgets";
-#if defined(OS_TIZEN)
-const char kTizenNamespacePrefix[] = "http://tizen.org/ns/widgets";
-#endif
-
namespace application_manifest_errors {
const char kInvalidDescription[] =
"Invalid value for 'description'.";
extern const char kPreferencesNameKey[];
extern const char kPreferencesValueKey[];
extern const char kPreferencesReadonlyKey[];
+ extern const char kWidgetNamespaceKey[];
+ extern const char kWidgetNamespacePrefix[];
#if defined(OS_TIZEN)
extern const char kTizenWidgetKey[];
extern const char kTizenApplicationKey[];
extern const char kTizenApplicationAppControlUriKey[];
extern const char kTizenApplicationAppControlMimeKey[];
extern const char kTizenApplicationAppControlChildNameAttrKey[];
+ extern const char kTizenNamespacePrefix[];
#endif
} // namespace application_widget_keys
-extern const char kW3CNamespaceKey[];
-extern const char kW3CNamespacePrefix[];
-#if defined(OS_TIZEN)
-extern const char kTizenNamespacePrefix[];
-#endif
namespace application_manifest_errors {
extern const char kInvalidDescription[];
if (app_value && app_value->IsType(base::Value::TYPE_DICTIONARY)) {
app_value->GetAsDictionary(&app_dict);
find = app_dict->GetString(keys::kNamespaceKey, &value);
- find = find && (value == kTizenNamespacePrefix);
+ find = find && (value == keys::kTizenNamespacePrefix);
} else if (app_value && app_value->IsType(base::Value::TYPE_LIST)) {
base::ListValue* list;
app_value->GetAsList(&list);
it != list->end(); ++it) {
(*it)->GetAsDictionary(&app_dict);
find = app_dict->GetString(keys::kNamespaceKey, &value);
- find = find && (value == kTizenNamespacePrefix);
+ find = find && (value == keys::kTizenNamespacePrefix);
if (find)
break;
}
const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
DCHECK(app_widget);
- if (!VerifyElementNamespace(dict, key, kTizenNamespacePrefix, error))
+ if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
return false;
std::string lang;
const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
DCHECK(app_widget);
- if (!VerifyElementNamespace(dict, key, kTizenNamespacePrefix, error))
+ if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
return false;
if (!app_widget->icon_src.empty()) {
const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
DCHECK(app_widget);
- if (!VerifyElementNamespace(dict, key, kTizenNamespacePrefix, error))
+ if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
return false;
TizenAppWidgetSize size;
const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
DCHECK(app_widget);
- if (!VerifyElementNamespace(dict, key, kTizenNamespacePrefix, error))
+ if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
return false;
if (!app_widget->content_drop_view.empty()) {
const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
DCHECK(app_widget);
- if (!VerifyElementNamespace(dict, key, kTizenNamespacePrefix, error))
+ if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
return false;
if (!app_widget->content_src.empty()) {
base::string16* error) {
DCHECK(app_widgets);
- if (!VerifyElementNamespace(dict, key, kTizenNamespacePrefix, error))
+ if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
return false;
TizenAppWidget app_widget;
manifest->SetString(
DotConnect(widget_keys::kWidgetKey, widget_keys::kNamespaceKey),
- kW3CNamespacePrefix);
+ widget_keys::kWidgetNamespacePrefix);
manifest->SetString(widget_keys::kVersionKey, kDefaultVersion);
manifest->SetString(widget_keys::kNameKey, kDefaultName);
manifest->SetString(
DotConnect(widget_keys::kTizenApplicationKey,
widget_keys::kNamespaceKey),
- kTizenNamespacePrefix);
+ widget_keys::kTizenNamespacePrefix);
manifest->SetString(
DotConnect(widget_keys::kTizenApplicationKey,
widget_keys::kTizenApplicationIdKey),
for (KeyMapIterator iter = map.begin(); iter != map.end(); ++iter) {
std::string string;
bool result = manifest->GetString(iter->first, &string);
+ if (result && !string.empty() && iter->first == keys::kAuthorHrefKey &&
+ !GURL(string).is_valid())
+ // When authorhref is an invalid URI, reset it an empty string.
+ string.clear();
widget_info->SetString(iter->second, result ? string : "");
}
const Manifest* manifest = application->GetManifest();
DCHECK(manifest);
std::string ns_value;
- manifest->GetString(kW3CNamespaceKey, &ns_value);
- if (base::strcasecmp(kW3CNamespacePrefix, ns_value.c_str()) != 0) {
+ manifest->GetString(keys::kWidgetNamespaceKey, &ns_value);
+ if (base::strcasecmp(keys::kWidgetNamespacePrefix, ns_value.c_str()) != 0) {
*error = std::string("The widget namespace is invalid.");
return false;
}
// No Preferences and full other information
void SetAllInfoToManifest(base::DictionaryValue* manifest) {
// Insert some key-value pairs into manifest use full key
- manifest->SetString(kW3CNamespaceKey, kW3CNamespacePrefix);
+ manifest->SetString(keys::kWidgetNamespaceKey,
+ keys::kWidgetNamespacePrefix);
manifest->SetString(keys::kAuthorKey, author);
manifest->SetString(keys::kDescriptionKey, decription);
manifest->SetString(keys::kNameKey, name);
TEST_F(WidgetHandlerTest, ParseManifestWithOnlyNameAndVersion) {
base::DictionaryValue manifest;
- manifest.SetString(kW3CNamespaceKey, kW3CNamespacePrefix);
+ manifest.SetString(keys::kWidgetNamespaceKey, keys::kWidgetNamespacePrefix);
manifest.SetString(keys::kNameKey, "no name");
manifest.SetString(keys::kVersionKey, "0");
EXPECT_TRUE(widget->Equals(widget_parsed_from_manifest));
}
+TEST_F(WidgetHandlerTest,
+ ParseManifestWithInvalidAuthorHrefValue) {
+ scoped_ptr<base::DictionaryValue> manifest(new base::DictionaryValue);
+ SetAllInfoToManifest(manifest.get());
+ manifest->SetString(keys::kAuthorHrefKey, "INVALID_HREF");
+
+ // Create an application use this manifest,
+ scoped_refptr<ApplicationData> application;
+ application = CreateApplication(*(manifest.get()));
+ EXPECT_TRUE(application.get());
+ EXPECT_EQ(application->manifest_type(), Manifest::TYPE_WIDGET);
+ // Get widget info from this application.
+ WidgetInfo* info = GetWidgetInfo(application);
+ EXPECT_TRUE(info);
+ std::string authorhref;
+ info->GetWidgetInfo()->GetString(keys::kAuthorHrefKey, &authorhref);
+ EXPECT_TRUE(authorhref.empty());
+}
} // namespace application
} // namespace xwalk
%define _binary_payload w3.gzdio
Name: crosswalk
-Version: 11.39.258.0
+Version: 11.39.260.0
Release: 0
Summary: Chromium-based app runtime
License: (BSD-3-Clause and LGPL-2.1+)
GURL Target::GetFaviconDataURL(WebContents* web_contents) const {
// Convert icon image to "data:" url.
+#if defined(OS_ANDROID)
+ // TODO(YangangHan): Add a new base parent class of WebContents
+ // for both Tizen and Android, so we can remove the current macro
+ // in the future.
+ return GURL();
+#endif
xwalk::Runtime* runtime =
static_cast<xwalk::Runtime*>(web_contents->GetDelegate());
if (!runtime || runtime->app_icon().IsEmpty())
//
// 2) FileDescriptorWatchers don't have HUP events.
//
-// As a POSIX-specific hack we MSG_PEEK and generate a syntetic HUP event
+// As a POSIX-specific hack we MSG_PEEK and generate a synthetic HUP event
// if we got 0 bytes.
//
// 3) You can't cancel a PostTask'd or PostDelayedTask'd task.
// cancelled if only there was a mechanism for it. The fingerprint in simply
// a monotonically increasing integer which is stored in the timer and also
// associated with a pending timeout. Whenever the timer is reconfigured
-// (delay updated, or timer disabled) the fingerpring is updated. Timeout
+// (delay updated, or timer disabled) the fingerprint is updated. Timeout
// callbacks with mismatching fingerprints are ignored.
//
// Originally we simply scheduled a task from the I/O thread to the UI thread
// hairier than I'm comfortable with... I think it'd be a better idea to run
// the mainloop fully in the I/O thread, have the resource sets live in the I/O
// thread also and have proxy object for them attached to the media backend
-// obejcts which would send requests and receive notifications to/from the
-// resource sets. We could avoid most of the threading related compliations
+// objects which would send requests and receive notifications to/from the
+// resource sets. We could avoid most of the threading related compilations
// for pumping the mainloop...
//
// Additionally to the basic mainloop adaptation we also set up the Murphy
void MurphyMainloop::Unregister(void* data) {
MRP_UNUSED(data);
- mrp_debug("unrgistering mainloop with data %p", data);
+ mrp_debug("unregistering mainloop with data %p", data);
}
bool MurphyMainloop::setupMainloop() {
IPC_MESSAGE_ROUTED1(MediaPlayerMsg_MediaPlayerPause, // NOLINT(*)
int /* player_id */)
-// Messages for controllering the media playback in browser process ----------
+// Messages for controlling the media playback in browser process ----------
// Destroy the media player object.
IPC_MESSAGE_ROUTED1(MediaPlayerHostMsg_DestroyMediaPlayer, // NOLINT(*)
// Starts the player.
void Start(MediaPlayerID player_id);
- // Pausees the player.
+ // Pauses the player.
void Pause(MediaPlayerID player_id);
// Destroy the player in the browser process