diff --git a/include/v8-profiler.h b/include/v8-profiler.h index 007ae2eca55..182b8d664cb 100644 --- a/include/v8-profiler.h +++ b/include/v8-profiler.h @@ -80,6 +80,11 @@ class V8_EXPORT CpuProfileNode { */ int GetColumnNumber() const; + /** + * Returns source line connected with current ProfileNode + */ + int GetSrcLine() const; + /** * Returns the number of the function's source lines that collect the samples. */ @@ -173,6 +178,18 @@ class V8_EXPORT CpuProfile { void Delete(); }; +/** + * HeapEventXDK contains the latest chunk of heap info + */ +class V8_EXPORT HeapEventXDK { + public: + const char* getSymbols(); + const char* getFrames(); + const char* getTypes(); + const char* getChunks(); + const char* getRetentions(); + unsigned int getDuration(); +}; /** * Interface for controlling CPU profiling. Instance of the @@ -341,6 +358,18 @@ class V8_EXPORT OutputStream { // NOLINT virtual WriteResult WriteHeapStatsChunk(HeapStatsUpdate* data, int count) { return kAbort; } + + /** + * Writes XDK object + */ + virtual WriteResult WriteHeapXDKChunk(const char* symbols, size_t symbolsSize, + const char* frames, size_t framesSize, + const char* types, size_t typesSize, + const char* chunks, size_t chunksSize, + const char* retentions, + size_t retentionSize) { + return kAbort; + } }; @@ -681,6 +710,14 @@ class V8_EXPORT HeapProfiler { */ void SetRetainedObjectInfo(UniqueId id, RetainedObjectInfo* info); + void StartTrackingHeapObjectsXDK(int stackDepth, bool retentions, + bool strict_collection = false); + /** + * @author amalyshe + */ + void GetHeapXDKStats(OutputStream* stream); + HeapEventXDK* StopTrackingHeapObjectsXDK(); + private: HeapProfiler(); ~HeapProfiler(); diff --git a/src/api.cc b/src/api.cc index 853bd50f219..e083cec2e90 100644 --- a/src/api.cc +++ b/src/api.cc @@ -49,12 +49,12 @@ #include "src/profiler/heap-snapshot-generator-inl.h" #include "src/profiler/profile-generator-inl.h" #include "src/profiler/sampler.h" -#include "src/property.h" #include "src/property-descriptor.h" #include "src/property-details.h" +#include "src/property.h" #include "src/prototype.h" -#include "src/runtime/runtime.h" #include "src/runtime-profiler.h" +#include "src/runtime/runtime.h" #include "src/simulator.h" #include "src/snapshot/natives.h" #include "src/snapshot/snapshot.h" @@ -65,7 +65,7 @@ #include "src/v8threads.h" #include "src/version.h" #include "src/vm-state-inl.h" - +#include "src/xdk-allocation.h" namespace v8 { @@ -8179,6 +8179,10 @@ int CpuProfileNode::GetColumnNumber() const { entry()->column_number(); } +int CpuProfileNode::GetSrcLine() const { + const i::ProfileNode* node = reinterpret_cast(this); + return node->src_line(); +} unsigned int CpuProfileNode::GetHitLineCount() const { const i::ProfileNode* node = reinterpret_cast(this); @@ -8420,6 +8424,41 @@ void HeapSnapshot::Delete() { } } +const char* HeapEventXDK::getSymbols() { + const i::HeapEventXDK* eventXDK = + reinterpret_cast(this); + return eventXDK->symbols(); +} + +const char* HeapEventXDK::getFrames() { + const i::HeapEventXDK* eventXDK = + reinterpret_cast(this); + return eventXDK->frames(); +} + +const char* HeapEventXDK::getTypes() { + const i::HeapEventXDK* eventXDK = + reinterpret_cast(this); + return eventXDK->types(); +} + +const char* HeapEventXDK::getChunks() { + const i::HeapEventXDK* eventXDK = + reinterpret_cast(this); + return eventXDK->chunks(); +} + +const char* HeapEventXDK::getRetentions() { + const i::HeapEventXDK* eventXDK = + reinterpret_cast(this); + return eventXDK->retentions(); +} + +unsigned int HeapEventXDK::getDuration() { + const i::HeapEventXDK* eventXDK = + reinterpret_cast(this); + return eventXDK->duration(); +} const HeapGraphNode* HeapSnapshot::GetRoot() const { return reinterpret_cast(ToInternal(this)->root()); @@ -8538,6 +8577,20 @@ AllocationProfile* HeapProfiler::GetAllocationProfile() { return reinterpret_cast(this)->GetAllocationProfile(); } +void HeapProfiler::GetHeapXDKStats(OutputStream* stream) { + reinterpret_cast(this)->PushHeapObjectsXDKStats(stream); +} + +void HeapProfiler::StartTrackingHeapObjectsXDK(int stackDepth, bool retentions, + bool strict_collection) { + reinterpret_cast(this)->StartHeapObjectsTrackingXDK( + stackDepth, retentions, strict_collection); +} + +HeapEventXDK* HeapProfiler::StopTrackingHeapObjectsXDK() { + return reinterpret_cast( + reinterpret_cast(this)->StopHeapObjectsTrackingXDK()); +} void HeapProfiler::DeleteAllHeapSnapshots() { reinterpret_cast(this)->DeleteAllSnapshots(); diff --git a/src/profiler/heap-profiler.cc b/src/profiler/heap-profiler.cc index 1305cae66ea..45109781f1e 100644 --- a/src/profiler/heap-profiler.cc +++ b/src/profiler/heap-profiler.cc @@ -9,6 +9,7 @@ #include "src/profiler/allocation-tracker.h" #include "src/profiler/heap-snapshot-generator-inl.h" #include "src/profiler/sampling-heap-profiler.h" +#include "src/xdk-allocation.h" namespace v8 { namespace internal { @@ -67,7 +68,7 @@ HeapSnapshot* HeapProfiler::TakeSnapshot( v8::HeapProfiler::ObjectNameResolver* resolver) { HeapSnapshot* result = new HeapSnapshot(this); { - HeapSnapshotGenerator generator(result, control, resolver, heap()); + HeapSnapshotGenerator generator(this, result, control, resolver, heap()); if (!generator.GenerateSnapshot()) { delete result; result = NULL; @@ -137,6 +138,42 @@ void HeapProfiler::StopHeapObjectsTracking() { } } +void HeapProfiler::StartHeapObjectsTrackingXDK(int stackDepth, bool retentions, + bool strict_collection) { + ids_->UpdateHeapObjectsMap(); + is_tracking_object_moves_ = true; + DCHECK(!is_tracking_allocations()); + allocation_tracker_xdk_.Reset( + new XDKAllocationTracker(this, ids_.get(), names_.get(), stackDepth, + retentions, strict_collection)); + heap()->DisableInlineAllocation(); + // init pre collected objects + allocation_tracker_xdk_->CollectFreedObjects(false, true); +} + +void HeapProfiler::PushHeapObjectsXDKStats(OutputStream* stream) { + // get the garbage here + if (!allocation_tracker_xdk_.is_empty()) { + allocation_tracker_xdk_->CollectFreedObjects(); + OutputStream::WriteResult result = + allocation_tracker_xdk_->SendChunk(stream); + // TODO(amalyshe): it's interesting why CDT can return kAbort. Need to + // investigate if we need add better error generation in the + // allocation_tracker_xdk_->SendChunk + if (result == OutputStream::kAbort) return; + stream->EndOfStream(); + } +} + +v8::internal::HeapEventXDK* HeapProfiler::StopHeapObjectsTrackingXDK() { + HeapEventXDK* event = NULL; + if (!allocation_tracker_xdk_.is_empty()) { + event = allocation_tracker_xdk_->stopTracking(); + allocation_tracker_xdk_.Reset(NULL); + heap()->EnableInlineAllocation(); + } + return event; +} size_t HeapProfiler::GetMemorySizeUsedByProfiler() { size_t size = sizeof(*this); @@ -169,9 +206,13 @@ SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle obj) { void HeapProfiler::ObjectMoveEvent(Address from, Address to, int size) { base::LockGuard guard(&profiler_mutex_); - bool known_object = ids_->MoveObject(from, to, size); - if (!known_object && !allocation_tracker_.is_empty()) { - allocation_tracker_->address_to_trace()->MoveObject(from, to, size); + if (allocation_tracker_xdk_.is_empty()) { + bool known_object = ids_->MoveObject(from, to, size); + if (!known_object && !allocation_tracker_.is_empty()) { + allocation_tracker_->address_to_trace()->MoveObject(from, to, size); + } + } else { + allocation_tracker_xdk_->OnMove(from, to, size); } } @@ -181,6 +222,9 @@ void HeapProfiler::AllocationEvent(Address addr, int size) { if (!allocation_tracker_.is_empty()) { allocation_tracker_->AllocationEvent(addr, size); } + if (!allocation_tracker_xdk_.is_empty()) { + allocation_tracker_xdk_->OnAlloc(addr, size); + } } diff --git a/src/profiler/heap-profiler.h b/src/profiler/heap-profiler.h index 32e143c74ff..dc5e43d7f7b 100644 --- a/src/profiler/heap-profiler.h +++ b/src/profiler/heap-profiler.h @@ -17,6 +17,8 @@ class AllocationTracker; class HeapObjectsMap; class HeapSnapshot; class SamplingHeapProfiler; +class HeapEventXDK; +class XDKAllocationTracker; class StringsStorage; class HeapProfiler { @@ -45,6 +47,11 @@ class HeapProfiler { SnapshotObjectId PushHeapObjectsStats(OutputStream* stream, int64_t* timestamp_us); + void PushHeapObjectsXDKStats(OutputStream* stream); + void StartHeapObjectsTrackingXDK(int stackDepth, bool retentions, + bool strict_collection = false); + v8::internal::HeapEventXDK* StopHeapObjectsTrackingXDK(); + int GetSnapshotsCount(); HeapSnapshot* GetSnapshot(int index); SnapshotObjectId GetSnapshotObjectId(Handle obj); @@ -66,7 +73,8 @@ class HeapProfiler { bool is_tracking_object_moves() const { return is_tracking_object_moves_; } bool is_tracking_allocations() const { - return !allocation_tracker_.is_empty(); + return (!allocation_tracker_.is_empty() || + !allocation_tracker_xdk_.is_empty()); } Handle FindHeapObjectById(SnapshotObjectId id); @@ -83,6 +91,7 @@ class HeapProfiler { base::SmartPointer names_; List wrapper_callbacks_; base::SmartPointer allocation_tracker_; + base::SmartPointer allocation_tracker_xdk_; bool is_tracking_object_moves_; base::Mutex profiler_mutex_; base::SmartPointer sampling_heap_profiler_; diff --git a/src/profiler/heap-snapshot-generator-inl.h b/src/profiler/heap-snapshot-generator-inl.h index 169ab569e82..db047957557 100644 --- a/src/profiler/heap-snapshot-generator-inl.h +++ b/src/profiler/heap-snapshot-generator-inl.h @@ -27,7 +27,7 @@ HeapSnapshot* HeapGraphEdge::snapshot() const { int HeapEntry::index() const { - return static_cast(this - &snapshot_->entries().first()); + return static_cast(this - &entries_->first()); } diff --git a/src/profiler/heap-snapshot-generator.cc b/src/profiler/heap-snapshot-generator.cc index 748f3074a19..50611d09c5d 100644 --- a/src/profiler/heap-snapshot-generator.cc +++ b/src/profiler/heap-snapshot-generator.cc @@ -43,21 +43,18 @@ void HeapGraphEdge::ReplaceToIndexWithEntry(HeapSnapshot* snapshot) { const int HeapEntry::kNoEntry = -1; -HeapEntry::HeapEntry(HeapSnapshot* snapshot, - Type type, - const char* name, - SnapshotObjectId id, - size_t self_size, - unsigned trace_node_id) +HeapEntry::HeapEntry(HeapSnapshot* snapshot, const List* entries, + Type type, const char* name, SnapshotObjectId id, + size_t self_size, unsigned trace_node_id) : type_(type), children_count_(0), children_index_(-1), self_size_(self_size), snapshot_(snapshot), + entries_(entries), name_(name), id_(id), - trace_node_id_(trace_node_id) { } - + trace_node_id_(trace_node_id) {} void HeapEntry::SetNamedReference(HeapGraphEdge::Type type, const char* name, @@ -167,12 +164,16 @@ template struct SnapshotSizeConstants; template <> struct SnapshotSizeConstants<4> { static const int kExpectedHeapGraphEdgeSize = 12; - static const int kExpectedHeapEntrySize = 28; + // This variable reflects the size of the HeapEntry structure + // it is increased to the 4 bytes in case of 32bit arch and for + // 8 bytes for 64 bit arch because for isolating HeapEntry from + // snapshot we need to add one more pointer to the List* entries_ + static const int kExpectedHeapEntrySize = 32; }; template <> struct SnapshotSizeConstants<8> { static const int kExpectedHeapGraphEdgeSize = 24; - static const int kExpectedHeapEntrySize = 40; + static const int kExpectedHeapEntrySize = 48; }; } // namespace @@ -263,7 +264,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type, SnapshotObjectId id, size_t size, unsigned trace_node_id) { - HeapEntry entry(this, type, name, id, size, trace_node_id); + HeapEntry entry(this, &this->entries(), type, name, id, size, trace_node_id); entries_.Add(entry); return &entries_.last(); } @@ -776,20 +777,16 @@ void HeapObjectsSet::SetTag(Object* obj, const char* tag) { cache_entry->value = const_cast(tag); } - -V8HeapExplorer::V8HeapExplorer( - HeapSnapshot* snapshot, - SnapshottingProgressReportingInterface* progress, - v8::HeapProfiler::ObjectNameResolver* resolver) - : heap_(snapshot->profiler()->heap_object_map()->heap()), +V8HeapExplorer::V8HeapExplorer(HeapProfiler* profiler, HeapSnapshot* snapshot, + SnapshottingProgressReportingInterface* progress, + v8::HeapProfiler::ObjectNameResolver* resolver) + : heap_(profiler->heap_object_map()->heap()), snapshot_(snapshot), - names_(snapshot_->profiler()->names()), - heap_object_map_(snapshot_->profiler()->heap_object_map()), + names_(profiler->names()), + heap_object_map_(profiler->heap_object_map()), progress_(progress), filler_(NULL), - global_object_name_resolver_(resolver) { -} - + global_object_name_resolver_(resolver) {} V8HeapExplorer::~V8HeapExplorer() { } @@ -893,13 +890,13 @@ HeapEntry* V8HeapExplorer::AddEntry(Address address, return snapshot_->AddEntry(type, name, object_id, size, trace_node_id); } - -class SnapshotFiller { +class CDTSnapshotFiller : public SnapshotFiller { public: - explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries) + explicit CDTSnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries) : snapshot_(snapshot), names_(snapshot->profiler()->names()), - entries_(entries) { } + entries_(entries) {} + virtual ~CDTSnapshotFiller() {} HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) { HeapEntry* entry = allocator->AllocateEntry(ptr); entries_->Pair(ptr, entry->index()); @@ -1805,11 +1802,15 @@ bool V8HeapExplorer::IterateAndExtractReferences( // Make sure builtin code objects get their builtin tags // first. Otherwise a particular JSFunction object could set // its custom name to a generic builtin. - RootsReferencesExtractor extractor(heap_); - heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG); - extractor.SetCollectingAllReferences(); - heap_->IterateRoots(&extractor, VISIT_ALL); - extractor.FillReferences(this); + // TODO(amalyshe): this condition should be refactored for catching + // root extractor + if (snapshot_) { + RootsReferencesExtractor extractor(heap_); + heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG); + extractor.SetCollectingAllReferences(); + heap_->IterateRoots(&extractor, VISIT_ALL); + extractor.FillReferences(this); + } // We have to do two passes as sometimes FixedArrays are used // to weakly hold their items, and it's impossible to distinguish @@ -2063,10 +2064,11 @@ void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj, void V8HeapExplorer::SetRootGcRootsReference() { - filler_->SetIndexedAutoIndexReference( - HeapGraphEdge::kElement, - snapshot_->root()->index(), - snapshot_->gc_roots()); + if (snapshot_) { + filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement, + snapshot_->root()->index(), + snapshot_->gc_roots()); + } } @@ -2081,10 +2083,11 @@ void V8HeapExplorer::SetUserGlobalReference(Object* child_obj) { void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) { - filler_->SetIndexedAutoIndexReference( - HeapGraphEdge::kElement, - snapshot_->gc_roots()->index(), - snapshot_->gc_subroot(tag)); + if (snapshot_) { + filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement, + snapshot_->gc_roots()->index(), + snapshot_->gc_subroot(tag)); + } } @@ -2240,14 +2243,12 @@ class GlobalHandlesExtractor : public ObjectVisitor { class BasicHeapEntriesAllocator : public HeapEntriesAllocator { public: - BasicHeapEntriesAllocator( - HeapSnapshot* snapshot, - HeapEntry::Type entries_type) - : snapshot_(snapshot), - names_(snapshot_->profiler()->names()), - heap_object_map_(snapshot_->profiler()->heap_object_map()), - entries_type_(entries_type) { - } + BasicHeapEntriesAllocator(HeapProfiler* profiler, HeapSnapshot* snapshot, + HeapEntry::Type entries_type) + : snapshot_(snapshot), + names_(profiler->names()), + heap_object_map_(profiler->heap_object_map()), + entries_type_(entries_type) {} virtual HeapEntry* AllocateEntry(HeapThing ptr); private: HeapSnapshot* snapshot_; @@ -2273,21 +2274,20 @@ HeapEntry* BasicHeapEntriesAllocator::AllocateEntry(HeapThing ptr) { 0); } - NativeObjectsExplorer::NativeObjectsExplorer( - HeapSnapshot* snapshot, + HeapProfiler* profiler, HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress) - : isolate_(snapshot->profiler()->heap_object_map()->heap()->isolate()), + : isolate_(profiler->heap_object_map()->heap()->isolate()), snapshot_(snapshot), - names_(snapshot_->profiler()->names()), + names_(profiler->names()), embedder_queried_(false), objects_by_info_(RetainedInfosMatch), native_groups_(StringsMatch), filler_(NULL) { synthetic_entries_allocator_ = - new BasicHeapEntriesAllocator(snapshot, HeapEntry::kSynthetic); + new BasicHeapEntriesAllocator(profiler, snapshot, HeapEntry::kSynthetic); native_entries_allocator_ = - new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative); + new BasicHeapEntriesAllocator(profiler, snapshot, HeapEntry::kNative); } @@ -2515,19 +2515,17 @@ void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) { GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p)); } - HeapSnapshotGenerator::HeapSnapshotGenerator( - HeapSnapshot* snapshot, + HeapProfiler* profiler, HeapSnapshot* snapshot, v8::ActivityControl* control, - v8::HeapProfiler::ObjectNameResolver* resolver, - Heap* heap) + v8::HeapProfiler::ObjectNameResolver* resolver, Heap* heap, + SnapshotFiller* filler) : snapshot_(snapshot), control_(control), - v8_heap_explorer_(snapshot_, this, resolver), - dom_explorer_(snapshot_, this), - heap_(heap) { -} - + v8_heap_explorer_(profiler, snapshot_, this, resolver), + dom_explorer_(profiler, snapshot_, this), + heap_(heap), + filler_(filler) {} bool HeapSnapshotGenerator::GenerateSnapshot() { v8_heap_explorer_.TagGlobalObjects(); @@ -2558,12 +2556,16 @@ bool HeapSnapshotGenerator::GenerateSnapshot() { } #endif - snapshot_->AddSyntheticRootEntries(); + if (snapshot_) { + snapshot_->AddSyntheticRootEntries(); + } if (!FillReferences()) return false; - snapshot_->FillChildren(); - snapshot_->RememberLastJSObjectId(); + if (snapshot_) { + snapshot_->FillChildren(); + snapshot_->RememberLastJSObjectId(); + } progress_counter_ = progress_total_; if (!ProgressReport(true)) return false; @@ -2599,9 +2601,16 @@ void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { bool HeapSnapshotGenerator::FillReferences() { - SnapshotFiller filler(snapshot_, &entries_); - return v8_heap_explorer_.IterateAndExtractReferences(&filler) - && dom_explorer_.IterateAndExtractReferences(&filler); + if (!filler_) { + CDTSnapshotFiller filler(snapshot_, &entries_); + return v8_heap_explorer_.IterateAndExtractReferences(&filler) && + dom_explorer_.IterateAndExtractReferences(&filler); + } else { + // TODO(amalyshe): finally this need to be returned back when XDK heap + // profiler supports toot extractor + // v8_heap_explorer_.AddRootEntries(filler_); + return v8_heap_explorer_.IterateAndExtractReferences(filler_); + } } diff --git a/src/profiler/heap-snapshot-generator.h b/src/profiler/heap-snapshot-generator.h index 857f2401bf0..d440952ff3f 100644 --- a/src/profiler/heap-snapshot-generator.h +++ b/src/profiler/heap-snapshot-generator.h @@ -14,6 +14,7 @@ namespace v8 { namespace internal { class AllocationTracker; +class XDKAllocationTracker; class AllocationTraceNode; class HeapEntry; class HeapIterator; @@ -95,11 +96,8 @@ class HeapEntry BASE_EMBEDDED { static const int kNoEntry; HeapEntry() { } - HeapEntry(HeapSnapshot* snapshot, - Type type, - const char* name, - SnapshotObjectId id, - size_t self_size, + HeapEntry(HeapSnapshot* snapshot, const List* entries, Type type, + const char* name, SnapshotObjectId id, size_t self_size, unsigned trace_node_id); HeapSnapshot* snapshot() { return snapshot_; } @@ -136,6 +134,7 @@ class HeapEntry BASE_EMBEDDED { int children_index_; size_t self_size_; HeapSnapshot* snapshot_; + const List* entries_; const char* name_; SnapshotObjectId id_; // id of allocation stack trace top node @@ -329,11 +328,31 @@ class SnapshottingProgressReportingInterface { virtual bool ProgressReport(bool force) = 0; }; +class SnapshotFiller { + public: + virtual ~SnapshotFiller() {} + + virtual HeapEntry* AddEntry(HeapThing ptr, + HeapEntriesAllocator* allocator) = 0; + virtual HeapEntry* FindEntry(HeapThing ptr) = 0; + virtual HeapEntry* FindOrAddEntry(HeapThing ptr, + HeapEntriesAllocator* allocator) = 0; + virtual void SetIndexedReference(HeapGraphEdge::Type type, int parent, + int index, HeapEntry* child_entry) = 0; + virtual void SetIndexedAutoIndexReference(HeapGraphEdge::Type type, + int parent, + HeapEntry* child_entry) = 0; + virtual void SetNamedReference(HeapGraphEdge::Type type, int parent, + const char* reference_name, + HeapEntry* child_entry) = 0; + virtual void SetNamedAutoIndexReference(HeapGraphEdge::Type type, int parent, + HeapEntry* child_entry) = 0; +}; // An implementation of V8 heap graph extractor. class V8HeapExplorer : public HeapEntriesAllocator { public: - V8HeapExplorer(HeapSnapshot* snapshot, + V8HeapExplorer(HeapProfiler* profiler, HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress, v8::HeapProfiler::ObjectNameResolver* resolver); virtual ~V8HeapExplorer(); @@ -484,7 +503,7 @@ class NativeGroupRetainedObjectInfo; // An implementation of retained native objects extractor. class NativeObjectsExplorer { public: - NativeObjectsExplorer(HeapSnapshot* snapshot, + NativeObjectsExplorer(HeapProfiler* profiler, HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress); virtual ~NativeObjectsExplorer(); int EstimateObjectsCount(); @@ -539,10 +558,10 @@ class NativeObjectsExplorer { class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface { public: - HeapSnapshotGenerator(HeapSnapshot* snapshot, + HeapSnapshotGenerator(HeapProfiler* profiler, HeapSnapshot* snapshot, v8::ActivityControl* control, v8::HeapProfiler::ObjectNameResolver* resolver, - Heap* heap); + Heap* heap, SnapshotFiller* filler = NULL); bool GenerateSnapshot(); private: @@ -561,6 +580,7 @@ class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface { int progress_counter_; int progress_total_; Heap* heap_; + SnapshotFiller* filler_; DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator); }; diff --git a/src/profiler/profile-generator-inl.h b/src/profiler/profile-generator-inl.h index 85edce2663b..1634c3f8267 100644 --- a/src/profiler/profile-generator-inl.h +++ b/src/profiler/profile-generator-inl.h @@ -29,16 +29,15 @@ CodeEntry::CodeEntry(Logger::LogEventsAndTags tag, const char* name, line_info_(line_info), instruction_start_(instruction_start) {} - -ProfileNode::ProfileNode(ProfileTree* tree, CodeEntry* entry) +ProfileNode::ProfileNode(ProfileTree* tree, CodeEntry* entry, int src_line) : tree_(tree), entry_(entry), + src_line_(src_line), self_ticks_(0), children_(CodeEntriesMatch), id_(tree->next_node_id()), line_ticks_(LineTickMatch) {} - inline unsigned ProfileNode::function_id() const { return tree_->GetFunctionId(this); } diff --git a/src/profiler/profile-generator.cc b/src/profiler/profile-generator.cc index abcd9e5d888..a87fd4a3a41 100644 --- a/src/profiler/profile-generator.cc +++ b/src/profiler/profile-generator.cc @@ -166,19 +166,38 @@ void ProfileNode::CollectDeoptInfo(CodeEntry* entry) { ProfileNode* ProfileNode::FindChild(CodeEntry* entry) { - HashMap::Entry* map_entry = children_.Lookup(entry, CodeEntryHash(entry)); + if (entry) { + StackEntry tmp_stackentry(entry, entry->line_number()); + return ProfileNode::FindChild(&tmp_stackentry); + } + return NULL; +} + +ProfileNode* ProfileNode::FindChild(StackEntry* stackentry) { + HashMap::Entry* map_entry = + children_.Lookup(stackentry->entry, + CodeEntryHash(stackentry->entry) ^ stackentry->srcLine); return map_entry != NULL ? reinterpret_cast(map_entry->value) : NULL; } ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) { - HashMap::Entry* map_entry = - children_.LookupOrInsert(entry, CodeEntryHash(entry)); + if (entry) { + StackEntry tmp_stackentry(entry, entry->line_number()); + return ProfileNode::FindOrAddChild(&tmp_stackentry); + } + return NULL; +} + +ProfileNode* ProfileNode::FindOrAddChild(StackEntry* stackentry) { + HashMap::Entry* map_entry = children_.LookupOrInsert( + stackentry->entry, + CodeEntryHash(stackentry->entry) ^ stackentry->srcLine); ProfileNode* node = reinterpret_cast(map_entry->value); if (node == NULL) { // New node added. - node = new ProfileNode(tree_, entry); + node = new ProfileNode(tree_, stackentry->entry, stackentry->srcLine); map_entry->value = node; children_list_.Add(node); } @@ -291,13 +310,27 @@ unsigned ProfileTree::GetFunctionId(const ProfileNode* node) { } ProfileNode* ProfileTree::AddPathFromEnd(const std::vector& path, + int src_line) { + if (path.empty()) return root_; + std::vector stackentrys(path.size()); + auto c = stackentrys.begin(); + for (auto e = path.begin(); e != path.end(); e++, c++) { + if (*e == NULL) continue; + c->entry = *e; + c->srcLine = (*e)->line_number(); + } + return ProfileTree::AddPathFromEnd(stackentrys, src_line); +} + +ProfileNode* ProfileTree::AddPathFromEnd(const std::vector& path, int src_line, bool update_stats) { ProfileNode* node = root_; CodeEntry* last_entry = NULL; - for (auto it = path.rbegin(); it != path.rend(); ++it) { - if (*it == NULL) continue; - last_entry = *it; - node = node->FindOrAddChild(*it); + for (auto stackentry = path.rbegin(); + stackentry != path.rend(); ++stackentry) { + if (stackentry->entry == NULL) continue; + node = node->FindOrAddChild(const_cast(&(*stackentry))); + last_entry = node->entry(); } if (last_entry && last_entry->has_deopt_info()) { node->CollectDeoptInfo(last_entry); @@ -369,7 +402,7 @@ CpuProfile::CpuProfile(Isolate* isolate, const char* title, bool record_samples) top_down_(isolate) {} void CpuProfile::AddPath(base::TimeTicks timestamp, - const std::vector& path, int src_line, + const std::vector& path, int src_line, bool update_stats) { ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path, src_line, update_stats); @@ -538,8 +571,8 @@ void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) { } void CpuProfilesCollection::AddPathToCurrentProfiles( - base::TimeTicks timestamp, const std::vector& path, - int src_line, bool update_stats) { + base::TimeTicks timestamp, const std::vector& path, int src_line, + bool update_stats) { // As starting / stopping profiles is rare relatively to this // method, we don't bother minimizing the duration of lock holding, // e.g. copying contents of the list to a local vector. @@ -589,10 +622,11 @@ ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles) void ProfileGenerator::RecordTickSample(const TickSample& sample) { - std::vector entries; + std::vector stackentrys; // Conservatively reserve space for stack frames + pc + function + vm-state. // There could in fact be more of them because of inlined entries. - entries.reserve(sample.frames_count + 3); + stackentrys.reserve(sample.frames_count + 3); + auto stackentry = stackentrys.begin(); // The ProfileNode knows nothing about all versions of generated code for // the same JS function. The line number information associated with @@ -608,7 +642,8 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { // Don't use PC when in external callback code, as it can point // inside callback's code, and we will erroneously report // that a callback calls itself. - entries.push_back(code_map_.FindEntry(sample.external_callback_entry)); + stackentry->entry = code_map_.FindEntry(sample.external_callback_entry); + stackentry++; } else { CodeEntry* pc_entry = code_map_.FindEntry(sample.pc); // If there is no pc_entry we're likely in native code. @@ -626,12 +661,14 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { if (pc_entry) { int pc_offset = static_cast(sample.pc - pc_entry->instruction_start()); - src_line = pc_entry->GetSourceLine(pc_offset); - if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) { - src_line = pc_entry->line_number(); + stackentry->entry = pc_entry; + stackentry->srcLine = pc_entry->GetSourceLine(pc_offset); + if (stackentry->srcLine == v8::CpuProfileNode::kNoLineNumberInfo) { + stackentry->srcLine = pc_entry->line_number(); } + src_line = stackentry->srcLine; src_line_not_found = false; - entries.push_back(pc_entry); + stackentry++; if (pc_entry->builtin_id() == Builtins::kFunctionPrototypeApply || pc_entry->builtin_id() == Builtins::kFunctionPrototypeCall) { @@ -642,7 +679,8 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { // former case we don't so we simply replace the frame with // 'unresolved' entry. if (sample.top_frame_type == StackFrame::JAVA_SCRIPT) { - entries.push_back(unresolved_entry_); + stackentry->entry = unresolved_entry_; + stackentry++; } } } @@ -651,47 +689,44 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { for (const Address *stack_pos = sample.stack, *stack_end = stack_pos + sample.frames_count; stack_pos != stack_end; ++stack_pos) { - CodeEntry* entry = code_map_.FindEntry(*stack_pos); - - if (entry) { - // Find out if the entry has an inlining stack associated. - int pc_offset = - static_cast(*stack_pos - entry->instruction_start()); - const std::vector* inline_stack = - entry->GetInlineStack(pc_offset); - if (inline_stack) { - entries.insert(entries.end(), inline_stack->rbegin(), - inline_stack->rend()); + stackentry->entry = code_map_.FindEntry(*stack_pos); + + // Skip unresolved frames (e.g. internal frame) and get source lines for + // each entry. Save source line (src_line) of the first JS caller + if (stackentry->entry) { + int pc_offset = static_cast( + *stack_pos - stackentry->entry->instruction_start()); + stackentry->srcLine = stackentry->entry->GetSourceLine(pc_offset); + if (stackentry->srcLine == v8::CpuProfileNode::kNoLineNumberInfo) { + stackentry->srcLine = stackentry->entry->line_number(); } - // Skip unresolved frames (e.g. internal frame) and get source line of - // the first JS caller. + if (src_line_not_found) { - src_line = entry->GetSourceLine(pc_offset); - if (src_line == v8::CpuProfileNode::kNoLineNumberInfo) { - src_line = entry->line_number(); - } + src_line = stackentry->srcLine; src_line_not_found = false; } } - entries.push_back(entry); + + stackentry++; } } if (FLAG_prof_browser_mode) { bool no_symbolized_entries = true; - for (auto e : entries) { - if (e != NULL) { + for (auto e = stackentrys.begin(); e != stackentry; ++e) { + if ((*e).entry != NULL) { no_symbolized_entries = false; break; } } // If no frames were symbolized, put the VM state entry in. if (no_symbolized_entries) { - entries.push_back(EntryForVMState(sample.state)); + stackentry->entry = EntryForVMState(sample.state); + stackentry++; } } - profiles_->AddPathToCurrentProfiles(sample.timestamp, entries, src_line, + profiles_->AddPathToCurrentProfiles(sample.timestamp, stackentrys, src_line, sample.update_stats); } diff --git a/src/profiler/profile-generator.h b/src/profiler/profile-generator.h index 194b4909299..ebf797feae0 100644 --- a/src/profiler/profile-generator.h +++ b/src/profiler/profile-generator.h @@ -136,20 +136,36 @@ class CodeEntry { DISALLOW_COPY_AND_ASSIGN(CodeEntry); }; +struct StackEntry { + public: + explicit StackEntry(CodeEntry* e = NULL, + int line = v8::CpuProfileNode::kNoLineNumberInfo) + : entry(e), srcLine(line) {} + + CodeEntry* entry; + int srcLine; +}; class ProfileTree; class ProfileNode { public: - inline ProfileNode(ProfileTree* tree, CodeEntry* entry); + inline ProfileNode(ProfileTree* tree, CodeEntry* entry, + int src_line = v8::CpuProfileNode::kNoLineNumberInfo); + ProfileNode* FindChild(StackEntry* stackentry); + ProfileNode* FindOrAddChild(StackEntry* stackentry); + + // Supporting old functions ProfileNode* FindChild(CodeEntry* entry); ProfileNode* FindOrAddChild(CodeEntry* entry); + void IncrementSelfTicks() { ++self_ticks_; } void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; } void IncrementLineTicks(int src_line); CodeEntry* entry() const { return entry_; } + int src_line() const { return src_line_; } unsigned self_ticks() const { return self_ticks_; } const List* children() const { return &children_list_; } unsigned id() const { return id_; } @@ -177,6 +193,7 @@ class ProfileNode { ProfileTree* tree_; CodeEntry* entry_; + int src_line_; unsigned self_ticks_; // Mapping from CodeEntry* to ProfileNode* HashMap children_; @@ -196,9 +213,15 @@ class ProfileTree { ~ProfileTree(); ProfileNode* AddPathFromEnd( - const std::vector& path, + const std::vector& path, int src_line = v8::CpuProfileNode::kNoLineNumberInfo, bool update_stats = true); + + // Support old function + ProfileNode* AddPathFromEnd( + const std::vector& path, + int src_line = v8::CpuProfileNode::kNoLineNumberInfo); + ProfileNode* root() const { return root_; } unsigned next_node_id() { return next_node_id_++; } unsigned GetFunctionId(const ProfileNode* node); @@ -230,7 +253,7 @@ class CpuProfile { CpuProfile(Isolate* isolate, const char* title, bool record_samples); // Add pc -> ... -> main() call path to the profile. - void AddPath(base::TimeTicks timestamp, const std::vector& path, + void AddPath(base::TimeTicks timestamp, const std::vector& path, int src_line, bool update_stats); void CalculateTotalTicksAndSamplingRate(); @@ -339,9 +362,8 @@ class CpuProfilesCollection { // Called from profile generator thread. void AddPathToCurrentProfiles(base::TimeTicks timestamp, - const std::vector& path, - int src_line, bool update_stats); - + const std::vector& path, int src_line, + bool update_stats); // Limits the number of profiles that can be simultaneously collected. static const int kMaxSimultaneousProfiles = 100; diff --git a/src/xdk-allocation.cc b/src/xdk-allocation.cc new file mode 100644 index 00000000000..e121d61a767 --- /dev/null +++ b/src/xdk-allocation.cc @@ -0,0 +1,526 @@ +// 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 +#include + +#include "src/v8.h" + +#include "src/xdk-allocation.h" + +#include "src/frames-inl.h" +#include "src/xdk-utils.h" + +#include "src/profiler/heap-snapshot-generator-inl.h" + +namespace v8 { +namespace internal { + +static List* g_la_list = NULL; +void XDKGCPrologueCallback(v8::Isolate*, GCType, GCCallbackFlags) { + if (g_la_list) { + g_la_list->Clear(); + } +} + +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), + a_treshold_(50), + a_current_(0) { + 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_, ids_->heap()); + + List stack_ooc; + stack_ooc.Add(symbols_->registerSymInfo(1, "OutOfContext", "NoSource", 0, 0)); + outOfContextFrame_ = collectedStacks_->registerStack(stack_ooc); + + List 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; + + g_la_list = &this->latest_allocations_; + v8::Isolate::GCCallback e = (v8::Isolate::GCCallback)&XDKGCPrologueCallback; + ids_->heap()->AddGCPrologueCallback(e, kGCTypeAll, false); +} + +XDKAllocationTracker::~XDKAllocationTracker() { + delete collectedStacks_; + delete classNames_; + delete aggregated_chunks_; + delete runtime_info_; + delete symbols_; + delete references_; + g_la_list = NULL; +} + +// 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(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. + heap->CreateFillerObjectAt(addr, size, ClearRecordedSlots::kNo); + + Isolate* isolate = heap->isolate(); + StackTraceFrameIterator it(isolate); + List 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; + + // init the type info for previous allocated object + if (latest_allocations_.length() == a_treshold_) { + // resolve next allocation to process + InfoToResolve& allocation = latest_allocations_.at(a_current_); + InitClassName(allocation.address_, allocation.info_); + a_current_++; + if (a_current_ >= a_treshold_) { + a_current_ = 0; + } + } + + if (latest_allocations_.length() < a_treshold_) { + InfoToResolve allocation; + allocation.address_ = addr; + allocation.info_ = info; + latest_allocations_.Add(allocation); + } else { + unsigned allocation_to_update = + a_current_ ? a_current_ - 1 : a_treshold_ - 1; + InfoToResolve& allocation = latest_allocations_.at(allocation_to_update); + allocation.address_ = addr; + allocation.info_ = info; + } +} + +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 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::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* 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, + PostCollectedInfo* info) { + if (info->className_ == (unsigned)-1) { + info->className_ = classNames_->GetConstructorName(address, runtime_info_); + } + return info->className_; +} + +unsigned XDKAllocationTracker::InitClassName(Address address, unsigned ts, + unsigned size) { + 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; + } + return InitClassName(address, info); +} + +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(ptr); + if (object->IsJSFunction()) { + JSFunction* func = JSFunction::cast(object); + SharedFunctionInfo* shared = func->shared(); + const char* name = names_->GetName(String::cast(shared->name())); + return AddEntry(ptr, object, HeapEntry::kClosure, name); + } else if (object->IsJSBoundFunction()) { + return AddEntry(ptr, object, HeapEntry::kClosure, "native_bind"); + } 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()) { + 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_.LookupOrInsert(thing, Hash(thing)); + DCHECK(cache_entry->value == NULL); + int index = pEntry->index(); + cache_entry->value = reinterpret_cast(static_cast(index)); + + // TODO(amalyshe): it seems this storage might be optimized + HashMap::Entry* address_entry = index_to_address_.LookupOrInsert( + reinterpret_cast(index + 1), HashInt(index + 1)); + address_entry->value = reinterpret_cast(address); + + return pEntry; +} + +HeapEntry* XDKSnapshotFiller::FindEntry(HeapThing thing) { + HashMap::Entry* cache_entry = heap_entries_.Lookup(thing, Hash(thing)); + if (cache_entry == NULL) return NULL; + return &heap_entries_list_[static_cast( + reinterpret_cast(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(child_entry->index() + 1), + HashInt(child_entry->index() + 1)); + DCHECK(address_entry_child != NULL); + if (!address_entry_child) { + return; + } + + Address child_addr = reinterpret_cast
(address_entry_child->value); + + std::map* 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(parent + 1), HashInt(parent + 1)); + DCHECK(address_entry != NULL); + if (!address_entry) { + return; + } + HeapEntry* parent_entry = &(heap_entries_list_[parent]); + Address parent_addr = reinterpret_cast
(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* 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(parent + 1), HashInt(parent + 1)); + DCHECK(address_entry != NULL); + if (!address_entry) { + return; + } + HeapEntry* parent_entry = &(heap_entries_list_[parent]); + Address parent_addr = reinterpret_cast
(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(child_entry->index() + 1), + HashInt(child_entry->index() + 1)); + DCHECK(address_entry_child != NULL); + if (!address_entry_child) { + return; + } + Address child_addr = reinterpret_cast
(address_entry_child->value); + + (*individualReteiners)[child_addr].references_.insert(parent_ref_id); +} + +void XDKSnapshotFiller::SetNamedAutoIndexReference(HeapGraphEdge::Type type, + int parent, + HeapEntry* child_entry) {} + +} // namespace internal + +} // namespace v8 diff --git a/src/xdk-allocation.h b/src/xdk-allocation.h new file mode 100644 index 00000000000..17e8fdf9c47 --- /dev/null +++ b/src/xdk-allocation.h @@ -0,0 +1,178 @@ +// 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 +#include +#include "src/base/platform/time.h" + +#include "src/profiler/heap-snapshot-generator.h" + +namespace v8 { +namespace internal { + +class HeapObjectsMap; +class HeapEventXDK; +class ClassNames; +class ShadowStack; +class SymbolsStorage; +class AggregatedChunks; +class RuntimeInfo; +class References; +struct RefSet; +struct PostCollectedInfo; + +class XDKSnapshotFiller : public SnapshotFiller { + public: + explicit XDKSnapshotFiller(HeapObjectsMap* heap_object_map, + StringsStorage* names, + XDKAllocationTracker* allocation_tracker) + : names_(names), + 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_; + XDKAllocationTracker* allocation_tracker_; + HashMap heap_entries_; + HashMap index_to_address_; + + List 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(reinterpret_cast(thing)), + v8::internal::kZeroHashSeed); + } + static uint32_t HashInt(int key) { + return ComputeIntegerHash(key, v8::internal::kZeroHashSeed); + } +}; + +struct InfoToResolve { + Address address_; + PostCollectedInfo* info_; +}; + +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* 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); + unsigned InitClassName(Address address, PostCollectedInfo* info); + + 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 individualReteiners_; + + // here is a loop container which stores the elements not more than + // a_treshold_ and when the capacity is reduced we start + // 1. resolve the a_current_ object's types + // 2. store new allocated object to the a_current_ position + // increas a_current_ until a_treshold_. At the moment when it reach the + // a_treshold_, a_current_ is assigned to 0 + // It id required because some types are defined as a analysis of another + // object and the allocation sequence might be different. Sometimes dependent + // object is allocated first, sometimes parent object is allocated first. + // We cannot find type of latest element, all dependent objects must be + // created + List latest_allocations_; + int a_treshold_; + int a_current_; +}; + +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 internal +} // namespace v8 + +#endif // V8_XDK_ALLOCATION_H_ diff --git a/src/xdk-utils.cc b/src/xdk-utils.cc new file mode 100644 index 00000000000..653274f44d7 --- /dev/null +++ b/src/xdk-utils.cc @@ -0,0 +1,616 @@ +// 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/profiler/strings-storage.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(reinterpret_cast(addr)), + v8::internal::kZeroHashSeed); +} + +static uint32_t AddressHash(Address addr) { + return ComputeIntegerHash( + static_cast(reinterpret_cast(addr)), + v8::internal::kZeroHashSeed); +} + +ClassNames::ClassNames(StringsStorage* names, Heap* heap) + : counter_(0), char_to_idx_(AddressesMatch), names_(names), heap_(heap) { + id_native_bind_ = registerName(names->GetCopy("native_bind")); + id_conc_string_ = registerName(names->GetCopy("(concatenated string)")); + id_sliced_string_ = registerName(names->GetCopy("(sliced string)")); + id_string_ = registerName(names->GetCopy("String")); + id_symbol_ = registerName(names->GetCopy("(symbol)")); + id_code_ = registerName(names->GetCopy("(compiled code)")); + id_system_ncontext_ = + registerName(names->GetCopy("(system / NativeContext)")); + id_system_context_ = registerName(names->GetCopy("(system / Context)")); + id_array_ = registerName(names->GetCopy("(array)")); + id_number_ = registerName(names->GetCopy("(number)")); + id_system_ = registerName(names->GetCopy("(system)")); + id_shared_fi_ = registerName(names->GetCopy("(shared function info)")); + id_script_ = registerName(names->GetCopy("(script)")); + id_regexp_ = registerName(names->GetCopy("RegExp")); + id_function_bindings_ = registerName(names->GetCopy("(function bindings)")); + id_function_literals_ = registerName(names->GetCopy("(function literals)")); + id_objects_properties_ = registerName(names->GetCopy("(object properties)")); + id_objects_elements_ = registerName(names->GetCopy("(object elements)")); + id_shared_function_info_ = + registerName(names->GetCopy("(shared function info)")); + id_context_ = registerName(names->GetCopy("(context)")); + id_code_relocation_info_ = + registerName(names->GetCopy("(code relocation info)")); + id_code_deopt_data_ = registerName(names->GetCopy("(code deopt data)")); +} + +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 + if (!name) { + return -2; + } + + unsigned counter; + HashMap::Entry* entry = char_to_idx_.LookupOrInsert( + const_cast(name), CharAddressHash(const_cast(name))); + if (entry->value == NULL) { + counter = ++counter_; + entry->value = reinterpret_cast(counter); + } else { + counter = static_cast(reinterpret_cast(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(reinterpret_cast(p->value)) + << "," << reinterpret_cast(p->key) << std::endl; + } + + return serialized.str(); +} + +bool ClassNames::IsEssentialObject(Object* object) { + return object->IsHeapObject() && !object->IsOddball() && + object != heap_->empty_byte_array() && + object != heap_->empty_fixed_array() && + object != heap_->empty_descriptor_array() && + object != heap_->fixed_array_map() && object != heap_->cell_map() && + object != heap_->global_property_cell_map() && + object != heap_->shared_function_info_map() && + object != heap_->free_space_map() && + object != heap_->one_pointer_filler_map() && + object != heap_->two_pointer_filler_map(); +} + +void ClassNames::registerNameForDependent(HeapObject* object, + RuntimeInfo* runtime_info, + unsigned id) { + if (object && IsEssentialObject(object)) { + PostCollectedInfo* info = + runtime_info->FindPostCollectedInfo(object->address()); + // TODO(amalyshe) here we are loosing some information because + // *some* of the objects are allocated without notification of explicit + // allocation and no XDKAllocationTracker::OnAlloc will be called for them. + // But these objects exist in the heap and can be achieved if we iterate + // through the heap. We cannot add here them explicitly because if + // XDKAllocationTracker::OnAlloc is called for this address, it will remove + // all useful information about type and even report wrong data because + // during removal these objects will be added to statistic and will be + // counted twice + if (info) { + info->className_ = id; + } + } +} + +unsigned ClassNames::GetConstructorName(Address address, + RuntimeInfo* runtime_info) { + unsigned id = (unsigned)-2; + HeapObject* heap_object = HeapObject::FromAddress(address); + + // support of all type, if some are built-in, we add hard-coded values + if (heap_object->IsJSObject()) { + JSObject* object = JSObject::cast(heap_object); + if (object->IsJSBoundFunction()) { + JSBoundFunction* js_fun = JSBoundFunction::cast(object); + registerNameForDependent(js_fun, runtime_info, id_function_bindings_); + } else if (object->IsJSFunction()) { + Heap* heap = object->GetHeap(); + const char* name = names_->GetName(String::cast(heap->closure_string())); + id = registerName(name); + JSFunction* js_fun = JSFunction::cast(object); + SharedFunctionInfo* shared_info = js_fun->shared(); + registerNameForDependent(js_fun->literals(), runtime_info, + id_function_literals_); + registerNameForDependent(shared_info, runtime_info, + id_shared_function_info_); + registerNameForDependent(js_fun->context(), runtime_info, id_context_); + } else { + DisallowHeapAllocation no_gc; + Isolate* isolate = heap_->isolate(); + HandleScope scope(isolate); + String* str_class_name = + *JSReceiver::GetConstructorName(handle(object, isolate)); + const char* name = names_->GetName(str_class_name); + id = registerName(name); + } + HeapObject* prop = reinterpret_cast(object->properties()); + registerNameForDependent(prop, runtime_info, id_objects_properties_); + HeapObject* elements = reinterpret_cast(object->elements()); + registerNameForDependent(elements, runtime_info, id_objects_elements_); + } else if (heap_object->IsJSRegExp()) { + id = id_regexp_; + } else if (heap_object->IsString()) { + String* string = String::cast(heap_object); + if (string->IsConsString()) + id = id_conc_string_; + else if (string->IsSlicedString()) + id = id_sliced_string_; + else + id = id_string_; + } else if (heap_object->IsSymbol()) { + id = id_symbol_; + } else if (heap_object->IsCode()) { + Code* code = Code::cast(heap_object); + registerNameForDependent(code->relocation_info(), runtime_info, + id_code_relocation_info_); + registerNameForDependent(code->deoptimization_data(), runtime_info, + id_code_deopt_data_); + id = id_code_; + } else if (heap_object->IsSharedFunctionInfo()) { + id = id_shared_fi_; + } else if (heap_object->IsScript()) { + id = id_script_; + } else if (heap_object->IsNativeContext()) { + id = id_system_ncontext_; + } else if (heap_object->IsContext()) { + id = id_system_context_; + } else if (heap_object->IsFixedArray() || heap_object->IsFixedDoubleArray() || + heap_object->IsByteArray()) { + id = id_array_; + } else if (heap_object->IsHeapNumber()) { + id = id_number_; + } else { + id = id_system_; + } + + return id; +} + +// ----------------------------------------------------------------------------- +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::iterator eit = allNodes_.begin(); + while (eit != allNodes_.end()) { + delete eit->second; + eit++; + } +} + +unsigned ShadowStack::registerStack(const List& 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::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::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(key1); + SymInfoKey* key_c2 = reinterpret_cast(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(p->value); + delete reinterpret_cast(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_.LookupOrInsert(reserved_key_, SymInfoHash(*reserved_key_)); + if (entry->value) { + return reinterpret_cast(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(p->value); + SymInfoKey* k = reinterpret_cast(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_.LookupOrInsert( + reinterpret_cast(pc), AddressHash(pc)); + if (sym_entry->value == NULL || + (reinterpret_cast(sym_entry->value)->function_ != + reinterpret_cast(frame->function()))) { + if (sym_entry->value) { + delete reinterpret_cast(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 offset = + static_cast(pc - frame->LookupCode()->instruction_start()); + int source_pos = code->SourcePosition(offset); + Object* maybe_script = shared->script(); + if (maybe_script && maybe_script->IsScript()) { + Handle