Revision: 4848
Author: [email protected]
Date: Fri Jun 11 09:34:59 2010
Log: The new JS Heap Profiler: the main part.

There is no test in this patch, because the test uses V8 API,
which is coming in a separate change list.

Review URL: http://codereview.chromium.org/2722005
http://code.google.com/p/v8/source/detail?r=4848

Modified:
 /branches/bleeding_edge/src/profile-generator-inl.h
 /branches/bleeding_edge/src/profile-generator.cc
 /branches/bleeding_edge/src/profile-generator.h

=======================================
--- /branches/bleeding_edge/src/profile-generator-inl.h Tue May 18 07:19:33 2010 +++ /branches/bleeding_edge/src/profile-generator-inl.h Fri Jun 11 09:34:59 2010
@@ -129,6 +129,17 @@
     default: return NULL;
   }
 }
+
+
+template<class Visitor>
+void HeapEntriesMap::Apply(Visitor* visitor) {
+  for (HashMap::Entry* p = entries_.Start();
+       p != NULL;
+       p = entries_.Next(p)) {
+    if (!IsAlias(p->value))
+      visitor->Apply(reinterpret_cast<HeapEntry*>(p->value));
+  }
+}

 } }  // namespace v8::internal

=======================================
--- /branches/bleeding_edge/src/profile-generator.cc Wed Jun 9 00:00:33 2010 +++ /branches/bleeding_edge/src/profile-generator.cc Fri Jun 11 09:34:59 2010
@@ -29,11 +29,12 @@

 #include "v8.h"
 #include "global-handles.h"
+#include "scopeinfo.h"
+#include "top.h"
+#include "zone-inl.h"

 #include "profile-generator-inl.h"

