Introduce object visitor to estimate the size of a native context.
authoryangguo <yangguo@chromium.org>
Wed, 5 Aug 2015 14:06:43 +0000 (07:06 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 5 Aug 2015 14:07:33 +0000 (14:07 +0000)
This is only an estimate since it counts objects that could be shared,
for example strings, cow arrays, heap numbers, etc.

It however ignores objects that could be shared, but may only be used
by the context to be measured, for example shared function infos,
script objects, scope infos, etc.

R=jochen@chromium.org

Review URL: https://codereview.chromium.org/1268333004

Cr-Commit-Position: refs/heads/master@{#30029}

BUILD.gn
include/v8.h
src/api.cc
src/context-measure.cc [new file with mode: 0644]
src/context-measure.h [new file with mode: 0644]
src/snapshot/serialize.h
test/cctest/test-api.cc
test/cctest/test-heap.cc
tools/gyp/v8.gyp

index 9219c18..b870453 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -822,6 +822,8 @@ source_set("v8_base") {
     "src/compiler/zone-pool.h",
     "src/compiler.cc",
     "src/compiler.h",
+    "src/context-measure.cc",
+    "src/context-measure.h",
     "src/contexts.cc",
     "src/contexts.h",
     "src/conversions-inl.h",
index 426246f..1b65770 100644 (file)
@@ -6662,6 +6662,11 @@ class V8_EXPORT Context {
   void SetErrorMessageForCodeGenerationFromStrings(Local<String> message);
 
   /**
+   * Estimate the memory in bytes retained by this context.
+   */
+  size_t EstimatedSize();
+
+  /**
    * Stack-allocated class which sets the execution context for all
    * operations executed within a local scope.
    */
index fea9f9e..fb9c7d3 100644 (file)
@@ -22,6 +22,7 @@
 #include "src/bootstrapper.h"
 #include "src/code-stubs.h"
 #include "src/compiler.h"
+#include "src/context-measure.h"
 #include "src/contexts.h"
 #include "src/conversions-inl.h"
 #include "src/counters.h"
@@ -5575,6 +5576,12 @@ void Context::SetErrorMessageForCodeGenerationFromStrings(Local<String> error) {
 }
 
 
+size_t Context::EstimatedSize() {
+  return static_cast<size_t>(
+      i::ContextMeasure(*Utils::OpenHandle(this)).Size());
+}
+
+
 MaybeLocal<v8::Object> ObjectTemplate::NewInstance(Local<Context> context) {
   PREPARE_FOR_EXECUTION(context, "v8::ObjectTemplate::NewInstance()", Object);
   auto self = Utils::OpenHandle(this);
diff --git a/src/context-measure.cc b/src/context-measure.cc
new file mode 100644 (file)
index 0000000..42a08be
--- /dev/null
@@ -0,0 +1,76 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/context-measure.h"
+
+namespace v8 {
+namespace internal {
+
+ContextMeasure::ContextMeasure(Context* context)
+    : context_(context),
+      root_index_map_(context->GetIsolate()),
+      recursion_depth_(0),
+      count_(0),
+      size_(0) {
+  DCHECK(context_->IsNativeContext());
+  Object* next_link = context_->get(Context::NEXT_CONTEXT_LINK);
+  MeasureObject(context_);
+  MeasureDeferredObjects();
+  context_->set(Context::NEXT_CONTEXT_LINK, next_link);
+}
+
+
+bool ContextMeasure::IsShared(HeapObject* object) {
+  if (object->IsScript()) return true;
+  if (object->IsSharedFunctionInfo()) return true;
+  if (object->IsScopeInfo()) return true;
+  if (object->IsCode() && !Code::cast(object)->is_optimized_code()) return true;
+  if (object->IsExecutableAccessorInfo()) return true;
+  if (object->IsWeakCell()) return true;
+  return false;
+}
+
+
+void ContextMeasure::MeasureObject(HeapObject* object) {
+  if (back_reference_map_.Lookup(object).is_valid()) return;
+  if (root_index_map_.Lookup(object) != RootIndexMap::kInvalidRootIndex) return;
+  if (IsShared(object)) return;
+  back_reference_map_.Add(object, BackReference::DummyReference());
+  recursion_depth_++;
+  if (recursion_depth_ > kMaxRecursion) {
+    deferred_objects_.Add(object);
+  } else {
+    MeasureAndRecurse(object);
+  }
+  recursion_depth_--;
+}
+
+
+void ContextMeasure::MeasureDeferredObjects() {
+  while (deferred_objects_.length() > 0) {
+    MeasureAndRecurse(deferred_objects_.RemoveLast());
+  }
+}
+
+
+void ContextMeasure::MeasureAndRecurse(HeapObject* object) {
+  int size = object->Size();
+  count_++;
+  size_ += size;
+  Map* map = object->map();
+  MeasureObject(map);
+  object->IterateBody(map->instance_type(), size, this);
+}
+
+
+void ContextMeasure::VisitPointers(Object** start, Object** end) {
+  for (Object** current = start; current < end; current++) {
+    if ((*current)->IsSmi()) continue;
+    MeasureObject(HeapObject::cast(*current));
+  }
+}
+}
+}  // namespace v8::internal
diff --git a/src/context-measure.h b/src/context-measure.h
new file mode 100644 (file)
index 0000000..f01c374
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_CONTEXT_MEASURE_H_
+#define V8_CONTEXT_MEASURE_H_
+
+#include "src/snapshot/serialize.h"
+
+namespace v8 {
+namespace internal {
+
+class ContextMeasure : public ObjectVisitor {
+ public:
+  explicit ContextMeasure(Context* context);
+
+  int Size() { return size_; }
+  int Count() { return count_; }
+
+  void VisitPointers(Object** start, Object** end);
+
+ private:
+  void MeasureObject(HeapObject* object);
+  void MeasureDeferredObjects();
+  void MeasureAndRecurse(HeapObject* object);
+  bool IsShared(HeapObject* object);
+
+  Context* context_;
+
+  BackReferenceMap back_reference_map_;
+  RootIndexMap root_index_map_;
+
+  static const int kMaxRecursion = 16;
+  int recursion_depth_;
+  List<HeapObject*> deferred_objects_;
+
+  int count_;
+  int size_;
+
+  DisallowHeapAllocation no_gc_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContextMeasure);
+};
+}
+}  // namespace v8::internal
+
+#endif  // V8_CONTEXT_MEASURE_H_
index 6510f9b..c92683d 100644 (file)
@@ -6,7 +6,6 @@
 #define V8_SNAPSHOT_SERIALIZE_H_
 
 #include "src/hashmap.h"
-#include "src/heap-profiler.h"
 #include "src/isolate.h"
 #include "src/snapshot/snapshot-source-sink.h"
 
@@ -156,6 +155,8 @@ class BackReference {
                          ChunkOffsetBits::encode(index));
   }
 
+  static BackReference DummyReference() { return BackReference(kDummyValue); }
+
   static BackReference Reference(AllocationSpace space, uint32_t chunk_index,
                                  uint32_t chunk_offset) {
     DCHECK(IsAligned(chunk_offset, kObjectAlignment));
@@ -201,6 +202,7 @@ class BackReference {
   static const uint32_t kInvalidValue = 0xFFFFFFFF;
   static const uint32_t kSourceValue = 0xFFFFFFFE;
   static const uint32_t kGlobalProxyValue = 0xFFFFFFFD;
+  static const uint32_t kDummyValue = 0xFFFFFFFC;
   static const int kChunkOffsetSize = kPageSizeBits - kObjectAlignmentBits;
   static const int kChunkIndexSize = 32 - kChunkOffsetSize - kSpaceTagSize;
 
index ef98faf..78800d4 100644 (file)
@@ -21827,3 +21827,11 @@ TEST(FutexInterruption) {
       "Atomics.futexWait(i32a, 0, 0);");
   CHECK(try_catch.HasTerminated());
 }
+
+
+TEST(EstimatedContextSize) {
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  LocalContext env;
+  CHECK(50000 < env->EstimatedSize());
+}
index e476bcc..dfabb92 100644 (file)
@@ -31,6 +31,7 @@
 #include "src/v8.h"
 
 #include "src/compilation-cache.h"
+#include "src/context-measure.h"
 #include "src/deoptimizer.h"
 #include "src/execution.h"
 #include "src/factory.h"
@@ -6268,3 +6269,30 @@ TEST(SlotsBufferObjectSlotsRemoval) {
                              FixedArrayBase::kLengthOffset));
   delete buffer;
 }
+
+
+TEST(ContextMeasure) {
+  CcTest::InitializeVM();
+  v8::HandleScope scope(CcTest::isolate());
+  Isolate* isolate = CcTest::i_isolate();
+  LocalContext context;
+
+  int size_upper_limit = 0;
+  int count_upper_limit = 0;
+  HeapIterator it(CcTest::heap());
+  for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) {
+    size_upper_limit += obj->Size();
+    count_upper_limit++;
+  }
+
+  ContextMeasure measure(*isolate->native_context());
+
+  PrintF("Context size        : %d bytes\n", measure.Size());
+  PrintF("Context object count: %d\n", measure.Count());
+
+  CHECK_LE(1000, measure.Count());
+  CHECK_LE(50000, measure.Size());
+
+  CHECK_LE(measure.Count(), count_upper_limit);
+  CHECK_LE(measure.Size(), size_upper_limit);
+}
index b5bd01e..637b01f 100644 (file)
         '../../src/compiler/zone-pool.h',
         '../../src/compiler.cc',
         '../../src/compiler.h',
+        '../../src/context-measure.cc',
+        '../../src/context-measure.h',
         '../../src/contexts.cc',
         '../../src/contexts.h',
         '../../src/conversions-inl.h',