| Index: src/profile-generator.cc
|
| ===================================================================
|
| --- src/profile-generator.cc (revision 11348)
|
| +++ src/profile-generator.cc (working copy)
|
| @@ -957,11 +957,6 @@
|
| }
|
|
|
|
|
| -HeapEntry* HeapGraphEdge::From() {
|
| - return reinterpret_cast<HeapEntry*>(this - child_index_) - 1;
|
| -}
|
| -
|
| -
|
| void HeapEntry::Init(HeapSnapshot* snapshot,
|
| Type type,
|
| const char* name,
|
| @@ -972,6 +967,7 @@
|
| snapshot_ = snapshot;
|
| type_ = type;
|
| painted_ = false;
|
| + reachable_from_window_ = false;
|
| name_ = name;
|
| self_size_ = self_size;
|
| retained_size_ = 0;
|
| @@ -1134,6 +1130,7 @@
|
| gc_roots_entry_(NULL),
|
| natives_root_entry_(NULL),
|
| raw_entries_(NULL),
|
| + number_of_edges_(0),
|
| max_snapshot_js_object_id_(0) {
|
| STATIC_CHECK(
|
| sizeof(HeapGraphEdge) ==
|
| @@ -1167,6 +1164,7 @@
|
| int children_count,
|
| int retainers_count) {
|
| ASSERT(raw_entries_ == NULL);
|
| + number_of_edges_ = children_count;
|
| raw_entries_size_ =
|
| HeapEntry::EntriesSize(entries_count, children_count, retainers_count);
|
| raw_entries_ = NewArray<char>(raw_entries_size_);
|
| @@ -1312,10 +1310,8 @@
|
| VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
|
|
|
| HeapObjectsMap::HeapObjectsMap()
|
| - : initial_fill_mode_(true),
|
| - next_id_(kFirstAvailableObjectId),
|
| - entries_map_(AddressesMatch),
|
| - entries_(new List<EntryInfo>()) {
|
| + : next_id_(kFirstAvailableObjectId),
|
| + entries_map_(AddressesMatch) {
|
| // This dummy element solves a problem with entries_map_.
|
| // When we do lookup in HashMap we see no difference between two cases:
|
| // it has an entry with NULL as the value or it has created
|
| @@ -1323,37 +1319,15 @@
|
| // With such dummy element we have a guaranty that all entries_map_ entries
|
| // will have the value field grater than 0.
|
| // This fact is using in MoveObject method.
|
| - entries_->Add(EntryInfo(0, NULL));
|
| + entries_.Add(EntryInfo(0, NULL, 0));
|
| }
|
|
|
|
|
| -HeapObjectsMap::~HeapObjectsMap() {
|
| - delete entries_;
|
| -}
|
| -
|
| -
|
| void HeapObjectsMap::SnapshotGenerationFinished() {
|
| - initial_fill_mode_ = false;
|
| RemoveDeadEntries();
|
| }
|
|
|
|
|
| -SnapshotObjectId HeapObjectsMap::FindObject(Address addr) {
|
| - if (!initial_fill_mode_) {
|
| - SnapshotObjectId existing = FindEntry(addr);
|
| - if (existing != 0) return existing;
|
| - }
|
| - SnapshotObjectId id = next_id_;
|
| - next_id_ += kObjectIdStep;
|
| - AddEntry(addr, id);
|
| - // Here and in other places the length of entries_ list has to be
|
| - // the same or greater than the length of entries_map_. But entries_ list
|
| - // has a dummy element at index 0.
|
| - ASSERT(static_cast<uint32_t>(entries_->length()) > entries_map_.occupancy());
|
| - return id;
|
| -}
|
| -
|
| -
|
| void HeapObjectsMap::MoveObject(Address from, Address to) {
|
| ASSERT(to != NULL);
|
| ASSERT(from != NULL);
|
| @@ -1362,7 +1336,7 @@
|
| if (from_value == NULL) return;
|
| int from_entry_info_index =
|
| static_cast<int>(reinterpret_cast<intptr_t>(from_value));
|
| - entries_->at(from_entry_info_index).addr = to;
|
| + entries_.at(from_entry_info_index).addr = to;
|
| HashMap::Entry* to_entry = entries_map_.Lookup(to, AddressHash(to), true);
|
| if (to_entry->value != NULL) {
|
| int to_entry_info_index =
|
| @@ -1371,56 +1345,39 @@
|
| // value in addr field. It is bad because later at RemoveDeadEntries
|
| // one of this entry will be removed with the corresponding entries_map_
|
| // entry.
|
| - entries_->at(to_entry_info_index).addr = NULL;
|
| + entries_.at(to_entry_info_index).addr = NULL;
|
| }
|
| to_entry->value = reinterpret_cast<void*>(from_entry_info_index);
|
| }
|
|
|
|
|
| -void HeapObjectsMap::AddEntry(Address addr, SnapshotObjectId id) {
|
| - HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
|
| - ASSERT(entry->value == NULL);
|
| - ASSERT(entries_->length() > 0 &&
|
| - entries_->at(0).id == 0 &&
|
| - entries_->at(0).addr == NULL);
|
| - ASSERT(entries_->at(entries_->length() - 1).id < id);
|
| - entry->value = reinterpret_cast<void*>(entries_->length());
|
| - entries_->Add(EntryInfo(id, addr));
|
| - ASSERT(static_cast<uint32_t>(entries_->length()) > entries_map_.occupancy());
|
| -}
|
| -
|
| -
|
| SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
|
| HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false);
|
| - if (entry != NULL) {
|
| - int entry_index =
|
| - static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
|
| - EntryInfo& entry_info = entries_->at(entry_index);
|
| - entry_info.accessed = true;
|
| - ASSERT(static_cast<uint32_t>(entries_->length()) >
|
| - entries_map_.occupancy());
|
| - return entry_info.id;
|
| - } else {
|
| - return 0;
|
| - }
|
| + 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());
|
| + return entry_info.id;
|
| }
|
|
|
|
|
| -SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr) {
|
| - ASSERT(static_cast<uint32_t>(entries_->length()) > entries_map_.occupancy());
|
| +SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
|
| + unsigned int size) {
|
| + ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
|
| HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
|
| if (entry->value != NULL) {
|
| int entry_index =
|
| static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
|
| - EntryInfo& entry_info = entries_->at(entry_index);
|
| + EntryInfo& entry_info = entries_.at(entry_index);
|
| entry_info.accessed = true;
|
| + entry_info.size = size;
|
| return entry_info.id;
|
| }
|
| - entry->value = reinterpret_cast<void*>(entries_->length());
|
| + entry->value = reinterpret_cast<void*>(entries_.length());
|
| SnapshotObjectId id = next_id_;
|
| next_id_ += kObjectIdStep;
|
| - entries_->Add(EntryInfo(id, addr));
|
| - ASSERT(static_cast<uint32_t>(entries_->length()) > entries_map_.occupancy());
|
| + entries_.Add(EntryInfo(id, addr, size));
|
| + ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
|
| return id;
|
| }
|
|
|
| @@ -1432,13 +1389,12 @@
|
| void HeapObjectsMap::UpdateHeapObjectsMap() {
|
| HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
|
| "HeapSnapshotsCollection::UpdateHeapObjectsMap");
|
| - HeapIterator iterator(HeapIterator::kFilterUnreachable);
|
| + HeapIterator iterator;
|
| for (HeapObject* obj = iterator.next();
|
| obj != NULL;
|
| obj = iterator.next()) {
|
| - FindOrAddEntry(obj->address());
|
| + FindOrAddEntry(obj->address(), obj->Size());
|
| }
|
| - initial_fill_mode_ = false;
|
| RemoveDeadEntries();
|
| }
|
|
|
| @@ -1448,22 +1404,27 @@
|
| time_intervals_.Add(TimeInterval(next_id_));
|
| int prefered_chunk_size = stream->GetChunkSize();
|
| List<uint32_t> stats_buffer;
|
| - ASSERT(!entries_->is_empty());
|
| - EntryInfo* entry_info = &entries_->first();
|
| - EntryInfo* end_entry_info = &entries_->last() + 1;
|
| + ASSERT(!entries_.is_empty());
|
| + EntryInfo* entry_info = &entries_.first();
|
| + EntryInfo* end_entry_info = &entries_.last() + 1;
|
| for (int time_interval_index = 0;
|
| time_interval_index < time_intervals_.length();
|
| ++time_interval_index) {
|
| TimeInterval& time_interval = time_intervals_[time_interval_index];
|
| SnapshotObjectId time_interval_id = time_interval.id;
|
| - uint32_t entries_count = 0;
|
| + uint32_t entries_size = 0;
|
| + EntryInfo* start_entry_info = entry_info;
|
| while (entry_info < end_entry_info && entry_info->id < time_interval_id) {
|
| - ++entries_count;
|
| + entries_size += entry_info->size;
|
| ++entry_info;
|
| }
|
| - if (time_interval.count != entries_count) {
|
| + uint32_t entries_count =
|
| + static_cast<uint32_t>(entry_info - start_entry_info);
|
| + if (time_interval.count != entries_count ||
|
| + time_interval.size != entries_size) {
|
| stats_buffer.Add(time_interval_index);
|
| stats_buffer.Add(time_interval.count = entries_count);
|
| + stats_buffer.Add(time_interval.size = entries_size);
|
| if (stats_buffer.length() >= prefered_chunk_size) {
|
| OutputStream::WriteResult result = stream->WriteUint32Chunk(
|
| &stats_buffer.first(), stats_buffer.length());
|
| @@ -1483,17 +1444,17 @@
|
|
|
|
|
| void HeapObjectsMap::RemoveDeadEntries() {
|
| - ASSERT(entries_->length() > 0 &&
|
| - entries_->at(0).id == 0 &&
|
| - entries_->at(0).addr == NULL);
|
| + ASSERT(entries_.length() > 0 &&
|
| + entries_.at(0).id == 0 &&
|
| + entries_.at(0).addr == NULL);
|
| int first_free_entry = 1;
|
| - for (int i = 1; i < entries_->length(); ++i) {
|
| - EntryInfo& entry_info = entries_->at(i);
|
| + for (int i = 1; i < entries_.length(); ++i) {
|
| + EntryInfo& entry_info = entries_.at(i);
|
| if (entry_info.accessed) {
|
| if (first_free_entry != i) {
|
| - entries_->at(first_free_entry) = entry_info;
|
| + entries_.at(first_free_entry) = entry_info;
|
| }
|
| - entries_->at(first_free_entry).accessed = false;
|
| + entries_.at(first_free_entry).accessed = false;
|
| HashMap::Entry* entry = entries_map_.Lookup(
|
| entry_info.addr, AddressHash(entry_info.addr), false);
|
| ASSERT(entry);
|
| @@ -1505,8 +1466,8 @@
|
| }
|
| }
|
| }
|
| - entries_->Rewind(first_free_entry);
|
| - ASSERT(static_cast<uint32_t>(entries_->length()) - 1 ==
|
| + entries_.Rewind(first_free_entry);
|
| + ASSERT(static_cast<uint32_t>(entries_.length()) - 1 ==
|
| entries_map_.occupancy());
|
| }
|
|
|
| @@ -1594,7 +1555,7 @@
|
| for (HeapObject* obj = iterator.next();
|
| obj != NULL;
|
| obj = iterator.next()) {
|
| - if (ids_.FindObject(obj->address()) == id) {
|
| + if (ids_.FindEntry(obj->address()) == id) {
|
| ASSERT(object == NULL);
|
| object = obj;
|
| // Can't break -- kFilterUnreachable requires full heap traversal.
|
| @@ -1895,10 +1856,13 @@
|
| const char* name,
|
| int children_count,
|
| int retainers_count) {
|
| + int object_size = object->Size();
|
| + SnapshotObjectId object_id =
|
| + collection_->GetObjectId(object->address(), object_size);
|
| return snapshot_->AddEntry(type,
|
| name,
|
| - collection_->GetObjectId(object->address()),
|
| - object->Size(),
|
| + object_id,
|
| + object_size,
|
| children_count,
|
| retainers_count);
|
| }
|
| @@ -2016,7 +1980,7 @@
|
| // We use JSGlobalProxy because this is what embedder (e.g. browser)
|
| // uses for the global object.
|
| JSGlobalProxy* proxy = JSGlobalProxy::cast(obj);
|
| - SetRootShortcutReference(proxy->map()->prototype());
|
| + SetWindowReference(proxy->map()->prototype());
|
| } else if (obj->IsJSObject()) {
|
| JSObject* js_obj = JSObject::cast(obj);
|
| ExtractClosureReferences(js_obj, entry);
|
| @@ -2187,7 +2151,7 @@
|
| if (!js_obj->IsJSFunction()) return;
|
|
|
| JSFunction* func = JSFunction::cast(js_obj);
|
| - Context* context = func->context();
|
| + Context* context = func->context()->declaration_context();
|
| ScopeInfo* scope_info = context->closure()->shared()->scope_info();
|
|
|
| if (func->shared()->bound()) {
|
| @@ -2284,15 +2248,15 @@
|
| Object* k = dictionary->KeyAt(i);
|
| if (dictionary->IsKey(k)) {
|
| Object* target = dictionary->ValueAt(i);
|
| - SetPropertyReference(
|
| - js_obj, entry, String::cast(k), target);
|
| // We assume that global objects can only have slow properties.
|
| - if (target->IsJSGlobalPropertyCell()) {
|
| - SetPropertyShortcutReference(js_obj,
|
| - entry,
|
| - String::cast(k),
|
| - JSGlobalPropertyCell::cast(
|
| - target)->value());
|
| + Object* value = target->IsJSGlobalPropertyCell()
|
| + ? JSGlobalPropertyCell::cast(target)->value()
|
| + : target;
|
| + if (String::cast(k)->length() > 0) {
|
| + SetPropertyReference(js_obj, entry, String::cast(k), value);
|
| + } else {
|
| + TagObject(value, "(hidden properties)");
|
| + SetInternalReference(js_obj, entry, "hidden_properties", value);
|
| }
|
| }
|
| }
|
| @@ -2661,7 +2625,7 @@
|
| }
|
|
|
|
|
| -void V8HeapExplorer::SetRootShortcutReference(Object* child_obj) {
|
| +void V8HeapExplorer::SetWindowReference(Object* child_obj) {
|
| HeapEntry* child_entry = GetEntry(child_obj);
|
| ASSERT(child_entry != NULL);
|
| filler_->SetNamedAutoIndexReference(
|
| @@ -2743,7 +2707,7 @@
|
| Handle<JSGlobalObject> global_obj = enumerator.at(i);
|
| Object* obj_document;
|
| if (global_obj->GetProperty(*document_string)->ToObject(&obj_document) &&
|
| - obj_document->IsJSObject()) {
|
| + obj_document->IsJSObject()) {
|
| JSObject* document = JSObject::cast(obj_document);
|
| Object* obj_url;
|
| if (document->GetProperty(*url_string)->ToObject(&obj_url) &&
|
| @@ -3297,19 +3261,61 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotGenerator::FillReversePostorderIndexes(
|
| +bool HeapSnapshotGenerator::IsWindowReference(const HeapGraphEdge& edge) {
|
| + ASSERT(edge.from() == snapshot_->root());
|
| + return edge.type() == HeapGraphEdge::kShortcut;
|
| +}
|
| +
|
| +
|
| +void HeapSnapshotGenerator::MarkWindowReachableObjects() {
|
| + List<HeapEntry*> worklist;
|
| +
|
| + Vector<HeapGraphEdge> children = snapshot_->root()->children();
|
| + for (int i = 0; i < children.length(); ++i) {
|
| + if (IsWindowReference(children[i])) {
|
| + worklist.Add(children[i].to());
|
| + }
|
| + }
|
| +
|
| + while (!worklist.is_empty()) {
|
| + HeapEntry* entry = worklist.RemoveLast();
|
| + if (entry->reachable_from_window()) continue;
|
| + entry->set_reachable_from_window();
|
| + Vector<HeapGraphEdge> children = entry->children();
|
| + for (int i = 0; i < children.length(); ++i) {
|
| + HeapEntry* child = children[i].to();
|
| + if (!child->reachable_from_window()) {
|
| + worklist.Add(child);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +static bool IsRetainingEdge(HeapGraphEdge* edge) {
|
| + if (edge->type() == HeapGraphEdge::kShortcut) return false;
|
| + // The edge is not retaining if it goes from system domain
|
| + // (i.e. an object not reachable from window) to the user domain
|
| + // (i.e. a reachable object).
|
| + return edge->from()->reachable_from_window()
|
| + || !edge->to()->reachable_from_window();
|
| +}
|
| +
|
| +
|
| +void HeapSnapshotGenerator::FillPostorderIndexes(
|
| Vector<HeapEntry*>* entries) {
|
| snapshot_->ClearPaint();
|
| int current_entry = 0;
|
| List<HeapEntry*> nodes_to_visit;
|
| - nodes_to_visit.Add(snapshot_->root());
|
| + HeapEntry* root = snapshot_->root();
|
| + nodes_to_visit.Add(root);
|
| snapshot_->root()->paint();
|
| while (!nodes_to_visit.is_empty()) {
|
| HeapEntry* entry = nodes_to_visit.last();
|
| Vector<HeapGraphEdge> children = entry->children();
|
| bool has_new_edges = false;
|
| for (int i = 0; i < children.length(); ++i) {
|
| - if (children[i].type() == HeapGraphEdge::kShortcut) continue;
|
| + if (entry != root && !IsRetainingEdge(&children[i])) continue;
|
| HeapEntry* child = children[i].to();
|
| if (!child->painted()) {
|
| nodes_to_visit.Add(child);
|
| @@ -3344,6 +3350,7 @@
|
| const Vector<HeapEntry*>& entries,
|
| Vector<int>* dominators) {
|
| if (entries.length() == 0) return true;
|
| + HeapEntry* root = snapshot_->root();
|
| const int entries_length = entries.length(), root_index = entries_length - 1;
|
| static const int kNoDominator = -1;
|
| for (int i = 0; i < root_index; ++i) (*dominators)[i] = kNoDominator;
|
| @@ -3372,8 +3379,8 @@
|
| int new_idom_index = kNoDominator;
|
| Vector<HeapGraphEdge*> rets = entries[i]->retainers();
|
| for (int j = 0; j < rets.length(); ++j) {
|
| - if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
|
| - int ret_index = rets[j]->From()->ordered_index();
|
| + if (rets[j]->from() != root && !IsRetainingEdge(rets[j])) continue;
|
| + int ret_index = rets[j]->from()->ordered_index();
|
| if (dominators->at(ret_index) != kNoDominator) {
|
| new_idom_index = new_idom_index == kNoDominator
|
| ? ret_index
|
| @@ -3399,9 +3406,10 @@
|
|
|
|
|
| bool HeapSnapshotGenerator::SetEntriesDominators() {
|
| - // This array is used for maintaining reverse postorder of nodes.
|
| + MarkWindowReachableObjects();
|
| + // This array is used for maintaining postorder of nodes.
|
| ScopedVector<HeapEntry*> ordered_entries(snapshot_->entries()->length());
|
| - FillReversePostorderIndexes(&ordered_entries);
|
| + FillPostorderIndexes(&ordered_entries);
|
| ScopedVector<int> dominators(ordered_entries.length());
|
| if (!BuildDominatorTree(ordered_entries, &dominators)) return false;
|
| for (int i = 0; i < ordered_entries.length(); ++i) {
|
| @@ -3576,16 +3584,39 @@
|
| }
|
|
|
|
|
| +void HeapSnapshotJSONSerializer::CalculateNodeIndexes(
|
| + const List<HeapEntry*>& nodes) {
|
| + // type,name,id,self_size,retained_size,dominator,children_index.
|
| + const int node_fields_count = 7;
|
| + // Root must be the first.
|
| + ASSERT(nodes.first() == snapshot_->root());
|
| + // Rewrite node indexes, so they refer to actual array positions. Do this
|
| + // only once.
|
| + if (nodes[0]->entry_index() == -1) {
|
| + int index = 0;
|
| + for (int i = 0; i < nodes.length(); ++i, index += node_fields_count) {
|
| + nodes[i]->set_entry_index(index);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| void HeapSnapshotJSONSerializer::SerializeImpl() {
|
| + List<HeapEntry*>& nodes = *(snapshot_->entries());
|
| + CalculateNodeIndexes(nodes);
|
| writer_->AddCharacter('{');
|
| writer_->AddString("\"snapshot\":{");
|
| SerializeSnapshot();
|
| if (writer_->aborted()) return;
|
| writer_->AddString("},\n");
|
| writer_->AddString("\"nodes\":[");
|
| - SerializeNodes();
|
| + SerializeNodes(nodes);
|
| if (writer_->aborted()) return;
|
| writer_->AddString("],\n");
|
| + writer_->AddString("\"edges\":[");
|
| + SerializeEdges(nodes);
|
| + if (writer_->aborted()) return;
|
| + writer_->AddString("],\n");
|
| writer_->AddString("\"strings\":[");
|
| SerializeStrings();
|
| if (writer_->aborted()) return;
|
| @@ -3630,7 +3661,8 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) {
|
| +void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
|
| + bool first_edge) {
|
| // The buffer needs space for 3 ints, 3 commas and \0
|
| static const int kBufferSize =
|
| MaxDecimalDigitsIn<sizeof(int)>::kSigned * 3 + 3 + 1; // NOLINT
|
| @@ -3640,7 +3672,9 @@
|
| || edge->type() == HeapGraphEdge::kWeak
|
| ? edge->index() : GetStringId(edge->name());
|
| int buffer_pos = 0;
|
| - buffer[buffer_pos++] = ',';
|
| + if (!first_edge) {
|
| + buffer[buffer_pos++] = ',';
|
| + }
|
| buffer_pos = itoa(edge->type(), buffer, buffer_pos);
|
| buffer[buffer_pos++] = ',';
|
| buffer_pos = itoa(edge_name_or_index, buffer, buffer_pos);
|
| @@ -3651,17 +3685,33 @@
|
| }
|
|
|
|
|
| -void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
|
| +void HeapSnapshotJSONSerializer::SerializeEdges(const List<HeapEntry*>& nodes) {
|
| + bool first_edge = true;
|
| + for (int i = 0; i < nodes.length(); ++i) {
|
| + HeapEntry* entry = nodes[i];
|
| + Vector<HeapGraphEdge> children = entry->children();
|
| + for (int j = 0; j < children.length(); ++j) {
|
| + SerializeEdge(&children[j], first_edge);
|
| + first_edge = false;
|
| + if (writer_->aborted()) return;
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry,
|
| + int edges_index) {
|
| // The buffer needs space for 6 ints, 1 uint32_t, 7 commas, \n and \0
|
| static const int kBufferSize =
|
| 6 * MaxDecimalDigitsIn<sizeof(int)>::kSigned // NOLINT
|
| + MaxDecimalDigitsIn<sizeof(uint32_t)>::kUnsigned // NOLINT
|
| + 7 + 1 + 1;
|
| EmbeddedVector<char, kBufferSize> buffer;
|
| - Vector<HeapGraphEdge> children = entry->children();
|
| int buffer_pos = 0;
|
| buffer[buffer_pos++] = '\n';
|
| - buffer[buffer_pos++] = ',';
|
| + if (entry->entry_index() != 0) {
|
| + buffer[buffer_pos++] = ',';
|
| + }
|
| buffer_pos = itoa(entry->type(), buffer, buffer_pos);
|
| buffer[buffer_pos++] = ',';
|
| buffer_pos = itoa(GetStringId(entry->name()), buffer, buffer_pos);
|
| @@ -3674,93 +3724,19 @@
|
| buffer[buffer_pos++] = ',';
|
| buffer_pos = itoa(entry->dominator()->entry_index(), buffer, buffer_pos);
|
| buffer[buffer_pos++] = ',';
|
| - buffer_pos = itoa(children.length(), buffer, buffer_pos);
|
| + buffer_pos = itoa(edges_index, buffer, buffer_pos);
|
| buffer[buffer_pos++] = '\0';
|
| writer_->AddString(buffer.start());
|
| - for (int i = 0; i < children.length(); ++i) {
|
| - SerializeEdge(&children[i]);
|
| - if (writer_->aborted()) return;
|
| - }
|
| }
|
|
|
|
|
| -void HeapSnapshotJSONSerializer::SerializeNodes() {
|
| - // The first (zero) item of nodes array is an object describing node
|
| - // serialization layout. We use a set of macros to improve
|
| - // readability.
|
| -#define JSON_A(s) "["s"]"
|
| -#define JSON_O(s) "{"s"}"
|
| -#define JSON_S(s) "\""s"\""
|
| - writer_->AddString(JSON_O(
|
| - JSON_S("fields") ":" JSON_A(
|
| - JSON_S("type")
|
| - "," JSON_S("name")
|
| - "," JSON_S("id")
|
| - "," JSON_S("self_size")
|
| - "," JSON_S("retained_size")
|
| - "," JSON_S("dominator")
|
| - "," JSON_S("children_count")
|
| - "," JSON_S("children"))
|
| - "," JSON_S("types") ":" JSON_A(
|
| - JSON_A(
|
| - JSON_S("hidden")
|
| - "," JSON_S("array")
|
| - "," JSON_S("string")
|
| - "," JSON_S("object")
|
| - "," JSON_S("code")
|
| - "," JSON_S("closure")
|
| - "," JSON_S("regexp")
|
| - "," JSON_S("number")
|
| - "," JSON_S("native")
|
| - "," JSON_S("synthetic"))
|
| - "," JSON_S("string")
|
| - "," JSON_S("number")
|
| - "," JSON_S("number")
|
| - "," JSON_S("number")
|
| - "," JSON_S("number")
|
| - "," JSON_S("number")
|
| - "," JSON_O(
|
| - JSON_S("fields") ":" JSON_A(
|
| - JSON_S("type")
|
| - "," JSON_S("name_or_index")
|
| - "," JSON_S("to_node"))
|
| - "," JSON_S("types") ":" JSON_A(
|
| - JSON_A(
|
| - JSON_S("context")
|
| - "," JSON_S("element")
|
| - "," JSON_S("property")
|
| - "," JSON_S("internal")
|
| - "," JSON_S("hidden")
|
| - "," JSON_S("shortcut")
|
| - "," JSON_S("weak"))
|
| - "," JSON_S("string_or_number")
|
| - "," JSON_S("node"))))));
|
| -#undef JSON_S
|
| -#undef JSON_O
|
| -#undef JSON_A
|
| -
|
| - const int node_fields_count = 7;
|
| - // type,name,id,self_size,retained_size,dominator,children_count.
|
| +void HeapSnapshotJSONSerializer::SerializeNodes(const List<HeapEntry*>& nodes) {
|
| const int edge_fields_count = 3; // type,name|index,to_node.
|
| -
|
| - List<HeapEntry*>& nodes = *(snapshot_->entries());
|
| - // Root must be the first.
|
| - ASSERT(nodes.first() == snapshot_->root());
|
| - // Rewrite node indexes, so they refer to actual array positions. Do this
|
| - // only once.
|
| - if (nodes[0]->entry_index() == -1) {
|
| - // Nodes start from array index 1.
|
| - int index = 1;
|
| - for (int i = 0; i < nodes.length(); ++i) {
|
| - HeapEntry* node = nodes[i];
|
| - node->set_entry_index(index);
|
| - index += node_fields_count +
|
| - node->children().length() * edge_fields_count;
|
| - }
|
| - }
|
| -
|
| + int edges_index = 0;
|
| for (int i = 0; i < nodes.length(); ++i) {
|
| - SerializeNode(nodes[i]);
|
| + HeapEntry* entry = nodes[i];
|
| + SerializeNode(entry, edges_index);
|
| + edges_index += entry->children().length() * edge_fields_count;
|
| if (writer_->aborted()) return;
|
| }
|
| }
|
| @@ -3772,6 +3748,61 @@
|
| writer_->AddString("\"");
|
| writer_->AddString(",\"uid\":");
|
| writer_->AddNumber(snapshot_->uid());
|
| + writer_->AddString(",\"meta\":");
|
| + // The object describing node serialization layout.
|
| + // We use a set of macros to improve readability.
|
| +#define JSON_A(s) "["s"]"
|
| +#define JSON_O(s) "{"s"}"
|
| +#define JSON_S(s) "\""s"\""
|
| + writer_->AddString(JSON_O(
|
| + JSON_S("node_fields") ":" JSON_A(
|
| + JSON_S("type") ","
|
| + JSON_S("name") ","
|
| + JSON_S("id") ","
|
| + JSON_S("self_size") ","
|
| + JSON_S("retained_size") ","
|
| + JSON_S("dominator") ","
|
| + JSON_S("edges_index")) ","
|
| + JSON_S("node_types") ":" JSON_A(
|
| + JSON_A(
|
| + JSON_S("hidden") ","
|
| + JSON_S("array") ","
|
| + JSON_S("string") ","
|
| + JSON_S("object") ","
|
| + JSON_S("code") ","
|
| + JSON_S("closure") ","
|
| + JSON_S("regexp") ","
|
| + JSON_S("number") ","
|
| + JSON_S("native") ","
|
| + JSON_S("synthetic")) ","
|
| + JSON_S("string") ","
|
| + JSON_S("number") ","
|
| + JSON_S("number") ","
|
| + JSON_S("number") ","
|
| + JSON_S("number") ","
|
| + JSON_S("number")) ","
|
| + JSON_S("edge_fields") ":" JSON_A(
|
| + JSON_S("type") ","
|
| + JSON_S("name_or_index") ","
|
| + JSON_S("to_node")) ","
|
| + JSON_S("edge_types") ":" JSON_A(
|
| + JSON_A(
|
| + JSON_S("context") ","
|
| + JSON_S("element") ","
|
| + JSON_S("property") ","
|
| + JSON_S("internal") ","
|
| + JSON_S("hidden") ","
|
| + JSON_S("shortcut") ","
|
| + JSON_S("weak")) ","
|
| + JSON_S("string_or_number") ","
|
| + JSON_S("node"))));
|
| +#undef JSON_S
|
| +#undef JSON_O
|
| +#undef JSON_A
|
| + writer_->AddString(",\"node_count\":");
|
| + writer_->AddNumber(snapshot_->entries()->length());
|
| + writer_->AddString(",\"edge_count\":");
|
| + writer_->AddNumber(snapshot_->number_of_edges());
|
| }
|
|
|
|
|
|
|