-#include "../include/v8-profiler.h"
-
 namespace v8 {
 namespace internal {

@@ -810,6 +811,794 @@

   profiles_->AddPathToCurrentProfiles(entries);
 }
+
+
+HeapGraphEdge::HeapGraphEdge(Type type,
+                             const char* name,
+                             HeapEntry* from,
+                             HeapEntry* to)
+    : type_(type), name_(name), from_(from), to_(to) {
+  ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY);
+}
+
+
+HeapGraphEdge::HeapGraphEdge(int index,
+                             HeapEntry* from,
+                             HeapEntry* to)
+    : type_(ELEMENT), index_(index), from_(from), to_(to) {
+}
+
+
+static void DeleteHeapGraphEdge(HeapGraphEdge** edge_ptr) {
+  delete *edge_ptr;
+}
+
+
+static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) {
+  delete *path_ptr;
+}
+
+
+HeapEntry::~HeapEntry() {
+  children_.Iterate(DeleteHeapGraphEdge);
+  retaining_paths_.Iterate(DeleteHeapGraphPath);
+}
+
+
+void HeapEntry::SetClosureReference(const char* name, HeapEntry* entry) {
+  HeapGraphEdge* edge =
+ new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry);
+  children_.Add(edge);
+  entry->retainers_.Add(edge);
+}
+
+
+void HeapEntry::SetElementReference(int index, HeapEntry* entry) {
+  HeapGraphEdge* edge = new HeapGraphEdge(index, this, entry);
+  children_.Add(edge);
+  entry->retainers_.Add(edge);
+}
+
+
+void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) {
+  HeapGraphEdge* edge =
+      new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry);
+  children_.Add(edge);
+  entry->retainers_.Add(edge);
+}
+
+
+void HeapEntry::SetAutoIndexReference(HeapEntry* entry) {
+  SetElementReference(next_auto_index_++, entry);
+}
+
+
+int HeapEntry::TotalSize() {
+  return total_size_ != kUnknownSize ? total_size_ : CalculateTotalSize();
+}
+
+
+int HeapEntry::NonSharedTotalSize() {
+  return non_shared_total_size_ != kUnknownSize ?
+      non_shared_total_size_ : CalculateNonSharedTotalSize();
+}
+
+
+int HeapEntry::CalculateTotalSize() {
+  snapshot_->ClearPaint();
+  List<HeapEntry*> list(10);
+  list.Add(this);
+  total_size_ = self_size_;
+  this->PaintReachable();
+  while (!list.is_empty()) {
+    HeapEntry* entry = list.RemoveLast();
+    const int children_count = entry->children_.length();
+    for (int i = 0; i < children_count; ++i) {
+      HeapEntry* child = entry->children_[i]->to();
+      if (!child->painted_reachable()) {
+        list.Add(child);
+        child->PaintReachable();
+        total_size_ += child->self_size_;
+      }
+    }
+  }
+  return total_size_;
+}
+
+
+namespace {
+
+class NonSharedSizeCalculator {
+ public:
+  NonSharedSizeCalculator()
+      : non_shared_total_size_(0) {
+  }
+
+  int non_shared_total_size() const { return non_shared_total_size_; }
+
+  void Apply(HeapEntry* entry) {
+    if (entry->painted_reachable()) {
+      non_shared_total_size_ += entry->self_size();
+    }
+  }
+
+ private:
+  int non_shared_total_size_;
+};
+
+}  // namespace
+
+int HeapEntry::CalculateNonSharedTotalSize() {
+  // To calculate non-shared total size, first we paint all reachable
+  // nodes in one color, then we paint all nodes reachable from other
+  // nodes with a different color. Then we consider only nodes painted
+  // with the first color for caclulating the total size.
+  snapshot_->ClearPaint();
+  List<HeapEntry*> list(10);
+  list.Add(this);
+  this->PaintReachable();
+  while (!list.is_empty()) {
+    HeapEntry* entry = list.RemoveLast();
+    const int children_count = entry->children_.length();
+    for (int i = 0; i < children_count; ++i) {
+      HeapEntry* child = entry->children_[i]->to();
+      if (!child->painted_reachable()) {
+        list.Add(child);
+        child->PaintReachable();
+      }
+    }
+  }
+
+  List<HeapEntry*> list2(10);
+  if (this != snapshot_->root()) {
+    list2.Add(snapshot_->root());
+    snapshot_->root()->PaintReachableFromOthers();
+  }
+  while (!list2.is_empty()) {
+    HeapEntry* entry = list2.RemoveLast();
+    const int children_count = entry->children_.length();
+    for (int i = 0; i < children_count; ++i) {
+      HeapEntry* child = entry->children_[i]->to();
+      if (child != this && child->not_painted_reachable_from_others()) {
+        list2.Add(child);
+        child->PaintReachableFromOthers();
+      }
+    }
+  }
+
+  NonSharedSizeCalculator calculator;
+  snapshot_->IterateEntries(&calculator);
+  return calculator.non_shared_total_size();
+}
+
+
+class CachedHeapGraphPath {
+ public:
+  CachedHeapGraphPath()
+      : nodes_(NodesMatch) { }
+  CachedHeapGraphPath(const CachedHeapGraphPath& src)
+ : nodes_(NodesMatch, &HashMap::DefaultAllocator, src.nodes_.capacity()),
+        path_(src.path_.length() + 1) {
+    for (HashMap::Entry* p = src.nodes_.Start();
+         p != NULL;
+         p = src.nodes_.Next(p)) {
+      nodes_.Lookup(p->key, p->hash, true);
+    }
+    path_.AddAll(src.path_);
+  }
+  void Add(HeapGraphEdge* edge) {
+    nodes_.Lookup(edge->to(), Hash(edge->to()), true);
+    path_.Add(edge);
+  }
+  bool ContainsNode(HeapEntry* node) {
+    return nodes_.Lookup(node, Hash(node), false) != NULL;
+  }
+  const List<HeapGraphEdge*>* path() const { return &path_; }
+
+ private:
+  static uint32_t Hash(HeapEntry* entry) {
+    return static_cast<uint32_t>(reinterpret_cast<intptr_t>(entry));
+  }
+  static bool NodesMatch(void* key1, void* key2) { return key1 == key2; }
+
+  HashMap nodes_;
+  List<HeapGraphEdge*> path_;
+};
+
+
+const List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() {
+  if (retaining_paths_.length() == 0 && retainers_.length() != 0) {
+    CachedHeapGraphPath path;
+    FindRetainingPaths(this, &path);
+  }
+  return &retaining_paths_;
+}
+
+
+void HeapEntry::FindRetainingPaths(HeapEntry* node,
+                                   CachedHeapGraphPath* prev_path) {
+  for (int i = 0; i < node->retainers_.length(); ++i) {
+    HeapGraphEdge* ret_edge = node->retainers_[i];
+    if (prev_path->ContainsNode(ret_edge->from())) continue;
+    if (ret_edge->from() != snapshot_->root()) {
+      CachedHeapGraphPath path(*prev_path);
+      path.Add(ret_edge);
+      FindRetainingPaths(ret_edge->from(), &path);
+    } else {
+      HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path());
+      ret_path->Set(0, ret_edge);
+      retaining_paths_.Add(ret_path);
+    }
+  }
+}
+
+
+static void RemoveEdge(List<HeapGraphEdge*>* list, HeapGraphEdge* edge) {
+  for (int i = 0; i < list->length(); ) {
+    if (list->at(i) == edge) {
+      list->Remove(i);
+      return;
+    } else {
+      ++i;
+    }
+  }
+  UNREACHABLE();
+}
+
+
+void HeapEntry::RemoveChild(HeapGraphEdge* edge) {
+  RemoveEdge(&children_, edge);
+  delete edge;
+}
+
+
+void HeapEntry::RemoveRetainer(HeapGraphEdge* edge) {
+  RemoveEdge(&retainers_, edge);
+}
+
+
+void HeapEntry::CutEdges() {
+  for (int i = 0; i < children_.length(); ++i) {
+    HeapGraphEdge* edge = children_[i];
+    edge->to()->RemoveRetainer(edge);
+  }
+  children_.Iterate(DeleteHeapGraphEdge);
+  children_.Clear();
+
+  for (int i = 0; i < retainers_.length(); ++i) {
+    HeapGraphEdge* edge = retainers_[i];
+    edge->from()->RemoveChild(edge);
+  }
+  retainers_.Clear();
+}
+
+
+void HeapEntry::Print(int max_depth, int indent) {
+  OS::Print("%6d %6d %6d", self_size_, TotalSize(), NonSharedTotalSize());
+  if (type_ != STRING) {
+    OS::Print("%s %.40s\n", TypeAsString(), name_);
+  } else {
+    OS::Print("\"");
+    const char* c = name_;
+    while (*c && (c - name_) <= 40) {
+      if (*c != '\n')
+        OS::Print("%c", *c);
+      else
+        OS::Print("\\n");
+      ++c;
+    }
+    OS::Print("\"\n");
+  }
+  if (--max_depth == 0) return;
+  const int children_count = children_.length();
+  for (int i = 0; i < children_count; ++i) {
+    HeapGraphEdge* edge = children_[i];
+    switch (edge->type()) {
+      case HeapGraphEdge::CONTEXT_VARIABLE:
+        OS::Print("  %*c #%s: ", indent, ' ', edge->name());
+        break;
+      case HeapGraphEdge::ELEMENT:
+        OS::Print("  %*c %d: ", indent, ' ', edge->index());
+        break;
+      case HeapGraphEdge::PROPERTY:
+        OS::Print("  %*c %s: ", indent, ' ', edge->name());
+        break;
+      default:
+        OS::Print("!!! unknown edge type: %d ", edge->type());
+    }
+    edge->to()->Print(max_depth, indent + 2);
+  }
+}
+
+
+const char* HeapEntry::TypeAsString() {
+  switch (type_) {
+    case INTERNAL: return "/internal/";
+    case JS_OBJECT: return "/object/";
+    case CLOSURE: return "/closure/";
+    case STRING: return "/string/";
+    case CODE: return "/code/";
+    case ARRAY: return "/array/";
+    default: return "???";
+  }
+}
+
+
+HeapGraphPath::HeapGraphPath(const List<HeapGraphEdge*>& path)
+    : path_(path.length() + 1) {
+  Add(NULL);
+  for (int i = path.length() - 1; i >= 0; --i) {
+    Add(path[i]);
+  }
+}
+
+
+void HeapGraphPath::Print() {
+  path_[0]->from()->Print(1, 0);
+  for (int i = 0; i < path_.length(); ++i) {
+    OS::Print(" -> ");
+    HeapGraphEdge* edge = path_[i];
+    switch (edge->type()) {
+      case HeapGraphEdge::CONTEXT_VARIABLE:
+        OS::Print("[#%s] ", edge->name());
+        break;
+      case HeapGraphEdge::ELEMENT:
+        OS::Print("[%d] ", edge->index());
+        break;
+      case HeapGraphEdge::PROPERTY:
+        OS::Print("[%s] ", edge->name());
+        break;
+      default:
+        OS::Print("!!! unknown edge type: %d ", edge->type());
+    }
+    edge->to()->Print(1, 0);
+  }
+  OS::Print("\n");
+}
+
+
+class IndexedReferencesExtractor : public ObjectVisitor {
+ public:
+  IndexedReferencesExtractor(HeapSnapshot* snapshot, HeapEntry* parent)
+      : snapshot_(snapshot),
+        parent_(parent) {
+  }
+
+  void VisitPointer(Object** o) {
+    if (!(*o)->IsHeapObject()) return;
+    HeapEntry* entry = snapshot_->GetEntry(HeapObject::cast(*o));
+    if (entry != NULL) {
+      parent_->SetAutoIndexReference(entry);
+    }
+  }
+
+  void VisitPointers(Object** start, Object** end) {
+    for (Object** p = start; p < end; p++) VisitPointer(p);
+  }
+
+ private:
+  HeapSnapshot* snapshot_;
+  HeapEntry* parent_;
+};
+
+
+HeapEntriesMap::HeapEntriesMap()
+    : entries_(HeapObjectsMatch) {
+}
+
+
+HeapEntriesMap::~HeapEntriesMap() {
+  for (HashMap::Entry* p = entries_.Start();
+       p != NULL;
+       p = entries_.Next(p)) {
+    if (!IsAlias(p->value)) delete reinterpret_cast<HeapEntry*>(p->value);
+  }
+}
+
+
+void HeapEntriesMap::Alias(HeapObject* object, HeapEntry* entry) {
+ HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
+  if (cache_entry->value == NULL)
+    cache_entry->value = reinterpret_cast<void*>(
+        reinterpret_cast<intptr_t>(entry) | kAliasTag);
+}
+
+
+void HeapEntriesMap::Apply(void (HeapEntry::*Func)(void)) {
+  for (HashMap::Entry* p = entries_.Start();
+       p != NULL;
+       p = entries_.Next(p)) {
+ if (!IsAlias(p->value)) (reinterpret_cast<HeapEntry*>(p->value)->*Func)();
+  }
+}
+
+
+HeapEntry* HeapEntriesMap::Map(HeapObject* object) {
+ HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false);
+  return cache_entry != NULL ?
+      reinterpret_cast<HeapEntry*>(
+ reinterpret_cast<intptr_t>(cache_entry->value) & (~kAliasTag)) : NULL;
+}
+
+
+void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) {
+ HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
+  ASSERT(cache_entry->value == NULL);
+  cache_entry->value = entry;
+}
+
+
+HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
+                           const char* title,
+                           unsigned uid)
+    : collection_(collection),
+      title_(title),
+      uid_(uid),
+      root_(this) {
+}
+
+
+void HeapSnapshot::ClearPaint() {
+  root_.ClearPaint();
+  entries_.Apply(&HeapEntry::ClearPaint);
+}
+
+
+HeapEntry* HeapSnapshot::GetEntry(Object* obj) {
+  if (!obj->IsHeapObject()) return NULL;
+  HeapObject* object = HeapObject::cast(obj);
+
+  {
+    HeapEntry* existing = FindEntry(object);
+    if (existing != NULL) return existing;
+  }
+
+  // Add new entry.
+  if (object->IsJSFunction()) {
+    JSFunction* func = JSFunction::cast(object);
+    SharedFunctionInfo* shared = func->shared();
+    String* name = String::cast(shared->name())->length() > 0 ?
+        String::cast(shared->name()) : shared->inferred_name();
+ return AddEntry(object, HeapEntry::CLOSURE, collection_->GetName(name));
+  } else if (object->IsJSObject()) {
+    return AddEntry(object,
+                    HeapEntry::JS_OBJECT,
+                    collection_->GetName(
+                        JSObject::cast(object)->constructor_name()));
+  } else if (object->IsJSGlobalPropertyCell()) {
+ HeapEntry* value = GetEntry(JSGlobalPropertyCell::cast(object)->value()); + // If GPC references an object that we have interest in, add the object.
+    // We don't store HeapEntries for GPCs. Instead, we make our hash map
+    // to point to object's HeapEntry by GPCs address.
+    if (value != NULL) AddEntryAlias(object, value);
+    return value;
+  } else if (object->IsString()) {
+    return AddEntry(object,
+                    HeapEntry::STRING,
+                    collection_->GetName(String::cast(object)));
+  } else if (object->IsCode()
+             || object->IsSharedFunctionInfo()
+             || object->IsScript()) {
+    return AddEntry(object, HeapEntry::CODE);
+  } else if (object->IsFixedArray()) {
+    return AddEntry(object, HeapEntry::ARRAY);
+  }
+  // No interest in this object.
+  return NULL;
+}
+
+
+void HeapSnapshot::SetClosureReference(HeapEntry* parent,
+                                       String* reference_name,
+                                       Object* child) {
+  HeapEntry* child_entry = GetEntry(child);
+  if (child_entry != NULL) {
+    parent->SetClosureReference(
+        collection_->GetName(reference_name), child_entry);
+  }
+}
+
+
+void HeapSnapshot::SetElementReference(HeapEntry* parent,
+                                       int index,
+                                       Object* child) {
+  HeapEntry* child_entry = GetEntry(child);
+  if (child_entry != NULL) {
+    parent->SetElementReference(index, child_entry);
+  }
+}
+
+
+void HeapSnapshot::SetPropertyReference(HeapEntry* parent,
+                                        String* reference_name,
+                                        Object* child) {
+  HeapEntry* child_entry = GetEntry(child);
+  if (child_entry != NULL) {
+    parent->SetPropertyReference(
+        collection_->GetName(reference_name), child_entry);
+  }
+}
+
+
+HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
+                                  HeapEntry::Type type,
+                                  const char* name) {
+  HeapEntry* entry = new HeapEntry(this,
+                                   type,
+                                   name,
+                                   GetObjectSize(object),
+                                   GetObjectSecurityToken(object));
+  entries_.Pair(object, entry);
+
+  // Detect, if this is a JS global object of the current context, and
+  // add it to snapshot's roots. There can be several JS global objects
+  // in a context.
+  if (object->IsJSGlobalProxy()) {
+    int global_security_token = GetGlobalSecurityToken();
+    int object_security_token =
+        collection_->token_enumerator()->GetTokenId(
+            Context::cast(
+                JSGlobalProxy::cast(object)->context())->security_token());
+    if (object_security_token == TokenEnumerator::kNoSecurityToken
+        || object_security_token == global_security_token) {
+      HeapEntry* global_object_entry =
+          GetEntry(HeapObject::cast(object->map()->prototype()));
+      ASSERT(global_object_entry != NULL);
+      root_.SetAutoIndexReference(global_object_entry);
+    }
+  }
+
+  return entry;
+}
+
+
+namespace {
+
+class EdgesCutter {
+ public:
+  explicit EdgesCutter(int global_security_token)
+      : global_security_token_(global_security_token) {
+  }
+
+  void Apply(HeapEntry* entry) {
+    if (entry->security_token_id() != TokenEnumerator::kNoSecurityToken
+        && entry->security_token_id() != global_security_token_) {
+      entry->CutEdges();
+    }
+  }
+
+ private:
+  const int global_security_token_;
+};
+
+}  // namespace
+
+void HeapSnapshot::CutObjectsFromForeignSecurityContexts() {
+  EdgesCutter cutter(GetGlobalSecurityToken());
+  entries_.Apply(&cutter);
+}
+
+
+int HeapSnapshot::GetGlobalSecurityToken() {
+  return collection_->token_enumerator()->GetTokenId(
+      Top::context()->global()->global_context()->security_token());
+}
+
+
+int HeapSnapshot::GetObjectSize(HeapObject* obj) {
+  return obj->IsJSObject() ?
+      CalculateNetworkSize(JSObject::cast(obj)) : obj->Size();
+}
+
+
+int HeapSnapshot::GetObjectSecurityToken(HeapObject* obj) {
+  if (obj->IsGlobalContext()) {
+    return collection_->token_enumerator()->GetTokenId(
+        Context::cast(obj)->security_token());
+  } else {
+    return TokenEnumerator::kNoSecurityToken;
+  }
+}
+
+
+int HeapSnapshot::CalculateNetworkSize(JSObject* obj) {
+  int size = obj->Size();
+  // If 'properties' and 'elements' are non-empty (thus, non-shared),
+  // take their size into account.
+  if (FixedArray::cast(obj->properties())->length() != 0) {
+    size += obj->properties()->Size();
+  }
+  if (FixedArray::cast(obj->elements())->length() != 0) {
+    size += obj->elements()->Size();
+  }
+  // For functions, also account non-empty context and literals sizes.
+  if (obj->IsJSFunction()) {
+    JSFunction* f = JSFunction::cast(obj);
+    if (f->unchecked_context()->IsContext()) {
+      size += f->context()->Size();
+    }
+    if (f->literals()->length() != 0) {
+      size += f->literals()->Size();
+    }
+  }
+  return size;
+}
+
+
+void HeapSnapshot::Print(int max_depth) {
+  root_.Print(max_depth, 0);
+}
+
+
+HeapSnapshotsCollection::HeapSnapshotsCollection()
+    : snapshots_uids_(HeapSnapshotsMatch),
+      token_enumerator_(new TokenEnumerator()) {
+}
+
+
+static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) {
+  delete *snapshot_ptr;
+}
+
+
+HeapSnapshotsCollection::~HeapSnapshotsCollection() {
+  delete token_enumerator_;
+  snapshots_.Iterate(DeleteHeapSnapshot);
+}
+
+
+HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
+                                                   unsigned uid) {
+  HeapSnapshot* snapshot = new HeapSnapshot(this, name, uid);
+  snapshots_.Add(snapshot);
+  HashMap::Entry* entry =
+      snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
+                             static_cast<uint32_t>(snapshot->uid()),
+                             true);
+  ASSERT(entry->value == NULL);
+  entry->value = snapshot;
+  return snapshot;
+}
+
+
+HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
+ HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid), + static_cast<uint32_t>(uid),
+                                                 false);
+ return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL;
+}
+
+
+HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot)
+    : snapshot_(snapshot) {
+}
+
+
+void HeapSnapshotGenerator::GenerateSnapshot() {
+  AssertNoAllocation no_alloc;
+
+  // Iterate heap contents.
+  HeapIterator iterator;
+ for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
+    ExtractReferences(obj);
+  }
+
+  snapshot_->CutObjectsFromForeignSecurityContexts();
+}
+
+
+void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) {
+  HeapEntry* entry = snapshot_->GetEntry(obj);
+  if (entry == NULL) return;
+  if (entry->visited()) return;
+
+  if (obj->IsJSObject()) {
+    JSObject* js_obj = JSObject::cast(obj);
+    ExtractClosureReferences(js_obj, entry);
+    ExtractPropertyReferences(js_obj, entry);
+    ExtractElementReferences(js_obj, entry);
+    snapshot_->SetPropertyReference(
+        entry, Heap::prototype_symbol(), js_obj->map()->prototype());
+  } else if (obj->IsJSGlobalPropertyCell()) {
+    JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(obj);
+    snapshot_->SetElementReference(entry, 0, cell->value());
+  } else if (obj->IsString()) {
+    if (obj->IsConsString()) {
+      ConsString* cs = ConsString::cast(obj);
+      snapshot_->SetElementReference(entry, 0, cs->first());
+      snapshot_->SetElementReference(entry, 1, cs->second());
+    }
+ } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) {
+    IndexedReferencesExtractor refs_extractor(snapshot_, entry);
+    obj->Iterate(&refs_extractor);
+  } else if (obj->IsFixedArray()) {
+    IndexedReferencesExtractor refs_extractor(snapshot_, entry);
+    obj->Iterate(&refs_extractor);
+  }
+  entry->MarkAsVisited();
+}
+
+
+void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj,
+                                                     HeapEntry* entry) {
+  if (js_obj->IsJSFunction()) {
+    HandleScope hs;
+    JSFunction* func = JSFunction::cast(js_obj);
+    Context* context = func->context();
+    ZoneScope zscope(DELETE_ON_EXIT);
+    ScopeInfo<ZoneListAllocationPolicy> scope_info(
+        context->closure()->shared()->code());
+    int locals_number = scope_info.NumberOfLocals();
+    for (int i = 0; i < locals_number; ++i) {
+      String* local_name = *scope_info.LocalName(i);
+      int idx = ScopeInfo<>::ContextSlotIndex(
+          context->closure()->shared()->code(), local_name, NULL);
+      if (idx >= 0 && idx < context->length()) {
+ snapshot_->SetClosureReference(entry, local_name, context->get(idx));
+      }
+    }
+  }
+}
+
+
+void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj,
+                                                      HeapEntry* entry) {
+  if (js_obj->HasFastProperties()) {
+    DescriptorArray* descs = js_obj->map()->instance_descriptors();
+    for (int i = 0; i < descs->number_of_descriptors(); i++) {
+      switch (descs->GetType(i)) {
+        case FIELD: {
+          int index = descs->GetFieldIndex(i);
+          snapshot_->SetPropertyReference(
+              entry, descs->GetKey(i), js_obj->FastPropertyAt(index));
+          break;
+        }
+        case CONSTANT_FUNCTION:
+          snapshot_->SetPropertyReference(
+              entry, descs->GetKey(i), descs->GetConstantFunction(i));
+          break;
+        default: ;
+      }
+    }
+  } else {
+    StringDictionary* dictionary = js_obj->property_dictionary();
+    int length = dictionary->Capacity();
+    for (int i = 0; i < length; ++i) {
+      Object* k = dictionary->KeyAt(i);
+      if (dictionary->IsKey(k)) {
+        snapshot_->SetPropertyReference(
+            entry, String::cast(k), dictionary->ValueAt(i));
+      }
+    }
+  }
+}
+
+
+void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
+                                                     HeapEntry* entry) {
+  if (js_obj->HasFastElements()) {
+    FixedArray* elements = FixedArray::cast(js_obj->elements());
+    int length = js_obj->IsJSArray() ?
+        Smi::cast(JSArray::cast(js_obj)->length())->value() :
+        elements->length();
+    for (int i = 0; i < length; ++i) {
+      if (!elements->get(i)->IsTheHole()) {
+        snapshot_->SetElementReference(entry, i, elements->get(i));
+      }
+    }
+  } else if (js_obj->HasDictionaryElements()) {
+    NumberDictionary* dictionary = js_obj->element_dictionary();
+    int length = dictionary->Capacity();
+    for (int i = 0; i < length; ++i) {
+      Object* k = dictionary->KeyAt(i);
+      if (dictionary->IsKey(k)) {
+        ASSERT(k->IsNumber());
+        uint32_t index = static_cast<uint32_t>(k->Number());
+ snapshot_->SetElementReference(entry, index, dictionary->ValueAt(i));
+      }
+    }
+  }
+}

 } }  // namespace v8::internal

