// Copyright 2013 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#include "heap-snapshot-generator-inl.h"
-
-#include "allocation-tracker.h"
-#include "code-stubs.h"
-#include "heap-profiler.h"
-#include "debug.h"
-#include "types.h"
+// 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/heap-snapshot-generator-inl.h"
+
+#include "src/allocation-tracker.h"
+#include "src/code-stubs.h"
+#include "src/conversions.h"
+#include "src/debug.h"
+#include "src/heap-profiler.h"
+#include "src/types.h"
namespace v8 {
namespace internal {
from_index_(from),
to_index_(to),
name_(name) {
- ASSERT(type == kContextVariable
+ DCHECK(type == kContextVariable
|| type == kProperty
|| type == kInternal
|| type == kShortcut
from_index_(from),
to_index_(to),
index_(index) {
- ASSERT(type == kElement || type == kHidden);
+ DCHECK(type == kElement || type == kHidden);
}
Type type,
const char* name,
SnapshotObjectId id,
- int self_size)
+ size_t self_size,
+ unsigned trace_node_id)
: type_(type),
children_count_(0),
children_index_(-1),
self_size_(self_size),
- id_(id),
snapshot_(snapshot),
- name_(name) { }
+ name_(name),
+ id_(id),
+ trace_node_id_(trace_node_id) { }
void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
void HeapEntry::Print(
const char* prefix, const char* edge_name, int max_depth, int indent) {
- STATIC_CHECK(sizeof(unsigned) == sizeof(id()));
- OS::Print("%6d @%6u %*c %s%s: ",
- self_size(), id(), indent, ' ', prefix, edge_name);
+ STATIC_ASSERT(sizeof(unsigned) == sizeof(id()));
+ base::OS::Print("%6" V8PRIuPTR " @%6u %*c %s%s: ", self_size(), id(), indent,
+ ' ', prefix, edge_name);
if (type() != kString) {
- OS::Print("%s %.40s\n", TypeAsString(), name_);
+ base::OS::Print("%s %.40s\n", TypeAsString(), name_);
} else {
- OS::Print("\"");
+ base::OS::Print("\"");
const char* c = name_;
while (*c && (c - name_) <= 40) {
if (*c != '\n')
- OS::Print("%c", *c);
+ base::OS::Print("%c", *c);
else
- OS::Print("\\n");
+ base::OS::Print("\\n");
++c;
}
- OS::Print("\"\n");
+ base::OS::Print("\"\n");
}
if (--max_depth == 0) return;
Vector<HeapGraphEdge*> ch = children();
edge_name = edge.name();
break;
case HeapGraphEdge::kElement:
- OS::SNPrintF(index, "%d", edge.index());
+ SNPrintF(index, "%d", edge.index());
break;
case HeapGraphEdge::kInternal:
edge_prefix = "$";
break;
case HeapGraphEdge::kHidden:
edge_prefix = "$";
- OS::SNPrintF(index, "%d", edge.index());
+ SNPrintF(index, "%d", edge.index());
break;
case HeapGraphEdge::kShortcut:
edge_prefix = "^";
edge_name = edge.name();
break;
default:
- OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
+ SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
}
edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
}
case kSynthetic: return "/synthetic/";
case kConsString: return "/concatenated string/";
case kSlicedString: return "/sliced string/";
+ case kSymbol: return "/symbol/";
default: return "???";
}
}
template <> struct SnapshotSizeConstants<4> {
static const int kExpectedHeapGraphEdgeSize = 12;
- static const int kExpectedHeapEntrySize = 24;
+ static const int kExpectedHeapEntrySize = 28;
};
template <> struct SnapshotSizeConstants<8> {
static const int kExpectedHeapGraphEdgeSize = 24;
- static const int kExpectedHeapEntrySize = 32;
+ static const int kExpectedHeapEntrySize = 40;
};
} // namespace
uid_(uid),
root_index_(HeapEntry::kNoEntry),
gc_roots_index_(HeapEntry::kNoEntry),
- natives_root_index_(HeapEntry::kNoEntry),
max_snapshot_js_object_id_(0) {
- STATIC_CHECK(
+ STATIC_ASSERT(
sizeof(HeapGraphEdge) ==
SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize);
- STATIC_CHECK(
+ STATIC_ASSERT(
sizeof(HeapEntry) ==
SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize);
USE(SnapshotSizeConstants<4>::kExpectedHeapGraphEdgeSize);
}
+void HeapSnapshot::AddSyntheticRootEntries() {
+ AddRootEntry();
+ AddGcRootsEntry();
+ SnapshotObjectId id = HeapObjectsMap::kGcRootsFirstSubrootId;
+ for (int tag = 0; tag < VisitorSynchronization::kNumberOfSyncTags; tag++) {
+ AddGcSubrootEntry(tag, id);
+ id += HeapObjectsMap::kObjectIdStep;
+ }
+ DCHECK(HeapObjectsMap::kFirstAvailableObjectId == id);
+}
+
+
HeapEntry* HeapSnapshot::AddRootEntry() {
- ASSERT(root_index_ == HeapEntry::kNoEntry);
- ASSERT(entries_.is_empty()); // Root entry must be the first one.
+ DCHECK(root_index_ == HeapEntry::kNoEntry);
+ DCHECK(entries_.is_empty()); // Root entry must be the first one.
HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
"",
HeapObjectsMap::kInternalRootObjectId,
+ 0,
0);
root_index_ = entry->index();
- ASSERT(root_index_ == 0);
+ DCHECK(root_index_ == 0);
return entry;
}
HeapEntry* HeapSnapshot::AddGcRootsEntry() {
- ASSERT(gc_roots_index_ == HeapEntry::kNoEntry);
+ DCHECK(gc_roots_index_ == HeapEntry::kNoEntry);
HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
"(GC roots)",
HeapObjectsMap::kGcRootsObjectId,
+ 0,
0);
gc_roots_index_ = entry->index();
return entry;
}
-HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag) {
- ASSERT(gc_subroot_indexes_[tag] == HeapEntry::kNoEntry);
- ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
- HeapEntry* entry = AddEntry(
- HeapEntry::kSynthetic,
- VisitorSynchronization::kTagNames[tag],
- HeapObjectsMap::GetNthGcSubrootId(tag),
- 0);
+HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag, SnapshotObjectId id) {
+ DCHECK(gc_subroot_indexes_[tag] == HeapEntry::kNoEntry);
+ DCHECK(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
+ HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
+ VisitorSynchronization::kTagNames[tag], id, 0, 0);
gc_subroot_indexes_[tag] = entry->index();
return entry;
}
HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
const char* name,
SnapshotObjectId id,
- int size) {
- HeapEntry entry(this, type, name, id, size);
+ size_t size,
+ unsigned trace_node_id) {
+ HeapEntry entry(this, type, name, id, size, trace_node_id);
entries_.Add(entry);
return &entries_.last();
}
void HeapSnapshot::FillChildren() {
- ASSERT(children().is_empty());
+ DCHECK(children().is_empty());
children().Allocate(edges().length());
int children_index = 0;
for (int i = 0; i < entries().length(); ++i) {
HeapEntry* entry = &entries()[i];
children_index = entry->set_children_index(children_index);
}
- ASSERT(edges().length() == children_index);
+ DCHECK(edges().length() == children_index);
for (int i = 0; i < edges().length(); ++i) {
HeapGraphEdge* edge = &edges()[i];
edge->ReplaceToIndexWithEntry(this);
}
-void HeapObjectsMap::MoveObject(Address from, Address to, int object_size) {
- ASSERT(to != NULL);
- ASSERT(from != NULL);
- if (from == to) return;
+bool HeapObjectsMap::MoveObject(Address from, Address to, int object_size) {
+ DCHECK(to != NULL);
+ DCHECK(from != NULL);
+ if (from == to) return false;
void* from_value = entries_map_.Remove(from, ComputePointerHash(from));
if (from_value == NULL) {
// It may occur that some untracked object moves to an address X and there
entries_.at(from_entry_info_index).size = object_size;
to_entry->value = from_value;
}
+ return from_value != NULL;
}
if (entry == NULL) return 0;
int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
EntryInfo& entry_info = entries_.at(entry_index);
- ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
+ DCHECK(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
return entry_info.id;
}
SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
unsigned int size,
bool accessed) {
- ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
+ DCHECK(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
HashMap::Entry* entry = entries_map_.Lookup(addr, ComputePointerHash(addr),
true);
if (entry->value != NULL) {
SnapshotObjectId id = next_id_;
next_id_ += kObjectIdStep;
entries_.Add(EntryInfo(id, addr, size, accessed));
- ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
+ DCHECK(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
return id;
}
time_intervals_.Add(TimeInterval(next_id_));
int prefered_chunk_size = stream->GetChunkSize();
List<v8::HeapStatsUpdate> stats_buffer;
- ASSERT(!entries_.is_empty());
+ DCHECK(!entries_.is_empty());
EntryInfo* entry_info = &entries_.first();
EntryInfo* end_entry_info = &entries_.last() + 1;
for (int time_interval_index = 0;
}
}
}
- ASSERT(entry_info == end_entry_info);
+ DCHECK(entry_info == end_entry_info);
if (!stats_buffer.is_empty()) {
OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
&stats_buffer.first(), stats_buffer.length());
void HeapObjectsMap::RemoveDeadEntries() {
- ASSERT(entries_.length() > 0 &&
+ DCHECK(entries_.length() > 0 &&
entries_.at(0).id == 0 &&
entries_.at(0).addr == NULL);
int first_free_entry = 1;
entries_.at(first_free_entry).accessed = false;
HashMap::Entry* entry = entries_map_.Lookup(
entry_info.addr, ComputePointerHash(entry_info.addr), false);
- ASSERT(entry);
+ DCHECK(entry);
entry->value = reinterpret_cast<void*>(first_free_entry);
++first_free_entry;
} else {
}
}
entries_.Rewind(first_free_entry);
- ASSERT(static_cast<uint32_t>(entries_.length()) - 1 ==
+ DCHECK(static_cast<uint32_t>(entries_.length()) - 1 ==
entries_map_.occupancy());
}
HeapEntriesMap::HeapEntriesMap()
- : entries_(HeapThingsMatch) {
+ : entries_(HashMap::PointersMatch) {
}
void HeapEntriesMap::Pair(HeapThing thing, int entry) {
HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true);
- ASSERT(cache_entry->value == NULL);
+ DCHECK(cache_entry->value == NULL);
cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(entry));
}
HeapObjectsSet::HeapObjectsSet()
- : entries_(HeapEntriesMap::HeapThingsMatch) {
+ : entries_(HashMap::PointersMatch) {
}
}
-HeapObject* const V8HeapExplorer::kInternalRootObject =
- reinterpret_cast<HeapObject*>(
- static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
-HeapObject* const V8HeapExplorer::kGcRootsObject =
- reinterpret_cast<HeapObject*>(
- static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
-HeapObject* const V8HeapExplorer::kFirstGcSubrootObject =
- reinterpret_cast<HeapObject*>(
- static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId));
-HeapObject* const V8HeapExplorer::kLastGcSubrootObject =
- reinterpret_cast<HeapObject*>(
- static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId));
-
-
V8HeapExplorer::V8HeapExplorer(
HeapSnapshot* snapshot,
SnapshottingProgressReportingInterface* progress,
HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object) {
- if (object == kInternalRootObject) {
- snapshot_->AddRootEntry();
- return snapshot_->root();
- } else if (object == kGcRootsObject) {
- HeapEntry* entry = snapshot_->AddGcRootsEntry();
- return entry;
- } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) {
- HeapEntry* entry = snapshot_->AddGcSubrootEntry(GetGcSubrootOrder(object));
- return entry;
- } else if (object->IsJSFunction()) {
+ if (object->IsJSFunction()) {
JSFunction* func = JSFunction::cast(object);
SharedFunctionInfo* shared = func->shared();
const char* name = shared->bound() ? "native_bind" :
return AddEntry(object,
HeapEntry::kString,
names_->GetName(String::cast(object)));
+ } else if (object->IsSymbol()) {
+ return AddEntry(object, HeapEntry::kSymbol, "symbol");
} else if (object->IsCode()) {
return AddEntry(object, HeapEntry::kCode, "");
} else if (object->IsSharedFunctionInfo()) {
HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
HeapEntry::Type type,
const char* name) {
- int object_size = object->Size();
- SnapshotObjectId object_id =
- heap_object_map_->FindOrAddEntry(object->address(), object_size);
- return snapshot_->AddEntry(type, name, object_id, object_size);
+ return AddEntry(object->address(), type, name, object->Size());
}
-class GcSubrootsEnumerator : public ObjectVisitor {
+HeapEntry* V8HeapExplorer::AddEntry(Address address,
+ HeapEntry::Type type,
+ const char* name,
+ size_t size) {
+ SnapshotObjectId object_id = heap_object_map_->FindOrAddEntry(
+ address, static_cast<unsigned int>(size));
+ unsigned trace_node_id = 0;
+ if (AllocationTracker* allocation_tracker =
+ snapshot_->profiler()->allocation_tracker()) {
+ trace_node_id =
+ allocation_tracker->address_to_trace()->GetTraceNodeId(address);
+ }
+ return snapshot_->AddEntry(type, name, object_id, size, trace_node_id);
+}
+
+
+class SnapshotFiller {
public:
- GcSubrootsEnumerator(
- SnapshotFillerInterface* filler, V8HeapExplorer* explorer)
- : filler_(filler),
- explorer_(explorer),
- previous_object_count_(0),
- object_count_(0) {
+ explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
+ : snapshot_(snapshot),
+ names_(snapshot->profiler()->names()),
+ entries_(entries) { }
+ HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+ HeapEntry* entry = allocator->AllocateEntry(ptr);
+ entries_->Pair(ptr, entry->index());
+ return entry;
}
- void VisitPointers(Object** start, Object** end) {
- object_count_ += end - start;
+ HeapEntry* FindEntry(HeapThing ptr) {
+ int index = entries_->Map(ptr);
+ return index != HeapEntry::kNoEntry ? &snapshot_->entries()[index] : NULL;
}
- void Synchronize(VisitorSynchronization::SyncTag tag) {
- // Skip empty subroots.
- if (previous_object_count_ != object_count_) {
- previous_object_count_ = object_count_;
- filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_);
- }
+ HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+ HeapEntry* entry = FindEntry(ptr);
+ return entry != NULL ? entry : AddEntry(ptr, allocator);
+ }
+ void SetIndexedReference(HeapGraphEdge::Type type,
+ int parent,
+ int index,
+ HeapEntry* child_entry) {
+ HeapEntry* parent_entry = &snapshot_->entries()[parent];
+ parent_entry->SetIndexedReference(type, index, child_entry);
+ }
+ void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent,
+ HeapEntry* child_entry) {
+ HeapEntry* parent_entry = &snapshot_->entries()[parent];
+ int index = parent_entry->children_count() + 1;
+ parent_entry->SetIndexedReference(type, index, child_entry);
+ }
+ void SetNamedReference(HeapGraphEdge::Type type,
+ int parent,
+ const char* reference_name,
+ HeapEntry* child_entry) {
+ HeapEntry* parent_entry = &snapshot_->entries()[parent];
+ parent_entry->SetNamedReference(type, reference_name, child_entry);
}
+ void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent,
+ HeapEntry* child_entry) {
+ HeapEntry* parent_entry = &snapshot_->entries()[parent];
+ int index = parent_entry->children_count() + 1;
+ parent_entry->SetNamedReference(
+ type,
+ names_->GetName(index),
+ child_entry);
+ }
+
private:
- SnapshotFillerInterface* filler_;
- V8HeapExplorer* explorer_;
- intptr_t previous_object_count_;
- intptr_t object_count_;
+ HeapSnapshot* snapshot_;
+ StringsStorage* names_;
+ HeapEntriesMap* entries_;
};
-void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
- filler->AddEntry(kInternalRootObject, this);
- filler->AddEntry(kGcRootsObject, this);
- GcSubrootsEnumerator enumerator(filler, this);
- heap_->IterateRoots(&enumerator, VISIT_ALL);
-}
-
-
const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
switch (object->map()->instance_type()) {
case MAP_TYPE:
static void MarkVisitedField(HeapObject* obj, int offset) {
if (offset < 0) return;
Address field = obj->address() + offset;
- ASSERT(!Memory::Object_at(field)->IsFailure());
- ASSERT(Memory::Object_at(field)->IsHeapObject());
- *field |= kFailureTag;
+ DCHECK(Memory::Object_at(field)->IsHeapObject());
+ intptr_t p = reinterpret_cast<intptr_t>(Memory::Object_at(field));
+ DCHECK(!IsMarked(p));
+ intptr_t p_tagged = p | kTag;
+ Memory::Object_at(field) = reinterpret_cast<Object*>(p_tagged);
}
private:
bool CheckVisitedAndUnmark(Object** field) {
- if ((*field)->IsFailure()) {
- intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
- *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
- ASSERT((*field)->IsHeapObject());
+ intptr_t p = reinterpret_cast<intptr_t>(*field);
+ if (IsMarked(p)) {
+ intptr_t p_untagged = (p & ~kTaggingMask) | kHeapObjectTag;
+ *field = reinterpret_cast<Object*>(p_untagged);
+ DCHECK((*field)->IsHeapObject());
return true;
}
return false;
}
+
+ static const intptr_t kTaggingMask = 3;
+ static const intptr_t kTag = 3;
+
+ static bool IsMarked(intptr_t p) { return (p & kTaggingMask) == kTag; }
+
V8HeapExplorer* generator_;
HeapObject* parent_obj_;
int parent_;
};
-void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
- HeapEntry* heap_entry = GetEntry(obj);
- if (heap_entry == NULL) return; // No interest in this object.
- int entry = heap_entry->index();
+bool V8HeapExplorer::ExtractReferencesPass1(int entry, HeapObject* obj) {
+ if (obj->IsFixedArray()) return false; // FixedArrays are processed on pass 2
if (obj->IsJSGlobalProxy()) {
ExtractJSGlobalProxyReferences(entry, JSGlobalProxy::cast(obj));
+ } else if (obj->IsJSArrayBuffer()) {
+ ExtractJSArrayBufferReferences(entry, JSArrayBuffer::cast(obj));
} else if (obj->IsJSObject()) {
+ if (obj->IsJSWeakSet()) {
+ ExtractJSWeakCollectionReferences(entry, JSWeakSet::cast(obj));
+ } else if (obj->IsJSWeakMap()) {
+ ExtractJSWeakCollectionReferences(entry, JSWeakMap::cast(obj));
+ } else if (obj->IsJSSet()) {
+ ExtractJSCollectionReferences(entry, JSSet::cast(obj));
+ } else if (obj->IsJSMap()) {
+ ExtractJSCollectionReferences(entry, JSMap::cast(obj));
+ }
ExtractJSObjectReferences(entry, JSObject::cast(obj));
} else if (obj->IsString()) {
ExtractStringReferences(entry, String::cast(obj));
- } else if (obj->IsContext()) {
- ExtractContextReferences(entry, Context::cast(obj));
+ } else if (obj->IsSymbol()) {
+ ExtractSymbolReferences(entry, Symbol::cast(obj));
} else if (obj->IsMap()) {
ExtractMapReferences(entry, Map::cast(obj));
} else if (obj->IsSharedFunctionInfo()) {
ExtractSharedFunctionInfoReferences(entry, SharedFunctionInfo::cast(obj));
} else if (obj->IsScript()) {
ExtractScriptReferences(entry, Script::cast(obj));
+ } else if (obj->IsAccessorInfo()) {
+ ExtractAccessorInfoReferences(entry, AccessorInfo::cast(obj));
} else if (obj->IsAccessorPair()) {
ExtractAccessorPairReferences(entry, AccessorPair::cast(obj));
} else if (obj->IsCodeCache()) {
} else if (obj->IsAllocationSite()) {
ExtractAllocationSiteReferences(entry, AllocationSite::cast(obj));
}
- SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
+ return true;
+}
+
- // Extract unvisited fields as hidden references and restore tags
- // of visited fields.
- IndexedReferencesExtractor refs_extractor(this, obj, entry);
- obj->Iterate(&refs_extractor);
+bool V8HeapExplorer::ExtractReferencesPass2(int entry, HeapObject* obj) {
+ if (!obj->IsFixedArray()) return false;
+
+ if (obj->IsContext()) {
+ ExtractContextReferences(entry, Context::cast(obj));
+ } else {
+ ExtractFixedArrayReferences(entry, FixedArray::cast(obj));
+ }
+ return true;
}
ExtractPropertyReferences(js_obj, entry);
ExtractElementReferences(js_obj, entry);
ExtractInternalReferences(js_obj, entry);
- SetPropertyReference(
- obj, entry, heap_->proto_string(), js_obj->GetPrototype());
+ PrototypeIterator iter(heap_->isolate(), js_obj);
+ SetPropertyReference(obj, entry, heap_->proto_string(), iter.GetCurrent());
if (obj->IsJSFunction()) {
JSFunction* js_fun = JSFunction::cast(js_obj);
Object* proto_or_map = js_fun->prototype_or_initial_map();
SetWeakReference(js_fun, entry,
"next_function_link", js_fun->next_function_link(),
JSFunction::kNextFunctionLinkOffset);
- STATIC_CHECK(JSFunction::kNextFunctionLinkOffset
+ STATIC_ASSERT(JSFunction::kNextFunctionLinkOffset
== JSFunction::kNonWeakFieldsEndOffset);
- STATIC_CHECK(JSFunction::kNextFunctionLinkOffset + kPointerSize
+ STATIC_ASSERT(JSFunction::kNextFunctionLinkOffset + kPointerSize
== JSFunction::kSize);
} else if (obj->IsGlobalObject()) {
GlobalObject* global_obj = GlobalObject::cast(obj);
"global_context", global_obj->global_context(),
GlobalObject::kGlobalContextOffset);
SetInternalReference(global_obj, entry,
- "global_receiver", global_obj->global_receiver(),
- GlobalObject::kGlobalReceiverOffset);
- STATIC_CHECK(GlobalObject::kHeaderSize - JSObject::kHeaderSize ==
+ "global_proxy", global_obj->global_proxy(),
+ GlobalObject::kGlobalProxyOffset);
+ STATIC_ASSERT(GlobalObject::kHeaderSize - JSObject::kHeaderSize ==
4 * kPointerSize);
} else if (obj->IsJSArrayBufferView()) {
JSArrayBufferView* view = JSArrayBufferView::cast(obj);
JSArrayBufferView::kBufferOffset);
SetWeakReference(view, entry, "weak_next", view->weak_next(),
JSArrayBufferView::kWeakNextOffset);
- } else if (obj->IsJSArrayBuffer()) {
- JSArrayBuffer* buffer = JSArrayBuffer::cast(obj);
- SetWeakReference(buffer, entry, "weak_next", buffer->weak_next(),
- JSArrayBuffer::kWeakNextOffset);
- SetWeakReference(buffer, entry,
- "weak_first_view", buffer->weak_first_view(),
- JSArrayBuffer::kWeakFirstViewOffset);
}
TagObject(js_obj->properties(), "(object properties)");
SetInternalReference(obj, entry,
}
+void V8HeapExplorer::ExtractSymbolReferences(int entry, Symbol* symbol) {
+ SetInternalReference(symbol, entry,
+ "name", symbol->name(),
+ Symbol::kNameOffset);
+}
+
+
+void V8HeapExplorer::ExtractJSCollectionReferences(int entry,
+ JSCollection* collection) {
+ SetInternalReference(collection, entry, "table", collection->table(),
+ JSCollection::kTableOffset);
+}
+
+
+void V8HeapExplorer::ExtractJSWeakCollectionReferences(
+ int entry, JSWeakCollection* collection) {
+ MarkAsWeakContainer(collection->table());
+ SetInternalReference(collection, entry,
+ "table", collection->table(),
+ JSWeakCollection::kTableOffset);
+}
+
+
void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
if (context == context->declaration_context()) {
ScopeInfo* scope_info = context->closure()->shared()->scope_info();
}
#define EXTRACT_CONTEXT_FIELD(index, type, name) \
- if (Context::index < Context::FIRST_WEAK_SLOT) { \
+ if (Context::index < Context::FIRST_WEAK_SLOT || \
+ Context::index == Context::MAP_CACHE_INDEX) { \
SetInternalReference(context, entry, #name, context->get(Context::index), \
FixedArray::OffsetOfElementAt(Context::index)); \
} else { \
EXTRACT_CONTEXT_FIELD(DEOPTIMIZED_CODE_LIST, unused, deoptimized_code_list);
EXTRACT_CONTEXT_FIELD(NEXT_CONTEXT_LINK, unused, next_context_link);
#undef EXTRACT_CONTEXT_FIELD
- STATIC_CHECK(Context::OPTIMIZED_FUNCTIONS_LIST == Context::FIRST_WEAK_SLOT);
- STATIC_CHECK(Context::NEXT_CONTEXT_LINK + 1
- == Context::NATIVE_CONTEXT_SLOTS);
- STATIC_CHECK(Context::FIRST_WEAK_SLOT + 5 == Context::NATIVE_CONTEXT_SLOTS);
+ STATIC_ASSERT(Context::OPTIMIZED_FUNCTIONS_LIST ==
+ Context::FIRST_WEAK_SLOT);
+ STATIC_ASSERT(Context::NEXT_CONTEXT_LINK + 1 ==
+ Context::NATIVE_CONTEXT_SLOTS);
+ STATIC_ASSERT(Context::FIRST_WEAK_SLOT + 5 ==
+ Context::NATIVE_CONTEXT_SLOTS);
}
}
TagObject(back_pointer, "(back pointer)");
SetInternalReference(transitions, transitions_entry,
"back_pointer", back_pointer);
+
+ if (FLAG_collect_maps && map->CanTransition()) {
+ if (!transitions->IsSimpleTransition()) {
+ if (transitions->HasPrototypeTransitions()) {
+ FixedArray* prototype_transitions =
+ transitions->GetPrototypeTransitions();
+ MarkAsWeakContainer(prototype_transitions);
+ TagObject(prototype_transitions, "(prototype transitions");
+ SetInternalReference(transitions, transitions_entry,
+ "prototype_transitions", prototype_transitions);
+ }
+ // TODO(alph): transitions keys are strong links.
+ MarkAsWeakContainer(transitions);
+ }
+ }
+
TagObject(transitions, "(transition array)");
SetInternalReference(map, entry,
"transitions", transitions,
"descriptors", descriptors,
Map::kDescriptorsOffset);
+ MarkAsWeakContainer(map->code_cache());
SetInternalReference(map, entry,
"code_cache", map->code_cache(),
Map::kCodeCacheOffset);
"constructor", map->constructor(),
Map::kConstructorOffset);
TagObject(map->dependent_code(), "(dependent code)");
+ MarkAsWeakContainer(map->dependent_code());
SetInternalReference(map, entry,
"dependent_code", map->dependent_code(),
Map::kDependentCodeOffset);
SetInternalReference(obj, entry,
"optimized_code_map", shared->optimized_code_map(),
SharedFunctionInfo::kOptimizedCodeMapOffset);
- SetWeakReference(obj, entry,
- "initial_map", shared->initial_map(),
- SharedFunctionInfo::kInitialMapOffset);
+ SetInternalReference(obj, entry,
+ "feedback_vector", shared->feedback_vector(),
+ SharedFunctionInfo::kFeedbackVectorOffset);
}
"name", script->name(),
Script::kNameOffset);
SetInternalReference(obj, entry,
- "data", script->data(),
- Script::kDataOffset);
- SetInternalReference(obj, entry,
"context_data", script->context_data(),
Script::kContextOffset);
TagObject(script->line_ends(), "(script line ends)");
}
+void V8HeapExplorer::ExtractAccessorInfoReferences(
+ int entry, AccessorInfo* accessor_info) {
+ SetInternalReference(accessor_info, entry, "name", accessor_info->name(),
+ AccessorInfo::kNameOffset);
+ SetInternalReference(accessor_info, entry, "expected_receiver_type",
+ accessor_info->expected_receiver_type(),
+ AccessorInfo::kExpectedReceiverTypeOffset);
+ if (accessor_info->IsDeclaredAccessorInfo()) {
+ DeclaredAccessorInfo* declared_accessor_info =
+ DeclaredAccessorInfo::cast(accessor_info);
+ SetInternalReference(declared_accessor_info, entry, "descriptor",
+ declared_accessor_info->descriptor(),
+ DeclaredAccessorInfo::kDescriptorOffset);
+ } else if (accessor_info->IsExecutableAccessorInfo()) {
+ ExecutableAccessorInfo* executable_accessor_info =
+ ExecutableAccessorInfo::cast(accessor_info);
+ SetInternalReference(executable_accessor_info, entry, "getter",
+ executable_accessor_info->getter(),
+ ExecutableAccessorInfo::kGetterOffset);
+ SetInternalReference(executable_accessor_info, entry, "setter",
+ executable_accessor_info->setter(),
+ ExecutableAccessorInfo::kSetterOffset);
+ SetInternalReference(executable_accessor_info, entry, "data",
+ executable_accessor_info->data(),
+ ExecutableAccessorInfo::kDataOffset);
+ }
+}
+
+
void V8HeapExplorer::ExtractAccessorPairReferences(
int entry, AccessorPair* accessors) {
SetInternalReference(accessors, entry, "getter", accessors->getter(),
void V8HeapExplorer::TagCodeObject(Code* code) {
if (code->kind() == Code::STUB) {
TagObject(code, names_->GetFormatted(
- "(%s code)", CodeStub::MajorName(
- static_cast<CodeStub::Major>(code->major_key()), true)));
+ "(%s code)", CodeStub::MajorName(
+ CodeStub::GetMajorKey(code), true)));
}
}
ExtractCellReferences(entry, cell);
SetInternalReference(cell, entry, "type", cell->type(),
PropertyCell::kTypeOffset);
+ MarkAsWeakContainer(cell->dependent_code());
SetInternalReference(cell, entry, "dependent_code", cell->dependent_code(),
PropertyCell::kDependentCodeOffset);
}
AllocationSite::kTransitionInfoOffset);
SetInternalReference(site, entry, "nested_site", site->nested_site(),
AllocationSite::kNestedSiteOffset);
+ MarkAsWeakContainer(site->dependent_code());
SetInternalReference(site, entry, "dependent_code", site->dependent_code(),
AllocationSite::kDependentCodeOffset);
// Do not visit weak_next as it is not visited by the StaticVisitor,
// and we're not very interested in weak_next field here.
- STATIC_CHECK(AllocationSite::kWeakNextOffset >=
+ STATIC_ASSERT(AllocationSite::kWeakNextOffset >=
AllocationSite::BodyDescriptor::kEndOffset);
}
+class JSArrayBufferDataEntryAllocator : public HeapEntriesAllocator {
+ public:
+ JSArrayBufferDataEntryAllocator(size_t size, V8HeapExplorer* explorer)
+ : size_(size)
+ , explorer_(explorer) {
+ }
+ virtual HeapEntry* AllocateEntry(HeapThing ptr) {
+ return explorer_->AddEntry(
+ static_cast<Address>(ptr),
+ HeapEntry::kNative, "system / JSArrayBufferData", size_);
+ }
+ private:
+ size_t size_;
+ V8HeapExplorer* explorer_;
+};
+
+
+void V8HeapExplorer::ExtractJSArrayBufferReferences(
+ int entry, JSArrayBuffer* buffer) {
+ SetWeakReference(buffer, entry, "weak_next", buffer->weak_next(),
+ JSArrayBuffer::kWeakNextOffset);
+ SetWeakReference(buffer, entry,
+ "weak_first_view", buffer->weak_first_view(),
+ JSArrayBuffer::kWeakFirstViewOffset);
+ // Setup a reference to a native memory backing_store object.
+ if (!buffer->backing_store())
+ return;
+ size_t data_size = NumberToSize(heap_->isolate(), buffer->byte_length());
+ JSArrayBufferDataEntryAllocator allocator(data_size, this);
+ HeapEntry* data_entry =
+ filler_->FindOrAddEntry(buffer->backing_store(), &allocator);
+ filler_->SetNamedReference(HeapGraphEdge::kInternal,
+ entry, "backing_store", data_entry);
+}
+
+
+void V8HeapExplorer::ExtractFixedArrayReferences(int entry, FixedArray* array) {
+ bool is_weak = weak_containers_.Contains(array);
+ for (int i = 0, l = array->length(); i < l; ++i) {
+ if (is_weak) {
+ SetWeakReference(array, entry,
+ i, array->get(i), array->OffsetOfElementAt(i));
+ } else {
+ SetInternalReference(array, entry,
+ i, array->get(i), array->OffsetOfElementAt(i));
+ }
+ }
+}
+
+
void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, int entry) {
if (!js_obj->IsJSFunction()) return;
for (int i = 0; i < real_size; i++) {
switch (descs->GetType(i)) {
case FIELD: {
+ Representation r = descs->GetDetails(i).representation();
+ if (r.IsSmi() || r.IsDouble()) break;
int index = descs->GetFieldIndex(i);
Name* k = descs->GetKey(i);
js_obj->GetInObjectPropertyOffset(index));
}
} else {
- Object* value = js_obj->RawFastPropertyAt(index);
+ FieldIndex field_index =
+ FieldIndex::ForDescriptor(js_obj->map(), i);
+ Object* value = js_obj->RawFastPropertyAt(field_index);
if (k != heap_->hidden_string()) {
SetPropertyReference(js_obj, entry, k, value);
} else {
descs->GetKey(i), descs->GetValue(i));
break;
case NORMAL: // only in slow mode
- case HANDLER: // only in lookup results, not in descriptors
- case INTERCEPTOR: // only in lookup results, not in descriptors
- break;
- case TRANSITION:
- case NONEXISTENT:
UNREACHABLE();
break;
}
for (int i = 0; i < length; ++i) {
Object* k = dictionary->KeyAt(i);
if (dictionary->IsKey(k)) {
- ASSERT(k->IsNumber());
+ DCHECK(k->IsNumber());
uint32_t index = static_cast<uint32_t>(k->Number());
SetElementReference(js_obj, entry, index, dictionary->ValueAt(i));
}
if (object->IsJSFunction()) return heap->closure_string();
String* constructor_name = object->constructor_name();
if (constructor_name == heap->Object_string()) {
- // Look up an immediate "constructor" property, if it is a function,
- // return its name. This is for instances of binding objects, which
- // have prototype constructor type "Object".
- Object* constructor_prop = NULL;
- LookupResult result(heap->isolate());
- object->LocalLookupRealNamedProperty(heap->constructor_string(), &result);
- if (!result.IsFound()) return object->constructor_name();
-
- constructor_prop = result.GetLazyValue();
- if (constructor_prop->IsJSFunction()) {
- Object* maybe_name =
- JSFunction::cast(constructor_prop)->shared()->name();
- if (maybe_name->IsString()) {
- String* name = String::cast(maybe_name);
- if (name->length() > 0) return name;
- }
- }
+ // TODO(verwaest): Try to get object.constructor.name in this case.
+ // This requires handlification of the V8HeapExplorer.
}
return object->constructor_name();
}
void SetCollectingAllReferences() { collecting_all_references_ = true; }
void FillReferences(V8HeapExplorer* explorer) {
- ASSERT(strong_references_.length() <= all_references_.length());
+ DCHECK(strong_references_.length() <= all_references_.length());
Builtins* builtins = heap_->isolate()->builtins();
- for (int i = 0; i < reference_tags_.length(); ++i) {
- explorer->SetGcRootsReference(reference_tags_[i].tag);
- }
int strong_index = 0, all_index = 0, tags_index = 0, builtin_index = 0;
while (all_index < all_references_.length()) {
bool is_strong = strong_index < strong_references_.length()
all_references_[all_index]);
if (reference_tags_[tags_index].tag ==
VisitorSynchronization::kBuiltins) {
- ASSERT(all_references_[all_index]->IsCode());
+ DCHECK(all_references_[all_index]->IsCode());
explorer->TagBuiltinCodeObject(
Code::cast(all_references_[all_index]),
builtins->name(builtin_index++));
bool V8HeapExplorer::IterateAndExtractReferences(
- SnapshotFillerInterface* filler) {
+ SnapshotFiller* filler) {
filler_ = filler;
+ // Create references to the synthetic roots.
+ SetRootGcRootsReference();
+ for (int tag = 0; tag < VisitorSynchronization::kNumberOfSyncTags; tag++) {
+ SetGcRootsReference(static_cast<VisitorSynchronization::SyncTag>(tag));
+ }
+
// Make sure builtin code objects get their builtin tags
// first. Otherwise a particular JSFunction object could set
// its custom name to a generic builtin.
- SetRootGcRootsReference();
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
+ // between these cases without processing the array owner first.
+ bool interrupted =
+ IterateAndExtractSinglePass<&V8HeapExplorer::ExtractReferencesPass1>() ||
+ IterateAndExtractSinglePass<&V8HeapExplorer::ExtractReferencesPass2>();
+
+ if (interrupted) {
+ filler_ = NULL;
+ return false;
+ }
+
+ filler_ = NULL;
+ return progress_->ProgressReport(true);
+}
+
+
+template<V8HeapExplorer::ExtractReferencesMethod extractor>
+bool V8HeapExplorer::IterateAndExtractSinglePass() {
// Now iterate the whole heap.
bool interrupted = false;
HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
for (HeapObject* obj = iterator.next();
obj != NULL;
obj = iterator.next(), progress_->ProgressStep()) {
- if (!interrupted) {
- ExtractReferences(obj);
- if (!progress_->ProgressReport(false)) interrupted = true;
+ if (interrupted) continue;
+
+ HeapEntry* heap_entry = GetEntry(obj);
+ int entry = heap_entry->index();
+ if ((this->*extractor)(entry, obj)) {
+ SetInternalReference(obj, entry,
+ "map", obj->map(), HeapObject::kMapOffset);
+ // Extract unvisited fields as hidden references and restore tags
+ // of visited fields.
+ IndexedReferencesExtractor refs_extractor(this, obj, entry);
+ obj->Iterate(&refs_extractor);
}
- }
- if (interrupted) {
- filler_ = NULL;
- return false;
- }
- filler_ = NULL;
- return progress_->ProgressReport(true);
+ if (!progress_->ProgressReport(false)) interrupted = true;
+ }
+ return interrupted;
}
String* reference_name,
Object* child_obj,
int field_offset) {
- ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ DCHECK(parent_entry == GetEntry(parent_obj)->index());
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry != NULL) {
filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
int parent_entry,
const char* reference_name,
Object* child_obj) {
- ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ DCHECK(parent_entry == GetEntry(parent_obj)->index());
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry != NULL) {
filler_->SetNamedReference(HeapGraphEdge::kShortcut,
int parent_entry,
int index,
Object* child_obj) {
- ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ DCHECK(parent_entry == GetEntry(parent_obj)->index());
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry != NULL) {
filler_->SetIndexedReference(HeapGraphEdge::kElement,
const char* reference_name,
Object* child_obj,
int field_offset) {
- ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ DCHECK(parent_entry == GetEntry(parent_obj)->index());
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry == NULL) return;
if (IsEssentialObject(child_obj)) {
int index,
Object* child_obj,
int field_offset) {
- ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ DCHECK(parent_entry == GetEntry(parent_obj)->index());
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry == NULL) return;
if (IsEssentialObject(child_obj)) {
int parent_entry,
int index,
Object* child_obj) {
- ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ DCHECK(parent_entry == GetEntry(parent_obj)->index());
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry != NULL && IsEssentialObject(child_obj)) {
filler_->SetIndexedReference(HeapGraphEdge::kHidden,
const char* reference_name,
Object* child_obj,
int field_offset) {
- ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ DCHECK(parent_entry == GetEntry(parent_obj)->index());
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry == NULL) return;
if (IsEssentialObject(child_obj)) {
}
+void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
+ int parent_entry,
+ int index,
+ Object* child_obj,
+ int field_offset) {
+ DCHECK(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry == NULL) return;
+ if (IsEssentialObject(child_obj)) {
+ filler_->SetNamedReference(HeapGraphEdge::kWeak,
+ parent_entry,
+ names_->GetFormatted("%d", index),
+ child_entry);
+ }
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
+}
+
+
void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
int parent_entry,
Name* reference_name,
Object* child_obj,
const char* name_format_string,
int field_offset) {
- ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ DCHECK(parent_entry == GetEntry(parent_obj)->index());
HeapEntry* child_entry = GetEntry(child_obj);
if (child_entry != NULL) {
HeapGraphEdge::Type type =
void V8HeapExplorer::SetUserGlobalReference(Object* child_obj) {
HeapEntry* child_entry = GetEntry(child_obj);
- ASSERT(child_entry != NULL);
+ DCHECK(child_entry != NULL);
filler_->SetNamedAutoIndexReference(
HeapGraphEdge::kShortcut,
snapshot_->root()->index(),
GlobalObject* global = context->global_object();
if (global->IsJSGlobalObject()) {
bool is_debug_object = false;
-#ifdef ENABLE_DEBUGGER_SUPPORT
is_debug_object = heap_->isolate()->debug()->IsDebugGlobal(global);
-#endif
if (!is_debug_object && !user_roots_.Contains(global)) {
user_roots_.Insert(global);
SetUserGlobalReference(global);
}
+void V8HeapExplorer::MarkAsWeakContainer(Object* object) {
+ if (IsEssentialObject(object) && object->IsFixedArray()) {
+ weak_containers_.Insert(object);
+ }
+}
+
+
class GlobalObjectsEnumerator : public ObjectVisitor {
public:
virtual void VisitPointers(Object** start, Object** end) {
entries_type_,
name,
heap_object_map_->GenerateId(info),
- size != -1 ? static_cast<int>(size) : 0);
+ size != -1 ? static_cast<int>(size) : 0,
+ 0);
}
HeapObject* parent = *group->parent;
int parent_entry =
filler_->FindOrAddEntry(parent, native_entries_allocator_)->index();
- ASSERT(parent_entry != HeapEntry::kNoEntry);
+ DCHECK(parent_entry != HeapEntry::kNoEntry);
Object*** children = group->children;
for (size_t j = 0; j < group->length; ++j) {
Object* child = *children[j];
bool NativeObjectsExplorer::IterateAndExtractReferences(
- SnapshotFillerInterface* filler) {
+ SnapshotFiller* filler) {
filler_ = filler;
FillRetainedObjects();
FillImplicitReferences();
v8::RetainedObjectInfo* info) {
HeapEntry* child_entry =
filler_->FindOrAddEntry(info, native_entries_allocator_);
- ASSERT(child_entry != NULL);
+ DCHECK(child_entry != NULL);
NativeGroupRetainedObjectInfo* group_info =
FindOrAddGroupInfo(info->GetGroupLabel());
HeapEntry* group_entry =
void NativeObjectsExplorer::SetWrapperNativeReferences(
HeapObject* wrapper, v8::RetainedObjectInfo* info) {
HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
- ASSERT(wrapper_entry != NULL);
+ DCHECK(wrapper_entry != NULL);
HeapEntry* info_entry =
filler_->FindOrAddEntry(info, native_entries_allocator_);
- ASSERT(info_entry != NULL);
+ DCHECK(info_entry != NULL);
filler_->SetNamedReference(HeapGraphEdge::kInternal,
wrapper_entry->index(),
"native",
static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
HeapEntry* group_entry =
filler_->FindOrAddEntry(group_info, native_entries_allocator_);
- ASSERT(group_entry != NULL);
+ DCHECK(group_entry != NULL);
filler_->SetIndexedAutoIndexReference(
HeapGraphEdge::kElement,
snapshot_->root()->index(),
}
-class SnapshotFiller : public SnapshotFillerInterface {
- public:
- explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
- : snapshot_(snapshot),
- names_(snapshot->profiler()->names()),
- entries_(entries) { }
- HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
- HeapEntry* entry = allocator->AllocateEntry(ptr);
- entries_->Pair(ptr, entry->index());
- return entry;
- }
- HeapEntry* FindEntry(HeapThing ptr) {
- int index = entries_->Map(ptr);
- return index != HeapEntry::kNoEntry ? &snapshot_->entries()[index] : NULL;
- }
- HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
- HeapEntry* entry = FindEntry(ptr);
- return entry != NULL ? entry : AddEntry(ptr, allocator);
- }
- void SetIndexedReference(HeapGraphEdge::Type type,
- int parent,
- int index,
- HeapEntry* child_entry) {
- HeapEntry* parent_entry = &snapshot_->entries()[parent];
- parent_entry->SetIndexedReference(type, index, child_entry);
- }
- void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
- int parent,
- HeapEntry* child_entry) {
- HeapEntry* parent_entry = &snapshot_->entries()[parent];
- int index = parent_entry->children_count() + 1;
- parent_entry->SetIndexedReference(type, index, child_entry);
- }
- void SetNamedReference(HeapGraphEdge::Type type,
- int parent,
- const char* reference_name,
- HeapEntry* child_entry) {
- HeapEntry* parent_entry = &snapshot_->entries()[parent];
- parent_entry->SetNamedReference(type, reference_name, child_entry);
- }
- void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
- int parent,
- HeapEntry* child_entry) {
- HeapEntry* parent_entry = &snapshot_->entries()[parent];
- int index = parent_entry->children_count() + 1;
- parent_entry->SetNamedReference(
- type,
- names_->GetName(index),
- child_entry);
- }
-
- private:
- HeapSnapshot* snapshot_;
- StringsStorage* names_;
- HeapEntriesMap* entries_;
-};
-
-
HeapSnapshotGenerator::HeapSnapshotGenerator(
HeapSnapshot* snapshot,
v8::ActivityControl* control,
#ifdef VERIFY_HEAP
Heap* debug_heap = heap_;
- CHECK(!debug_heap->old_data_space()->was_swept_conservatively());
- CHECK(!debug_heap->old_pointer_space()->was_swept_conservatively());
- CHECK(!debug_heap->code_space()->was_swept_conservatively());
- CHECK(!debug_heap->cell_space()->was_swept_conservatively());
- CHECK(!debug_heap->property_cell_space()->
- was_swept_conservatively());
- CHECK(!debug_heap->map_space()->was_swept_conservatively());
-#endif
-
- // The following code uses heap iterators, so we want the heap to be
- // stable. It should follow TagGlobalObjects as that can allocate.
- DisallowHeapAllocation no_alloc;
-
-#ifdef VERIFY_HEAP
debug_heap->Verify();
#endif
- SetProgressTotal(1); // 1 pass.
+ SetProgressTotal(2); // 2 passes.
#ifdef VERIFY_HEAP
debug_heap->Verify();
#endif
+ snapshot_->AddSyntheticRootEntries();
+
if (!FillReferences()) return false;
snapshot_->FillChildren();
bool HeapSnapshotGenerator::FillReferences() {
SnapshotFiller filler(snapshot_, &entries_);
- v8_heap_explorer_.AddRootEntries(&filler);
return v8_heap_explorer_.IterateAndExtractReferences(&filler)
&& dom_explorer_.IterateAndExtractReferences(&filler);
}
chunk_(chunk_size_),
chunk_pos_(0),
aborted_(false) {
- ASSERT(chunk_size_ > 0);
+ DCHECK(chunk_size_ > 0);
}
bool aborted() { return aborted_; }
void AddCharacter(char c) {
- ASSERT(c != '\0');
- ASSERT(chunk_pos_ < chunk_size_);
+ DCHECK(c != '\0');
+ DCHECK(chunk_pos_ < chunk_size_);
chunk_[chunk_pos_++] = c;
MaybeWriteChunk();
}
}
void AddSubstring(const char* s, int n) {
if (n <= 0) return;
- ASSERT(static_cast<size_t>(n) <= strlen(s));
+ DCHECK(static_cast<size_t>(n) <= strlen(s));
const char* s_end = s + n;
while (s < s_end) {
- int s_chunk_size = Min(
- chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
- ASSERT(s_chunk_size > 0);
- OS::MemCopy(chunk_.start() + chunk_pos_, s, s_chunk_size);
+ int s_chunk_size =
+ Min(chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
+ DCHECK(s_chunk_size > 0);
+ MemCopy(chunk_.start() + chunk_pos_, s, s_chunk_size);
s += s_chunk_size;
chunk_pos_ += s_chunk_size;
MaybeWriteChunk();
void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
void Finalize() {
if (aborted_) return;
- ASSERT(chunk_pos_ < chunk_size_);
+ DCHECK(chunk_pos_ < chunk_size_);
if (chunk_pos_ != 0) {
WriteChunk();
}
static const int kMaxNumberSize =
MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1;
if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) {
- int result = OS::SNPrintF(
+ int result = SNPrintF(
chunk_.SubVector(chunk_pos_, chunk_size_), format, n);
- ASSERT(result != -1);
+ DCHECK(result != -1);
chunk_pos_ += result;
MaybeWriteChunk();
} else {
EmbeddedVector<char, kMaxNumberSize> buffer;
- int result = OS::SNPrintF(buffer, format, n);
+ int result = SNPrintF(buffer, format, n);
USE(result);
- ASSERT(result != -1);
+ DCHECK(result != -1);
AddString(buffer.start());
}
}
void MaybeWriteChunk() {
- ASSERT(chunk_pos_ <= chunk_size_);
+ DCHECK(chunk_pos_ <= chunk_size_);
if (chunk_pos_ == chunk_size_) {
WriteChunk();
}
// type, name|index, to_node.
const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3;
-// type, name, id, self_size, children_index.
-const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 5;
+// type, name, id, self_size, edge_count, trace_node_id.
+const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 6;
void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
if (AllocationTracker* allocation_tracker =
snapshot_->profiler()->allocation_tracker()) {
allocation_tracker->PrepareForSerialization();
}
- ASSERT(writer_ == NULL);
+ DCHECK(writer_ == NULL);
writer_ = new OutputStreamWriter(stream);
SerializeImpl();
delete writer_;
void HeapSnapshotJSONSerializer::SerializeImpl() {
- ASSERT(0 == snapshot_->root()->index());
+ DCHECK(0 == snapshot_->root()->index());
writer_->AddCharacter('{');
writer_->AddString("\"snapshot\":{");
SerializeSnapshot();
}
-static int utoa(unsigned value, const Vector<char>& buffer, int buffer_pos) {
+namespace {
+
+template<size_t size> struct ToUnsigned;
+
+template<> struct ToUnsigned<4> {
+ typedef uint32_t Type;
+};
+
+template<> struct ToUnsigned<8> {
+ typedef uint64_t Type;
+};
+
+} // namespace
+
+
+template<typename T>
+static int utoa_impl(T value, const Vector<char>& buffer, int buffer_pos) {
+ STATIC_ASSERT(static_cast<T>(-1) > 0); // Check that T is unsigned
int number_of_digits = 0;
- unsigned t = value;
+ T t = value;
do {
++number_of_digits;
} while (t /= 10);
buffer_pos += number_of_digits;
int result = buffer_pos;
do {
- int last_digit = value % 10;
+ int last_digit = static_cast<int>(value % 10);
buffer[--buffer_pos] = '0' + last_digit;
value /= 10;
} while (value);
}
+template<typename T>
+static int utoa(T value, const Vector<char>& buffer, int buffer_pos) {
+ typename ToUnsigned<sizeof(value)>::Type unsigned_value = value;
+ STATIC_ASSERT(sizeof(value) == sizeof(unsigned_value));
+ return utoa_impl(unsigned_value, buffer, buffer_pos);
+}
+
+
void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
bool first_edge) {
// The buffer needs space for 3 unsigned ints, 3 commas, \n and \0
void HeapSnapshotJSONSerializer::SerializeEdges() {
List<HeapGraphEdge*>& edges = snapshot_->children();
for (int i = 0; i < edges.length(); ++i) {
- ASSERT(i == 0 ||
+ DCHECK(i == 0 ||
edges[i - 1]->from()->index() <= edges[i]->from()->index());
SerializeEdge(edges[i], i == 0);
if (writer_->aborted()) return;
void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
- // The buffer needs space for 5 unsigned ints, 5 commas, \n and \0
+ // The buffer needs space for 4 unsigned ints, 1 size_t, 5 commas, \n and \0
static const int kBufferSize =
5 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
- + 5 + 1 + 1;
+ + MaxDecimalDigitsIn<sizeof(size_t)>::kUnsigned // NOLINT
+ + 6 + 1 + 1;
EmbeddedVector<char, kBufferSize> buffer;
int buffer_pos = 0;
if (entry_index(entry) != 0) {
buffer_pos = utoa(entry->self_size(), buffer, buffer_pos);
buffer[buffer_pos++] = ',';
buffer_pos = utoa(entry->children_count(), buffer, buffer_pos);
+ buffer[buffer_pos++] = ',';
+ buffer_pos = utoa(entry->trace_node_id(), buffer, buffer_pos);
buffer[buffer_pos++] = '\n';
buffer[buffer_pos++] = '\0';
writer_->AddString(buffer.start());
JSON_S("name") ","
JSON_S("id") ","
JSON_S("self_size") ","
- JSON_S("edge_count")) ","
+ JSON_S("edge_count") ","
+ JSON_S("trace_node_id")) ","
JSON_S("node_types") ":" JSON_A(
JSON_A(
JSON_S("hidden") ","
JSON_S("column")) ","
JSON_S("trace_node_fields") ":" JSON_A(
JSON_S("id") ","
- JSON_S("function_id") ","
+ JSON_S("function_info_index") ","
JSON_S("count") ","
JSON_S("size") ","
JSON_S("children"))));
uint32_t count = 0;
AllocationTracker* tracker = snapshot_->profiler()->allocation_tracker();
if (tracker) {
- count = tracker->id_to_function_info()->occupancy();
+ count = tracker->function_info_list().length();
}
writer_->AddNumber(count);
}
int buffer_pos = 0;
buffer_pos = utoa(node->id(), buffer, buffer_pos);
buffer[buffer_pos++] = ',';
- buffer_pos = utoa(node->function_id(), buffer, buffer_pos);
+ buffer_pos = utoa(node->function_info_index(), buffer, buffer_pos);
buffer[buffer_pos++] = ',';
buffer_pos = utoa(node->allocation_count(), buffer, buffer_pos);
buffer[buffer_pos++] = ',';
if (position == -1) {
buffer[buffer_pos++] = '0';
} else {
- ASSERT(position >= 0);
+ DCHECK(position >= 0);
buffer_pos = utoa(static_cast<unsigned>(position + 1), buffer, buffer_pos);
}
return buffer_pos;
6 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
+ 6 + 1 + 1;
EmbeddedVector<char, kBufferSize> buffer;
- HashMap* id_to_function_info = tracker->id_to_function_info();
+ const List<AllocationTracker::FunctionInfo*>& list =
+ tracker->function_info_list();
bool first_entry = true;
- for (HashMap::Entry* p = id_to_function_info->Start();
- p != NULL;
- p = id_to_function_info->Next(p)) {
- SnapshotObjectId id =
- static_cast<SnapshotObjectId>(reinterpret_cast<intptr_t>(p->key));
- AllocationTracker::FunctionInfo* info =
- reinterpret_cast<AllocationTracker::FunctionInfo* >(p->value);
+ for (int i = 0; i < list.length(); i++) {
+ AllocationTracker::FunctionInfo* info = list[i];
int buffer_pos = 0;
if (first_entry) {
first_entry = false;
} else {
buffer[buffer_pos++] = ',';
}
- buffer_pos = utoa(id, buffer, buffer_pos);
+ buffer_pos = utoa(info->function_id, buffer, buffer_pos);
buffer[buffer_pos++] = ',';
buffer_pos = utoa(GetStringId(info->name), buffer, buffer_pos);
buffer[buffer_pos++] = ',';
unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
if (c != unibrow::Utf8::kBadChar) {
WriteUChar(writer_, c);
- ASSERT(cursor != 0);
+ DCHECK(cursor != 0);
s += cursor - 1;
} else {
writer_->AddCharacter('?');