=======================================
--- /branches/bleeding_edge/src/profile-generator.h     Wed Jun  9 00:00:33 2010
+++ /branches/bleeding_edge/src/profile-generator.h     Fri Jun 11 09:34:59 2010
@@ -31,6 +31,7 @@
 #ifdef ENABLE_LOGGING_AND_PROFILING

 #include "hashmap.h"
+#include "../include/v8-profiler.h"

 namespace v8 {
 namespace internal {
@@ -53,6 +54,8 @@
   List<bool> token_removed_;

   friend class TokenEnumeratorTester;
+
+  DISALLOW_COPY_AND_ASSIGN(TokenEnumerator);
 };


@@ -357,6 +360,8 @@
   unsigned measurements_count_;
   unsigned wall_time_query_countdown_;
   double last_wall_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(SampleRateCalculator);
 };


@@ -416,6 +421,310 @@
   DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
 };

+
+class HeapSnapshot;
+class HeapEntry;
+
+
+class HeapGraphEdge {
+ public:
+  enum Type {
+    CONTEXT_VARIABLE,
+    ELEMENT,
+    PROPERTY
+  };
+
+ HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry* to);
+  HeapGraphEdge(int index, HeapEntry* from, HeapEntry* to);
+
+  Type type() const { return type_; }
+  int index() const {
+    ASSERT(type_ == ELEMENT);
+    return index_;
+  }
+  const char* name() const {
+    ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY);
+    return name_;
+  }
+  HeapEntry* from() const { return from_; }
+  HeapEntry* to() const { return to_; }
+
+ private:
+  Type type_;
+  union {
+    int index_;
+    const char* name_;
+  };
+  HeapEntry* from_;
+  HeapEntry* to_;
+
+  DISALLOW_COPY_AND_ASSIGN(HeapGraphEdge);
+};
+
+
+class HeapGraphPath;
+class CachedHeapGraphPath;
+
+class HeapEntry {
+ public:
+  enum Type {
+    INTERNAL,
+    ARRAY,
+    STRING,
+    JS_OBJECT,
+    CODE,
+    CLOSURE
+  };
+
+  explicit HeapEntry(HeapSnapshot* snapshot)
+      : snapshot_(snapshot),
+        visited_(false),
+        type_(INTERNAL),
+        name_(""),
+        next_auto_index_(0),
+        self_size_(0),
+        security_token_id_(TokenEnumerator::kNoSecurityToken),
+        children_(1),
+        retainers_(0),
+        retaining_paths_(0),
+        total_size_(kUnknownSize),
+        non_shared_total_size_(kUnknownSize),
+        painted_(kUnpainted) { }
+  HeapEntry(HeapSnapshot* snapshot,
+            Type type,
+            const char* name,
+            int self_size,
+            int security_token_id)
+      : snapshot_(snapshot),
+        visited_(false),
+        type_(type),
+        name_(name),
+        next_auto_index_(1),
+        self_size_(self_size),
+        security_token_id_(security_token_id),
+        children_(4),
+        retainers_(4),
+        retaining_paths_(4),
+        total_size_(kUnknownSize),
+        non_shared_total_size_(kUnknownSize),
+        painted_(kUnpainted) { }
+  ~HeapEntry();
+
+  bool visited() const { return visited_; }
+  Type type() const { return type_; }
+  const char* name() const { return name_; }
+  int self_size() const { return self_size_; }
+  int security_token_id() const { return security_token_id_; }
+  bool painted_reachable() { return painted_ == kPaintReachable; }
+  bool not_painted_reachable_from_others() {
+    return painted_ != kPaintReachableFromOthers;
+  }
+  const List<HeapGraphEdge*>* children() const { return &children_; }
+  const List<HeapGraphEdge*>* retainers() const { return &retainers_; }
+  const List<HeapGraphPath*>* GetRetainingPaths();
+
+  void ClearPaint() { painted_ = kUnpainted; }
+  void CutEdges();
+  void MarkAsVisited() { visited_ = true; }
+  void PaintReachable() {
+    ASSERT(painted_ == kUnpainted);
+    painted_ = kPaintReachable;
+  }
+  void PaintReachableFromOthers() { painted_ = kPaintReachableFromOthers; }
+  void SetClosureReference(const char* name, HeapEntry* entry);
+  void SetElementReference(int index, HeapEntry* entry);
+  void SetPropertyReference(const char* name, HeapEntry* entry);
+  void SetAutoIndexReference(HeapEntry* entry);
+
+  int TotalSize();
+  int NonSharedTotalSize();
+
+  void Print(int max_depth, int indent);
+
+ private:
+  int CalculateTotalSize();
+  int CalculateNonSharedTotalSize();
+  void FindRetainingPaths(HeapEntry* node, CachedHeapGraphPath* prev_path);
+  void RemoveChild(HeapGraphEdge* edge);
+  void RemoveRetainer(HeapGraphEdge* edge);
+
+  const char* TypeAsString();
+
+  HeapSnapshot* snapshot_;
+  bool visited_;
+  Type type_;
+  const char* name_;
+  int next_auto_index_;
+  int self_size_;
+  int security_token_id_;
+  List<HeapGraphEdge*> children_;
+  List<HeapGraphEdge*> retainers_;
+  List<HeapGraphPath*> retaining_paths_;
+  int total_size_;
+  int non_shared_total_size_;
+  int painted_;
+
+  static const int kUnknownSize = -1;
+  static const int kUnpainted = 0;
+  static const int kPaintReachable = 1;
+  static const int kPaintReachableFromOthers = 2;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(HeapEntry);
+};
+
+
+class HeapGraphPath {
+ public:
+  HeapGraphPath()
+      : path_(8) { }
+  explicit HeapGraphPath(const List<HeapGraphEdge*>& path);
+
+  void Add(HeapGraphEdge* edge) { path_.Add(edge); }
+  void Set(int index, HeapGraphEdge* edge) { path_[index] = edge; }
+  const List<HeapGraphEdge*>* path() const { return &path_; }
+
+  void Print();
+
+ private:
+  List<HeapGraphEdge*> path_;
+
+  DISALLOW_COPY_AND_ASSIGN(HeapGraphPath);
+};
+
+
+class HeapEntriesMap {
+ public:
+  HeapEntriesMap();
+  ~HeapEntriesMap();
+
+  void Alias(HeapObject* object, HeapEntry* entry);
+  void Apply(void (HeapEntry::*Func)(void));
+  template<class Visitor>
+  void Apply(Visitor* visitor);
+  HeapEntry* Map(HeapObject* object);
+  void Pair(HeapObject* object, HeapEntry* entry);
+
+ private:
+  INLINE(uint32_t Hash(HeapObject* object)) {
+    return static_cast<uint32_t>(reinterpret_cast<intptr_t>(object));
+  }
+  INLINE(static bool HeapObjectsMatch(void* key1, void* key2)) {
+    return key1 == key2;
+  }
+  INLINE(bool IsAlias(void* ptr)) {
+    return reinterpret_cast<intptr_t>(ptr) & kAliasTag;
+  }
+
+  static const intptr_t kAliasTag = 1;
+
+  HashMap entries_;
+
+  DISALLOW_COPY_AND_ASSIGN(HeapEntriesMap);
+};
+
+
+class HeapSnapshotsCollection;
+
+// HeapSnapshot represents a single heap snapshot. It is stored in
+// HeapSnapshotsCollection, which is also a factory for
+// HeapSnapshots. All HeapSnapshots share strings copied from JS heap
+// to be able to return them even if they were collected.
+// HeapSnapshotGenerator fills in a HeapSnapshot.
+class HeapSnapshot {
+ public:
+  HeapSnapshot(HeapSnapshotsCollection* collection,
+               const char* title,
+               unsigned uid);
+  void ClearPaint();
+  void CutObjectsFromForeignSecurityContexts();
+  HeapEntry* GetEntry(Object* object);
+  void SetClosureReference(
+      HeapEntry* parent, String* reference_name, Object* child);
+  void SetElementReference(HeapEntry* parent, int index, Object* child);
+  void SetPropertyReference(
+      HeapEntry* parent, String* reference_name, Object* child);
+
+  INLINE(const char* title() const) { return title_; }
+  INLINE(unsigned uid() const) { return uid_; }
+  const HeapEntry* const_root() const { return &root_; }
+  HeapEntry* root() { return &root_; }
+  template<class Visitor>
+  void IterateEntries(Visitor* visitor) { entries_.Apply(visitor); }
+
+  void Print(int max_depth);
+
+ private:
+  HeapEntry* AddEntry(HeapObject* object, HeapEntry::Type type) {
+    return AddEntry(object, type, "");
+  }
+  HeapEntry* AddEntry(
+      HeapObject* object, HeapEntry::Type type, const char* name);
+  void AddEntryAlias(HeapObject* object, HeapEntry* entry) {
+    entries_.Alias(object, entry);
+  }
+  HeapEntry* FindEntry(HeapObject* object) {
+    return entries_.Map(object);
+  }
+  int GetGlobalSecurityToken();
+  int GetObjectSecurityToken(HeapObject* obj);
+  static int GetObjectSize(HeapObject* obj);
+  static int CalculateNetworkSize(JSObject* obj);
+
+  HeapSnapshotsCollection* collection_;
+  const char* title_;
+  unsigned uid_;
+  HeapEntry root_;
+  // HeapObject* -> HeapEntry*
+  HeapEntriesMap entries_;
+
+  DISALLOW_COPY_AND_ASSIGN(HeapSnapshot);
+};
+
+
+class HeapSnapshotsCollection {
+ public:
+  HeapSnapshotsCollection();
+  ~HeapSnapshotsCollection();
+
+  HeapSnapshot* NewSnapshot(const char* name, unsigned uid);
+  List<HeapSnapshot*>* snapshots() { return &snapshots_; }
+  HeapSnapshot* GetSnapshot(unsigned uid);
+
+  const char* GetName(String* name) { return names_.GetName(name); }
+
+  TokenEnumerator* token_enumerator() { return token_enumerator_; }
+
+ private:
+  INLINE(static bool HeapSnapshotsMatch(void* key1, void* key2)) {
+    return key1 == key2;
+  }
+
+  List<HeapSnapshot*> snapshots_;
+  // uid -> HeapSnapshot*
+  HashMap snapshots_uids_;
+  StringsStorage names_;
+  TokenEnumerator* token_enumerator_;
+
+  DISALLOW_COPY_AND_ASSIGN(HeapSnapshotsCollection);
+};
+
+
+class HeapSnapshotGenerator {
+ public:
+  explicit HeapSnapshotGenerator(HeapSnapshot* snapshot);
+  void GenerateSnapshot();
+
+ private:
+  void ExtractReferences(HeapObject* obj);
+  void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry);
+  void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry);
+  void ExtractElementReferences(JSObject* js_obj, HeapEntry* entry);
+
+  HeapSnapshot* snapshot_;
+
+  DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator);
+};
+
 } }  // namespace v8::internal

 #endif  // ENABLE_LOGGING_AND_PROFILING

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to