2011-05-03 08:23:58 +00:00
|
|
|
// Copyright 2011 the V8 project authors. All rights reserved.
|
2013-03-07 11:12:26 +00:00
|
|
|
// 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.
|
2009-09-16 13:41:24 +00:00
|
|
|
//
|
|
|
|
// Tests for heap profiler
|
|
|
|
|
2012-04-19 15:58:42 +00:00
|
|
|
#include <ctype.h>
|
|
|
|
|
2016-07-25 10:24:45 +00:00
|
|
|
#include <memory>
|
|
|
|
|
2019-05-24 13:51:59 +00:00
|
|
|
#include "src/init/v8.h"
|
2012-04-19 16:31:01 +00:00
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "include/v8-profiler.h"
|
2019-05-17 12:13:44 +00:00
|
|
|
#include "src/api/api-inl.h"
|
2016-06-09 17:58:10 +00:00
|
|
|
#include "src/base/hashmap.h"
|
2019-01-22 09:02:52 +00:00
|
|
|
#include "src/base/optional.h"
|
2019-05-21 09:30:15 +00:00
|
|
|
#include "src/codegen/assembler-inl.h"
|
2015-07-31 11:07:50 +00:00
|
|
|
#include "src/debug/debug.h"
|
2019-02-14 21:10:30 +00:00
|
|
|
#include "src/heap/heap-inl.h"
|
2019-05-23 08:51:46 +00:00
|
|
|
#include "src/objects/objects-inl.h"
|
2015-09-28 19:34:08 +00:00
|
|
|
#include "src/profiler/allocation-tracker.h"
|
|
|
|
#include "src/profiler/heap-profiler.h"
|
|
|
|
#include "src/profiler/heap-snapshot-generator-inl.h"
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "test/cctest/cctest.h"
|
2019-05-16 09:43:58 +00:00
|
|
|
#include "test/cctest/collector.h"
|
2019-08-09 07:09:27 +00:00
|
|
|
#include "test/cctest/heap/heap-utils.h"
|
2009-09-16 13:41:24 +00:00
|
|
|
|
2013-11-14 12:13:26 +00:00
|
|
|
using i::AllocationTraceNode;
|
|
|
|
using i::AllocationTraceTree;
|
|
|
|
using i::AllocationTracker;
|
2016-04-28 07:59:52 +00:00
|
|
|
using i::ArrayVector;
|
2018-08-20 12:14:25 +00:00
|
|
|
using i::SourceLocation;
|
2013-11-14 12:13:26 +00:00
|
|
|
using i::Vector;
|
2018-08-20 12:14:25 +00:00
|
|
|
using v8::base::Optional;
|
2020-09-24 15:27:07 +00:00
|
|
|
using v8::internal::heap::GrowNewSpaceToMaximumCapacity;
|
2013-11-14 12:13:26 +00:00
|
|
|
|
2010-06-15 11:44:07 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class NamedEntriesDetector {
|
|
|
|
public:
|
|
|
|
NamedEntriesDetector()
|
2010-11-08 15:18:12 +00:00
|
|
|
: has_A2(false), has_B2(false), has_C2(false) {
|
2010-06-15 11:44:07 +00:00
|
|
|
}
|
|
|
|
|
2012-02-27 15:42:36 +00:00
|
|
|
void CheckEntry(i::HeapEntry* entry) {
|
|
|
|
if (strcmp(entry->name(), "A2") == 0) has_A2 = true;
|
|
|
|
if (strcmp(entry->name(), "B2") == 0) has_B2 = true;
|
|
|
|
if (strcmp(entry->name(), "C2") == 0) has_C2 = true;
|
2010-08-09 11:37:24 +00:00
|
|
|
}
|
|
|
|
|
2012-02-27 15:42:36 +00:00
|
|
|
void CheckAllReachables(i::HeapEntry* root) {
|
2016-09-30 16:16:47 +00:00
|
|
|
v8::base::HashMap visited;
|
2017-08-29 12:40:31 +00:00
|
|
|
std::vector<i::HeapEntry*> list;
|
|
|
|
list.push_back(root);
|
2012-02-27 15:42:36 +00:00
|
|
|
CheckEntry(root);
|
2017-08-29 12:40:31 +00:00
|
|
|
while (!list.empty()) {
|
|
|
|
i::HeapEntry* entry = list.back();
|
|
|
|
list.pop_back();
|
2016-12-30 16:27:15 +00:00
|
|
|
for (int i = 0; i < entry->children_count(); ++i) {
|
|
|
|
i::HeapGraphEdge* edge = entry->child(i);
|
|
|
|
if (edge->type() == i::HeapGraphEdge::kShortcut) continue;
|
|
|
|
i::HeapEntry* child = edge->to();
|
2016-06-09 17:58:10 +00:00
|
|
|
v8::base::HashMap::Entry* entry = visited.LookupOrInsert(
|
2012-05-23 05:27:08 +00:00
|
|
|
reinterpret_cast<void*>(child),
|
2015-04-13 19:01:15 +00:00
|
|
|
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(child)));
|
2012-05-23 05:27:08 +00:00
|
|
|
if (entry->value)
|
|
|
|
continue;
|
|
|
|
entry->value = reinterpret_cast<void*>(1);
|
2017-08-29 12:40:31 +00:00
|
|
|
list.push_back(child);
|
2012-05-23 05:27:08 +00:00
|
|
|
CheckEntry(child);
|
2012-02-27 15:42:36 +00:00
|
|
|
}
|
|
|
|
}
|
2010-06-15 11:44:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool has_A2;
|
|
|
|
bool has_B2;
|
|
|
|
bool has_C2;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2010-06-17 12:56:55 +00:00
|
|
|
|
|
|
|
static const v8::HeapGraphNode* GetGlobalObject(
|
|
|
|
const v8::HeapSnapshot* snapshot) {
|
2015-12-02 12:35:12 +00:00
|
|
|
// The 0th-child is (GC Roots), 1st is the user root.
|
2010-11-18 10:38:25 +00:00
|
|
|
const v8::HeapGraphNode* global_obj =
|
2015-12-02 12:35:12 +00:00
|
|
|
snapshot->GetRoot()->GetChild(1)->GetToNode();
|
2011-05-30 14:31:47 +00:00
|
|
|
CHECK_EQ(0, strncmp("Object", const_cast<i::HeapEntry*>(
|
|
|
|
reinterpret_cast<const i::HeapEntry*>(global_obj))->name(), 6));
|
2010-11-18 10:38:25 +00:00
|
|
|
return global_obj;
|
2010-06-17 12:56:55 +00:00
|
|
|
}
|
|
|
|
|
2018-01-31 19:55:31 +00:00
|
|
|
static const char* GetName(const v8::HeapGraphNode* node) {
|
|
|
|
return const_cast<i::HeapEntry*>(reinterpret_cast<const i::HeapEntry*>(node))
|
|
|
|
->name();
|
|
|
|
}
|
|
|
|
|
2018-07-12 07:52:20 +00:00
|
|
|
static const char* GetName(const v8::HeapGraphEdge* edge) {
|
|
|
|
return const_cast<i::HeapGraphEdge*>(
|
|
|
|
reinterpret_cast<const i::HeapGraphEdge*>(edge))
|
|
|
|
->name();
|
|
|
|
}
|
|
|
|
|
2018-01-31 19:55:31 +00:00
|
|
|
static size_t GetSize(const v8::HeapGraphNode* node) {
|
|
|
|
return const_cast<i::HeapEntry*>(reinterpret_cast<const i::HeapEntry*>(node))
|
|
|
|
->self_size();
|
|
|
|
}
|
|
|
|
|
|
|
|
static const v8::HeapGraphNode* GetChildByName(const v8::HeapGraphNode* node,
|
|
|
|
const char* name) {
|
|
|
|
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphNode* child = node->GetChild(i)->GetToNode();
|
|
|
|
if (!strcmp(name, GetName(child))) {
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-07-12 07:52:20 +00:00
|
|
|
static const v8::HeapGraphEdge* GetEdgeByChildName(
|
|
|
|
const v8::HeapGraphNode* node, const char* name) {
|
|
|
|
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* edge = node->GetChild(i);
|
|
|
|
const v8::HeapGraphNode* child = edge->GetToNode();
|
|
|
|
if (!strcmp(name, GetName(child))) {
|
|
|
|
return edge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-01-31 19:55:31 +00:00
|
|
|
static const v8::HeapGraphNode* GetRootChild(const v8::HeapSnapshot* snapshot,
|
|
|
|
const char* name) {
|
|
|
|
return GetChildByName(snapshot->GetRoot(), name);
|
|
|
|
}
|
|
|
|
|
2018-08-20 12:14:25 +00:00
|
|
|
static Optional<SourceLocation> GetLocation(const v8::HeapSnapshot* s,
|
|
|
|
const v8::HeapGraphNode* node) {
|
|
|
|
const i::HeapSnapshot* snapshot = reinterpret_cast<const i::HeapSnapshot*>(s);
|
|
|
|
const std::vector<SourceLocation>& locations = snapshot->locations();
|
2018-10-04 05:00:10 +00:00
|
|
|
const i::HeapEntry* entry = reinterpret_cast<const i::HeapEntry*>(node);
|
2018-08-20 12:14:25 +00:00
|
|
|
for (const auto& loc : locations) {
|
2018-10-04 05:00:10 +00:00
|
|
|
if (loc.entry_index == entry->index()) {
|
2018-08-20 12:14:25 +00:00
|
|
|
return Optional<SourceLocation>(loc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Optional<SourceLocation>();
|
|
|
|
}
|
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
static const v8::HeapGraphNode* GetProperty(v8::Isolate* isolate,
|
|
|
|
const v8::HeapGraphNode* node,
|
2010-06-17 12:56:55 +00:00
|
|
|
v8::HeapGraphEdge::Type type,
|
|
|
|
const char* name) {
|
|
|
|
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = node->GetChild(i);
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value prop_name(isolate, prop->GetName());
|
2010-06-17 12:56:55 +00:00
|
|
|
if (prop->GetType() == type && strcmp(name, *prop_name) == 0)
|
|
|
|
return prop->GetToNode();
|
|
|
|
}
|
2017-10-13 16:33:03 +00:00
|
|
|
return nullptr;
|
2010-06-17 12:56:55 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
static bool HasString(v8::Isolate* isolate, const v8::HeapGraphNode* node,
|
|
|
|
const char* contents) {
|
2010-06-17 12:56:55 +00:00
|
|
|
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = node->GetChild(i);
|
|
|
|
const v8::HeapGraphNode* node = prop->GetToNode();
|
2010-08-09 11:37:24 +00:00
|
|
|
if (node->GetType() == v8::HeapGraphNode::kString) {
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value node_name(isolate, node->GetName());
|
2010-06-17 12:56:55 +00:00
|
|
|
if (strcmp(contents, *node_name) == 0) return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
static void EnsureNoUninstrumentedInternals(v8::Isolate* isolate,
|
|
|
|
const v8::HeapGraphNode* node) {
|
2017-07-20 17:48:57 +00:00
|
|
|
for (int i = 0; i < 20; ++i) {
|
|
|
|
i::ScopedVector<char> buffer(10);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* internal =
|
|
|
|
GetProperty(isolate, node, v8::HeapGraphEdge::kInternal,
|
|
|
|
i::IntToCString(i, buffer));
|
2017-07-20 17:48:57 +00:00
|
|
|
CHECK(!internal);
|
|
|
|
}
|
|
|
|
}
|
2010-06-17 12:56:55 +00:00
|
|
|
|
2013-07-10 12:40:42 +00:00
|
|
|
// Check that snapshot has no unretained entries except root.
|
2013-07-17 13:12:48 +00:00
|
|
|
static bool ValidateSnapshot(const v8::HeapSnapshot* snapshot, int depth = 3) {
|
2013-07-10 12:40:42 +00:00
|
|
|
i::HeapSnapshot* heap_snapshot = const_cast<i::HeapSnapshot*>(
|
|
|
|
reinterpret_cast<const i::HeapSnapshot*>(snapshot));
|
|
|
|
|
2016-09-30 16:16:47 +00:00
|
|
|
v8::base::HashMap visited;
|
2016-12-30 16:27:15 +00:00
|
|
|
std::deque<i::HeapGraphEdge>& edges = heap_snapshot->edges();
|
|
|
|
for (size_t i = 0; i < edges.size(); ++i) {
|
2016-06-09 17:58:10 +00:00
|
|
|
v8::base::HashMap::Entry* entry = visited.LookupOrInsert(
|
2013-07-10 12:40:42 +00:00
|
|
|
reinterpret_cast<void*>(edges[i].to()),
|
2015-04-13 19:01:15 +00:00
|
|
|
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(edges[i].to())));
|
2013-07-10 12:40:42 +00:00
|
|
|
uint32_t ref_count = static_cast<uint32_t>(
|
|
|
|
reinterpret_cast<uintptr_t>(entry->value));
|
|
|
|
entry->value = reinterpret_cast<void*>(ref_count + 1);
|
|
|
|
}
|
|
|
|
uint32_t unretained_entries_count = 0;
|
2018-10-04 05:00:10 +00:00
|
|
|
std::deque<i::HeapEntry>& entries = heap_snapshot->entries();
|
2017-08-29 12:40:31 +00:00
|
|
|
for (i::HeapEntry& entry : entries) {
|
|
|
|
v8::base::HashMap::Entry* map_entry = visited.Lookup(
|
|
|
|
reinterpret_cast<void*>(&entry),
|
|
|
|
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&entry)));
|
|
|
|
if (!map_entry && entry.id() != 1) {
|
|
|
|
entry.Print("entry with no retainer", "", depth, 0);
|
|
|
|
++unretained_entries_count;
|
2013-07-10 12:40:42 +00:00
|
|
|
}
|
|
|
|
}
|
2013-07-16 17:47:35 +00:00
|
|
|
return unretained_entries_count == 0;
|
2013-07-10 12:40:42 +00:00
|
|
|
}
|
|
|
|
|
2018-02-20 16:06:21 +00:00
|
|
|
bool EndsWith(const char* a, const char* b) {
|
|
|
|
size_t length_a = strlen(a);
|
|
|
|
size_t length_b = strlen(b);
|
|
|
|
return (length_a >= length_b) && !strcmp(a + length_a - length_b, b);
|
|
|
|
}
|
2013-07-10 12:40:42 +00:00
|
|
|
|
2010-06-15 11:44:07 +00:00
|
|
|
TEST(HeapSnapshot) {
|
2010-07-15 13:21:50 +00:00
|
|
|
LocalContext env2;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env2->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
|
2010-06-15 11:44:07 +00:00
|
|
|
|
2010-10-18 09:15:38 +00:00
|
|
|
CompileRun(
|
2010-06-15 11:44:07 +00:00
|
|
|
"function A2() {}\n"
|
|
|
|
"function B2(x) { return function() { return typeof x; }; }\n"
|
|
|
|
"function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
|
|
|
|
"var a2 = new A2();\n"
|
|
|
|
"var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
|
|
|
|
"var c2 = new C2(a2);");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot_env2 = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot_env2));
|
2010-06-17 12:56:55 +00:00
|
|
|
const v8::HeapGraphNode* global_env2 = GetGlobalObject(snapshot_env2);
|
2010-06-15 11:44:07 +00:00
|
|
|
|
2010-11-08 15:18:12 +00:00
|
|
|
// Verify, that JS global object of env2 has '..2' properties.
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* a2_node = GetProperty(
|
|
|
|
env2->GetIsolate(), global_env2, v8::HeapGraphEdge::kProperty, "a2");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(a2_node);
|
2017-08-24 21:49:48 +00:00
|
|
|
CHECK(GetProperty(env2->GetIsolate(), global_env2,
|
|
|
|
v8::HeapGraphEdge::kProperty, "b2_1"));
|
|
|
|
CHECK(GetProperty(env2->GetIsolate(), global_env2,
|
|
|
|
v8::HeapGraphEdge::kProperty, "b2_2"));
|
|
|
|
CHECK(GetProperty(env2->GetIsolate(), global_env2,
|
|
|
|
v8::HeapGraphEdge::kProperty, "c2"));
|
2010-06-15 11:44:07 +00:00
|
|
|
|
|
|
|
NamedEntriesDetector det;
|
2012-02-27 15:42:36 +00:00
|
|
|
det.CheckAllReachables(const_cast<i::HeapEntry*>(
|
|
|
|
reinterpret_cast<const i::HeapEntry*>(global_env2)));
|
2010-06-15 11:44:07 +00:00
|
|
|
CHECK(det.has_A2);
|
|
|
|
CHECK(det.has_B2);
|
|
|
|
CHECK(det.has_C2);
|
|
|
|
}
|
|
|
|
|
2018-08-20 12:14:25 +00:00
|
|
|
TEST(HeapSnapshotLocations) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"function X(a) { return function() { return a; } }\n"
|
2018-08-21 09:50:51 +00:00
|
|
|
"function* getid() { yield 1; }\n"
|
2018-08-24 07:21:50 +00:00
|
|
|
"class A {}\n"
|
2018-08-21 09:50:51 +00:00
|
|
|
"var x = X(1);\n"
|
2018-08-24 07:21:50 +00:00
|
|
|
"var g = getid();\n"
|
|
|
|
"var o = new A();");
|
2018-08-20 12:14:25 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* x =
|
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "x");
|
|
|
|
CHECK(x);
|
|
|
|
|
2018-08-21 09:50:51 +00:00
|
|
|
Optional<SourceLocation> x_loc = GetLocation(snapshot, x);
|
|
|
|
CHECK(x_loc);
|
|
|
|
CHECK_EQ(0, x_loc->line);
|
|
|
|
CHECK_EQ(31, x_loc->col);
|
|
|
|
|
|
|
|
const v8::HeapGraphNode* g =
|
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "g");
|
|
|
|
CHECK(x);
|
|
|
|
|
|
|
|
Optional<SourceLocation> g_loc = GetLocation(snapshot, g);
|
|
|
|
CHECK(g_loc);
|
|
|
|
CHECK_EQ(1, g_loc->line);
|
|
|
|
CHECK_EQ(15, g_loc->col);
|
2018-08-24 07:21:50 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* o =
|
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "o");
|
|
|
|
CHECK(x);
|
|
|
|
|
|
|
|
Optional<SourceLocation> o_loc = GetLocation(snapshot, o);
|
|
|
|
CHECK(o_loc);
|
|
|
|
CHECK_EQ(2, o_loc->line);
|
|
|
|
CHECK_EQ(0, o_loc->col);
|
2018-08-20 12:14:25 +00:00
|
|
|
}
|
2010-06-17 12:56:55 +00:00
|
|
|
|
2010-08-09 11:37:24 +00:00
|
|
|
TEST(HeapSnapshotObjectSizes) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2010-08-09 11:37:24 +00:00
|
|
|
|
|
|
|
// -a-> X1 --a
|
|
|
|
// x -b-> X2 <-|
|
2010-10-18 09:15:38 +00:00
|
|
|
CompileRun(
|
2010-08-09 11:37:24 +00:00
|
|
|
"function X(a, b) { this.a = a; this.b = b; }\n"
|
|
|
|
"x = new X(new X(), new X());\n"
|
2012-04-16 14:31:13 +00:00
|
|
|
"dummy = new X();\n"
|
2010-11-18 10:38:25 +00:00
|
|
|
"(function() { x.a.a = x.b; })();");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2010-08-09 11:37:24 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* x =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "x");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(x);
|
2010-08-09 11:37:24 +00:00
|
|
|
const v8::HeapGraphNode* x1 =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), x, v8::HeapGraphEdge::kProperty, "a");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(x1);
|
2010-08-09 11:37:24 +00:00
|
|
|
const v8::HeapGraphNode* x2 =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), x, v8::HeapGraphEdge::kProperty, "b");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(x2);
|
2010-11-22 14:00:40 +00:00
|
|
|
|
2012-02-27 15:42:36 +00:00
|
|
|
// Test sizes.
|
2014-02-18 13:22:07 +00:00
|
|
|
CHECK_NE(0, static_cast<int>(x->GetShallowSize()));
|
|
|
|
CHECK_NE(0, static_cast<int>(x1->GetShallowSize()));
|
|
|
|
CHECK_NE(0, static_cast<int>(x2->GetShallowSize()));
|
2010-08-09 11:37:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-10 13:54:20 +00:00
|
|
|
TEST(BoundFunctionInSnapshot) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2012-02-10 13:54:20 +00:00
|
|
|
CompileRun(
|
|
|
|
"function myFunction(a, b) { this.a = a; this.b = b; }\n"
|
|
|
|
"function AAAAA() {}\n"
|
|
|
|
"boundFunction = myFunction.bind(new AAAAA(), 20, new Number(12)); \n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2012-02-10 13:54:20 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* f = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "boundFunction");
|
2012-02-10 13:54:20 +00:00
|
|
|
CHECK(f);
|
2015-12-08 18:41:54 +00:00
|
|
|
CHECK(v8_str("native_bind")->Equals(env.local(), f->GetName()).FromJust());
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* bindings = GetProperty(
|
|
|
|
env->GetIsolate(), f, v8::HeapGraphEdge::kInternal, "bindings");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(bindings);
|
2012-02-10 13:54:20 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kArray, bindings->GetType());
|
2015-12-27 06:30:53 +00:00
|
|
|
CHECK_EQ(1, bindings->GetChildrenCount());
|
2012-02-10 13:54:20 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* bound_this = GetProperty(
|
|
|
|
env->GetIsolate(), f, v8::HeapGraphEdge::kInternal, "bound_this");
|
2012-02-10 13:54:20 +00:00
|
|
|
CHECK(bound_this);
|
|
|
|
CHECK_EQ(v8::HeapGraphNode::kObject, bound_this->GetType());
|
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* bound_function = GetProperty(
|
|
|
|
env->GetIsolate(), f, v8::HeapGraphEdge::kInternal, "bound_function");
|
2012-02-10 13:54:20 +00:00
|
|
|
CHECK(bound_function);
|
|
|
|
CHECK_EQ(v8::HeapGraphNode::kClosure, bound_function->GetType());
|
|
|
|
|
|
|
|
const v8::HeapGraphNode* bound_argument = GetProperty(
|
2017-08-24 21:49:48 +00:00
|
|
|
env->GetIsolate(), f, v8::HeapGraphEdge::kShortcut, "bound_argument_1");
|
2012-02-10 13:54:20 +00:00
|
|
|
CHECK(bound_argument);
|
|
|
|
CHECK_EQ(v8::HeapGraphNode::kObject, bound_argument->GetType());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-09 11:37:24 +00:00
|
|
|
TEST(HeapSnapshotEntryChildren) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2010-08-09 11:37:24 +00:00
|
|
|
|
2010-10-18 09:15:38 +00:00
|
|
|
CompileRun(
|
2010-08-09 11:37:24 +00:00
|
|
|
"function A() { }\n"
|
|
|
|
"a = new A;");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2010-08-09 11:37:24 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
for (int i = 0, count = global->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = global->GetChild(i);
|
|
|
|
CHECK_EQ(global, prop->GetFromNode());
|
|
|
|
}
|
|
|
|
const v8::HeapGraphNode* a =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(a);
|
2010-08-09 11:37:24 +00:00
|
|
|
for (int i = 0, count = a->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = a->GetChild(i);
|
|
|
|
CHECK_EQ(a, prop->GetFromNode());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-06-17 12:56:55 +00:00
|
|
|
TEST(HeapSnapshotCodeObjects) {
|
2010-07-15 13:21:50 +00:00
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2010-06-17 12:56:55 +00:00
|
|
|
|
2010-10-18 09:15:38 +00:00
|
|
|
CompileRun(
|
2010-06-17 12:56:55 +00:00
|
|
|
"function lazy(x) { return x - 1; }\n"
|
2018-06-19 09:32:03 +00:00
|
|
|
"function compiled(x) { ()=>x; return x + 1; }\n"
|
2010-08-18 08:19:29 +00:00
|
|
|
"var anonymous = (function() { return function() { return 0; } })();\n"
|
2010-06-17 12:56:55 +00:00
|
|
|
"compiled(1)");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2010-06-17 12:56:55 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* compiled = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "compiled");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(compiled);
|
2010-08-09 11:37:24 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType());
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* lazy = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "lazy");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(lazy);
|
2010-08-09 11:37:24 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* anonymous = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "anonymous");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(anonymous);
|
2010-08-18 08:19:29 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value anonymous_name(env->GetIsolate(), anonymous->GetName());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(0, strcmp("", *anonymous_name));
|
2010-06-17 12:56:55 +00:00
|
|
|
|
2018-03-22 16:09:55 +00:00
|
|
|
// Find references to shared function info.
|
|
|
|
const v8::HeapGraphNode* compiled_sfi = GetProperty(
|
2017-08-24 21:49:48 +00:00
|
|
|
env->GetIsolate(), compiled, v8::HeapGraphEdge::kInternal, "shared");
|
2018-03-22 16:09:55 +00:00
|
|
|
CHECK(compiled_sfi);
|
|
|
|
const v8::HeapGraphNode* lazy_sfi = GetProperty(
|
2017-08-24 21:49:48 +00:00
|
|
|
env->GetIsolate(), lazy, v8::HeapGraphEdge::kInternal, "shared");
|
2018-03-22 16:09:55 +00:00
|
|
|
CHECK(lazy_sfi);
|
|
|
|
|
|
|
|
// TODO(leszeks): Check that there's bytecode on the compiled function, but
|
|
|
|
// not the lazy function.
|
|
|
|
|
|
|
|
// Verify that non-compiled function doesn't contain references to "x"
|
|
|
|
// literal, while compiled function does. The scope info is stored in
|
2021-02-19 17:09:34 +00:00
|
|
|
// ScopeInfo objects attached to the SharedFunctionInfo.
|
2010-06-17 12:56:55 +00:00
|
|
|
bool compiled_references_x = false, lazy_references_x = false;
|
2018-03-22 16:09:55 +00:00
|
|
|
for (int i = 0, count = compiled_sfi->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = compiled_sfi->GetChild(i);
|
2010-06-17 12:56:55 +00:00
|
|
|
const v8::HeapGraphNode* node = prop->GetToNode();
|
2021-02-19 17:09:34 +00:00
|
|
|
if (node->GetType() == v8::HeapGraphNode::kHidden &&
|
|
|
|
!strcmp("system / ScopeInfo", GetName(node))) {
|
2017-08-24 21:49:48 +00:00
|
|
|
if (HasString(env->GetIsolate(), node, "x")) {
|
2010-06-17 12:56:55 +00:00
|
|
|
compiled_references_x = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-22 16:09:55 +00:00
|
|
|
for (int i = 0, count = lazy_sfi->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = lazy_sfi->GetChild(i);
|
2010-06-17 12:56:55 +00:00
|
|
|
const v8::HeapGraphNode* node = prop->GetToNode();
|
2021-02-19 17:09:34 +00:00
|
|
|
if (node->GetType() == v8::HeapGraphNode::kHidden &&
|
|
|
|
!strcmp("system / ScopeInfo", GetName(node))) {
|
2017-08-24 21:49:48 +00:00
|
|
|
if (HasString(env->GetIsolate(), node, "x")) {
|
2010-06-17 12:56:55 +00:00
|
|
|
lazy_references_x = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK(compiled_references_x);
|
2016-11-02 11:17:01 +00:00
|
|
|
if (i::FLAG_lazy) {
|
2016-03-24 18:37:56 +00:00
|
|
|
CHECK(!lazy_references_x);
|
|
|
|
}
|
2010-06-17 12:56:55 +00:00
|
|
|
}
|
|
|
|
|
2010-07-15 13:21:50 +00:00
|
|
|
|
2010-10-18 09:15:38 +00:00
|
|
|
TEST(HeapSnapshotHeapNumbers) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2010-10-18 09:15:38 +00:00
|
|
|
CompileRun(
|
|
|
|
"a = 1; // a is Smi\n"
|
|
|
|
"b = 2.5; // b is HeapNumber");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2010-10-18 09:15:38 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
CHECK(!GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty,
|
|
|
|
"a"));
|
2010-10-18 09:15:38 +00:00
|
|
|
const v8::HeapGraphNode* b =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "b");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(b);
|
2010-10-18 09:15:38 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
|
|
|
|
}
|
|
|
|
|
2018-03-09 18:23:37 +00:00
|
|
|
TEST(HeapSnapshotHeapBigInts) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
CompileRun(
|
|
|
|
"a = 1n;"
|
|
|
|
"b = Object(BigInt(2))");
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* a =
|
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
|
|
|
|
CHECK(a);
|
|
|
|
CHECK_EQ(v8::HeapGraphNode::kBigInt, a->GetType());
|
|
|
|
const v8::HeapGraphNode* b =
|
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "b");
|
|
|
|
CHECK(b);
|
|
|
|
CHECK_EQ(v8::HeapGraphNode::kObject, b->GetType());
|
|
|
|
}
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2011-10-21 13:05:37 +00:00
|
|
|
TEST(HeapSnapshotSlicedString) {
|
2017-11-02 06:22:13 +00:00
|
|
|
if (!i::FLAG_string_slices) return;
|
2011-10-21 13:05:37 +00:00
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2011-10-21 13:05:37 +00:00
|
|
|
CompileRun(
|
|
|
|
"parent_string = \"123456789.123456789.123456789.123456789.123456789."
|
|
|
|
"123456789.123456789.123456789.123456789.123456789."
|
|
|
|
"123456789.123456789.123456789.123456789.123456789."
|
2015-02-11 09:15:19 +00:00
|
|
|
"123456789.123456789.123456789.123456789.123456789."
|
|
|
|
"123456789.123456789.123456789.123456789.123456789."
|
|
|
|
"123456789.123456789.123456789.123456789.123456789."
|
|
|
|
"123456789.123456789.123456789.123456789.123456789."
|
2018-05-02 15:30:06 +00:00
|
|
|
"123456789.123456789.123456789.123456789.123456789.\";"
|
2011-10-21 13:05:37 +00:00
|
|
|
"child_string = parent_string.slice(100);");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2011-10-21 13:05:37 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* parent_string = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "parent_string");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(parent_string);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* child_string = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "child_string");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(child_string);
|
2013-09-10 11:12:35 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kSlicedString, child_string->GetType());
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* parent = GetProperty(
|
|
|
|
env->GetIsolate(), child_string, v8::HeapGraphEdge::kInternal, "parent");
|
2011-10-21 13:05:37 +00:00
|
|
|
CHECK_EQ(parent_string, parent);
|
2013-09-10 11:12:35 +00:00
|
|
|
heap_profiler->DeleteAllHeapSnapshots();
|
2011-10-21 13:05:37 +00:00
|
|
|
}
|
2010-10-18 09:15:38 +00:00
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2013-09-10 11:12:35 +00:00
|
|
|
TEST(HeapSnapshotConsString) {
|
2013-09-19 08:54:58 +00:00
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
2013-09-10 11:12:35 +00:00
|
|
|
v8::HandleScope scope(isolate);
|
2014-01-08 06:53:31 +00:00
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
2013-09-10 11:12:35 +00:00
|
|
|
global_template->SetInternalFieldCount(1);
|
2017-10-13 16:33:03 +00:00
|
|
|
LocalContext env(nullptr, global_template);
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> global_proxy = env->Global();
|
|
|
|
v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
|
2013-09-10 11:12:35 +00:00
|
|
|
CHECK_EQ(1, global->InternalFieldCount());
|
|
|
|
|
2013-09-19 09:17:13 +00:00
|
|
|
i::Factory* factory = CcTest::i_isolate()->factory();
|
2014-09-10 12:38:12 +00:00
|
|
|
i::Handle<i::String> first = factory->NewStringFromStaticChars("0123456789");
|
|
|
|
i::Handle<i::String> second = factory->NewStringFromStaticChars("0123456789");
|
2014-04-03 12:30:37 +00:00
|
|
|
i::Handle<i::String> cons_string =
|
|
|
|
factory->NewConsString(first, second).ToHandleChecked();
|
2013-09-10 11:12:35 +00:00
|
|
|
|
|
|
|
global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string));
|
|
|
|
|
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-09-10 11:12:35 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
|
|
|
|
|
|
|
|
const v8::HeapGraphNode* string_node =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(isolate, global_node, v8::HeapGraphEdge::kInternal, "0");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(string_node);
|
2013-09-10 11:12:35 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kConsString, string_node->GetType());
|
|
|
|
|
|
|
|
const v8::HeapGraphNode* first_node =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(isolate, string_node, v8::HeapGraphEdge::kInternal, "first");
|
2013-09-10 11:12:35 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kString, first_node->GetType());
|
|
|
|
|
|
|
|
const v8::HeapGraphNode* second_node =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(isolate, string_node, v8::HeapGraphEdge::kInternal, "second");
|
2013-09-10 11:12:35 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kString, second_node->GetType());
|
|
|
|
|
|
|
|
heap_profiler->DeleteAllHeapSnapshots();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-22 11:26:48 +00:00
|
|
|
TEST(HeapSnapshotSymbol) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun("a = Symbol('mySymbol');\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2014-05-22 11:26:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* a =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(a);
|
2014-05-22 11:26:48 +00:00
|
|
|
CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol);
|
2015-12-08 18:41:54 +00:00
|
|
|
CHECK(v8_str("symbol")->Equals(env.local(), a->GetName()).FromJust());
|
2014-05-22 11:26:48 +00:00
|
|
|
const v8::HeapGraphNode* name =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), a, v8::HeapGraphEdge::kInternal, "name");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(name);
|
2015-12-08 18:41:54 +00:00
|
|
|
CHECK(v8_str("mySymbol")->Equals(env.local(), name->GetName()).FromJust());
|
2014-05-22 11:26:48 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 11:05:22 +00:00
|
|
|
TEST(HeapSnapshotWeakCollection) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
2014-07-10 10:54:47 +00:00
|
|
|
CompileRun(
|
|
|
|
"k = {}; v = {}; s = 'str';\n"
|
|
|
|
"ws = new WeakSet(); ws.add(k); ws.add(v); ws[s] = s;\n"
|
|
|
|
"wm = new WeakMap(); wm.set(k, v); wm[s] = s;\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2014-05-23 11:05:22 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* k =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "k");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(k);
|
2014-05-23 11:05:22 +00:00
|
|
|
const v8::HeapGraphNode* v =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "v");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(v);
|
2014-07-10 10:54:47 +00:00
|
|
|
const v8::HeapGraphNode* s =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "s");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(s);
|
2014-05-23 11:05:22 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* ws = GetProperty(env->GetIsolate(), global,
|
|
|
|
v8::HeapGraphEdge::kProperty, "ws");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(ws);
|
2014-05-23 11:05:22 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType());
|
2015-12-08 18:41:54 +00:00
|
|
|
CHECK(v8_str("WeakSet")->Equals(env.local(), ws->GetName()).FromJust());
|
2014-05-23 11:05:22 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* ws_table =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), ws, v8::HeapGraphEdge::kInternal, "table");
|
2014-05-23 11:05:22 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kArray, ws_table->GetType());
|
|
|
|
CHECK_GT(ws_table->GetChildrenCount(), 0);
|
|
|
|
int weak_entries = 0;
|
|
|
|
for (int i = 0, count = ws_table->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = ws_table->GetChild(i);
|
|
|
|
if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
|
|
|
|
if (k->GetId() == prop->GetToNode()->GetId()) {
|
|
|
|
++weak_entries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK_EQ(1, weak_entries);
|
2014-07-10 10:54:47 +00:00
|
|
|
const v8::HeapGraphNode* ws_s =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), ws, v8::HeapGraphEdge::kProperty, "str");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(ws_s);
|
|
|
|
CHECK_EQ(s->GetId(), ws_s->GetId());
|
2014-05-23 11:05:22 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* wm = GetProperty(env->GetIsolate(), global,
|
|
|
|
v8::HeapGraphEdge::kProperty, "wm");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(wm);
|
2014-05-23 11:05:22 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType());
|
2015-12-08 18:41:54 +00:00
|
|
|
CHECK(v8_str("WeakMap")->Equals(env.local(), wm->GetName()).FromJust());
|
2014-05-23 11:05:22 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* wm_table =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), wm, v8::HeapGraphEdge::kInternal, "table");
|
2014-05-23 11:05:22 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kArray, wm_table->GetType());
|
|
|
|
CHECK_GT(wm_table->GetChildrenCount(), 0);
|
|
|
|
weak_entries = 0;
|
|
|
|
for (int i = 0, count = wm_table->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = wm_table->GetChild(i);
|
|
|
|
if (prop->GetType() != v8::HeapGraphEdge::kWeak) continue;
|
|
|
|
const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
|
|
|
|
if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
|
|
|
|
++weak_entries;
|
|
|
|
}
|
|
|
|
}
|
2018-08-27 16:28:59 +00:00
|
|
|
CHECK_EQ(2, weak_entries); // Key and value are weak.
|
2014-07-10 10:54:47 +00:00
|
|
|
const v8::HeapGraphNode* wm_s =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), wm, v8::HeapGraphEdge::kProperty, "str");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(wm_s);
|
|
|
|
CHECK_EQ(s->GetId(), wm_s->GetId());
|
2014-07-10 10:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(HeapSnapshotCollection) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"k = {}; v = {}; s = 'str';\n"
|
|
|
|
"set = new Set(); set.add(k); set.add(v); set[s] = s;\n"
|
|
|
|
"map = new Map(); map.set(k, v); map[s] = s;\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2014-07-10 10:54:47 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* k =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "k");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(k);
|
2014-07-10 10:54:47 +00:00
|
|
|
const v8::HeapGraphNode* v =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "v");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(v);
|
2014-07-10 10:54:47 +00:00
|
|
|
const v8::HeapGraphNode* s =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "s");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(s);
|
2014-07-10 10:54:47 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* set = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "set");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(set);
|
2014-07-10 10:54:47 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType());
|
2015-12-08 18:41:54 +00:00
|
|
|
CHECK(v8_str("Set")->Equals(env.local(), set->GetName()).FromJust());
|
2014-07-10 10:54:47 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* set_table = GetProperty(
|
|
|
|
env->GetIsolate(), set, v8::HeapGraphEdge::kInternal, "table");
|
2014-07-10 10:54:47 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kArray, set_table->GetType());
|
|
|
|
CHECK_GT(set_table->GetChildrenCount(), 0);
|
|
|
|
int entries = 0;
|
|
|
|
for (int i = 0, count = set_table->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = set_table->GetChild(i);
|
|
|
|
const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
|
|
|
|
if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
|
|
|
|
++entries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK_EQ(2, entries);
|
|
|
|
const v8::HeapGraphNode* set_s =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), set, v8::HeapGraphEdge::kProperty, "str");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(set_s);
|
|
|
|
CHECK_EQ(s->GetId(), set_s->GetId());
|
2014-07-10 10:54:47 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* map = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "map");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(map);
|
2014-07-10 10:54:47 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType());
|
2015-12-08 18:41:54 +00:00
|
|
|
CHECK(v8_str("Map")->Equals(env.local(), map->GetName()).FromJust());
|
2014-07-10 10:54:47 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* map_table = GetProperty(
|
|
|
|
env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "table");
|
2014-07-10 10:54:47 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kArray, map_table->GetType());
|
|
|
|
CHECK_GT(map_table->GetChildrenCount(), 0);
|
|
|
|
entries = 0;
|
|
|
|
for (int i = 0, count = map_table->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = map_table->GetChild(i);
|
|
|
|
const v8::SnapshotObjectId to_node_id = prop->GetToNode()->GetId();
|
|
|
|
if (to_node_id == k->GetId() || to_node_id == v->GetId()) {
|
|
|
|
++entries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK_EQ(2, entries);
|
|
|
|
const v8::HeapGraphNode* map_s =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kProperty, "str");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(map_s);
|
|
|
|
CHECK_EQ(s->GetId(), map_s->GetId());
|
2014-05-23 11:05:22 +00:00
|
|
|
}
|
|
|
|
|
2016-08-02 13:02:50 +00:00
|
|
|
TEST(HeapSnapshotMap) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun(
|
2018-04-05 15:40:15 +00:00
|
|
|
"function Z() { this.foo = {}; this.bar = 0; }\n"
|
2016-08-02 13:02:50 +00:00
|
|
|
"z = new Z();\n");
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* z =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "z");
|
2016-08-02 13:02:50 +00:00
|
|
|
CHECK(z);
|
|
|
|
const v8::HeapGraphNode* map =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), z, v8::HeapGraphEdge::kInternal, "map");
|
2016-08-02 13:02:50 +00:00
|
|
|
CHECK(map);
|
2017-08-24 21:49:48 +00:00
|
|
|
CHECK(
|
|
|
|
GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "map"));
|
|
|
|
CHECK(GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal,
|
|
|
|
"prototype"));
|
2018-04-05 15:40:15 +00:00
|
|
|
const v8::HeapGraphNode* parent_map = GetProperty(
|
|
|
|
env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "back_pointer");
|
|
|
|
CHECK(parent_map);
|
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
CHECK(GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal,
|
|
|
|
"back_pointer"));
|
|
|
|
CHECK(GetProperty(env->GetIsolate(), map, v8::HeapGraphEdge::kInternal,
|
|
|
|
"descriptors"));
|
2018-04-05 15:40:15 +00:00
|
|
|
CHECK(GetProperty(env->GetIsolate(), parent_map, v8::HeapGraphEdge::kWeak,
|
|
|
|
"transition"));
|
2016-08-02 13:02:50 +00:00
|
|
|
}
|
2014-05-23 11:05:22 +00:00
|
|
|
|
2010-10-18 09:15:38 +00:00
|
|
|
TEST(HeapSnapshotInternalReferences) {
|
2013-09-19 08:54:58 +00:00
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HandleScope scope(isolate);
|
2014-01-08 06:53:31 +00:00
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
2010-10-18 09:15:38 +00:00
|
|
|
global_template->SetInternalFieldCount(2);
|
2017-10-13 16:33:03 +00:00
|
|
|
LocalContext env(nullptr, global_template);
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> global_proxy = env->Global();
|
|
|
|
v8::Local<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
|
2010-10-18 09:15:38 +00:00
|
|
|
CHECK_EQ(2, global->InternalFieldCount());
|
2014-01-03 14:31:17 +00:00
|
|
|
v8::Local<v8::Object> obj = v8::Object::New(isolate);
|
2010-10-18 09:15:38 +00:00
|
|
|
global->SetInternalField(0, v8_num(17));
|
|
|
|
global->SetInternalField(1, obj);
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2010-10-18 09:15:38 +00:00
|
|
|
const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
|
|
|
|
// The first reference will not present, because it's a Smi.
|
2017-08-24 21:49:48 +00:00
|
|
|
CHECK(!GetProperty(env->GetIsolate(), global_node,
|
|
|
|
v8::HeapGraphEdge::kInternal, "0"));
|
2010-10-18 09:15:38 +00:00
|
|
|
// The second reference is to an object.
|
2017-08-24 21:49:48 +00:00
|
|
|
CHECK(GetProperty(env->GetIsolate(), global_node,
|
|
|
|
v8::HeapGraphEdge::kInternal, "1"));
|
2010-10-18 09:15:38 +00:00
|
|
|
}
|
|
|
|
|
2018-02-20 16:06:21 +00:00
|
|
|
TEST(HeapSnapshotEphemeron) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"class KeyClass{};\n"
|
|
|
|
"class ValueClass{};\n"
|
|
|
|
"var wm = new WeakMap();\n"
|
|
|
|
"function foo(key) { wm.set(key, new ValueClass()); }\n"
|
|
|
|
"var key = new KeyClass();\n"
|
|
|
|
"foo(key);");
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
|
|
|
|
const v8::HeapGraphNode* key = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "key");
|
|
|
|
CHECK(key);
|
2019-10-31 13:27:02 +00:00
|
|
|
const v8::HeapGraphNode* weakmap = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "wm");
|
|
|
|
CHECK(weakmap);
|
|
|
|
const v8::HeapGraphNode* weakmap_table = GetProperty(
|
|
|
|
env->GetIsolate(), weakmap, v8::HeapGraphEdge::kInternal, "table");
|
|
|
|
CHECK(weakmap_table);
|
2018-02-20 16:06:21 +00:00
|
|
|
bool success = false;
|
|
|
|
for (int i = 0, count = key->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* edge = key->GetChild(i);
|
|
|
|
const v8::HeapGraphNode* child = edge->GetToNode();
|
|
|
|
if (!strcmp("ValueClass", GetName(child))) {
|
|
|
|
v8::String::Utf8Value edge_name(CcTest::isolate(), edge->GetName());
|
2019-10-31 13:27:02 +00:00
|
|
|
std::stringstream end_of_label;
|
|
|
|
end_of_label << "/ part of key (KeyClass @" << key->GetId()
|
|
|
|
<< ") -> value (ValueClass @" << child->GetId()
|
|
|
|
<< ") pair in WeakMap (table @" << weakmap_table->GetId()
|
|
|
|
<< ")";
|
|
|
|
CHECK(EndsWith(*edge_name, end_of_label.str().c_str()));
|
2018-02-20 16:06:21 +00:00
|
|
|
success = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK(success);
|
|
|
|
}
|
2010-10-18 09:15:38 +00:00
|
|
|
|
2013-02-21 13:16:17 +00:00
|
|
|
TEST(HeapSnapshotAddressReuse) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2013-02-21 13:16:17 +00:00
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"function A() {}\n"
|
|
|
|
"var a = [];\n"
|
|
|
|
"for (var i = 0; i < 10000; ++i)\n"
|
|
|
|
" a[i] = new A();\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot1));
|
2013-02-21 13:16:17 +00:00
|
|
|
v8::SnapshotObjectId maxId1 = snapshot1->GetMaxSnapshotJSObjectId();
|
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"for (var i = 0; i < 10000; ++i)\n"
|
|
|
|
" a[i] = new A();\n");
|
2017-04-26 22:16:41 +00:00
|
|
|
CcTest::CollectAllGarbage();
|
2013-02-21 13:16:17 +00:00
|
|
|
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
|
2016-05-31 17:40:26 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot2));
|
2013-02-21 13:16:17 +00:00
|
|
|
const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
|
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* array_node = GetProperty(
|
|
|
|
env->GetIsolate(), global2, v8::HeapGraphEdge::kProperty, "a");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(array_node);
|
2013-02-21 13:16:17 +00:00
|
|
|
int wrong_count = 0;
|
|
|
|
for (int i = 0, count = array_node->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = array_node->GetChild(i);
|
|
|
|
if (prop->GetType() != v8::HeapGraphEdge::kElement)
|
|
|
|
continue;
|
|
|
|
v8::SnapshotObjectId id = prop->GetToNode()->GetId();
|
|
|
|
if (id < maxId1)
|
|
|
|
++wrong_count;
|
|
|
|
}
|
2013-02-25 16:09:57 +00:00
|
|
|
CHECK_EQ(0, wrong_count);
|
2013-02-21 13:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-20 14:35:05 +00:00
|
|
|
TEST(HeapEntryIdsAndArrayShift) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2012-03-20 14:35:05 +00:00
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"function AnObject() {\n"
|
|
|
|
" this.first = 'first';\n"
|
|
|
|
" this.second = 'second';\n"
|
|
|
|
"}\n"
|
|
|
|
"var a = new Array();\n"
|
|
|
|
"for (var i = 0; i < 10; ++i)\n"
|
|
|
|
" a.push(new AnObject());\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot1));
|
2012-03-20 14:35:05 +00:00
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"for (var i = 0; i < 1; ++i)\n"
|
|
|
|
" a.shift();\n");
|
|
|
|
|
2017-04-26 22:16:41 +00:00
|
|
|
CcTest::CollectAllGarbage();
|
2012-03-20 14:35:05 +00:00
|
|
|
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot2));
|
2012-03-20 14:35:05 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
|
|
|
|
const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_NE(0u, global1->GetId());
|
|
|
|
CHECK_EQ(global1->GetId(), global2->GetId());
|
2012-03-20 14:35:05 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* a1 = GetProperty(env->GetIsolate(), global1,
|
|
|
|
v8::HeapGraphEdge::kProperty, "a");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(a1);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* k1 = GetProperty(
|
|
|
|
env->GetIsolate(), a1, v8::HeapGraphEdge::kInternal, "elements");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(k1);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* a2 = GetProperty(env->GetIsolate(), global2,
|
|
|
|
v8::HeapGraphEdge::kProperty, "a");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(a2);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* k2 = GetProperty(
|
|
|
|
env->GetIsolate(), a2, v8::HeapGraphEdge::kInternal, "elements");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(k2);
|
2012-03-20 14:35:05 +00:00
|
|
|
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(a1->GetId(), a2->GetId());
|
|
|
|
CHECK_EQ(k1->GetId(), k2->GetId());
|
2012-03-20 14:35:05 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2010-07-15 13:21:50 +00:00
|
|
|
TEST(HeapEntryIdsAndGC) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2010-07-15 13:21:50 +00:00
|
|
|
|
2010-10-18 09:15:38 +00:00
|
|
|
CompileRun(
|
2010-07-15 13:21:50 +00:00
|
|
|
"function A() {}\n"
|
|
|
|
"function B(x) { this.x = x; }\n"
|
|
|
|
"var a = new A();\n"
|
|
|
|
"var b = new B(a);");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot1 = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot1));
|
2010-07-15 13:21:50 +00:00
|
|
|
|
2017-04-26 22:16:41 +00:00
|
|
|
CcTest::CollectAllGarbage();
|
2010-07-15 13:21:50 +00:00
|
|
|
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot2 = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot2));
|
2012-03-27 11:54:47 +00:00
|
|
|
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_GT(snapshot1->GetMaxSnapshotJSObjectId(), 7000u);
|
2012-03-29 14:18:11 +00:00
|
|
|
CHECK(snapshot1->GetMaxSnapshotJSObjectId() <=
|
|
|
|
snapshot2->GetMaxSnapshotJSObjectId());
|
2010-07-15 13:21:50 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* global1 = GetGlobalObject(snapshot1);
|
|
|
|
const v8::HeapGraphNode* global2 = GetGlobalObject(snapshot2);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_NE(0u, global1->GetId());
|
|
|
|
CHECK_EQ(global1->GetId(), global2->GetId());
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* A1 = GetProperty(env->GetIsolate(), global1,
|
|
|
|
v8::HeapGraphEdge::kProperty, "A");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(A1);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* A2 = GetProperty(env->GetIsolate(), global2,
|
|
|
|
v8::HeapGraphEdge::kProperty, "A");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(A2);
|
|
|
|
CHECK_NE(0u, A1->GetId());
|
|
|
|
CHECK_EQ(A1->GetId(), A2->GetId());
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* B1 = GetProperty(env->GetIsolate(), global1,
|
|
|
|
v8::HeapGraphEdge::kProperty, "B");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(B1);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* B2 = GetProperty(env->GetIsolate(), global2,
|
|
|
|
v8::HeapGraphEdge::kProperty, "B");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(B2);
|
|
|
|
CHECK_NE(0u, B1->GetId());
|
|
|
|
CHECK_EQ(B1->GetId(), B2->GetId());
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* a1 = GetProperty(env->GetIsolate(), global1,
|
|
|
|
v8::HeapGraphEdge::kProperty, "a");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(a1);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* a2 = GetProperty(env->GetIsolate(), global2,
|
|
|
|
v8::HeapGraphEdge::kProperty, "a");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(a2);
|
|
|
|
CHECK_NE(0u, a1->GetId());
|
|
|
|
CHECK_EQ(a1->GetId(), a2->GetId());
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* b1 = GetProperty(env->GetIsolate(), global1,
|
|
|
|
v8::HeapGraphEdge::kProperty, "b");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(b1);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* b2 = GetProperty(env->GetIsolate(), global2,
|
|
|
|
v8::HeapGraphEdge::kProperty, "b");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(b2);
|
|
|
|
CHECK_NE(0u, b1->GetId());
|
|
|
|
CHECK_EQ(b1->GetId(), b2->GetId());
|
2010-07-15 13:21:50 +00:00
|
|
|
}
|
|
|
|
|
2010-09-14 11:49:06 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class TestJSONStream : public v8::OutputStream {
|
|
|
|
public:
|
2010-09-14 15:11:24 +00:00
|
|
|
TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
|
|
|
|
explicit TestJSONStream(int abort_countdown)
|
|
|
|
: eos_signaled_(0), abort_countdown_(abort_countdown) {}
|
2018-09-14 15:34:02 +00:00
|
|
|
~TestJSONStream() override = default;
|
|
|
|
void EndOfStream() override { ++eos_signaled_; }
|
|
|
|
WriteResult WriteAsciiChunk(char* buffer, int chars_written) override {
|
2010-09-14 15:11:24 +00:00
|
|
|
if (abort_countdown_ > 0) --abort_countdown_;
|
|
|
|
if (abort_countdown_ == 0) return kAbort;
|
2010-09-14 11:49:06 +00:00
|
|
|
CHECK_GT(chars_written, 0);
|
|
|
|
i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
|
2019-04-29 11:06:49 +00:00
|
|
|
i::MemCopy(chunk.begin(), buffer, chars_written);
|
2010-09-14 15:11:24 +00:00
|
|
|
return kContinue;
|
2010-09-14 11:49:06 +00:00
|
|
|
}
|
2012-04-13 08:52:25 +00:00
|
|
|
virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
|
2017-12-19 11:39:50 +00:00
|
|
|
UNREACHABLE();
|
2012-04-13 08:52:25 +00:00
|
|
|
}
|
2010-09-14 11:49:06 +00:00
|
|
|
void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
|
|
|
|
int eos_signaled() { return eos_signaled_; }
|
|
|
|
int size() { return buffer_.size(); }
|
2012-04-13 08:52:25 +00:00
|
|
|
|
2010-09-14 11:49:06 +00:00
|
|
|
private:
|
|
|
|
i::Collector<char> buffer_;
|
|
|
|
int eos_signaled_;
|
2010-09-14 15:11:24 +00:00
|
|
|
int abort_countdown_;
|
2010-09-14 11:49:06 +00:00
|
|
|
};
|
|
|
|
|
2014-09-10 12:38:12 +00:00
|
|
|
class OneByteResource : public v8::String::ExternalOneByteStringResource {
|
2010-09-14 11:49:06 +00:00
|
|
|
public:
|
2019-04-29 11:06:49 +00:00
|
|
|
explicit OneByteResource(i::Vector<char> string) : data_(string.begin()) {
|
2010-09-14 11:49:06 +00:00
|
|
|
length_ = string.length();
|
|
|
|
}
|
2018-09-14 15:34:02 +00:00
|
|
|
const char* data() const override { return data_; }
|
|
|
|
size_t length() const override { return length_; }
|
|
|
|
|
2010-09-14 11:49:06 +00:00
|
|
|
private:
|
|
|
|
const char* data_;
|
|
|
|
size_t length_;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
TEST(HeapSnapshotJSONSerialization) {
|
2014-11-24 10:30:39 +00:00
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
2010-09-14 11:49:06 +00:00
|
|
|
LocalContext env;
|
2014-11-24 10:30:39 +00:00
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
2010-09-14 11:49:06 +00:00
|
|
|
|
|
|
|
#define STRING_LITERAL_FOR_TEST \
|
|
|
|
"\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
|
2010-10-18 09:15:38 +00:00
|
|
|
CompileRun(
|
2010-09-14 11:49:06 +00:00
|
|
|
"function A(s) { this.s = s; }\n"
|
|
|
|
"function B(x) { this.x = x; }\n"
|
|
|
|
"var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
|
|
|
|
"var b = new B(a);");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2013-07-10 12:40:42 +00:00
|
|
|
|
2010-09-14 11:49:06 +00:00
|
|
|
TestJSONStream stream;
|
|
|
|
snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
|
|
|
|
CHECK_GT(stream.size(), 0);
|
|
|
|
CHECK_EQ(1, stream.eos_signaled());
|
|
|
|
i::ScopedVector<char> json(stream.size());
|
|
|
|
stream.WriteTo(json);
|
|
|
|
|
|
|
|
// Verify that snapshot string is valid JSON.
|
2014-09-10 12:38:12 +00:00
|
|
|
OneByteResource* json_res = new OneByteResource(json);
|
2013-11-28 08:21:26 +00:00
|
|
|
v8::Local<v8::String> json_string =
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::String::NewExternalOneByte(env->GetIsolate(), json_res)
|
|
|
|
.ToLocalChecked();
|
|
|
|
env->Global()
|
|
|
|
->Set(env.local(), v8_str("json_snapshot"), json_string)
|
|
|
|
.FromJust();
|
2010-09-14 11:49:06 +00:00
|
|
|
v8::Local<v8::Value> snapshot_parse_result = CompileRun(
|
|
|
|
"var parsed = JSON.parse(json_snapshot); true;");
|
|
|
|
CHECK(!snapshot_parse_result.IsEmpty());
|
|
|
|
|
|
|
|
// Verify that snapshot object has required fields.
|
|
|
|
v8::Local<v8::Object> parsed_snapshot =
|
2015-12-08 18:41:54 +00:00
|
|
|
env->Global()
|
|
|
|
->Get(env.local(), v8_str("parsed"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
->ToObject(env.local())
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK(parsed_snapshot->Has(env.local(), v8_str("snapshot")).FromJust());
|
|
|
|
CHECK(parsed_snapshot->Has(env.local(), v8_str("nodes")).FromJust());
|
|
|
|
CHECK(parsed_snapshot->Has(env.local(), v8_str("edges")).FromJust());
|
2018-08-20 12:14:25 +00:00
|
|
|
CHECK(parsed_snapshot->Has(env.local(), v8_str("locations")).FromJust());
|
2015-12-08 18:41:54 +00:00
|
|
|
CHECK(parsed_snapshot->Has(env.local(), v8_str("strings")).FromJust());
|
2010-09-14 11:49:06 +00:00
|
|
|
|
|
|
|
// Get node and edge "member" offsets.
|
|
|
|
v8::Local<v8::Value> meta_analysis_result = CompileRun(
|
2012-04-13 12:50:48 +00:00
|
|
|
"var meta = parsed.snapshot.meta;\n"
|
2012-06-06 10:57:20 +00:00
|
|
|
"var edge_count_offset = meta.node_fields.indexOf('edge_count');\n"
|
2012-04-13 12:50:48 +00:00
|
|
|
"var node_fields_count = meta.node_fields.length;\n"
|
|
|
|
"var edge_fields_count = meta.edge_fields.length;\n"
|
|
|
|
"var edge_type_offset = meta.edge_fields.indexOf('type');\n"
|
|
|
|
"var edge_name_offset = meta.edge_fields.indexOf('name_or_index');\n"
|
|
|
|
"var edge_to_node_offset = meta.edge_fields.indexOf('to_node');\n"
|
2010-09-14 11:49:06 +00:00
|
|
|
"var property_type ="
|
2012-04-13 12:50:48 +00:00
|
|
|
" meta.edge_types[edge_type_offset].indexOf('property');\n"
|
2010-11-18 10:38:25 +00:00
|
|
|
"var shortcut_type ="
|
2012-04-13 12:50:48 +00:00
|
|
|
" meta.edge_types[edge_type_offset].indexOf('shortcut');\n"
|
2012-06-06 10:57:20 +00:00
|
|
|
"var node_count = parsed.nodes.length / node_fields_count;\n"
|
|
|
|
"var first_edge_indexes = parsed.first_edge_indexes = [];\n"
|
|
|
|
"for (var i = 0, first_edge_index = 0; i < node_count; ++i) {\n"
|
|
|
|
" first_edge_indexes[i] = first_edge_index;\n"
|
|
|
|
" first_edge_index += edge_fields_count *\n"
|
|
|
|
" parsed.nodes[i * node_fields_count + edge_count_offset];\n"
|
2013-06-28 12:53:52 +00:00
|
|
|
"}\n"
|
|
|
|
"first_edge_indexes[node_count] = first_edge_index;\n");
|
2010-09-14 11:49:06 +00:00
|
|
|
CHECK(!meta_analysis_result.IsEmpty());
|
|
|
|
|
|
|
|
// A helper function for processing encoded nodes.
|
|
|
|
CompileRun(
|
2010-11-18 10:38:25 +00:00
|
|
|
"function GetChildPosByProperty(pos, prop_name, prop_type) {\n"
|
2010-09-14 11:49:06 +00:00
|
|
|
" var nodes = parsed.nodes;\n"
|
2012-04-13 12:50:48 +00:00
|
|
|
" var edges = parsed.edges;\n"
|
2010-09-14 11:49:06 +00:00
|
|
|
" var strings = parsed.strings;\n"
|
2012-06-06 10:57:20 +00:00
|
|
|
" var node_ordinal = pos / node_fields_count;\n"
|
|
|
|
" for (var i = parsed.first_edge_indexes[node_ordinal],\n"
|
|
|
|
" count = parsed.first_edge_indexes[node_ordinal + 1];\n"
|
2012-04-13 12:50:48 +00:00
|
|
|
" i < count; i += edge_fields_count) {\n"
|
|
|
|
" if (edges[i + edge_type_offset] === prop_type\n"
|
|
|
|
" && strings[edges[i + edge_name_offset]] === prop_name)\n"
|
|
|
|
" return edges[i + edge_to_node_offset];\n"
|
2010-09-14 11:49:06 +00:00
|
|
|
" }\n"
|
|
|
|
" return null;\n"
|
|
|
|
"}\n");
|
|
|
|
// Get the string index using the path: <root> -> <global>.b.x.s
|
|
|
|
v8::Local<v8::Value> string_obj_pos_val = CompileRun(
|
|
|
|
"GetChildPosByProperty(\n"
|
|
|
|
" GetChildPosByProperty(\n"
|
|
|
|
" GetChildPosByProperty("
|
2013-06-28 12:53:52 +00:00
|
|
|
" parsed.edges[edge_fields_count + edge_to_node_offset],"
|
2012-04-16 14:31:13 +00:00
|
|
|
" \"b\", property_type),\n"
|
2010-11-18 10:38:25 +00:00
|
|
|
" \"x\", property_type),"
|
|
|
|
" \"s\", property_type)");
|
2010-09-14 11:49:06 +00:00
|
|
|
CHECK(!string_obj_pos_val.IsEmpty());
|
2015-12-08 18:41:54 +00:00
|
|
|
int string_obj_pos = static_cast<int>(
|
|
|
|
string_obj_pos_val->ToNumber(env.local()).ToLocalChecked()->Value());
|
2010-09-14 11:49:06 +00:00
|
|
|
v8::Local<v8::Object> nodes_array =
|
2015-12-08 18:41:54 +00:00
|
|
|
parsed_snapshot->Get(env.local(), v8_str("nodes"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
->ToObject(env.local())
|
|
|
|
.ToLocalChecked();
|
|
|
|
int string_index =
|
|
|
|
static_cast<int>(nodes_array->Get(env.local(), string_obj_pos + 1)
|
|
|
|
.ToLocalChecked()
|
|
|
|
->ToNumber(env.local())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Value());
|
2010-09-14 11:49:06 +00:00
|
|
|
CHECK_GT(string_index, 0);
|
|
|
|
v8::Local<v8::Object> strings_array =
|
2015-12-08 18:41:54 +00:00
|
|
|
parsed_snapshot->Get(env.local(), v8_str("strings"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
->ToObject(env.local())
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::String> string = strings_array->Get(env.local(), string_index)
|
|
|
|
.ToLocalChecked()
|
|
|
|
->ToString(env.local())
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::String> ref_string = CompileRun(STRING_LITERAL_FOR_TEST)
|
|
|
|
->ToString(env.local())
|
|
|
|
.ToLocalChecked();
|
2010-09-14 11:49:06 +00:00
|
|
|
#undef STRING_LITERAL_FOR_TEST
|
2017-08-24 21:49:48 +00:00
|
|
|
CHECK_EQ(0, strcmp(*v8::String::Utf8Value(env->GetIsolate(), ref_string),
|
|
|
|
*v8::String::Utf8Value(env->GetIsolate(), string)));
|
2010-09-14 11:49:06 +00:00
|
|
|
}
|
|
|
|
|
2010-09-14 15:11:24 +00:00
|
|
|
|
|
|
|
TEST(HeapSnapshotJSONSerializationAborting) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2010-09-14 15:11:24 +00:00
|
|
|
TestJSONStream stream(5);
|
|
|
|
snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
|
|
|
|
CHECK_GT(stream.size(), 0);
|
|
|
|
CHECK_EQ(0, stream.eos_signaled());
|
|
|
|
}
|
|
|
|
|
2012-04-13 08:52:25 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class TestStatsStream : public v8::OutputStream {
|
|
|
|
public:
|
|
|
|
TestStatsStream()
|
|
|
|
: eos_signaled_(0),
|
2012-04-17 13:10:17 +00:00
|
|
|
updates_written_(0),
|
2012-04-13 08:52:25 +00:00
|
|
|
entries_count_(0),
|
2012-04-16 09:44:42 +00:00
|
|
|
entries_size_(0),
|
2012-04-13 08:52:25 +00:00
|
|
|
intervals_count_(0),
|
|
|
|
first_interval_index_(-1) { }
|
2019-01-16 18:29:52 +00:00
|
|
|
TestStatsStream(const TestStatsStream& stream) V8_NOEXCEPT = default;
|
2018-09-14 15:34:02 +00:00
|
|
|
~TestStatsStream() override = default;
|
|
|
|
void EndOfStream() override { ++eos_signaled_; }
|
|
|
|
WriteResult WriteAsciiChunk(char* buffer, int chars_written) override {
|
2017-12-19 11:39:50 +00:00
|
|
|
UNREACHABLE();
|
2012-04-13 08:52:25 +00:00
|
|
|
}
|
2018-09-14 15:34:02 +00:00
|
|
|
WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
|
|
|
|
int updates_written) override {
|
2012-04-13 08:52:25 +00:00
|
|
|
++intervals_count_;
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(updates_written);
|
2012-04-17 13:10:17 +00:00
|
|
|
updates_written_ += updates_written;
|
2012-04-13 08:52:25 +00:00
|
|
|
entries_count_ = 0;
|
2012-04-17 13:10:17 +00:00
|
|
|
if (first_interval_index_ == -1 && updates_written != 0)
|
|
|
|
first_interval_index_ = buffer[0].index;
|
|
|
|
for (int i = 0; i < updates_written; ++i) {
|
|
|
|
entries_count_ += buffer[i].count;
|
|
|
|
entries_size_ += buffer[i].size;
|
2012-04-16 09:44:42 +00:00
|
|
|
}
|
2012-04-13 08:52:25 +00:00
|
|
|
|
|
|
|
return kContinue;
|
|
|
|
}
|
|
|
|
int eos_signaled() { return eos_signaled_; }
|
2012-04-17 13:10:17 +00:00
|
|
|
int updates_written() { return updates_written_; }
|
2012-04-13 08:52:25 +00:00
|
|
|
uint32_t entries_count() const { return entries_count_; }
|
2012-04-16 09:44:42 +00:00
|
|
|
uint32_t entries_size() const { return entries_size_; }
|
2012-04-13 08:52:25 +00:00
|
|
|
int intervals_count() const { return intervals_count_; }
|
|
|
|
int first_interval_index() const { return first_interval_index_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
int eos_signaled_;
|
2012-04-17 13:10:17 +00:00
|
|
|
int updates_written_;
|
2012-04-13 08:52:25 +00:00
|
|
|
uint32_t entries_count_;
|
2012-04-16 09:44:42 +00:00
|
|
|
uint32_t entries_size_;
|
2012-04-13 08:52:25 +00:00
|
|
|
int intervals_count_;
|
|
|
|
int first_interval_index_;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2012-06-01 16:10:52 +00:00
|
|
|
static TestStatsStream GetHeapStatsUpdate(
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler,
|
2017-10-13 16:33:03 +00:00
|
|
|
v8::SnapshotObjectId* object_id = nullptr) {
|
2012-04-13 08:52:25 +00:00
|
|
|
TestStatsStream stream;
|
2015-03-26 08:49:52 +00:00
|
|
|
int64_t timestamp = -1;
|
|
|
|
v8::SnapshotObjectId last_seen_id =
|
|
|
|
heap_profiler->GetHeapStats(&stream, ×tamp);
|
2012-06-01 16:10:52 +00:00
|
|
|
if (object_id)
|
|
|
|
*object_id = last_seen_id;
|
2015-03-26 08:49:52 +00:00
|
|
|
CHECK_NE(-1, timestamp);
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(1, stream.eos_signaled());
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(HeapSnapshotObjectsStats) {
|
2020-09-10 15:02:19 +00:00
|
|
|
// Concurrent allocation might break results
|
|
|
|
v8::internal::FLAG_stress_concurrent_allocation = false;
|
|
|
|
|
2012-04-13 08:52:25 +00:00
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2012-04-13 08:52:25 +00:00
|
|
|
|
2013-04-02 08:03:01 +00:00
|
|
|
heap_profiler->StartTrackingHeapObjects();
|
2012-06-25 13:55:36 +00:00
|
|
|
// We have to call GC 6 times. In other case the garbage will be
|
2012-04-13 08:52:25 +00:00
|
|
|
// the reason of flakiness.
|
2012-06-25 13:55:36 +00:00
|
|
|
for (int i = 0; i < 6; ++i) {
|
2017-04-26 22:16:41 +00:00
|
|
|
CcTest::CollectAllGarbage();
|
2012-04-13 08:52:25 +00:00
|
|
|
}
|
|
|
|
|
2012-06-01 16:10:52 +00:00
|
|
|
v8::SnapshotObjectId initial_id;
|
2012-04-13 08:52:25 +00:00
|
|
|
{
|
|
|
|
// Single chunk of data expected in update. Initial data.
|
2013-04-02 08:03:01 +00:00
|
|
|
TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
|
|
|
|
&initial_id);
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(1, stats_update.intervals_count());
|
2012-04-17 13:10:17 +00:00
|
|
|
CHECK_EQ(1, stats_update.updates_written());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_LT(0u, stats_update.entries_size());
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(0, stats_update.first_interval_index());
|
|
|
|
}
|
|
|
|
|
|
|
|
// No data expected in update because nothing has happened.
|
2012-06-01 16:10:52 +00:00
|
|
|
v8::SnapshotObjectId same_id;
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &same_id).updates_written());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(initial_id, same_id);
|
2012-06-01 16:10:52 +00:00
|
|
|
|
2012-04-13 08:52:25 +00:00
|
|
|
{
|
2012-06-01 16:10:52 +00:00
|
|
|
v8::SnapshotObjectId additional_string_id;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope inner_scope_1(env->GetIsolate());
|
2012-04-16 13:27:38 +00:00
|
|
|
v8_str("string1");
|
2012-04-13 08:52:25 +00:00
|
|
|
{
|
|
|
|
// Single chunk of data with one new entry expected in update.
|
2013-04-02 08:03:01 +00:00
|
|
|
TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler,
|
|
|
|
&additional_string_id);
|
2012-06-01 16:10:52 +00:00
|
|
|
CHECK_LT(same_id, additional_string_id);
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(1, stats_update.intervals_count());
|
2012-04-17 13:10:17 +00:00
|
|
|
CHECK_EQ(1, stats_update.updates_written());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_LT(0u, stats_update.entries_size());
|
|
|
|
CHECK_EQ(1u, stats_update.entries_count());
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(2, stats_update.first_interval_index());
|
|
|
|
}
|
|
|
|
|
|
|
|
// No data expected in update because nothing happened.
|
2012-06-01 16:10:52 +00:00
|
|
|
v8::SnapshotObjectId last_id;
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(0, GetHeapStatsUpdate(heap_profiler, &last_id).updates_written());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(additional_string_id, last_id);
|
2012-04-13 08:52:25 +00:00
|
|
|
|
|
|
|
{
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope inner_scope_2(env->GetIsolate());
|
2012-04-16 13:27:38 +00:00
|
|
|
v8_str("string2");
|
2012-04-13 08:52:25 +00:00
|
|
|
|
2012-04-16 09:44:42 +00:00
|
|
|
uint32_t entries_size;
|
2012-04-13 08:52:25 +00:00
|
|
|
{
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope inner_scope_3(env->GetIsolate());
|
2012-04-16 13:27:38 +00:00
|
|
|
v8_str("string3");
|
|
|
|
v8_str("string4");
|
2012-04-13 08:52:25 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// Single chunk of data with three new entries expected in update.
|
2013-04-02 08:03:01 +00:00
|
|
|
TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(1, stats_update.intervals_count());
|
2012-04-17 13:10:17 +00:00
|
|
|
CHECK_EQ(1, stats_update.updates_written());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_LT(0u, entries_size = stats_update.entries_size());
|
|
|
|
CHECK_EQ(3u, stats_update.entries_count());
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(4, stats_update.first_interval_index());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Single chunk of data with two left entries expected in update.
|
2013-04-02 08:03:01 +00:00
|
|
|
TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(1, stats_update.intervals_count());
|
2012-04-17 13:10:17 +00:00
|
|
|
CHECK_EQ(1, stats_update.updates_written());
|
2012-04-16 09:44:42 +00:00
|
|
|
CHECK_GT(entries_size, stats_update.entries_size());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(1u, stats_update.entries_count());
|
2012-04-13 08:52:25 +00:00
|
|
|
// Two strings from forth interval were released.
|
|
|
|
CHECK_EQ(4, stats_update.first_interval_index());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Single chunk of data with 0 left entries expected in update.
|
2013-04-02 08:03:01 +00:00
|
|
|
TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(1, stats_update.intervals_count());
|
2012-04-17 13:10:17 +00:00
|
|
|
CHECK_EQ(1, stats_update.updates_written());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(0u, stats_update.entries_size());
|
|
|
|
CHECK_EQ(0u, stats_update.entries_count());
|
2012-04-13 08:52:25 +00:00
|
|
|
// The last string from forth interval was released.
|
|
|
|
CHECK_EQ(4, stats_update.first_interval_index());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Single chunk of data with 0 left entries expected in update.
|
2013-04-02 08:03:01 +00:00
|
|
|
TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
|
2012-04-13 08:52:25 +00:00
|
|
|
CHECK_EQ(1, stats_update.intervals_count());
|
2012-04-17 13:10:17 +00:00
|
|
|
CHECK_EQ(1, stats_update.updates_written());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(0u, stats_update.entries_size());
|
|
|
|
CHECK_EQ(0u, stats_update.entries_count());
|
2012-04-13 08:52:25 +00:00
|
|
|
// The only string from the second interval was released.
|
|
|
|
CHECK_EQ(2, stats_update.first_interval_index());
|
|
|
|
}
|
|
|
|
|
2013-11-28 08:21:26 +00:00
|
|
|
v8::Local<v8::Array> array = v8::Array::New(env->GetIsolate());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(0u, array->Length());
|
2012-04-16 09:44:42 +00:00
|
|
|
// Force array's buffer allocation.
|
2015-12-08 18:41:54 +00:00
|
|
|
array->Set(env.local(), 2, v8_num(7)).FromJust();
|
2012-04-16 09:44:42 +00:00
|
|
|
|
|
|
|
uint32_t entries_size;
|
|
|
|
{
|
|
|
|
// Single chunk of data with 2 entries expected in update.
|
2013-04-02 08:03:01 +00:00
|
|
|
TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
|
2012-04-16 09:44:42 +00:00
|
|
|
CHECK_EQ(1, stats_update.intervals_count());
|
2012-04-17 13:10:17 +00:00
|
|
|
CHECK_EQ(1, stats_update.updates_written());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_LT(0u, entries_size = stats_update.entries_size());
|
2012-04-16 09:44:42 +00:00
|
|
|
// They are the array and its buffer.
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(2u, stats_update.entries_count());
|
2012-04-16 09:44:42 +00:00
|
|
|
CHECK_EQ(8, stats_update.first_interval_index());
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < 100; ++i)
|
2015-12-08 18:41:54 +00:00
|
|
|
array->Set(env.local(), i, v8_num(i)).FromJust();
|
2012-04-16 09:44:42 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// Single chunk of data with 1 entry expected in update.
|
2013-04-02 08:03:01 +00:00
|
|
|
TestStatsStream stats_update = GetHeapStatsUpdate(heap_profiler);
|
2012-04-16 09:44:42 +00:00
|
|
|
CHECK_EQ(1, stats_update.intervals_count());
|
|
|
|
// The first interval was changed because old buffer was collected.
|
|
|
|
// The second interval was changed because new buffer was allocated.
|
2012-04-17 13:10:17 +00:00
|
|
|
CHECK_EQ(2, stats_update.updates_written());
|
2012-04-16 09:44:42 +00:00
|
|
|
CHECK_LT(entries_size, stats_update.entries_size());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(2u, stats_update.entries_count());
|
2012-04-16 09:44:42 +00:00
|
|
|
CHECK_EQ(8, stats_update.first_interval_index());
|
|
|
|
}
|
|
|
|
|
2013-04-02 08:03:01 +00:00
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
2012-04-13 08:52:25 +00:00
|
|
|
}
|
|
|
|
|
2010-11-24 10:47:18 +00:00
|
|
|
|
2013-12-18 08:17:03 +00:00
|
|
|
TEST(HeapObjectIds) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
const int kLength = 10;
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> objects[kLength];
|
2013-12-18 08:17:03 +00:00
|
|
|
v8::SnapshotObjectId ids[kLength];
|
|
|
|
|
|
|
|
heap_profiler->StartTrackingHeapObjects(false);
|
|
|
|
|
|
|
|
for (int i = 0; i < kLength; i++) {
|
|
|
|
objects[i] = v8::Object::New(isolate);
|
|
|
|
}
|
|
|
|
GetHeapStatsUpdate(heap_profiler);
|
|
|
|
|
|
|
|
for (int i = 0; i < kLength; i++) {
|
|
|
|
v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
|
2013-12-18 08:17:03 +00:00
|
|
|
ids[i] = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
2016-09-07 10:02:58 +00:00
|
|
|
CcTest::CollectAllAvailableGarbage();
|
2013-12-18 08:17:03 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < kLength; i++) {
|
|
|
|
v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(ids[i], id);
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
|
|
|
|
CHECK(objects[i]->Equals(env.local(), obj).FromJust());
|
2013-12-18 08:17:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
heap_profiler->ClearObjectIds();
|
|
|
|
for (int i = 0; i < kLength; i++) {
|
|
|
|
v8::SnapshotObjectId id = heap_profiler->GetObjectId(objects[i]);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(v8::HeapProfiler::kUnknownObjectId, id);
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Value> obj = heap_profiler->FindObjectById(ids[i]);
|
2013-12-18 08:17:03 +00:00
|
|
|
CHECK(obj.IsEmpty());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-06 17:41:47 +00:00
|
|
|
static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
|
|
|
|
const v8::HeapGraphNode* node,
|
|
|
|
int level, int max_level) {
|
|
|
|
if (level > max_level) return;
|
|
|
|
CHECK_EQ(node, snapshot->GetNodeById(node->GetId()));
|
|
|
|
for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = node->GetChild(i);
|
|
|
|
const v8::HeapGraphNode* child =
|
|
|
|
snapshot->GetNodeById(prop->GetToNode()->GetId());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(prop->GetToNode()->GetId(), child->GetId());
|
2011-12-06 17:41:47 +00:00
|
|
|
CHECK_EQ(prop->GetToNode(), child);
|
|
|
|
CheckChildrenIds(snapshot, child, level + 1, max_level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-02 15:38:51 +00:00
|
|
|
TEST(HeapSnapshotGetNodeById) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2010-12-02 15:38:51 +00:00
|
|
|
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2010-12-02 15:38:51 +00:00
|
|
|
const v8::HeapGraphNode* root = snapshot->GetRoot();
|
2011-12-06 17:41:47 +00:00
|
|
|
CheckChildrenIds(snapshot, root, 0, 3);
|
2010-12-02 15:38:51 +00:00
|
|
|
// Check a big id, which should not exist yet.
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!snapshot->GetNodeById(0x1000000UL));
|
2010-12-02 15:38:51 +00:00
|
|
|
}
|
|
|
|
|
2010-12-13 10:42:06 +00:00
|
|
|
|
2012-04-16 15:36:19 +00:00
|
|
|
TEST(HeapSnapshotGetSnapshotObjectId) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2012-04-16 15:36:19 +00:00
|
|
|
CompileRun("globalObject = {};\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2012-04-16 15:36:19 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* global_object = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "globalObject");
|
2012-04-16 15:36:19 +00:00
|
|
|
CHECK(global_object);
|
|
|
|
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Value> globalObjectHandle =
|
|
|
|
env->Global()->Get(env.local(), v8_str("globalObject")).ToLocalChecked();
|
2012-04-16 15:36:19 +00:00
|
|
|
CHECK(!globalObjectHandle.IsEmpty());
|
|
|
|
CHECK(globalObjectHandle->IsObject());
|
|
|
|
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::SnapshotObjectId id = heap_profiler->GetObjectId(globalObjectHandle);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, id);
|
|
|
|
CHECK_EQ(id, global_object->GetId());
|
2012-04-16 15:36:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(HeapSnapshotUnknownSnapshotObjectId) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2012-04-16 15:36:19 +00:00
|
|
|
CompileRun("globalObject = {};\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2012-04-16 15:36:19 +00:00
|
|
|
const v8::HeapGraphNode* node =
|
|
|
|
snapshot->GetNodeById(v8::HeapProfiler::kUnknownObjectId);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!node);
|
2012-04-16 15:36:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-13 10:42:06 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class TestActivityControl : public v8::ActivityControl {
|
|
|
|
public:
|
|
|
|
explicit TestActivityControl(int abort_count)
|
2018-03-06 12:36:02 +00:00
|
|
|
: done_(0),
|
|
|
|
total_(0),
|
|
|
|
abort_count_(abort_count),
|
|
|
|
reported_finish_(false) {}
|
2018-09-14 15:34:02 +00:00
|
|
|
ControlOption ReportProgressValue(int done, int total) override {
|
2010-12-13 10:42:06 +00:00
|
|
|
done_ = done;
|
|
|
|
total_ = total;
|
2018-03-06 12:36:02 +00:00
|
|
|
CHECK_LE(done_, total_);
|
|
|
|
if (done_ == total_) {
|
|
|
|
CHECK(!reported_finish_);
|
|
|
|
reported_finish_ = true;
|
|
|
|
}
|
2010-12-13 10:42:06 +00:00
|
|
|
return --abort_count_ != 0 ? kContinue : kAbort;
|
|
|
|
}
|
|
|
|
int done() { return done_; }
|
|
|
|
int total() { return total_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
int done_;
|
|
|
|
int total_;
|
|
|
|
int abort_count_;
|
2018-03-06 12:36:02 +00:00
|
|
|
bool reported_finish_;
|
2010-12-13 10:42:06 +00:00
|
|
|
};
|
2015-09-30 13:46:56 +00:00
|
|
|
|
|
|
|
} // namespace
|
2010-12-13 10:42:06 +00:00
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2010-12-13 10:42:06 +00:00
|
|
|
TEST(TakeHeapSnapshotAborting) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2010-12-13 10:42:06 +00:00
|
|
|
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
const int snapshots_count = heap_profiler->GetSnapshotCount();
|
2012-02-24 15:46:45 +00:00
|
|
|
TestActivityControl aborting_control(1);
|
2010-12-13 10:42:06 +00:00
|
|
|
const v8::HeapSnapshot* no_snapshot =
|
2015-03-10 15:14:01 +00:00
|
|
|
heap_profiler->TakeHeapSnapshot(&aborting_control);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!no_snapshot);
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(snapshots_count, heap_profiler->GetSnapshotCount());
|
2010-12-13 10:42:06 +00:00
|
|
|
CHECK_GT(aborting_control.total(), aborting_control.done());
|
|
|
|
|
|
|
|
TestActivityControl control(-1); // Don't abort.
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2013-07-10 12:40:42 +00:00
|
|
|
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(snapshot);
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(snapshots_count + 1, heap_profiler->GetSnapshotCount());
|
2010-12-13 10:42:06 +00:00
|
|
|
CHECK_EQ(control.total(), control.done());
|
|
|
|
CHECK_GT(control.total(), 0);
|
|
|
|
}
|
|
|
|
|
2018-03-06 12:36:02 +00:00
|
|
|
TEST(TakeHeapSnapshotReportFinishOnce) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
TestActivityControl control(-1);
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(&control);
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
CHECK_EQ(control.total(), control.done());
|
|
|
|
CHECK_GT(control.total(), 0);
|
|
|
|
}
|
2011-03-10 12:05:31 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2018-03-05 14:34:36 +00:00
|
|
|
class EmbedderGraphBuilder : public v8::PersistentHandleVisitor {
|
2011-03-10 12:05:31 +00:00
|
|
|
public:
|
2018-03-05 14:34:36 +00:00
|
|
|
class Node : public v8::EmbedderGraph::Node {
|
|
|
|
public:
|
|
|
|
Node(const char* name, size_t size) : name_(name), size_(size) {}
|
|
|
|
// v8::EmbedderGraph::Node
|
|
|
|
const char* Name() override { return name_; }
|
|
|
|
size_t SizeInBytes() override { return size_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
const char* name_;
|
|
|
|
size_t size_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Group : public Node {
|
|
|
|
public:
|
|
|
|
explicit Group(const char* name) : Node(name, 0) {}
|
|
|
|
// v8::EmbedderGraph::EmbedderNode
|
2018-09-14 15:34:02 +00:00
|
|
|
bool IsRootNode() override { return true; }
|
2018-03-05 14:34:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
EmbedderGraphBuilder(v8::Isolate* isolate, v8::EmbedderGraph* graph)
|
|
|
|
: isolate_(isolate), graph_(graph) {
|
|
|
|
classid_to_group_[0] = nullptr;
|
|
|
|
classid_to_group_[1] =
|
|
|
|
graph->AddNode(std::unique_ptr<Group>(new Group("aaa-group")));
|
|
|
|
classid_to_group_[2] =
|
|
|
|
graph->AddNode(std::unique_ptr<Group>(new Group("ccc-group")));
|
2011-03-10 12:05:31 +00:00
|
|
|
}
|
2018-03-05 14:34:36 +00:00
|
|
|
|
2018-06-01 20:27:43 +00:00
|
|
|
static void BuildEmbedderGraph(v8::Isolate* isolate, v8::EmbedderGraph* graph,
|
|
|
|
void* data) {
|
2018-03-05 14:34:36 +00:00
|
|
|
EmbedderGraphBuilder builder(isolate, graph);
|
|
|
|
isolate->VisitHandlesWithClassIds(&builder);
|
2011-03-10 12:05:31 +00:00
|
|
|
}
|
2018-03-05 14:34:36 +00:00
|
|
|
|
|
|
|
void VisitPersistentHandle(v8::Persistent<v8::Value>* value,
|
|
|
|
uint16_t class_id) override {
|
|
|
|
v8::Local<v8::Value> wrapper = v8::Local<v8::Value>::New(
|
|
|
|
isolate_, v8::Persistent<v8::Value>::Cast(*value));
|
2011-03-10 12:05:31 +00:00
|
|
|
if (class_id == 1) {
|
|
|
|
if (wrapper->IsString()) {
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value utf8(CcTest::isolate(), wrapper);
|
2018-03-05 14:34:36 +00:00
|
|
|
DCHECK(!strcmp(*utf8, "AAA") || !strcmp(*utf8, "BBB"));
|
|
|
|
v8::EmbedderGraph::Node* node = graph_->V8Node(wrapper);
|
|
|
|
v8::EmbedderGraph::Node* group = classid_to_group_[1];
|
|
|
|
graph_->AddEdge(node, group);
|
|
|
|
graph_->AddEdge(group, node);
|
2011-03-10 12:05:31 +00:00
|
|
|
}
|
|
|
|
} else if (class_id == 2) {
|
|
|
|
if (wrapper->IsString()) {
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value utf8(CcTest::isolate(), wrapper);
|
2018-03-05 14:34:36 +00:00
|
|
|
DCHECK(!strcmp(*utf8, "CCC"));
|
|
|
|
v8::EmbedderGraph::Node* node = graph_->V8Node(wrapper);
|
|
|
|
v8::EmbedderGraph::Node* group = classid_to_group_[2];
|
|
|
|
graph_->AddEdge(node, group);
|
|
|
|
graph_->AddEdge(group, node);
|
2011-03-10 12:05:31 +00:00
|
|
|
}
|
2018-03-05 14:34:36 +00:00
|
|
|
} else {
|
|
|
|
UNREACHABLE();
|
2011-03-10 12:05:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-03-05 14:34:36 +00:00
|
|
|
v8::Isolate* isolate_;
|
|
|
|
v8::EmbedderGraph* graph_;
|
|
|
|
v8::EmbedderGraph::Node* classid_to_group_[3];
|
2011-03-10 12:05:31 +00:00
|
|
|
};
|
|
|
|
|
2015-09-30 13:46:56 +00:00
|
|
|
} // namespace
|
2011-03-10 12:05:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
|
|
|
|
v8::HeapGraphNode::Type type,
|
|
|
|
const char* name) {
|
|
|
|
for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
|
|
|
|
if (node->GetType() == type && strcmp(name,
|
|
|
|
const_cast<i::HeapEntry*>(
|
|
|
|
reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
2017-10-13 16:33:03 +00:00
|
|
|
return nullptr;
|
2011-03-10 12:05:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(HeapSnapshotRetainedObjectInfo) {
|
|
|
|
LocalContext env;
|
2013-01-25 08:31:46 +00:00
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(isolate);
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
2011-03-10 12:05:31 +00:00
|
|
|
|
2018-06-01 20:27:43 +00:00
|
|
|
heap_profiler->AddBuildEmbedderGraphCallback(
|
|
|
|
EmbedderGraphBuilder::BuildEmbedderGraph, nullptr);
|
2013-05-28 11:54:52 +00:00
|
|
|
v8::Persistent<v8::String> p_AAA(isolate, v8_str("AAA"));
|
2013-09-03 07:34:34 +00:00
|
|
|
p_AAA.SetWrapperClassId(1);
|
2013-05-28 11:54:52 +00:00
|
|
|
v8::Persistent<v8::String> p_BBB(isolate, v8_str("BBB"));
|
2013-09-03 07:34:34 +00:00
|
|
|
p_BBB.SetWrapperClassId(1);
|
2013-05-28 11:54:52 +00:00
|
|
|
v8::Persistent<v8::String> p_CCC(isolate, v8_str("CCC"));
|
2013-09-03 07:34:34 +00:00
|
|
|
p_CCC.SetWrapperClassId(2);
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2011-03-10 12:05:31 +00:00
|
|
|
|
2018-03-05 14:34:36 +00:00
|
|
|
const v8::HeapGraphNode* native_group_aaa =
|
2019-03-20 08:46:47 +00:00
|
|
|
GetNode(snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "aaa-group");
|
|
|
|
CHECK_NOT_NULL(native_group_aaa);
|
2018-03-05 14:34:36 +00:00
|
|
|
const v8::HeapGraphNode* native_group_ccc =
|
2019-03-20 08:46:47 +00:00
|
|
|
GetNode(snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "ccc-group");
|
|
|
|
CHECK_NOT_NULL(native_group_ccc);
|
2018-03-05 14:34:36 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* n_AAA =
|
|
|
|
GetNode(native_group_aaa, v8::HeapGraphNode::kString, "AAA");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(n_AAA);
|
2018-03-05 14:34:36 +00:00
|
|
|
const v8::HeapGraphNode* n_BBB =
|
|
|
|
GetNode(native_group_aaa, v8::HeapGraphNode::kString, "BBB");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(n_BBB);
|
2018-03-05 14:34:36 +00:00
|
|
|
const v8::HeapGraphNode* n_CCC =
|
|
|
|
GetNode(native_group_ccc, v8::HeapGraphNode::kString, "CCC");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(n_CCC);
|
2011-03-10 12:05:31 +00:00
|
|
|
|
2018-03-05 14:34:36 +00:00
|
|
|
CHECK_EQ(native_group_aaa, GetChildByName(n_AAA, "aaa-group"));
|
|
|
|
CHECK_EQ(native_group_aaa, GetChildByName(n_BBB, "aaa-group"));
|
|
|
|
CHECK_EQ(native_group_ccc, GetChildByName(n_CCC, "ccc-group"));
|
2011-03-10 12:05:31 +00:00
|
|
|
}
|
|
|
|
|
2019-08-09 07:09:27 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class EmbedderGraphBuilderForNativeSnapshotObjectId final {
|
|
|
|
public:
|
|
|
|
class RegularNode : public v8::EmbedderGraph::Node {
|
|
|
|
public:
|
|
|
|
RegularNode(v8::NativeObject native_object, const char* name, size_t size,
|
|
|
|
Node* wrapper_node)
|
|
|
|
: name_(name),
|
|
|
|
size_(size),
|
|
|
|
native_object_(native_object),
|
|
|
|
wrapper_node_(wrapper_node) {}
|
|
|
|
// v8::EmbedderGraph::Node
|
|
|
|
const char* Name() override { return name_; }
|
|
|
|
size_t SizeInBytes() override { return size_; }
|
|
|
|
Node* WrapperNode() override { return wrapper_node_; }
|
|
|
|
v8::NativeObject GetNativeObject() override {
|
|
|
|
return native_object_ ? native_object_ : this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const char* name_;
|
|
|
|
size_t size_;
|
|
|
|
v8::NativeObject native_object_;
|
|
|
|
Node* wrapper_node_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class RootNode : public RegularNode {
|
|
|
|
public:
|
|
|
|
explicit RootNode(const char* name)
|
|
|
|
: RegularNode(nullptr, name, 0, nullptr) {}
|
|
|
|
// v8::EmbedderGraph::EmbedderNode
|
|
|
|
bool IsRootNode() override { return true; }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct BuildParameter {
|
|
|
|
v8::Persistent<v8::String>* wrapper;
|
|
|
|
void* native1;
|
|
|
|
void* native2;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void BuildEmbedderGraph(v8::Isolate* isolate, v8::EmbedderGraph* graph,
|
|
|
|
void* data) {
|
|
|
|
BuildParameter* parameter = reinterpret_cast<BuildParameter*>(data);
|
|
|
|
v8::Local<v8::String> local_str =
|
|
|
|
v8::Local<v8::String>::New(isolate, *(parameter->wrapper));
|
|
|
|
auto* v8_node = graph->V8Node(local_str);
|
|
|
|
CHECK(!v8_node->IsEmbedderNode());
|
|
|
|
auto* root_node =
|
|
|
|
graph->AddNode(std::unique_ptr<RootNode>(new RootNode("root")));
|
|
|
|
auto* non_merged_node = graph->AddNode(std::unique_ptr<RegularNode>(
|
|
|
|
new RegularNode(parameter->native1, "non-merged", 0, nullptr)));
|
|
|
|
auto* merged_node = graph->AddNode(std::unique_ptr<RegularNode>(
|
|
|
|
new RegularNode(parameter->native2, "merged", 0, v8_node)));
|
|
|
|
graph->AddEdge(root_node, non_merged_node);
|
|
|
|
graph->AddEdge(root_node, merged_node);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
TEST(NativeSnapshotObjectId) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
|
|
|
|
|
|
|
v8::Persistent<v8::String> wrapper(isolate, v8_str("wrapper"));
|
|
|
|
int native1;
|
|
|
|
int native2;
|
|
|
|
|
|
|
|
EmbedderGraphBuilderForNativeSnapshotObjectId::BuildParameter parameter{
|
|
|
|
&wrapper, &native1, &native2};
|
|
|
|
heap_profiler->AddBuildEmbedderGraphCallback(
|
|
|
|
EmbedderGraphBuilderForNativeSnapshotObjectId::BuildEmbedderGraph,
|
|
|
|
¶meter);
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
|
|
|
|
v8::SnapshotObjectId non_merged_id = heap_profiler->GetObjectId(&native1);
|
|
|
|
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, non_merged_id);
|
|
|
|
v8::SnapshotObjectId merged_id = heap_profiler->GetObjectId(&native2);
|
|
|
|
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, merged_id);
|
|
|
|
CHECK_NE(non_merged_id, merged_id);
|
|
|
|
const v8::HeapGraphNode* non_merged_node =
|
|
|
|
snapshot->GetNodeById(non_merged_id);
|
|
|
|
CHECK_NOT_NULL(non_merged_node);
|
|
|
|
const v8::HeapGraphNode* merged_node = snapshot->GetNodeById(merged_id);
|
|
|
|
CHECK_NOT_NULL(merged_node);
|
|
|
|
|
|
|
|
heap_profiler->ClearObjectIds();
|
|
|
|
CHECK_EQ(v8::HeapProfiler::kUnknownObjectId,
|
|
|
|
heap_profiler->GetObjectId(&native1));
|
|
|
|
CHECK_EQ(v8::HeapProfiler::kUnknownObjectId,
|
|
|
|
heap_profiler->GetObjectId(&native2));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(NativeSnapshotObjectIdMoving) {
|
|
|
|
// Required to allow moving specific objects.
|
|
|
|
i::FLAG_manual_evacuation_candidates_selection = true;
|
|
|
|
|
|
|
|
LocalContext env;
|
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
|
|
|
heap_profiler->StartTrackingHeapObjects(true);
|
|
|
|
|
|
|
|
v8::Persistent<v8::String> wrapper(isolate, v8_str("wrapper"));
|
|
|
|
int native1;
|
|
|
|
int native2;
|
|
|
|
|
|
|
|
EmbedderGraphBuilderForNativeSnapshotObjectId::BuildParameter parameter{
|
|
|
|
&wrapper, &native1, &native2};
|
|
|
|
heap_profiler->AddBuildEmbedderGraphCallback(
|
|
|
|
EmbedderGraphBuilderForNativeSnapshotObjectId::BuildEmbedderGraph,
|
|
|
|
¶meter);
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
|
|
|
|
v8::SnapshotObjectId non_merged_id = heap_profiler->GetObjectId(&native1);
|
|
|
|
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, non_merged_id);
|
|
|
|
v8::SnapshotObjectId merged_id = heap_profiler->GetObjectId(&native2);
|
|
|
|
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, merged_id);
|
|
|
|
CHECK_NE(non_merged_id, merged_id);
|
|
|
|
const v8::HeapGraphNode* non_merged_node =
|
|
|
|
snapshot->GetNodeById(non_merged_id);
|
|
|
|
CHECK_NOT_NULL(non_merged_node);
|
|
|
|
const v8::HeapGraphNode* merged_node = snapshot->GetNodeById(merged_id);
|
|
|
|
CHECK_NOT_NULL(merged_node);
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
auto local = v8::Local<v8::String>::New(isolate, wrapper);
|
|
|
|
i::Handle<i::String> internal = i::Handle<i::String>::cast(
|
|
|
|
v8::Utils::OpenHandle(*v8::Local<v8::String>::Cast(local)));
|
|
|
|
i::heap::ForceEvacuationCandidate(i::Page::FromHeapObject(*internal));
|
|
|
|
}
|
|
|
|
CcTest::CollectAllGarbage();
|
|
|
|
|
|
|
|
non_merged_id = heap_profiler->GetObjectId(&native1);
|
|
|
|
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, non_merged_id);
|
|
|
|
merged_id = heap_profiler->GetObjectId(&native2);
|
|
|
|
CHECK_NE(v8::HeapProfiler::kUnknownObjectId, merged_id);
|
|
|
|
CHECK_NE(non_merged_id, merged_id);
|
|
|
|
|
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
|
|
|
}
|
|
|
|
|
2011-03-22 16:10:01 +00:00
|
|
|
TEST(DeleteAllHeapSnapshots) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CHECK_EQ(0, heap_profiler->GetSnapshotCount());
|
|
|
|
heap_profiler->DeleteAllHeapSnapshots();
|
|
|
|
CHECK_EQ(0, heap_profiler->GetSnapshotCount());
|
2015-03-10 15:14:01 +00:00
|
|
|
CHECK(heap_profiler->TakeHeapSnapshot());
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(1, heap_profiler->GetSnapshotCount());
|
|
|
|
heap_profiler->DeleteAllHeapSnapshots();
|
|
|
|
CHECK_EQ(0, heap_profiler->GetSnapshotCount());
|
2015-03-10 15:14:01 +00:00
|
|
|
CHECK(heap_profiler->TakeHeapSnapshot());
|
|
|
|
CHECK(heap_profiler->TakeHeapSnapshot());
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(2, heap_profiler->GetSnapshotCount());
|
|
|
|
heap_profiler->DeleteAllHeapSnapshots();
|
|
|
|
CHECK_EQ(0, heap_profiler->GetSnapshotCount());
|
2011-03-22 16:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-10 15:14:01 +00:00
|
|
|
static bool FindHeapSnapshot(v8::HeapProfiler* profiler,
|
|
|
|
const v8::HeapSnapshot* snapshot) {
|
2013-05-28 08:00:16 +00:00
|
|
|
int length = profiler->GetSnapshotCount();
|
|
|
|
for (int i = 0; i < length; i++) {
|
2015-03-10 15:14:01 +00:00
|
|
|
if (snapshot == profiler->GetHeapSnapshot(i)) return true;
|
2013-05-28 08:00:16 +00:00
|
|
|
}
|
2015-03-10 15:14:01 +00:00
|
|
|
return false;
|
2013-05-28 08:00:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-22 16:10:01 +00:00
|
|
|
TEST(DeleteHeapSnapshot) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2011-03-22 16:10:01 +00:00
|
|
|
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(0, heap_profiler->GetSnapshotCount());
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* s1 = heap_profiler->TakeHeapSnapshot();
|
2013-07-10 12:40:42 +00:00
|
|
|
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(s1);
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(1, heap_profiler->GetSnapshotCount());
|
2015-03-10 15:14:01 +00:00
|
|
|
CHECK(FindHeapSnapshot(heap_profiler, s1));
|
2011-03-22 16:10:01 +00:00
|
|
|
const_cast<v8::HeapSnapshot*>(s1)->Delete();
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(0, heap_profiler->GetSnapshotCount());
|
2015-03-10 15:14:01 +00:00
|
|
|
CHECK(!FindHeapSnapshot(heap_profiler, s1));
|
2011-03-22 16:10:01 +00:00
|
|
|
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* s2 = heap_profiler->TakeHeapSnapshot();
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(s2);
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(1, heap_profiler->GetSnapshotCount());
|
2015-03-10 15:14:01 +00:00
|
|
|
CHECK(FindHeapSnapshot(heap_profiler, s2));
|
|
|
|
const v8::HeapSnapshot* s3 = heap_profiler->TakeHeapSnapshot();
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(s3);
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(2, heap_profiler->GetSnapshotCount());
|
2015-03-10 15:14:01 +00:00
|
|
|
CHECK_NE(s2, s3);
|
|
|
|
CHECK(FindHeapSnapshot(heap_profiler, s3));
|
2011-03-22 16:10:01 +00:00
|
|
|
const_cast<v8::HeapSnapshot*>(s2)->Delete();
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(1, heap_profiler->GetSnapshotCount());
|
2015-03-10 15:14:01 +00:00
|
|
|
CHECK(!FindHeapSnapshot(heap_profiler, s2));
|
|
|
|
CHECK(FindHeapSnapshot(heap_profiler, s3));
|
2011-03-22 16:10:01 +00:00
|
|
|
const_cast<v8::HeapSnapshot*>(s3)->Delete();
|
2013-04-02 08:03:01 +00:00
|
|
|
CHECK_EQ(0, heap_profiler->GetSnapshotCount());
|
2015-03-10 15:14:01 +00:00
|
|
|
CHECK(!FindHeapSnapshot(heap_profiler, s3));
|
2011-03-22 16:10:01 +00:00
|
|
|
}
|
|
|
|
|
2011-05-30 14:31:47 +00:00
|
|
|
|
2012-12-04 17:17:55 +00:00
|
|
|
class NameResolver : public v8::HeapProfiler::ObjectNameResolver {
|
|
|
|
public:
|
2018-09-14 15:34:02 +00:00
|
|
|
const char* GetName(v8::Local<v8::Object> object) override {
|
2012-12-04 17:17:55 +00:00
|
|
|
return "Global object name";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2012-12-04 17:17:55 +00:00
|
|
|
TEST(GlobalObjectName) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2012-12-04 17:17:55 +00:00
|
|
|
|
|
|
|
CompileRun("document = { URL:\"abcdefgh\" };");
|
|
|
|
|
|
|
|
NameResolver name_resolver;
|
|
|
|
const v8::HeapSnapshot* snapshot =
|
2017-10-13 16:33:03 +00:00
|
|
|
heap_profiler->TakeHeapSnapshot(nullptr, &name_resolver);
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2012-12-04 17:17:55 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global);
|
|
|
|
CHECK_EQ(0,
|
|
|
|
strcmp("Object / Global object name",
|
|
|
|
const_cast<i::HeapEntry*>(
|
|
|
|
reinterpret_cast<const i::HeapEntry*>(global))->name()));
|
2012-12-04 17:17:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-30 12:17:21 +00:00
|
|
|
TEST(GlobalObjectFields) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
CompileRun("obj = {};");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2014-01-30 12:17:21 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* native_context =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kInternal,
|
|
|
|
"native_context");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(native_context);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* global_proxy = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kInternal, "global_proxy");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global_proxy);
|
2014-01-30 12:17:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-11-18 11:42:39 +00:00
|
|
|
TEST(NoHandleLeaks) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2011-11-18 11:42:39 +00:00
|
|
|
|
|
|
|
CompileRun("document = { URL:\"abcdefgh\" };");
|
|
|
|
|
2013-09-19 09:17:13 +00:00
|
|
|
i::Isolate* isolate = CcTest::i_isolate();
|
2013-02-25 14:46:09 +00:00
|
|
|
int count_before = i::HandleScope::NumberOfHandles(isolate);
|
2015-03-10 15:14:01 +00:00
|
|
|
heap_profiler->TakeHeapSnapshot();
|
2013-02-25 14:46:09 +00:00
|
|
|
int count_after = i::HandleScope::NumberOfHandles(isolate);
|
2011-11-18 11:42:39 +00:00
|
|
|
CHECK_EQ(count_before, count_after);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-21 08:02:34 +00:00
|
|
|
TEST(NodesIteration) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2011-06-21 08:02:34 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global);
|
2011-06-21 08:02:34 +00:00
|
|
|
// Verify that we can find this object by iteration.
|
|
|
|
const int nodes_count = snapshot->GetNodesCount();
|
|
|
|
int count = 0;
|
|
|
|
for (int i = 0; i < nodes_count; ++i) {
|
|
|
|
if (snapshot->GetNode(i) == global)
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
CHECK_EQ(1, count);
|
|
|
|
}
|
2011-08-23 12:24:54 +00:00
|
|
|
|
|
|
|
|
2013-12-23 09:33:16 +00:00
|
|
|
TEST(GetHeapValueForNode) {
|
2011-09-26 14:38:29 +00:00
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2011-09-26 14:38:29 +00:00
|
|
|
|
2014-07-01 15:02:31 +00:00
|
|
|
CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2011-09-26 14:38:29 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2013-12-23 09:33:16 +00:00
|
|
|
CHECK(heap_profiler->FindObjectById(global->GetId())->IsObject());
|
2011-09-26 14:38:29 +00:00
|
|
|
v8::Local<v8::Object> js_global =
|
|
|
|
env->Global()->GetPrototype().As<v8::Object>();
|
2019-04-02 16:48:56 +00:00
|
|
|
CHECK_EQ(js_global, heap_profiler->FindObjectById(global->GetId()));
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* obj =
|
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
|
2013-12-23 09:33:16 +00:00
|
|
|
CHECK(heap_profiler->FindObjectById(obj->GetId())->IsObject());
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> js_obj = js_global->Get(env.local(), v8_str("a"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
2019-04-02 16:48:56 +00:00
|
|
|
CHECK_EQ(js_obj, heap_profiler->FindObjectById(obj->GetId()));
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* s_prop = GetProperty(
|
|
|
|
env->GetIsolate(), obj, v8::HeapGraphEdge::kProperty, "s_prop");
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::String> js_s_prop = js_obj->Get(env.local(), v8_str("s_prop"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::String>();
|
2019-04-02 16:48:56 +00:00
|
|
|
CHECK_EQ(js_s_prop, heap_profiler->FindObjectById(s_prop->GetId()));
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* n_prop = GetProperty(
|
|
|
|
env->GetIsolate(), obj, v8::HeapGraphEdge::kProperty, "n_prop");
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::String> js_n_prop = js_obj->Get(env.local(), v8_str("n_prop"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::String>();
|
2019-04-02 16:48:56 +00:00
|
|
|
CHECK_EQ(js_n_prop, heap_profiler->FindObjectById(n_prop->GetId()));
|
2011-09-26 14:38:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(GetHeapValueForDeletedObject) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2011-09-26 14:38:29 +00:00
|
|
|
|
|
|
|
// It is impossible to delete a global property, so we are about to delete a
|
|
|
|
// property of the "a" object. Also, the "p" object can't be an empty one
|
|
|
|
// because the empty object is static and isn't actually deleted.
|
|
|
|
CompileRun("a = { p: { r: {} } };");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2011-09-26 14:38:29 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* obj =
|
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "a");
|
|
|
|
const v8::HeapGraphNode* prop =
|
|
|
|
GetProperty(env->GetIsolate(), obj, v8::HeapGraphEdge::kProperty, "p");
|
2011-09-26 14:38:29 +00:00
|
|
|
{
|
|
|
|
// Perform the check inside a nested local scope to avoid creating a
|
|
|
|
// reference to the object we are deleting.
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-12-23 09:33:16 +00:00
|
|
|
CHECK(heap_profiler->FindObjectById(prop->GetId())->IsObject());
|
2011-09-26 14:38:29 +00:00
|
|
|
}
|
|
|
|
CompileRun("delete a.p;");
|
2013-12-23 09:33:16 +00:00
|
|
|
CHECK(heap_profiler->FindObjectById(prop->GetId()).IsEmpty());
|
2011-09-26 14:38:29 +00:00
|
|
|
}
|
|
|
|
|
2018-11-27 00:48:42 +00:00
|
|
|
static int StringCmp(const char* ref, i::String act) {
|
2019-05-23 07:47:44 +00:00
|
|
|
std::unique_ptr<char[]> s_act = act.ToCString();
|
2013-12-09 07:41:20 +00:00
|
|
|
int result = strcmp(ref, s_act.get());
|
2011-08-23 12:24:54 +00:00
|
|
|
if (result != 0)
|
2013-12-09 07:41:20 +00:00
|
|
|
fprintf(stderr, "Expected: \"%s\", Actual: \"%s\"\n", ref, s_act.get());
|
2011-08-23 12:24:54 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-08-24 07:21:50 +00:00
|
|
|
TEST(GetConstructor) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"function Constructor1() {};\n"
|
|
|
|
"var obj1 = new Constructor1();\n"
|
|
|
|
"var Constructor2 = function() {};\n"
|
|
|
|
"var obj2 = new Constructor2();\n"
|
|
|
|
"var obj3 = {};\n"
|
|
|
|
"obj3.__proto__ = { constructor: function Constructor3() {} };\n"
|
|
|
|
"var obj4 = {};\n"
|
|
|
|
"// Slow properties\n"
|
|
|
|
"for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
|
|
|
|
"obj4.__proto__ = { constructor: function Constructor4() {} };\n"
|
|
|
|
"var obj5 = {};\n"
|
|
|
|
"var obj6 = {};\n"
|
|
|
|
"obj6.constructor = 6;");
|
|
|
|
v8::Local<v8::Object> js_global =
|
|
|
|
env->Global()->GetPrototype().As<v8::Object>();
|
|
|
|
v8::Local<v8::Object> obj1 = js_global->Get(env.local(), v8_str("obj1"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
|
|
|
i::Handle<i::JSObject> js_obj1 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj1));
|
2018-12-08 02:59:17 +00:00
|
|
|
CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj1).is_null());
|
2018-08-24 07:21:50 +00:00
|
|
|
v8::Local<v8::Object> obj2 = js_global->Get(env.local(), v8_str("obj2"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
|
|
|
i::Handle<i::JSObject> js_obj2 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj2));
|
2018-12-08 02:59:17 +00:00
|
|
|
CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj2).is_null());
|
2018-08-24 07:21:50 +00:00
|
|
|
v8::Local<v8::Object> obj3 = js_global->Get(env.local(), v8_str("obj3"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
|
|
|
i::Handle<i::JSObject> js_obj3 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj3));
|
2018-12-08 02:59:17 +00:00
|
|
|
CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj3).is_null());
|
2018-08-24 07:21:50 +00:00
|
|
|
v8::Local<v8::Object> obj4 = js_global->Get(env.local(), v8_str("obj4"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
|
|
|
i::Handle<i::JSObject> js_obj4 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj4));
|
2018-12-08 02:59:17 +00:00
|
|
|
CHECK(!i::V8HeapExplorer::GetConstructor(*js_obj4).is_null());
|
2018-08-24 07:21:50 +00:00
|
|
|
v8::Local<v8::Object> obj5 = js_global->Get(env.local(), v8_str("obj5"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
|
|
|
i::Handle<i::JSObject> js_obj5 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj5));
|
2018-12-08 02:59:17 +00:00
|
|
|
CHECK(i::V8HeapExplorer::GetConstructor(*js_obj5).is_null());
|
2018-08-24 07:21:50 +00:00
|
|
|
v8::Local<v8::Object> obj6 = js_global->Get(env.local(), v8_str("obj6"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
|
|
|
i::Handle<i::JSObject> js_obj6 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj6));
|
2018-12-08 02:59:17 +00:00
|
|
|
CHECK(i::V8HeapExplorer::GetConstructor(*js_obj6).is_null());
|
2018-08-24 07:21:50 +00:00
|
|
|
}
|
2011-08-23 12:24:54 +00:00
|
|
|
|
|
|
|
TEST(GetConstructorName) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2011-08-23 12:24:54 +00:00
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"function Constructor1() {};\n"
|
|
|
|
"var obj1 = new Constructor1();\n"
|
|
|
|
"var Constructor2 = function() {};\n"
|
|
|
|
"var obj2 = new Constructor2();\n"
|
|
|
|
"var obj3 = {};\n"
|
2015-11-27 13:10:00 +00:00
|
|
|
"obj3.__proto__ = { constructor: function Constructor3() {} };\n"
|
2011-08-23 12:24:54 +00:00
|
|
|
"var obj4 = {};\n"
|
|
|
|
"// Slow properties\n"
|
|
|
|
"for (var i=0; i<2000; ++i) obj4[\"p\" + i] = i;\n"
|
2015-11-27 13:10:00 +00:00
|
|
|
"obj4.__proto__ = { constructor: function Constructor4() {} };\n"
|
2011-08-23 12:24:54 +00:00
|
|
|
"var obj5 = {};\n"
|
|
|
|
"var obj6 = {};\n"
|
|
|
|
"obj6.constructor = 6;");
|
|
|
|
v8::Local<v8::Object> js_global =
|
|
|
|
env->Global()->GetPrototype().As<v8::Object>();
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> obj1 = js_global->Get(env.local(), v8_str("obj1"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
2015-11-16 16:48:43 +00:00
|
|
|
i::Handle<i::JSObject> js_obj1 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj1));
|
2011-08-23 12:24:54 +00:00
|
|
|
CHECK_EQ(0, StringCmp(
|
|
|
|
"Constructor1", i::V8HeapExplorer::GetConstructorName(*js_obj1)));
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> obj2 = js_global->Get(env.local(), v8_str("obj2"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
2015-11-16 16:48:43 +00:00
|
|
|
i::Handle<i::JSObject> js_obj2 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj2));
|
2011-08-23 12:24:54 +00:00
|
|
|
CHECK_EQ(0, StringCmp(
|
|
|
|
"Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> obj3 = js_global->Get(env.local(), v8_str("obj3"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
2015-11-16 16:48:43 +00:00
|
|
|
i::Handle<i::JSObject> js_obj3 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj3));
|
2015-11-27 13:10:00 +00:00
|
|
|
CHECK_EQ(0, StringCmp("Constructor3",
|
|
|
|
i::V8HeapExplorer::GetConstructorName(*js_obj3)));
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> obj4 = js_global->Get(env.local(), v8_str("obj4"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
2015-11-16 16:48:43 +00:00
|
|
|
i::Handle<i::JSObject> js_obj4 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj4));
|
2015-11-27 13:10:00 +00:00
|
|
|
CHECK_EQ(0, StringCmp("Constructor4",
|
|
|
|
i::V8HeapExplorer::GetConstructorName(*js_obj4)));
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> obj5 = js_global->Get(env.local(), v8_str("obj5"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
2015-11-16 16:48:43 +00:00
|
|
|
i::Handle<i::JSObject> js_obj5 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj5));
|
2011-08-23 12:24:54 +00:00
|
|
|
CHECK_EQ(0, StringCmp(
|
|
|
|
"Object", i::V8HeapExplorer::GetConstructorName(*js_obj5)));
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> obj6 = js_global->Get(env.local(), v8_str("obj6"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>();
|
2015-11-16 16:48:43 +00:00
|
|
|
i::Handle<i::JSObject> js_obj6 =
|
|
|
|
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj6));
|
2011-08-23 12:24:54 +00:00
|
|
|
CHECK_EQ(0, StringCmp(
|
|
|
|
"Object", i::V8HeapExplorer::GetConstructorName(*js_obj6)));
|
|
|
|
}
|
2011-11-14 11:13:29 +00:00
|
|
|
|
2011-11-18 11:42:39 +00:00
|
|
|
|
2013-06-25 11:55:33 +00:00
|
|
|
TEST(FastCaseAccessors) {
|
2011-11-14 11:13:29 +00:00
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2011-11-14 11:13:29 +00:00
|
|
|
|
|
|
|
CompileRun("var obj1 = {};\n"
|
|
|
|
"obj1.__defineGetter__('propWithGetter', function Y() {\n"
|
|
|
|
" return 42;\n"
|
|
|
|
"});\n"
|
|
|
|
"obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
|
|
|
|
" return this.value_ = value;\n"
|
|
|
|
"});\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2011-11-14 11:13:29 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* obj1 = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj1");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(obj1);
|
2013-06-25 11:55:33 +00:00
|
|
|
const v8::HeapGraphNode* func;
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"get propWithGetter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(func);
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"set propWithGetter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!func);
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"set propWithSetter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(func);
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"get propWithSetter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!func);
|
2011-11-14 11:13:29 +00:00
|
|
|
}
|
2011-12-06 17:41:47 +00:00
|
|
|
|
2013-06-25 11:55:33 +00:00
|
|
|
|
2014-12-16 13:22:23 +00:00
|
|
|
TEST(FastCaseRedefinedAccessors) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"var obj1 = {};\n"
|
|
|
|
"Object.defineProperty(obj1, 'prop', { "
|
|
|
|
" get: function() { return 42; },\n"
|
|
|
|
" set: function(value) { return this.prop_ = value; },\n"
|
|
|
|
" configurable: true,\n"
|
|
|
|
" enumerable: true,\n"
|
|
|
|
"});\n"
|
|
|
|
"Object.defineProperty(obj1, 'prop', { "
|
|
|
|
" get: function() { return 153; },\n"
|
|
|
|
" set: function(value) { return this.prop_ = value; },\n"
|
|
|
|
" configurable: true,\n"
|
|
|
|
" enumerable: true,\n"
|
|
|
|
"});\n");
|
|
|
|
v8::Local<v8::Object> js_global =
|
|
|
|
env->Global()->GetPrototype().As<v8::Object>();
|
2015-11-16 16:48:43 +00:00
|
|
|
i::Handle<i::JSReceiver> js_obj1 =
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Utils::OpenHandle(*js_global->Get(env.local(), v8_str("obj1"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
.As<v8::Object>());
|
2014-12-16 13:22:23 +00:00
|
|
|
USE(js_obj1);
|
|
|
|
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2014-12-16 13:22:23 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* obj1 = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj1");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(obj1);
|
2014-12-16 13:22:23 +00:00
|
|
|
const v8::HeapGraphNode* func;
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"get prop");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(func);
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"set prop");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(func);
|
2014-12-16 13:22:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-25 11:55:33 +00:00
|
|
|
TEST(SlowCaseAccessors) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun("var obj1 = {};\n"
|
|
|
|
"for (var i = 0; i < 100; ++i) obj1['z' + i] = {};"
|
|
|
|
"obj1.__defineGetter__('propWithGetter', function Y() {\n"
|
|
|
|
" return 42;\n"
|
|
|
|
"});\n"
|
|
|
|
"obj1.__defineSetter__('propWithSetter', function Z(value) {\n"
|
|
|
|
" return this.value_ = value;\n"
|
|
|
|
"});\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2013-06-25 11:55:33 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* obj1 = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj1");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(obj1);
|
2013-06-25 11:55:33 +00:00
|
|
|
const v8::HeapGraphNode* func;
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"get propWithGetter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(func);
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"set propWithGetter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!func);
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"set propWithSetter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(func);
|
2017-08-24 21:49:48 +00:00
|
|
|
func = GetProperty(env->GetIsolate(), obj1, v8::HeapGraphEdge::kProperty,
|
|
|
|
"get propWithSetter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!func);
|
2013-06-25 11:55:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-02 12:46:31 +00:00
|
|
|
TEST(HiddenPropertiesFastCase) {
|
2014-11-24 10:30:39 +00:00
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
2012-07-02 12:46:31 +00:00
|
|
|
LocalContext env;
|
2014-11-24 10:30:39 +00:00
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
2012-07-02 12:46:31 +00:00
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"function C(x) { this.a = this; this.b = x; }\n"
|
|
|
|
"c = new C(2012);\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2012-07-02 12:46:31 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* c =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "c");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(c);
|
2012-07-02 12:46:31 +00:00
|
|
|
const v8::HeapGraphNode* hidden_props =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "<symbol>");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!hidden_props);
|
2012-07-02 12:46:31 +00:00
|
|
|
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Value> cHandle =
|
|
|
|
env->Global()->Get(env.local(), v8_str("c")).ToLocalChecked();
|
2012-07-02 12:46:31 +00:00
|
|
|
CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
|
2015-12-08 18:41:54 +00:00
|
|
|
cHandle->ToObject(env.local())
|
|
|
|
.ToLocalChecked()
|
2015-10-29 14:17:24 +00:00
|
|
|
->SetPrivate(env.local(),
|
|
|
|
v8::Private::ForApi(env->GetIsolate(), v8_str("key")),
|
|
|
|
v8_str("val"))
|
|
|
|
.FromJust();
|
2012-07-02 12:46:31 +00:00
|
|
|
|
2015-03-10 15:14:01 +00:00
|
|
|
snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2012-07-02 12:46:31 +00:00
|
|
|
global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
c = GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "c");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(c);
|
2017-08-24 21:49:48 +00:00
|
|
|
hidden_props =
|
|
|
|
GetProperty(isolate, c, v8::HeapGraphEdge::kProperty, "<symbol>");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(hidden_props);
|
2012-07-02 12:46:31 +00:00
|
|
|
}
|
2011-12-06 17:41:47 +00:00
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2014-09-11 11:08:40 +00:00
|
|
|
TEST(AccessorInfo) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun("function foo(x) { }\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2014-09-11 11:08:40 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* foo = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "foo");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(foo);
|
2014-09-11 11:08:40 +00:00
|
|
|
const v8::HeapGraphNode* map =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), foo, v8::HeapGraphEdge::kInternal, "map");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(map);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* descriptors = GetProperty(
|
|
|
|
env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "descriptors");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(descriptors);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* length_name = GetProperty(
|
2018-11-26 15:40:55 +00:00
|
|
|
env->GetIsolate(), descriptors, v8::HeapGraphEdge::kInternal, "0");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(length_name);
|
2017-08-24 21:49:48 +00:00
|
|
|
CHECK_EQ(0, strcmp("length", *v8::String::Utf8Value(env->GetIsolate(),
|
|
|
|
length_name->GetName())));
|
|
|
|
const v8::HeapGraphNode* length_accessor = GetProperty(
|
2018-11-26 15:40:55 +00:00
|
|
|
env->GetIsolate(), descriptors, v8::HeapGraphEdge::kInternal, "2");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(length_accessor);
|
2016-01-18 15:08:36 +00:00
|
|
|
CHECK_EQ(0, strcmp("system / AccessorInfo",
|
2017-08-24 21:49:48 +00:00
|
|
|
*v8::String::Utf8Value(env->GetIsolate(),
|
|
|
|
length_accessor->GetName())));
|
|
|
|
const v8::HeapGraphNode* name = GetProperty(
|
|
|
|
env->GetIsolate(), length_accessor, v8::HeapGraphEdge::kInternal, "name");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(name);
|
2014-09-11 11:08:40 +00:00
|
|
|
const v8::HeapGraphNode* getter =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), length_accessor,
|
|
|
|
v8::HeapGraphEdge::kInternal, "getter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(getter);
|
2014-09-11 11:08:40 +00:00
|
|
|
const v8::HeapGraphNode* setter =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), length_accessor,
|
|
|
|
v8::HeapGraphEdge::kInternal, "setter");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(setter);
|
2014-09-11 11:08:40 +00:00
|
|
|
}
|
|
|
|
|
2018-07-24 14:05:02 +00:00
|
|
|
TEST(JSGeneratorObject) {
|
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"function* foo() { yield 1; }\n"
|
|
|
|
"g = foo();\n");
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* g =
|
|
|
|
GetProperty(isolate, global, v8::HeapGraphEdge::kProperty, "g");
|
|
|
|
CHECK(g);
|
|
|
|
const v8::HeapGraphNode* function = GetProperty(
|
|
|
|
env->GetIsolate(), g, v8::HeapGraphEdge::kInternal, "function");
|
|
|
|
CHECK(function);
|
|
|
|
const v8::HeapGraphNode* context = GetProperty(
|
|
|
|
env->GetIsolate(), g, v8::HeapGraphEdge::kInternal, "context");
|
|
|
|
CHECK(context);
|
|
|
|
const v8::HeapGraphNode* receiver = GetProperty(
|
|
|
|
env->GetIsolate(), g, v8::HeapGraphEdge::kInternal, "receiver");
|
|
|
|
CHECK(receiver);
|
|
|
|
const v8::HeapGraphNode* parameters_and_registers =
|
|
|
|
GetProperty(env->GetIsolate(), g, v8::HeapGraphEdge::kInternal,
|
|
|
|
"parameters_and_registers");
|
|
|
|
CHECK(parameters_and_registers);
|
|
|
|
}
|
2014-09-11 11:08:40 +00:00
|
|
|
|
2011-12-06 17:41:47 +00:00
|
|
|
bool HasWeakEdge(const v8::HeapGraphNode* node) {
|
|
|
|
for (int i = 0; i < node->GetChildrenCount(); ++i) {
|
|
|
|
const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
|
|
|
|
if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool HasWeakGlobalHandle() {
|
2013-09-19 08:54:58 +00:00
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2011-12-06 17:41:47 +00:00
|
|
|
const v8::HeapGraphNode* gc_roots = GetNode(
|
2013-07-04 16:32:18 +00:00
|
|
|
snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(gc_roots);
|
2011-12-06 17:41:47 +00:00
|
|
|
const v8::HeapGraphNode* global_handles = GetNode(
|
2013-07-04 16:32:18 +00:00
|
|
|
gc_roots, v8::HeapGraphNode::kSynthetic, "(Global handles)");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global_handles);
|
2011-12-06 17:41:47 +00:00
|
|
|
return HasWeakEdge(global_handles);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(WeakGlobalHandle) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2011-12-06 17:41:47 +00:00
|
|
|
|
|
|
|
CHECK(!HasWeakGlobalHandle());
|
|
|
|
|
2020-09-23 22:10:06 +00:00
|
|
|
v8::Global<v8::Object> handle;
|
2017-05-29 11:06:13 +00:00
|
|
|
|
|
|
|
handle.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
|
2020-09-23 22:10:06 +00:00
|
|
|
handle.SetWeak();
|
2011-12-06 17:41:47 +00:00
|
|
|
|
|
|
|
CHECK(HasWeakGlobalHandle());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(SfiAndJsFunctionWeakRefs) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2011-12-06 17:41:47 +00:00
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"fun = (function (x) { return function () { return x + 1; } })(1);");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2011-12-06 17:41:47 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* fun = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "fun");
|
2013-07-17 18:23:16 +00:00
|
|
|
CHECK(!HasWeakEdge(fun));
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* shared = GetProperty(
|
|
|
|
env->GetIsolate(), fun, v8::HeapGraphEdge::kInternal, "shared");
|
2013-07-17 18:23:16 +00:00
|
|
|
CHECK(!HasWeakEdge(shared));
|
2011-12-06 17:41:47 +00:00
|
|
|
}
|
2012-03-07 17:38:50 +00:00
|
|
|
|
|
|
|
|
2012-04-19 15:58:42 +00:00
|
|
|
TEST(AllStrongGcRootsHaveNames) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2012-04-19 15:58:42 +00:00
|
|
|
|
|
|
|
CompileRun("foo = {};");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2012-04-19 15:58:42 +00:00
|
|
|
const v8::HeapGraphNode* gc_roots = GetNode(
|
2013-07-04 16:32:18 +00:00
|
|
|
snapshot->GetRoot(), v8::HeapGraphNode::kSynthetic, "(GC roots)");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(gc_roots);
|
2012-04-19 15:58:42 +00:00
|
|
|
const v8::HeapGraphNode* strong_roots = GetNode(
|
2013-07-04 16:32:18 +00:00
|
|
|
gc_roots, v8::HeapGraphNode::kSynthetic, "(Strong roots)");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(strong_roots);
|
2012-04-19 15:58:42 +00:00
|
|
|
for (int i = 0; i < strong_roots->GetChildrenCount(); ++i) {
|
|
|
|
const v8::HeapGraphEdge* edge = strong_roots->GetChild(i);
|
|
|
|
CHECK_EQ(v8::HeapGraphEdge::kInternal, edge->GetType());
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value name(env->GetIsolate(), edge->GetName());
|
2012-04-19 15:58:42 +00:00
|
|
|
CHECK(isalpha(**name));
|
|
|
|
}
|
|
|
|
}
|
2012-04-20 14:03:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
TEST(NoRefsToNonEssentialEntries) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2012-04-20 14:03:06 +00:00
|
|
|
CompileRun("global_object = {};\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2012-04-20 14:03:06 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* global_object = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "global_object");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global_object);
|
2012-04-20 14:03:06 +00:00
|
|
|
const v8::HeapGraphNode* properties =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global_object,
|
|
|
|
v8::HeapGraphEdge::kInternal, "properties");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!properties);
|
2012-04-20 14:03:06 +00:00
|
|
|
const v8::HeapGraphNode* elements =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), global_object,
|
|
|
|
v8::HeapGraphEdge::kInternal, "elements");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!elements);
|
2012-04-20 14:03:06 +00:00
|
|
|
}
|
2012-08-16 14:25:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
TEST(MapHasDescriptorsAndTransitions) {
|
|
|
|
LocalContext env;
|
2013-03-15 12:06:53 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2012-08-16 14:25:55 +00:00
|
|
|
CompileRun("obj = { a: 10 };\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2012-08-16 14:25:55 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* global_object = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global_object);
|
Sharing of descriptor arrays.
This CL adds multiple things:
Transition arrays do not directly point at their descriptor array anymore, but rather do so via an indirect pointer (a JSGlobalPropertyCell).
An ownership bit is added to maps indicating whether it owns its own descriptor array or not.
Maps owning a descriptor array can pass on ownership if a transition from that map is generated; but only if the descriptor array stays exactly the same; or if a descriptor is added.
Maps that don't have ownership get ownership back if their direct child to which ownership was passed is cleared in ClearNonLiveTransitions.
To detect which descriptors in an array are valid, each map knows its own NumberOfOwnDescriptors. Since the descriptors are sorted in order of addition, if we search and find a descriptor with index bigger than this number, it is not valid for the given map.
We currently still build up an enumeration cache (although this may disappear). The enumeration cache is always built for the entire descriptor array, even if not all descriptors are owned by the map. Once a descriptor array has an enumeration cache for a given map; this invariant will always be true, even if the descriptor array was extended. The extended array will inherit the enumeration cache from the smaller descriptor array. If a map with more descriptors needs an enumeration cache, it's EnumLength will still be set to invalid, so it will have to recompute the enumeration cache. This new cache will also be valid for smaller maps since they have their own enumlength; and use this to loop over the cache. If the EnumLength is still invalid, but there is already a cache present that is big enough; we just initialize the EnumLength field for the map.
When we apply ClearNonLiveTransitions and descriptor ownership is passed back to a parent map, the descriptor array is trimmed in-place and resorted. At the same time, the enumeration cache is trimmed in-place.
Only transition arrays contain descriptor arrays. If we transition to a map and pass ownership of the descriptor array along, the child map will not store the descriptor array it owns. Rather its parent will keep the pointer. So for every leaf-map, we find the descriptor array by following the back pointer, reading out the transition array, and fetching the descriptor array from the JSGlobalPropertyCell. If a map has a transition array, we fetch it from there. If a map has undefined as its back-pointer and has no transition array; it is considered to have an empty descriptor array.
When we modify properties, we cannot share the descriptor array. To accommodate this, the child map will get its own transition array; even if there are not necessarily any transitions leaving from the child map. This is necessary since it's the only way to store its own descriptor array.
Review URL: https://chromiumcodereview.appspot.com/10909007
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12492 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-09-12 16:43:57 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* map = GetProperty(
|
|
|
|
env->GetIsolate(), global_object, v8::HeapGraphEdge::kInternal, "map");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(map);
|
Sharing of descriptor arrays.
This CL adds multiple things:
Transition arrays do not directly point at their descriptor array anymore, but rather do so via an indirect pointer (a JSGlobalPropertyCell).
An ownership bit is added to maps indicating whether it owns its own descriptor array or not.
Maps owning a descriptor array can pass on ownership if a transition from that map is generated; but only if the descriptor array stays exactly the same; or if a descriptor is added.
Maps that don't have ownership get ownership back if their direct child to which ownership was passed is cleared in ClearNonLiveTransitions.
To detect which descriptors in an array are valid, each map knows its own NumberOfOwnDescriptors. Since the descriptors are sorted in order of addition, if we search and find a descriptor with index bigger than this number, it is not valid for the given map.
We currently still build up an enumeration cache (although this may disappear). The enumeration cache is always built for the entire descriptor array, even if not all descriptors are owned by the map. Once a descriptor array has an enumeration cache for a given map; this invariant will always be true, even if the descriptor array was extended. The extended array will inherit the enumeration cache from the smaller descriptor array. If a map with more descriptors needs an enumeration cache, it's EnumLength will still be set to invalid, so it will have to recompute the enumeration cache. This new cache will also be valid for smaller maps since they have their own enumlength; and use this to loop over the cache. If the EnumLength is still invalid, but there is already a cache present that is big enough; we just initialize the EnumLength field for the map.
When we apply ClearNonLiveTransitions and descriptor ownership is passed back to a parent map, the descriptor array is trimmed in-place and resorted. At the same time, the enumeration cache is trimmed in-place.
Only transition arrays contain descriptor arrays. If we transition to a map and pass ownership of the descriptor array along, the child map will not store the descriptor array it owns. Rather its parent will keep the pointer. So for every leaf-map, we find the descriptor array by following the back pointer, reading out the transition array, and fetching the descriptor array from the JSGlobalPropertyCell. If a map has a transition array, we fetch it from there. If a map has undefined as its back-pointer and has no transition array; it is considered to have an empty descriptor array.
When we modify properties, we cannot share the descriptor array. To accommodate this, the child map will get its own transition array; even if there are not necessarily any transitions leaving from the child map. This is necessary since it's the only way to store its own descriptor array.
Review URL: https://chromiumcodereview.appspot.com/10909007
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12492 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-09-12 16:43:57 +00:00
|
|
|
const v8::HeapGraphNode* own_descriptors = GetProperty(
|
2017-08-24 21:49:48 +00:00
|
|
|
env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "descriptors");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(own_descriptors);
|
Sharing of descriptor arrays.
This CL adds multiple things:
Transition arrays do not directly point at their descriptor array anymore, but rather do so via an indirect pointer (a JSGlobalPropertyCell).
An ownership bit is added to maps indicating whether it owns its own descriptor array or not.
Maps owning a descriptor array can pass on ownership if a transition from that map is generated; but only if the descriptor array stays exactly the same; or if a descriptor is added.
Maps that don't have ownership get ownership back if their direct child to which ownership was passed is cleared in ClearNonLiveTransitions.
To detect which descriptors in an array are valid, each map knows its own NumberOfOwnDescriptors. Since the descriptors are sorted in order of addition, if we search and find a descriptor with index bigger than this number, it is not valid for the given map.
We currently still build up an enumeration cache (although this may disappear). The enumeration cache is always built for the entire descriptor array, even if not all descriptors are owned by the map. Once a descriptor array has an enumeration cache for a given map; this invariant will always be true, even if the descriptor array was extended. The extended array will inherit the enumeration cache from the smaller descriptor array. If a map with more descriptors needs an enumeration cache, it's EnumLength will still be set to invalid, so it will have to recompute the enumeration cache. This new cache will also be valid for smaller maps since they have their own enumlength; and use this to loop over the cache. If the EnumLength is still invalid, but there is already a cache present that is big enough; we just initialize the EnumLength field for the map.
When we apply ClearNonLiveTransitions and descriptor ownership is passed back to a parent map, the descriptor array is trimmed in-place and resorted. At the same time, the enumeration cache is trimmed in-place.
Only transition arrays contain descriptor arrays. If we transition to a map and pass ownership of the descriptor array along, the child map will not store the descriptor array it owns. Rather its parent will keep the pointer. So for every leaf-map, we find the descriptor array by following the back pointer, reading out the transition array, and fetching the descriptor array from the JSGlobalPropertyCell. If a map has a transition array, we fetch it from there. If a map has undefined as its back-pointer and has no transition array; it is considered to have an empty descriptor array.
When we modify properties, we cannot share the descriptor array. To accommodate this, the child map will get its own transition array; even if there are not necessarily any transitions leaving from the child map. This is necessary since it's the only way to store its own descriptor array.
Review URL: https://chromiumcodereview.appspot.com/10909007
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12492 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-09-12 16:43:57 +00:00
|
|
|
const v8::HeapGraphNode* own_transitions = GetProperty(
|
2017-08-24 21:49:48 +00:00
|
|
|
env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "transitions");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!own_transitions);
|
2012-08-16 14:25:55 +00:00
|
|
|
}
|
2013-03-13 17:38:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
TEST(ManyLocalsInSharedContext) {
|
2018-11-28 08:42:49 +00:00
|
|
|
// This test gets very slow with slow asserts (18 minutes instead of 1:30,
|
|
|
|
// as of November 2018).
|
|
|
|
#ifdef ENABLE_SLOW_DCHECKS
|
|
|
|
i::FLAG_enable_slow_asserts = false;
|
|
|
|
#endif
|
2013-03-13 17:38:04 +00:00
|
|
|
LocalContext env;
|
2013-04-02 08:03:01 +00:00
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2013-03-14 14:42:00 +00:00
|
|
|
int num_objects = 6000;
|
2013-03-13 17:38:04 +00:00
|
|
|
CompileRun(
|
2013-03-14 14:42:00 +00:00
|
|
|
"var n = 6000;"
|
2013-03-13 17:38:04 +00:00
|
|
|
"var result = [];"
|
|
|
|
"result.push('(function outer() {');"
|
|
|
|
"for (var i = 0; i < n; i++) {"
|
|
|
|
" var f = 'function f_' + i + '() { ';"
|
|
|
|
" if (i > 0)"
|
|
|
|
" f += 'f_' + (i - 1) + '();';"
|
|
|
|
" f += ' }';"
|
|
|
|
" result.push(f);"
|
|
|
|
"}"
|
|
|
|
"result.push('return f_' + (n - 1) + ';');"
|
|
|
|
"result.push('})()');"
|
|
|
|
"var ok = eval(result.join('\\n'));");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2013-07-10 12:40:42 +00:00
|
|
|
|
2013-03-13 17:38:04 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* ok_object = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "ok");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(ok_object);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* context_object = GetProperty(
|
|
|
|
env->GetIsolate(), ok_object, v8::HeapGraphEdge::kInternal, "context");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(context_object);
|
2013-03-13 17:38:04 +00:00
|
|
|
// Check the objects are not duplicated in the context.
|
Reland x3 "[runtime] Remove extension slots from context objects"
Original change's description:
> [runtime] Remove extension slots from context objects
>
> Context objects have an extension slot, which contains further
> additional data that depends on the type of the context.
>
> This CL removes the extension slot from contexts that don't need
> them, hence reducing memory.
>
> The following contexts will still have an extension slot: native,
> module, await, block and with contexts. See objects/contexts.h for
> what the slot is used for.
> The following contexts will not have an extension slot anymore (they
> were not used before): script, catch and builtin contexts.
> Eval and function contexts only have the extension slot if they
> contain a sloppy eval.
>
> Bug: v8:9744
> Change-Id: I8ca56c22fa02437bbac392ea72174ebfca80e030
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1863191
> Commit-Queue: Victor Gomes <victorgomes@google.com>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Peter Marshall <petermarshall@chromium.org>
> Auto-Submit: Victor Gomes <victorgomes@google.com>
> Cr-Commit-Position: refs/heads/master@{#64372}
TBR=verwaest@chromium.org,jgruber@chromium.org,ulan@chromium.org,leszeks@chromium.org,petermarshall@chromium.org
Bug: v8:9744
Change-Id: I8700ed2fa62c89e86c39bb16ac3167f38ea8d63f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1873695
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64477}
2019-10-22 12:59:24 +00:00
|
|
|
CHECK_EQ(v8::internal::Context::MIN_CONTEXT_EXTENDED_SLOTS + num_objects - 1,
|
2013-03-13 17:38:04 +00:00
|
|
|
context_object->GetChildrenCount());
|
|
|
|
// Check all the objects have got their names.
|
2013-11-26 11:32:39 +00:00
|
|
|
// ... well check just every 15th because otherwise it's too slow in debug.
|
|
|
|
for (int i = 0; i < num_objects - 1; i += 15) {
|
2013-03-13 18:35:30 +00:00
|
|
|
i::EmbeddedVector<char, 100> var_name;
|
2014-06-13 16:43:27 +00:00
|
|
|
i::SNPrintF(var_name, "f_%d", i);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* f_object =
|
|
|
|
GetProperty(env->GetIsolate(), context_object,
|
2019-04-29 11:06:49 +00:00
|
|
|
v8::HeapGraphEdge::kContextVariable, var_name.begin());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(f_object);
|
2013-03-13 17:38:04 +00:00
|
|
|
}
|
|
|
|
}
|
2013-07-09 14:16:59 +00:00
|
|
|
|
|
|
|
TEST(AllocationSitesAreVisible) {
|
2020-10-15 13:04:01 +00:00
|
|
|
i::FLAG_lazy_feedback_allocation = false;
|
2013-07-09 14:16:59 +00:00
|
|
|
LocalContext env;
|
2014-01-03 14:31:17 +00:00
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
2013-07-09 14:16:59 +00:00
|
|
|
CompileRun(
|
|
|
|
"fun = function () { var a = [3, 2, 1]; return a; }\n"
|
|
|
|
"fun();");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2013-07-09 14:16:59 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(global);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* fun_code = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "fun");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(fun_code);
|
2018-02-22 12:04:01 +00:00
|
|
|
const v8::HeapGraphNode* feedback_cell =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), fun_code, v8::HeapGraphEdge::kInternal,
|
2018-02-22 12:04:01 +00:00
|
|
|
"feedback_cell");
|
|
|
|
CHECK(feedback_cell);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* vector = GetProperty(
|
2018-02-22 12:04:01 +00:00
|
|
|
env->GetIsolate(), feedback_cell, v8::HeapGraphEdge::kInternal, "value");
|
2020-10-15 13:04:01 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kHidden, vector->GetType());
|
|
|
|
CHECK_EQ(4, vector->GetChildrenCount());
|
2017-02-06 10:18:05 +00:00
|
|
|
|
2020-10-15 13:04:01 +00:00
|
|
|
// The last value in the feedback vector should be the boilerplate,
|
|
|
|
// found in AllocationSite.transition_info.
|
|
|
|
const v8::HeapGraphEdge* prop = vector->GetChild(3);
|
2013-07-09 14:16:59 +00:00
|
|
|
const v8::HeapGraphNode* allocation_site = prop->GetToNode();
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value name(env->GetIsolate(), allocation_site->GetName());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(0, strcmp("system / AllocationSite", *name));
|
2013-07-09 14:16:59 +00:00
|
|
|
const v8::HeapGraphNode* transition_info =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), allocation_site,
|
|
|
|
v8::HeapGraphEdge::kInternal, "transition_info");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(transition_info);
|
2013-07-09 14:16:59 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* elements =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), transition_info,
|
|
|
|
v8::HeapGraphEdge::kInternal, "elements");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(elements);
|
2013-07-09 14:16:59 +00:00
|
|
|
CHECK_EQ(v8::HeapGraphNode::kArray, elements->GetType());
|
2014-02-18 13:22:07 +00:00
|
|
|
CHECK_EQ(v8::internal::FixedArray::SizeFor(3),
|
|
|
|
static_cast<int>(elements->GetShallowSize()));
|
2013-07-09 14:16:59 +00:00
|
|
|
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Value> array_val =
|
2013-12-23 09:33:16 +00:00
|
|
|
heap_profiler->FindObjectById(transition_info->GetId());
|
|
|
|
CHECK(array_val->IsArray());
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(array_val);
|
2013-07-09 14:16:59 +00:00
|
|
|
// Verify the array is "a" in the code above.
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(3u, array->Length());
|
|
|
|
CHECK(v8::Integer::New(isolate, 3)
|
2015-12-08 18:41:54 +00:00
|
|
|
->Equals(env.local(),
|
|
|
|
array->Get(env.local(), v8::Integer::New(isolate, 0))
|
|
|
|
.ToLocalChecked())
|
|
|
|
.FromJust());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(v8::Integer::New(isolate, 2)
|
2015-12-08 18:41:54 +00:00
|
|
|
->Equals(env.local(),
|
|
|
|
array->Get(env.local(), v8::Integer::New(isolate, 1))
|
|
|
|
.ToLocalChecked())
|
|
|
|
.FromJust());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(v8::Integer::New(isolate, 1)
|
2015-12-08 18:41:54 +00:00
|
|
|
->Equals(env.local(),
|
|
|
|
array->Get(env.local(), v8::Integer::New(isolate, 2))
|
|
|
|
.ToLocalChecked())
|
|
|
|
.FromJust());
|
2013-07-09 14:16:59 +00:00
|
|
|
}
|
2013-07-17 13:12:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
TEST(JSFunctionHasCodeLink) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
CompileRun("function foo(x, y) { return x + y; }\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-07-17 13:12:48 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* foo_func = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "foo");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(foo_func);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* code = GetProperty(
|
|
|
|
env->GetIsolate(), foo_func, v8::HeapGraphEdge::kInternal, "code");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(code);
|
2013-07-17 13:12:48 +00:00
|
|
|
}
|
2013-10-14 12:41:28 +00:00
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
static const v8::HeapGraphNode* GetNodeByPath(v8::Isolate* isolate,
|
|
|
|
const v8::HeapSnapshot* snapshot,
|
|
|
|
const char* path[], int depth) {
|
2013-11-05 13:23:03 +00:00
|
|
|
const v8::HeapGraphNode* node = snapshot->GetRoot();
|
|
|
|
for (int current_depth = 0; current_depth < depth; ++current_depth) {
|
|
|
|
int i, count = node->GetChildrenCount();
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* edge = node->GetChild(i);
|
|
|
|
const v8::HeapGraphNode* to_node = edge->GetToNode();
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value edge_name(isolate, edge->GetName());
|
|
|
|
v8::String::Utf8Value node_name(isolate, to_node->GetName());
|
2013-11-05 13:23:03 +00:00
|
|
|
i::EmbeddedVector<char, 100> name;
|
2014-06-13 16:43:27 +00:00
|
|
|
i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
|
2019-04-29 11:06:49 +00:00
|
|
|
if (strstr(name.begin(), path[current_depth])) {
|
2013-11-05 13:23:03 +00:00
|
|
|
node = to_node;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-10-13 16:33:03 +00:00
|
|
|
if (i == count) return nullptr;
|
2013-11-05 13:23:03 +00:00
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(CheckCodeNames) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
CompileRun("var a = 1.1;");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2013-11-05 13:23:03 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
|
2015-01-30 09:17:01 +00:00
|
|
|
const char* builtin_path1[] = {"::(GC roots)", "::(Builtins)",
|
2019-11-06 04:45:10 +00:00
|
|
|
"::(KeyedLoadIC_PolymorphicName builtin)"};
|
2018-11-21 12:30:24 +00:00
|
|
|
const v8::HeapGraphNode* node = GetNodeByPath(
|
|
|
|
env->GetIsolate(), snapshot, builtin_path1, arraysize(builtin_path1));
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(node);
|
2014-01-29 17:03:13 +00:00
|
|
|
|
2014-09-17 15:29:42 +00:00
|
|
|
const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
|
|
|
|
"::(CompileLazy builtin)"};
|
2017-08-24 21:49:48 +00:00
|
|
|
node = GetNodeByPath(env->GetIsolate(), snapshot, builtin_path2,
|
|
|
|
arraysize(builtin_path2));
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(node);
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value node_name(env->GetIsolate(), node->GetName());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(0, strcmp("(CompileLazy builtin)", *node_name));
|
2013-11-05 13:23:03 +00:00
|
|
|
}
|
2013-11-14 12:13:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
static const char* record_trace_tree_source =
|
|
|
|
"var topFunctions = [];\n"
|
|
|
|
"var global = this;\n"
|
|
|
|
"function generateFunctions(width, depth) {\n"
|
|
|
|
" var script = [];\n"
|
|
|
|
" for (var i = 0; i < width; i++) {\n"
|
|
|
|
" for (var j = 0; j < depth; j++) {\n"
|
|
|
|
" script.push('function f_' + i + '_' + j + '(x) {\\n');\n"
|
|
|
|
" script.push(' try {\\n');\n"
|
|
|
|
" if (j < depth-2) {\n"
|
|
|
|
" script.push(' return f_' + i + '_' + (j+1) + '(x+1);\\n');\n"
|
|
|
|
" } else if (j == depth - 2) {\n"
|
|
|
|
" script.push(' return new f_' + i + '_' + (depth - 1) + '();\\n');\n"
|
|
|
|
" } else if (j == depth - 1) {\n"
|
|
|
|
" script.push(' this.ts = Date.now();\\n');\n"
|
|
|
|
" }\n"
|
|
|
|
" script.push(' } catch (e) {}\\n');\n"
|
|
|
|
" script.push('}\\n');\n"
|
|
|
|
" \n"
|
|
|
|
" }\n"
|
|
|
|
" }\n"
|
|
|
|
" var script = script.join('');\n"
|
|
|
|
" // throw script;\n"
|
|
|
|
" global.eval(script);\n"
|
|
|
|
" for (var i = 0; i < width; i++) {\n"
|
|
|
|
" topFunctions.push(this['f_' + i + '_0']);\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"\n"
|
|
|
|
"var width = 3;\n"
|
|
|
|
"var depth = 3;\n"
|
|
|
|
"generateFunctions(width, depth);\n"
|
|
|
|
"var instances = [];\n"
|
|
|
|
"function start() {\n"
|
|
|
|
" for (var i = 0; i < width; i++) {\n"
|
|
|
|
" instances.push(topFunctions[i](0));\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"\n"
|
|
|
|
"for (var i = 0; i < 100; i++) start();\n";
|
|
|
|
|
|
|
|
|
|
|
|
static AllocationTraceNode* FindNode(
|
|
|
|
AllocationTracker* tracker, const Vector<const char*>& names) {
|
|
|
|
AllocationTraceNode* node = tracker->trace_tree()->root();
|
2017-10-13 16:33:03 +00:00
|
|
|
for (int i = 0; node != nullptr && i < names.length(); i++) {
|
2013-11-14 12:13:26 +00:00
|
|
|
const char* name = names[i];
|
2017-09-07 11:58:25 +00:00
|
|
|
const std::vector<AllocationTraceNode*>& children = node->children();
|
2017-10-13 16:33:03 +00:00
|
|
|
node = nullptr;
|
2017-09-07 11:58:25 +00:00
|
|
|
for (AllocationTraceNode* child : children) {
|
|
|
|
unsigned index = child->function_info_index();
|
2014-03-07 11:32:01 +00:00
|
|
|
AllocationTracker::FunctionInfo* info =
|
|
|
|
tracker->function_info_list()[index];
|
2013-11-14 12:13:26 +00:00
|
|
|
if (info && strcmp(info->name, name) == 0) {
|
2017-09-07 11:58:25 +00:00
|
|
|
node = child;
|
2013-11-14 12:13:26 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-11-29 09:54:38 +00:00
|
|
|
TEST(ArrayGrowLeftTrim) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2013-12-02 14:27:24 +00:00
|
|
|
heap_profiler->StartTrackingHeapObjects(true);
|
2013-11-29 09:54:38 +00:00
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"var a = [];\n"
|
|
|
|
"for (var i = 0; i < 5; ++i)\n"
|
|
|
|
" a[i] = i;\n"
|
|
|
|
"for (var i = 0; i < 3; ++i)\n"
|
|
|
|
" a.shift();\n");
|
|
|
|
|
2014-08-05 07:08:39 +00:00
|
|
|
const char* names[] = {""};
|
2013-12-03 09:48:30 +00:00
|
|
|
AllocationTracker* tracker =
|
|
|
|
reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(tracker);
|
2013-11-29 09:54:38 +00:00
|
|
|
// Resolve all function locations.
|
|
|
|
tracker->PrepareForSerialization();
|
|
|
|
// Print for better diagnostics in case of failure.
|
|
|
|
tracker->trace_tree()->Print(tracker);
|
|
|
|
|
2016-04-28 07:59:52 +00:00
|
|
|
AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(node);
|
|
|
|
CHECK_GE(node->allocation_count(), 2u);
|
|
|
|
CHECK_GE(node->allocation_size(), 4u * 5u);
|
2013-12-02 14:27:24 +00:00
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
2013-11-29 09:54:38 +00:00
|
|
|
}
|
|
|
|
|
2016-08-29 11:26:46 +00:00
|
|
|
TEST(TrackHeapAllocationsWithInlining) {
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2016-08-29 11:26:46 +00:00
|
|
|
LocalContext env;
|
|
|
|
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
heap_profiler->StartTrackingHeapObjects(true);
|
|
|
|
|
|
|
|
CompileRun(record_trace_tree_source);
|
|
|
|
|
|
|
|
AllocationTracker* tracker =
|
|
|
|
reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
|
|
|
|
CHECK(tracker);
|
|
|
|
// Resolve all function locations.
|
|
|
|
tracker->PrepareForSerialization();
|
|
|
|
// Print for better diagnostics in case of failure.
|
|
|
|
tracker->trace_tree()->Print(tracker);
|
|
|
|
|
|
|
|
const char* names[] = {"", "start", "f_0_0"};
|
|
|
|
AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
|
|
|
|
CHECK(node);
|
2018-12-19 13:17:05 +00:00
|
|
|
// In lite mode, there is feedback and feedback metadata.
|
|
|
|
unsigned int num_nodes = (i::FLAG_lite_mode) ? 6 : 8;
|
2019-02-14 10:29:28 +00:00
|
|
|
// Without forced source position collection, there is no source position
|
|
|
|
// table.
|
|
|
|
if (i::FLAG_enable_lazy_source_positions) num_nodes -= 1;
|
2018-12-19 13:17:05 +00:00
|
|
|
CHECK_GE(node->allocation_count(), num_nodes);
|
2016-08-29 11:26:46 +00:00
|
|
|
CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
|
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
|
|
|
}
|
2013-11-29 09:54:38 +00:00
|
|
|
|
2016-08-29 11:26:46 +00:00
|
|
|
TEST(TrackHeapAllocationsWithoutInlining) {
|
2016-09-23 16:23:17 +00:00
|
|
|
i::FLAG_turbo_inlining = false;
|
2017-07-17 12:11:58 +00:00
|
|
|
// Disable inlining
|
|
|
|
i::FLAG_max_inlined_bytecode_size = 0;
|
|
|
|
i::FLAG_max_inlined_bytecode_size_small = 0;
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2013-11-14 12:13:26 +00:00
|
|
|
LocalContext env;
|
|
|
|
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2013-12-02 14:27:24 +00:00
|
|
|
heap_profiler->StartTrackingHeapObjects(true);
|
2013-11-14 12:13:26 +00:00
|
|
|
|
|
|
|
CompileRun(record_trace_tree_source);
|
|
|
|
|
2013-12-03 09:48:30 +00:00
|
|
|
AllocationTracker* tracker =
|
|
|
|
reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(tracker);
|
2013-11-14 12:13:26 +00:00
|
|
|
// Resolve all function locations.
|
|
|
|
tracker->PrepareForSerialization();
|
|
|
|
// Print for better diagnostics in case of failure.
|
|
|
|
tracker->trace_tree()->Print(tracker);
|
|
|
|
|
2014-08-05 07:08:39 +00:00
|
|
|
const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
|
2016-04-28 07:59:52 +00:00
|
|
|
AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(node);
|
|
|
|
CHECK_GE(node->allocation_count(), 100u);
|
2013-11-14 12:13:26 +00:00
|
|
|
CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
|
2013-12-02 14:27:24 +00:00
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
2013-11-14 12:13:26 +00:00
|
|
|
}
|
2013-11-15 14:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
static const char* inline_heap_allocation_source =
|
2015-10-15 12:01:52 +00:00
|
|
|
"function f_0(x) {\n"
|
|
|
|
" return f_1(x+1);\n"
|
|
|
|
"}\n"
|
|
|
|
"%NeverOptimizeFunction(f_0);\n"
|
|
|
|
"function f_1(x) {\n"
|
|
|
|
" return new f_2(x+1);\n"
|
|
|
|
"}\n"
|
|
|
|
"%NeverOptimizeFunction(f_1);\n"
|
|
|
|
"function f_2(x) {\n"
|
|
|
|
" this.foo = x;\n"
|
|
|
|
"}\n"
|
|
|
|
"var instances = [];\n"
|
|
|
|
"function start() {\n"
|
|
|
|
" instances.push(f_0(0));\n"
|
|
|
|
"}\n"
|
|
|
|
"\n"
|
|
|
|
"for (var i = 0; i < 100; i++) start();\n";
|
2013-11-15 14:47:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
TEST(TrackBumpPointerAllocations) {
|
|
|
|
i::FLAG_allow_natives_syntax = true;
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2013-11-15 14:47:11 +00:00
|
|
|
LocalContext env;
|
|
|
|
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2014-08-05 07:08:39 +00:00
|
|
|
const char* names[] = {"", "start", "f_0", "f_1"};
|
2013-11-15 14:47:11 +00:00
|
|
|
// First check that normally all allocations are recorded.
|
|
|
|
{
|
2013-12-02 14:27:24 +00:00
|
|
|
heap_profiler->StartTrackingHeapObjects(true);
|
2013-11-15 14:47:11 +00:00
|
|
|
|
|
|
|
CompileRun(inline_heap_allocation_source);
|
|
|
|
|
2013-12-03 09:48:30 +00:00
|
|
|
AllocationTracker* tracker =
|
|
|
|
reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(tracker);
|
2013-11-15 14:47:11 +00:00
|
|
|
// Resolve all function locations.
|
|
|
|
tracker->PrepareForSerialization();
|
|
|
|
// Print for better diagnostics in case of failure.
|
|
|
|
tracker->trace_tree()->Print(tracker);
|
|
|
|
|
2016-04-28 07:59:52 +00:00
|
|
|
AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(node);
|
|
|
|
CHECK_GE(node->allocation_count(), 100u);
|
2013-11-15 14:47:11 +00:00
|
|
|
CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
|
2013-12-02 14:27:24 +00:00
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
2013-11-15 14:47:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2013-12-02 14:27:24 +00:00
|
|
|
heap_profiler->StartTrackingHeapObjects(true);
|
2013-11-15 14:47:11 +00:00
|
|
|
|
|
|
|
// Now check that not all allocations are tracked if we manually reenable
|
|
|
|
// inline allocations.
|
|
|
|
CHECK(CcTest::heap()->inline_allocation_disabled());
|
|
|
|
CcTest::heap()->EnableInlineAllocation();
|
|
|
|
|
|
|
|
CompileRun(inline_heap_allocation_source);
|
|
|
|
|
2013-12-03 09:48:30 +00:00
|
|
|
AllocationTracker* tracker =
|
|
|
|
reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(tracker);
|
2013-11-15 14:47:11 +00:00
|
|
|
// Resolve all function locations.
|
|
|
|
tracker->PrepareForSerialization();
|
|
|
|
// Print for better diagnostics in case of failure.
|
|
|
|
tracker->trace_tree()->Print(tracker);
|
|
|
|
|
2016-04-28 07:59:52 +00:00
|
|
|
AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(node);
|
|
|
|
CHECK_LT(node->allocation_count(), 100u);
|
2013-11-15 14:47:11 +00:00
|
|
|
|
|
|
|
CcTest::heap()->DisableInlineAllocation();
|
2013-12-02 14:27:24 +00:00
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
2013-11-15 14:47:11 +00:00
|
|
|
}
|
|
|
|
}
|
2014-01-24 15:10:09 +00:00
|
|
|
|
|
|
|
|
2014-03-07 11:32:01 +00:00
|
|
|
TEST(TrackV8ApiAllocation) {
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2014-03-07 11:32:01 +00:00
|
|
|
LocalContext env;
|
|
|
|
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
const char* names[] = { "(V8 API)" };
|
|
|
|
heap_profiler->StartTrackingHeapObjects(true);
|
|
|
|
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Object> o1 = v8::Object::New(env->GetIsolate());
|
2014-03-07 11:32:01 +00:00
|
|
|
o1->Clone();
|
|
|
|
|
|
|
|
AllocationTracker* tracker =
|
|
|
|
reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(tracker);
|
2014-03-07 11:32:01 +00:00
|
|
|
// Resolve all function locations.
|
|
|
|
tracker->PrepareForSerialization();
|
|
|
|
// Print for better diagnostics in case of failure.
|
|
|
|
tracker->trace_tree()->Print(tracker);
|
|
|
|
|
2016-04-28 07:59:52 +00:00
|
|
|
AllocationTraceNode* node = FindNode(tracker, ArrayVector(names));
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(node);
|
|
|
|
CHECK_GE(node->allocation_count(), 2u);
|
2014-03-07 11:32:01 +00:00
|
|
|
CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
|
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-24 15:10:09 +00:00
|
|
|
TEST(ArrayBufferAndArrayBufferView) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
CompileRun("arr1 = new Uint32Array(100);\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2014-01-24 15:10:09 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* arr1_obj = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "arr1");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(arr1_obj);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* arr1_buffer = GetProperty(
|
|
|
|
env->GetIsolate(), arr1_obj, v8::HeapGraphEdge::kInternal, "buffer");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(arr1_buffer);
|
2014-02-13 15:31:39 +00:00
|
|
|
const v8::HeapGraphNode* backing_store =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), arr1_buffer, v8::HeapGraphEdge::kInternal,
|
|
|
|
"backing_store");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(backing_store);
|
2014-02-18 13:22:07 +00:00
|
|
|
CHECK_EQ(400, static_cast<int>(backing_store->GetShallowSize()));
|
2014-01-24 15:10:09 +00:00
|
|
|
}
|
2014-02-04 11:43:19 +00:00
|
|
|
|
|
|
|
|
2014-02-17 15:24:39 +00:00
|
|
|
static int GetRetainersCount(const v8::HeapSnapshot* snapshot,
|
|
|
|
const v8::HeapGraphNode* node) {
|
|
|
|
int count = 0;
|
|
|
|
for (int i = 0, l = snapshot->GetNodesCount(); i < l; ++i) {
|
|
|
|
const v8::HeapGraphNode* parent = snapshot->GetNode(i);
|
|
|
|
for (int j = 0, l2 = parent->GetChildrenCount(); j < l2; ++j) {
|
|
|
|
if (parent->GetChild(j)->GetToNode() == node) {
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(ArrayBufferSharedBackingStore) {
|
|
|
|
LocalContext env;
|
2014-02-18 15:57:44 +00:00
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
|
|
|
|
|
|
|
v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
|
|
|
|
CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
|
2019-10-29 19:00:28 +00:00
|
|
|
std::shared_ptr<v8::BackingStore> backing_store = ab->GetBackingStore();
|
2014-02-18 15:57:44 +00:00
|
|
|
|
2019-10-29 19:00:28 +00:00
|
|
|
CHECK_EQ(1024, static_cast<int>(backing_store->ByteLength()));
|
|
|
|
void* data = backing_store->Data();
|
2017-10-18 09:06:55 +00:00
|
|
|
CHECK_NOT_NULL(data);
|
2019-10-29 19:00:28 +00:00
|
|
|
v8::Local<v8::ArrayBuffer> ab2 = v8::ArrayBuffer::New(isolate, backing_store);
|
2015-12-08 18:41:54 +00:00
|
|
|
env->Global()->Set(env.local(), v8_str("ab1"), ab).FromJust();
|
|
|
|
env->Global()->Set(env.local(), v8_str("ab2"), ab2).FromJust();
|
2014-02-18 15:57:44 +00:00
|
|
|
|
2015-12-08 18:41:54 +00:00
|
|
|
v8::Local<v8::Value> result = CompileRun("ab2.byteLength");
|
|
|
|
CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
|
2014-02-18 15:57:44 +00:00
|
|
|
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2014-02-17 15:24:39 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
2014-02-18 15:57:44 +00:00
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* ab1_node = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "ab1");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(ab1_node);
|
2014-02-18 15:57:44 +00:00
|
|
|
const v8::HeapGraphNode* ab1_data =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), ab1_node, v8::HeapGraphEdge::kInternal,
|
|
|
|
"backing_store");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(ab1_data);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* ab2_node = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "ab2");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(ab2_node);
|
2014-02-18 15:57:44 +00:00
|
|
|
const v8::HeapGraphNode* ab2_data =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), ab2_node, v8::HeapGraphEdge::kInternal,
|
|
|
|
"backing_store");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(ab2_data);
|
2014-02-18 15:57:44 +00:00
|
|
|
CHECK_EQ(ab1_data, ab2_data);
|
|
|
|
CHECK_EQ(2, GetRetainersCount(snapshot, ab1_data));
|
2014-02-17 15:24:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 11:19:53 +00:00
|
|
|
TEST(WeakContainers) {
|
|
|
|
i::FLAG_allow_natives_syntax = true;
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
2017-04-28 15:36:46 +00:00
|
|
|
if (!CcTest::i_isolate()->use_optimizer()) return;
|
2014-04-02 11:19:53 +00:00
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
CompileRun(
|
|
|
|
"function foo(a) { return a.x; }\n"
|
|
|
|
"obj = {x : 123};\n"
|
2019-04-30 11:04:41 +00:00
|
|
|
"%PrepareFunctionForOptimization(foo);"
|
2014-04-02 11:19:53 +00:00
|
|
|
"foo(obj);\n"
|
|
|
|
"foo(obj);\n"
|
|
|
|
"%OptimizeFunctionOnNextCall(foo);\n"
|
|
|
|
"foo(obj);\n");
|
2015-03-10 15:14:01 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
2014-04-02 11:19:53 +00:00
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* obj = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "obj");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(obj);
|
2014-04-02 11:19:53 +00:00
|
|
|
const v8::HeapGraphNode* map =
|
2017-08-24 21:49:48 +00:00
|
|
|
GetProperty(env->GetIsolate(), obj, v8::HeapGraphEdge::kInternal, "map");
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(map);
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* dependent_code = GetProperty(
|
|
|
|
env->GetIsolate(), map, v8::HeapGraphEdge::kInternal, "dependent_code");
|
2014-04-02 11:19:53 +00:00
|
|
|
if (!dependent_code) return;
|
|
|
|
int count = dependent_code->GetChildrenCount();
|
|
|
|
CHECK_NE(0, count);
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* prop = dependent_code->GetChild(i);
|
[map] Support in-place field representation changes.
This adds a new flag --modify-field-representation-inplace (enabled by
default), which lets the runtime perform field representation changes
for Smi to Tagged or for HeapObject to Tagged in-place instead of
creating new maps and marking the previous map tree as deprecated.
That means we create (a lot) fewer Maps and DescriptorArrays in the
beginning and also need to self-heal fewer objects later (migrating
off the deprecated maps). In TurboFan we just take the "field owner
dependency" whenever we use the field representation, which is very
similar to what we already do for the field types. That means if we
change the representation of a field that we used in optimized code,
we will simply deoptimize that code and have TurboFan potentially
later optimize it again with the new field representation.
On the Speedometer2/ElmJS-TodoMVC test, this reduces the total execution
time from around 415ms to around 352ms, which corresponds to a **15%**
improvement. The overall Speedometer2 score improves from around 74.1
to around 78.3 (on local runs with content_shell), corresponding to a
**5.6%** improvement here. 🎉
On the CNN desktop browsing story, it seems that we reduce map space
utilization/fragmentation by about 4-5%. But since we allocate a lot
less (fewer Maps and DescriptorArrays) we also significantly change
the GC timing, which heavily influences the results here. So take this
with a grain of salt. 🤷
Note: For Double fields, this doesn't change anything, meaning they
still create new maps and deprecate the previous map trees.
Bug: v8:8749, v8:8865, v8:9114
Change-Id: Ibd70efcb59be982863905663dbfaa89aa5b31e14
Cq-Include-Trybots: luci.chromium.try:linux-rel,win7-rel
Doc: http://bit.ly/v8-in-place-field-representation-changes
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1565891
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60822}
2019-04-12 13:38:34 +00:00
|
|
|
CHECK(prop->GetType() == v8::HeapGraphEdge::kInternal ||
|
|
|
|
prop->GetType() == v8::HeapGraphEdge::kWeak);
|
2014-04-02 11:19:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-20 17:48:57 +00:00
|
|
|
TEST(JSPromise) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
CompileRun(
|
|
|
|
"function A() {}\n"
|
|
|
|
"function B() {}\n"
|
|
|
|
"resolved = Promise.resolve(new A());\n"
|
|
|
|
"rejected = Promise.reject(new B());\n"
|
|
|
|
"pending = new Promise(() => 0);\n"
|
|
|
|
"chained = pending.then(A, B);\n");
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* resolved = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "resolved");
|
|
|
|
CHECK(GetProperty(env->GetIsolate(), resolved, v8::HeapGraphEdge::kInternal,
|
2018-02-08 16:36:52 +00:00
|
|
|
"reactions_or_result"));
|
2017-08-24 21:49:48 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* rejected = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "rejected");
|
|
|
|
CHECK(GetProperty(env->GetIsolate(), rejected, v8::HeapGraphEdge::kInternal,
|
2018-02-08 16:36:52 +00:00
|
|
|
"reactions_or_result"));
|
2017-08-24 21:49:48 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* pending = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, "pending");
|
|
|
|
CHECK(GetProperty(env->GetIsolate(), pending, v8::HeapGraphEdge::kInternal,
|
2018-02-08 16:36:52 +00:00
|
|
|
"reactions_or_result"));
|
2017-07-20 17:48:57 +00:00
|
|
|
|
|
|
|
const char* objectNames[] = {"resolved", "rejected", "pending", "chained"};
|
|
|
|
for (auto objectName : objectNames) {
|
2017-08-24 21:49:48 +00:00
|
|
|
const v8::HeapGraphNode* promise = GetProperty(
|
|
|
|
env->GetIsolate(), global, v8::HeapGraphEdge::kProperty, objectName);
|
|
|
|
EnsureNoUninstrumentedInternals(env->GetIsolate(), promise);
|
2017-07-20 17:48:57 +00:00
|
|
|
}
|
|
|
|
}
|
2014-04-02 11:19:53 +00:00
|
|
|
|
2018-03-15 10:35:04 +00:00
|
|
|
TEST(HeapSnapshotScriptContext) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
CompileRun("class Foo{}; const foo = new Foo();");
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* native_context =
|
|
|
|
GetProperty(env->GetIsolate(), global, v8::HeapGraphEdge::kInternal,
|
|
|
|
"native_context");
|
|
|
|
CHECK(native_context);
|
|
|
|
const v8::HeapGraphNode* script_context_table =
|
|
|
|
GetProperty(env->GetIsolate(), native_context,
|
|
|
|
v8::HeapGraphEdge::kInternal, "script_context_table");
|
|
|
|
CHECK(script_context_table);
|
|
|
|
bool found_foo = false;
|
|
|
|
for (int i = 0, count = script_context_table->GetChildrenCount(); i < count;
|
|
|
|
++i) {
|
|
|
|
const v8::HeapGraphNode* context =
|
|
|
|
script_context_table->GetChild(i)->GetToNode();
|
|
|
|
const v8::HeapGraphNode* foo = GetProperty(
|
|
|
|
env->GetIsolate(), context, v8::HeapGraphEdge::kContextVariable, "foo");
|
|
|
|
if (foo) {
|
|
|
|
found_foo = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK(found_foo);
|
|
|
|
}
|
|
|
|
|
2018-01-31 19:55:31 +00:00
|
|
|
class EmbedderNode : public v8::EmbedderGraph::Node {
|
|
|
|
public:
|
2018-02-20 13:16:34 +00:00
|
|
|
EmbedderNode(const char* name, size_t size,
|
|
|
|
v8::EmbedderGraph::Node* wrapper_node = nullptr)
|
|
|
|
: name_(name), size_(size), wrapper_node_(wrapper_node) {}
|
2018-01-31 19:55:31 +00:00
|
|
|
|
|
|
|
// Graph::Node overrides.
|
|
|
|
const char* Name() override { return name_; }
|
|
|
|
size_t SizeInBytes() override { return size_; }
|
2018-02-20 13:16:34 +00:00
|
|
|
Node* WrapperNode() override { return wrapper_node_; }
|
2018-01-31 19:55:31 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
const char* name_;
|
|
|
|
size_t size_;
|
2018-02-20 13:16:34 +00:00
|
|
|
Node* wrapper_node_;
|
2018-01-31 19:55:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class EmbedderRootNode : public EmbedderNode {
|
|
|
|
public:
|
|
|
|
explicit EmbedderRootNode(const char* name) : EmbedderNode(name, 0) {}
|
|
|
|
// Graph::Node override.
|
2018-09-14 15:34:02 +00:00
|
|
|
bool IsRootNode() override { return true; }
|
2018-01-31 19:55:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Used to pass the global object to the BuildEmbedderGraph callback.
|
|
|
|
// Otherwise, the callback has to iterate the global handles to find the
|
|
|
|
// global object.
|
|
|
|
v8::Local<v8::Value>* global_object_pointer;
|
|
|
|
|
2018-06-01 20:27:43 +00:00
|
|
|
void BuildEmbedderGraph(v8::Isolate* v8_isolate, v8::EmbedderGraph* graph,
|
|
|
|
void* data) {
|
2018-01-31 19:55:31 +00:00
|
|
|
using Node = v8::EmbedderGraph::Node;
|
|
|
|
Node* global_node = graph->V8Node(*global_object_pointer);
|
|
|
|
Node* embedder_node_A = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
|
|
|
|
Node* embedder_node_B = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
|
|
|
|
Node* embedder_node_C = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeC", 30)));
|
|
|
|
Node* embedder_root = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderRootNode("EmbedderRoot")));
|
|
|
|
graph->AddEdge(global_node, embedder_node_A);
|
|
|
|
graph->AddEdge(embedder_node_A, embedder_node_B);
|
|
|
|
graph->AddEdge(embedder_root, embedder_node_C);
|
|
|
|
graph->AddEdge(embedder_node_C, global_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckEmbedderGraphSnapshot(v8::Isolate* isolate,
|
|
|
|
const v8::HeapSnapshot* snapshot) {
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* embedder_node_A =
|
|
|
|
GetChildByName(global, "EmbedderNodeA");
|
|
|
|
CHECK_EQ(10, GetSize(embedder_node_A));
|
|
|
|
const v8::HeapGraphNode* embedder_node_B =
|
|
|
|
GetChildByName(embedder_node_A, "EmbedderNodeB");
|
|
|
|
CHECK_EQ(20, GetSize(embedder_node_B));
|
|
|
|
const v8::HeapGraphNode* embedder_root =
|
|
|
|
GetRootChild(snapshot, "EmbedderRoot");
|
|
|
|
CHECK(embedder_root);
|
|
|
|
const v8::HeapGraphNode* embedder_node_C =
|
|
|
|
GetChildByName(embedder_root, "EmbedderNodeC");
|
|
|
|
CHECK_EQ(30, GetSize(embedder_node_C));
|
|
|
|
const v8::HeapGraphNode* global_reference =
|
|
|
|
GetChildByName(embedder_node_C, "Object");
|
|
|
|
CHECK(global_reference);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(EmbedderGraph) {
|
2018-02-01 16:43:39 +00:00
|
|
|
i::FLAG_heap_profiler_use_embedder_graph = true;
|
2018-01-31 19:55:31 +00:00
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
|
|
|
|
v8::Local<v8::Value> global_object =
|
|
|
|
v8::Utils::ToLocal(i::Handle<i::JSObject>(
|
2019-05-23 07:47:44 +00:00
|
|
|
(isolate->context().native_context().global_object()), isolate));
|
2018-01-31 19:55:31 +00:00
|
|
|
global_object_pointer = &global_object;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2018-06-01 20:27:43 +00:00
|
|
|
heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraph, nullptr);
|
2018-01-31 19:55:31 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
CheckEmbedderGraphSnapshot(env->GetIsolate(), snapshot);
|
|
|
|
}
|
|
|
|
|
2018-07-12 07:52:20 +00:00
|
|
|
void BuildEmbedderGraphWithNamedEdges(v8::Isolate* v8_isolate,
|
|
|
|
v8::EmbedderGraph* graph, void* data) {
|
|
|
|
using Node = v8::EmbedderGraph::Node;
|
|
|
|
Node* global_node = graph->V8Node(*global_object_pointer);
|
|
|
|
Node* embedder_node_A = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
|
|
|
|
Node* embedder_node_B = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
|
|
|
|
Node* embedder_node_C = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeC", 30)));
|
|
|
|
graph->AddEdge(global_node, embedder_node_A, "global_to_a");
|
|
|
|
graph->AddEdge(embedder_node_A, embedder_node_B, "a_to_b");
|
|
|
|
graph->AddEdge(embedder_node_B, embedder_node_C);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckEmbedderGraphWithNamedEdges(v8::Isolate* isolate,
|
|
|
|
const v8::HeapSnapshot* snapshot) {
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphEdge* global_to_a =
|
|
|
|
GetEdgeByChildName(global, "EmbedderNodeA");
|
|
|
|
CHECK(global_to_a);
|
|
|
|
CHECK_EQ(v8::HeapGraphEdge::kInternal, global_to_a->GetType());
|
|
|
|
CHECK(global_to_a->GetName()->IsString());
|
|
|
|
CHECK_EQ(0, strcmp("global_to_a", GetName(global_to_a)));
|
|
|
|
const v8::HeapGraphNode* embedder_node_A = global_to_a->GetToNode();
|
|
|
|
CHECK_EQ(0, strcmp("EmbedderNodeA", GetName(embedder_node_A)));
|
|
|
|
CHECK_EQ(10, GetSize(embedder_node_A));
|
|
|
|
|
|
|
|
const v8::HeapGraphEdge* a_to_b =
|
|
|
|
GetEdgeByChildName(embedder_node_A, "EmbedderNodeB");
|
|
|
|
CHECK(a_to_b);
|
|
|
|
CHECK(a_to_b->GetName()->IsString());
|
|
|
|
CHECK_EQ(0, strcmp("a_to_b", GetName(a_to_b)));
|
|
|
|
CHECK_EQ(v8::HeapGraphEdge::kInternal, a_to_b->GetType());
|
|
|
|
const v8::HeapGraphNode* embedder_node_B = a_to_b->GetToNode();
|
|
|
|
CHECK_EQ(0, strcmp("EmbedderNodeB", GetName(embedder_node_B)));
|
|
|
|
CHECK_EQ(20, GetSize(embedder_node_B));
|
|
|
|
|
|
|
|
const v8::HeapGraphEdge* b_to_c =
|
|
|
|
GetEdgeByChildName(embedder_node_B, "EmbedderNodeC");
|
|
|
|
CHECK(b_to_c);
|
|
|
|
CHECK(b_to_c->GetName()->IsNumber());
|
|
|
|
CHECK_EQ(v8::HeapGraphEdge::kElement, b_to_c->GetType());
|
|
|
|
const v8::HeapGraphNode* embedder_node_C = b_to_c->GetToNode();
|
|
|
|
CHECK_EQ(0, strcmp("EmbedderNodeC", GetName(embedder_node_C)));
|
|
|
|
CHECK_EQ(30, GetSize(embedder_node_C));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(EmbedderGraphWithNamedEdges) {
|
|
|
|
i::FLAG_heap_profiler_use_embedder_graph = true;
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
|
|
|
|
v8::Local<v8::Value> global_object =
|
|
|
|
v8::Utils::ToLocal(i::Handle<i::JSObject>(
|
2019-05-23 07:47:44 +00:00
|
|
|
(isolate->context().native_context().global_object()), isolate));
|
2018-07-12 07:52:20 +00:00
|
|
|
global_object_pointer = &global_object;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithNamedEdges,
|
|
|
|
nullptr);
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
CheckEmbedderGraphWithNamedEdges(env->GetIsolate(), snapshot);
|
|
|
|
}
|
|
|
|
|
2018-06-01 20:27:43 +00:00
|
|
|
struct GraphBuildingContext {
|
|
|
|
int counter = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
void CheckEmbedderGraphSnapshotWithContext(
|
|
|
|
v8::Isolate* isolate, const v8::HeapSnapshot* snapshot,
|
|
|
|
const GraphBuildingContext* context) {
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
CHECK_GE(context->counter, 1);
|
|
|
|
CHECK_LE(context->counter, 2);
|
|
|
|
|
|
|
|
const v8::HeapGraphNode* embedder_node_A =
|
|
|
|
GetChildByName(global, "EmbedderNodeA");
|
|
|
|
CHECK_EQ(10, GetSize(embedder_node_A));
|
|
|
|
|
|
|
|
const v8::HeapGraphNode* embedder_node_B =
|
|
|
|
GetChildByName(global, "EmbedderNodeB");
|
|
|
|
if (context->counter == 2) {
|
|
|
|
CHECK_NOT_NULL(embedder_node_B);
|
|
|
|
CHECK_EQ(20, GetSize(embedder_node_B));
|
|
|
|
} else {
|
|
|
|
CHECK_NULL(embedder_node_B);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BuildEmbedderGraphWithContext(v8::Isolate* v8_isolate,
|
|
|
|
v8::EmbedderGraph* graph, void* data) {
|
|
|
|
using Node = v8::EmbedderGraph::Node;
|
|
|
|
GraphBuildingContext* context = static_cast<GraphBuildingContext*>(data);
|
|
|
|
Node* global_node = graph->V8Node(*global_object_pointer);
|
|
|
|
|
|
|
|
CHECK_GE(context->counter, 0);
|
|
|
|
CHECK_LE(context->counter, 1);
|
|
|
|
switch (context->counter++) {
|
|
|
|
case 0: {
|
|
|
|
Node* embedder_node_A = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeA", 10)));
|
|
|
|
graph->AddEdge(global_node, embedder_node_A);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1: {
|
|
|
|
Node* embedder_node_B = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("EmbedderNodeB", 20)));
|
|
|
|
graph->AddEdge(global_node, embedder_node_B);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(EmbedderGraphMultipleCallbacks) {
|
|
|
|
i::FLAG_heap_profiler_use_embedder_graph = true;
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
|
|
|
|
v8::Local<v8::Value> global_object =
|
|
|
|
v8::Utils::ToLocal(i::Handle<i::JSObject>(
|
2019-05-23 07:47:44 +00:00
|
|
|
(isolate->context().native_context().global_object()), isolate));
|
2018-06-01 20:27:43 +00:00
|
|
|
global_object_pointer = &global_object;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
GraphBuildingContext context;
|
|
|
|
|
|
|
|
heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
|
|
|
|
&context);
|
|
|
|
heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
|
|
|
|
&context);
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK_EQ(context.counter, 2);
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context);
|
|
|
|
|
|
|
|
heap_profiler->RemoveBuildEmbedderGraphCallback(BuildEmbedderGraphWithContext,
|
|
|
|
&context);
|
|
|
|
context.counter = 0;
|
|
|
|
|
|
|
|
snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK_EQ(context.counter, 1);
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
CheckEmbedderGraphSnapshotWithContext(env->GetIsolate(), snapshot, &context);
|
|
|
|
}
|
|
|
|
|
2018-02-19 10:53:00 +00:00
|
|
|
TEST(StrongHandleAnnotation) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::Persistent<v8::Object> handle1, handle2;
|
|
|
|
handle1.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
|
|
|
|
handle2.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
|
|
|
|
handle1.AnnotateStrongRetainer("my_label");
|
|
|
|
handle2.AnnotateStrongRetainer("my_label");
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
const v8::HeapGraphNode* gc_roots = GetRootChild(snapshot, "(GC roots)");
|
|
|
|
CHECK(gc_roots);
|
|
|
|
const v8::HeapGraphNode* global_handles =
|
|
|
|
GetChildByName(gc_roots, "(Global handles)");
|
|
|
|
CHECK(global_handles);
|
|
|
|
int found = 0;
|
|
|
|
for (int i = 0, count = global_handles->GetChildrenCount(); i < count; ++i) {
|
|
|
|
const v8::HeapGraphEdge* edge = global_handles->GetChild(i);
|
|
|
|
v8::String::Utf8Value edge_name(CcTest::isolate(), edge->GetName());
|
|
|
|
if (EndsWith(*edge_name, "my_label")) ++found;
|
|
|
|
}
|
|
|
|
CHECK_EQ(2, found);
|
|
|
|
}
|
|
|
|
|
2018-02-20 13:16:34 +00:00
|
|
|
void BuildEmbedderGraphWithWrapperNode(v8::Isolate* v8_isolate,
|
2018-06-01 20:27:43 +00:00
|
|
|
v8::EmbedderGraph* graph, void* data) {
|
2018-02-20 13:16:34 +00:00
|
|
|
using Node = v8::EmbedderGraph::Node;
|
|
|
|
Node* global_node = graph->V8Node(*global_object_pointer);
|
|
|
|
Node* wrapper_node = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("WrapperNode / TAG", 10)));
|
|
|
|
Node* embedder_node = graph->AddNode(std::unique_ptr<Node>(
|
|
|
|
new EmbedderNode("EmbedderNode", 10, wrapper_node)));
|
|
|
|
Node* other_node =
|
|
|
|
graph->AddNode(std::unique_ptr<Node>(new EmbedderNode("OtherNode", 20)));
|
|
|
|
graph->AddEdge(global_node, embedder_node);
|
|
|
|
graph->AddEdge(wrapper_node, other_node);
|
2018-02-21 13:04:46 +00:00
|
|
|
|
|
|
|
Node* wrapper_node2 = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNode("WrapperNode2", 10)));
|
|
|
|
Node* embedder_node2 = graph->AddNode(std::unique_ptr<Node>(
|
|
|
|
new EmbedderNode("EmbedderNode2", 10, wrapper_node2)));
|
|
|
|
graph->AddEdge(global_node, embedder_node2);
|
|
|
|
graph->AddEdge(embedder_node2, wrapper_node2);
|
|
|
|
graph->AddEdge(wrapper_node2, other_node);
|
2018-02-20 13:16:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(EmbedderGraphWithWrapperNode) {
|
|
|
|
i::FLAG_heap_profiler_use_embedder_graph = true;
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
|
|
|
|
v8::Local<v8::Value> global_object =
|
|
|
|
v8::Utils::ToLocal(i::Handle<i::JSObject>(
|
2019-05-23 07:47:44 +00:00
|
|
|
(isolate->context().native_context().global_object()), isolate));
|
2018-02-20 13:16:34 +00:00
|
|
|
global_object_pointer = &global_object;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2018-06-01 20:27:43 +00:00
|
|
|
heap_profiler->AddBuildEmbedderGraphCallback(
|
|
|
|
BuildEmbedderGraphWithWrapperNode, nullptr);
|
2018-02-20 13:16:34 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* embedder_node =
|
|
|
|
GetChildByName(global, "EmbedderNode / TAG");
|
|
|
|
const v8::HeapGraphNode* other_node =
|
|
|
|
GetChildByName(embedder_node, "OtherNode");
|
|
|
|
CHECK(other_node);
|
|
|
|
const v8::HeapGraphNode* wrapper_node =
|
|
|
|
GetChildByName(embedder_node, "WrapperNode / TAG");
|
|
|
|
CHECK(!wrapper_node);
|
2018-02-21 13:04:46 +00:00
|
|
|
|
|
|
|
const v8::HeapGraphNode* embedder_node2 =
|
|
|
|
GetChildByName(global, "EmbedderNode2");
|
|
|
|
other_node = GetChildByName(embedder_node2, "OtherNode");
|
|
|
|
CHECK(other_node);
|
|
|
|
const v8::HeapGraphNode* wrapper_node2 =
|
|
|
|
GetChildByName(embedder_node, "WrapperNode2");
|
|
|
|
CHECK(!wrapper_node2);
|
2018-02-20 13:16:34 +00:00
|
|
|
}
|
|
|
|
|
2018-02-20 13:52:38 +00:00
|
|
|
class EmbedderNodeWithPrefix : public v8::EmbedderGraph::Node {
|
|
|
|
public:
|
|
|
|
EmbedderNodeWithPrefix(const char* prefix, const char* name)
|
|
|
|
: prefix_(prefix), name_(name) {}
|
|
|
|
|
|
|
|
// Graph::Node overrides.
|
|
|
|
const char* Name() override { return name_; }
|
|
|
|
size_t SizeInBytes() override { return 0; }
|
|
|
|
const char* NamePrefix() override { return prefix_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
const char* prefix_;
|
|
|
|
const char* name_;
|
|
|
|
};
|
|
|
|
|
|
|
|
void BuildEmbedderGraphWithPrefix(v8::Isolate* v8_isolate,
|
2018-06-01 20:27:43 +00:00
|
|
|
v8::EmbedderGraph* graph, void* data) {
|
2018-02-20 13:52:38 +00:00
|
|
|
using Node = v8::EmbedderGraph::Node;
|
|
|
|
Node* global_node = graph->V8Node(*global_object_pointer);
|
|
|
|
Node* node = graph->AddNode(
|
|
|
|
std::unique_ptr<Node>(new EmbedderNodeWithPrefix("Detached", "Node")));
|
|
|
|
graph->AddEdge(global_node, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(EmbedderGraphWithPrefix) {
|
|
|
|
i::FLAG_heap_profiler_use_embedder_graph = true;
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
|
|
|
|
v8::Local<v8::Value> global_object =
|
|
|
|
v8::Utils::ToLocal(i::Handle<i::JSObject>(
|
2019-05-23 07:47:44 +00:00
|
|
|
(isolate->context().native_context().global_object()), isolate));
|
2018-02-20 13:52:38 +00:00
|
|
|
global_object_pointer = &global_object;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
2018-06-01 20:27:43 +00:00
|
|
|
heap_profiler->AddBuildEmbedderGraphCallback(BuildEmbedderGraphWithPrefix,
|
|
|
|
nullptr);
|
2018-02-20 13:52:38 +00:00
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
|
|
|
const v8::HeapGraphNode* node = GetChildByName(global, "Detached Node");
|
|
|
|
CHECK(node);
|
|
|
|
}
|
|
|
|
|
2018-04-13 22:28:05 +00:00
|
|
|
static inline i::Address ToAddress(int n) { return static_cast<i::Address>(n); }
|
2014-03-07 16:13:22 +00:00
|
|
|
|
|
|
|
TEST(AddressToTraceMap) {
|
|
|
|
i::AddressToTraceMap map;
|
|
|
|
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(150)));
|
2014-03-07 16:13:22 +00:00
|
|
|
|
|
|
|
// [0x100, 0x200) -> 1
|
|
|
|
map.AddRange(ToAddress(0x100), 0x100, 1U);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x50)));
|
|
|
|
CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x100)));
|
|
|
|
CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x150)));
|
|
|
|
CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x100 + 0x100)));
|
|
|
|
CHECK_EQ(1u, map.size());
|
2014-03-07 16:13:22 +00:00
|
|
|
|
|
|
|
// [0x100, 0x200) -> 1, [0x200, 0x300) -> 2
|
|
|
|
map.AddRange(ToAddress(0x200), 0x100, 2U);
|
2017-12-02 00:30:37 +00:00
|
|
|
CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x2A0)));
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(2u, map.size());
|
2014-03-07 16:13:22 +00:00
|
|
|
|
|
|
|
// [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2
|
|
|
|
map.AddRange(ToAddress(0x180), 0x100, 3U);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
|
|
|
|
CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
|
|
|
|
CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
|
|
|
|
CHECK_EQ(3u, map.size());
|
2014-03-07 16:13:22 +00:00
|
|
|
|
|
|
|
// [0x100, 0x180) -> 1, [0x180, 0x280) -> 3, [0x280, 0x300) -> 2,
|
|
|
|
// [0x400, 0x500) -> 4
|
|
|
|
map.AddRange(ToAddress(0x400), 0x100, 4U);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(1u, map.GetTraceNodeId(ToAddress(0x17F)));
|
|
|
|
CHECK_EQ(2u, map.GetTraceNodeId(ToAddress(0x280)));
|
|
|
|
CHECK_EQ(3u, map.GetTraceNodeId(ToAddress(0x180)));
|
|
|
|
CHECK_EQ(4u, map.GetTraceNodeId(ToAddress(0x450)));
|
|
|
|
CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x500)));
|
|
|
|
CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x350)));
|
|
|
|
CHECK_EQ(4u, map.size());
|
2014-03-07 16:13:22 +00:00
|
|
|
|
|
|
|
// [0x100, 0x180) -> 1, [0x180, 0x200) -> 3, [0x200, 0x600) -> 5
|
|
|
|
map.AddRange(ToAddress(0x200), 0x400, 5U);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
|
|
|
|
CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x400)));
|
|
|
|
CHECK_EQ(3u, map.size());
|
2014-03-07 16:13:22 +00:00
|
|
|
|
|
|
|
// [0x100, 0x180) -> 1, [0x180, 0x200) -> 7, [0x200, 0x600) ->5
|
|
|
|
map.AddRange(ToAddress(0x180), 0x80, 6U);
|
|
|
|
map.AddRange(ToAddress(0x180), 0x80, 7U);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(7u, map.GetTraceNodeId(ToAddress(0x180)));
|
|
|
|
CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200)));
|
|
|
|
CHECK_EQ(3u, map.size());
|
2014-03-07 16:13:22 +00:00
|
|
|
|
|
|
|
map.Clear();
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK_EQ(0u, map.size());
|
|
|
|
CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x400)));
|
2014-03-07 16:13:22 +00:00
|
|
|
}
|
2016-01-22 16:36:40 +00:00
|
|
|
|
|
|
|
static const v8::AllocationProfile::Node* FindAllocationProfileNode(
|
2019-09-10 01:19:59 +00:00
|
|
|
v8::Isolate* isolate, v8::AllocationProfile* profile,
|
2017-08-24 21:49:48 +00:00
|
|
|
const Vector<const char*>& names) {
|
2019-09-10 01:19:59 +00:00
|
|
|
v8::AllocationProfile::Node* node = profile->GetRootNode();
|
2016-01-22 16:36:40 +00:00
|
|
|
for (int i = 0; node != nullptr && i < names.length(); ++i) {
|
|
|
|
const char* name = names[i];
|
|
|
|
auto children = node->children;
|
|
|
|
node = nullptr;
|
|
|
|
for (v8::AllocationProfile::Node* child : children) {
|
2017-08-24 21:49:48 +00:00
|
|
|
v8::String::Utf8Value child_name(isolate, child->name);
|
2016-01-22 16:36:40 +00:00
|
|
|
if (strcmp(*child_name, name) == 0) {
|
|
|
|
node = child;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2016-04-06 14:21:04 +00:00
|
|
|
static void CheckNoZeroCountNodes(v8::AllocationProfile::Node* node) {
|
|
|
|
for (auto alloc : node->allocations) {
|
|
|
|
CHECK_GT(alloc.count, 0u);
|
|
|
|
}
|
|
|
|
for (auto child : node->children) {
|
|
|
|
CheckNoZeroCountNodes(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-20 12:13:30 +00:00
|
|
|
static int NumberOfAllocations(const v8::AllocationProfile::Node* node) {
|
|
|
|
int count = 0;
|
|
|
|
for (auto allocation : node->allocations) {
|
|
|
|
count += allocation.count;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2018-03-07 22:33:10 +00:00
|
|
|
static const char* simple_sampling_heap_profiler_script =
|
|
|
|
"var A = [];\n"
|
|
|
|
"function bar(size) { return new Array(size); }\n"
|
|
|
|
"var foo = function() {\n"
|
|
|
|
" for (var i = 0; i < 1024; ++i) {\n"
|
|
|
|
" A[i] = bar(1024);\n"
|
|
|
|
" }\n"
|
|
|
|
"}\n"
|
|
|
|
"foo();";
|
|
|
|
|
2016-01-22 16:36:40 +00:00
|
|
|
TEST(SamplingHeapProfiler) {
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2016-01-22 16:36:40 +00:00
|
|
|
LocalContext env;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
// Turn off always_opt. Inlining can cause stack traces to be shorter than
|
|
|
|
// what we expect in this test.
|
|
|
|
v8::internal::FLAG_always_opt = false;
|
|
|
|
|
|
|
|
// Suppress randomness to avoid flakiness in tests.
|
|
|
|
v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
|
|
|
|
|
|
|
|
// Sample should be empty if requested before sampling has started.
|
|
|
|
{
|
|
|
|
v8::AllocationProfile* profile = heap_profiler->GetAllocationProfile();
|
2017-10-18 09:06:55 +00:00
|
|
|
CHECK_NULL(profile);
|
2016-01-22 16:36:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2016-02-24 08:04:16 +00:00
|
|
|
heap_profiler->StartSamplingHeapProfiler(1024);
|
2018-03-07 22:33:10 +00:00
|
|
|
CompileRun(simple_sampling_heap_profiler_script);
|
2016-01-22 16:36:40 +00:00
|
|
|
|
2016-07-25 11:12:42 +00:00
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
2016-01-22 16:36:40 +00:00
|
|
|
heap_profiler->GetAllocationProfile());
|
2016-07-25 11:12:42 +00:00
|
|
|
CHECK(profile);
|
2016-01-22 16:36:40 +00:00
|
|
|
|
|
|
|
const char* names[] = {"", "foo", "bar"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node_bar = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2017-08-24 21:49:48 +00:00
|
|
|
ArrayVector(names));
|
2016-01-22 16:36:40 +00:00
|
|
|
CHECK(node_bar);
|
|
|
|
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Samples should get cleared once sampling is stopped.
|
|
|
|
{
|
|
|
|
v8::AllocationProfile* profile = heap_profiler->GetAllocationProfile();
|
2017-10-18 09:06:55 +00:00
|
|
|
CHECK_NULL(profile);
|
2016-01-22 16:36:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// A more complicated test cases with deeper call graph and dynamically
|
|
|
|
// generated function names.
|
|
|
|
{
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(64);
|
|
|
|
CompileRun(record_trace_tree_source);
|
|
|
|
|
2016-07-25 11:12:42 +00:00
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
2016-01-22 16:36:40 +00:00
|
|
|
heap_profiler->GetAllocationProfile());
|
2016-07-25 11:12:42 +00:00
|
|
|
CHECK(profile);
|
2016-01-22 16:36:40 +00:00
|
|
|
|
|
|
|
const char* names1[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node1 = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2017-08-24 21:49:48 +00:00
|
|
|
ArrayVector(names1));
|
2016-01-22 16:36:40 +00:00
|
|
|
CHECK(node1);
|
|
|
|
|
|
|
|
const char* names2[] = {"", "generateFunctions"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node2 = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2017-08-24 21:49:48 +00:00
|
|
|
ArrayVector(names2));
|
2016-01-22 16:36:40 +00:00
|
|
|
CHECK(node2);
|
|
|
|
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
2016-03-31 06:36:05 +00:00
|
|
|
|
|
|
|
// A test case with scripts unloaded before profile gathered
|
|
|
|
{
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(64);
|
|
|
|
CompileRun(
|
|
|
|
"for (var i = 0; i < 1024; i++) {\n"
|
|
|
|
" eval(\"new Array(100)\");\n"
|
|
|
|
"}\n");
|
|
|
|
|
2017-04-26 22:16:41 +00:00
|
|
|
CcTest::CollectAllGarbage();
|
2016-03-31 06:36:05 +00:00
|
|
|
|
2016-07-25 11:12:42 +00:00
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
2016-03-31 06:36:05 +00:00
|
|
|
heap_profiler->GetAllocationProfile());
|
2016-07-25 11:12:42 +00:00
|
|
|
CHECK(profile);
|
2016-03-31 06:36:05 +00:00
|
|
|
|
2016-04-06 14:21:04 +00:00
|
|
|
CheckNoZeroCountNodes(profile->GetRootNode());
|
|
|
|
|
2016-03-31 06:36:05 +00:00
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
2016-01-22 16:36:40 +00:00
|
|
|
}
|
|
|
|
|
2018-03-07 22:33:10 +00:00
|
|
|
TEST(SamplingHeapProfilerRateAgnosticEstimates) {
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2018-03-07 22:33:10 +00:00
|
|
|
LocalContext env;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
// Turn off always_opt. Inlining can cause stack traces to be shorter than
|
|
|
|
// what we expect in this test.
|
|
|
|
v8::internal::FLAG_always_opt = false;
|
|
|
|
|
2018-08-23 09:50:54 +00:00
|
|
|
// Disable compilation cache to force compilation in both cases
|
|
|
|
v8::internal::FLAG_compilation_cache = false;
|
|
|
|
|
2018-03-07 22:33:10 +00:00
|
|
|
// Suppress randomness to avoid flakiness in tests.
|
|
|
|
v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
|
|
|
|
|
|
|
|
// stress_incremental_marking adds randomness to the test.
|
|
|
|
v8::internal::FLAG_stress_incremental_marking = false;
|
|
|
|
|
2018-08-23 09:50:54 +00:00
|
|
|
// warmup compilation
|
|
|
|
CompileRun(simple_sampling_heap_profiler_script);
|
|
|
|
|
2018-03-07 22:33:10 +00:00
|
|
|
int count_1024 = 0;
|
|
|
|
{
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(1024);
|
|
|
|
CompileRun(simple_sampling_heap_profiler_script);
|
|
|
|
|
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
|
|
|
heap_profiler->GetAllocationProfile());
|
|
|
|
CHECK(profile);
|
|
|
|
|
2018-04-20 12:13:30 +00:00
|
|
|
const char* path_to_foo[] = {"", "foo"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node_foo = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2018-04-20 12:13:30 +00:00
|
|
|
ArrayVector(path_to_foo));
|
|
|
|
CHECK(node_foo);
|
|
|
|
const char* path_to_bar[] = {"", "foo", "bar"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node_bar = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2018-04-20 12:13:30 +00:00
|
|
|
ArrayVector(path_to_bar));
|
2018-03-07 22:33:10 +00:00
|
|
|
CHECK(node_bar);
|
|
|
|
|
2018-04-20 12:13:30 +00:00
|
|
|
// Function bar can be inlined in foo.
|
|
|
|
count_1024 = NumberOfAllocations(node_foo) + NumberOfAllocations(node_bar);
|
2018-03-07 22:33:10 +00:00
|
|
|
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sampling at a higher rate should give us similar numbers of objects.
|
|
|
|
{
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(128);
|
|
|
|
CompileRun(simple_sampling_heap_profiler_script);
|
|
|
|
|
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
|
|
|
heap_profiler->GetAllocationProfile());
|
|
|
|
CHECK(profile);
|
|
|
|
|
2018-04-20 12:13:30 +00:00
|
|
|
const char* path_to_foo[] = {"", "foo"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node_foo = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2018-04-20 12:13:30 +00:00
|
|
|
ArrayVector(path_to_foo));
|
|
|
|
CHECK(node_foo);
|
|
|
|
const char* path_to_bar[] = {"", "foo", "bar"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node_bar = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2018-04-20 12:13:30 +00:00
|
|
|
ArrayVector(path_to_bar));
|
2018-03-07 22:33:10 +00:00
|
|
|
CHECK(node_bar);
|
|
|
|
|
2018-04-20 12:13:30 +00:00
|
|
|
// Function bar can be inlined in foo.
|
|
|
|
int count_128 =
|
|
|
|
NumberOfAllocations(node_foo) + NumberOfAllocations(node_bar);
|
2018-03-07 22:33:10 +00:00
|
|
|
|
|
|
|
// We should have similar unsampled counts of allocations. Though
|
|
|
|
// we will sample different numbers of objects at different rates,
|
|
|
|
// the unsampling process should produce similar final estimates
|
|
|
|
// at the true number of allocations. However, the process to
|
|
|
|
// determine these unsampled counts is probabilisitic so we need to
|
|
|
|
// account for error.
|
|
|
|
double max_count = std::max(count_128, count_1024);
|
|
|
|
double min_count = std::min(count_128, count_1024);
|
|
|
|
double percent_difference = (max_count - min_count) / min_count;
|
2018-04-20 12:13:30 +00:00
|
|
|
CHECK_LT(percent_difference, 0.1);
|
2018-03-07 22:33:10 +00:00
|
|
|
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
|
|
|
}
|
2016-01-22 16:36:40 +00:00
|
|
|
|
|
|
|
TEST(SamplingHeapProfilerApiAllocation) {
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2016-01-22 16:36:40 +00:00
|
|
|
LocalContext env;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
// Suppress randomness to avoid flakiness in tests.
|
|
|
|
v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
|
|
|
|
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(256);
|
|
|
|
|
|
|
|
for (int i = 0; i < 8 * 1024; ++i) v8::Object::New(env->GetIsolate());
|
|
|
|
|
2016-07-25 11:12:42 +00:00
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
2016-01-22 16:36:40 +00:00
|
|
|
heap_profiler->GetAllocationProfile());
|
2016-07-25 11:12:42 +00:00
|
|
|
CHECK(profile);
|
2016-01-22 16:36:40 +00:00
|
|
|
const char* names[] = {"(V8 API)"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2017-08-24 21:49:48 +00:00
|
|
|
ArrayVector(names));
|
2016-01-22 16:36:40 +00:00
|
|
|
CHECK(node);
|
|
|
|
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
2016-04-13 15:11:02 +00:00
|
|
|
|
2018-10-18 16:50:33 +00:00
|
|
|
TEST(SamplingHeapProfilerApiSamples) {
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2018-10-18 16:50:33 +00:00
|
|
|
LocalContext env;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
// Suppress randomness to avoid flakiness in tests.
|
|
|
|
v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
|
|
|
|
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(1024);
|
|
|
|
|
|
|
|
size_t count = 8 * 1024;
|
|
|
|
for (size_t i = 0; i < count; ++i) v8::Object::New(env->GetIsolate());
|
|
|
|
|
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
|
|
|
heap_profiler->GetAllocationProfile());
|
|
|
|
CHECK(profile);
|
|
|
|
|
|
|
|
std::vector<v8::AllocationProfile::Node*> nodes_to_visit;
|
|
|
|
std::unordered_set<uint32_t> node_ids;
|
|
|
|
nodes_to_visit.push_back(profile->GetRootNode());
|
|
|
|
while (!nodes_to_visit.empty()) {
|
|
|
|
v8::AllocationProfile::Node* node = nodes_to_visit.back();
|
|
|
|
nodes_to_visit.pop_back();
|
|
|
|
CHECK_LT(0, node->node_id);
|
|
|
|
CHECK_EQ(0, node_ids.count(node->node_id));
|
|
|
|
node_ids.insert(node->node_id);
|
|
|
|
nodes_to_visit.insert(nodes_to_visit.end(), node->children.begin(),
|
|
|
|
node->children.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t total_size = 0;
|
|
|
|
std::unordered_set<uint64_t> samples_set;
|
|
|
|
for (auto& sample : profile->GetSamples()) {
|
|
|
|
total_size += sample.size * sample.count;
|
|
|
|
CHECK_EQ(0, samples_set.count(sample.sample_id));
|
|
|
|
CHECK_EQ(1, node_ids.count(sample.node_id));
|
|
|
|
CHECK_GT(sample.node_id, 0);
|
|
|
|
CHECK_GT(sample.sample_id, 0);
|
|
|
|
samples_set.insert(sample.sample_id);
|
|
|
|
}
|
|
|
|
size_t object_size = total_size / count;
|
|
|
|
CHECK_GE(object_size, sizeof(void*) * 2);
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
|
|
|
|
2016-04-13 15:11:02 +00:00
|
|
|
TEST(SamplingHeapProfilerLeftTrimming) {
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2016-04-13 15:11:02 +00:00
|
|
|
LocalContext env;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
// Suppress randomness to avoid flakiness in tests.
|
|
|
|
v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
|
|
|
|
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(64);
|
|
|
|
|
|
|
|
CompileRun(
|
|
|
|
"for (var j = 0; j < 500; ++j) {\n"
|
|
|
|
" var a = [];\n"
|
|
|
|
" for (var i = 0; i < 5; ++i)\n"
|
|
|
|
" a[i] = i;\n"
|
|
|
|
" for (var i = 0; i < 3; ++i)\n"
|
|
|
|
" a.shift();\n"
|
|
|
|
"}\n");
|
|
|
|
|
2016-09-07 10:02:58 +00:00
|
|
|
CcTest::CollectGarbage(v8::internal::NEW_SPACE);
|
2016-04-13 15:11:02 +00:00
|
|
|
// Should not crash.
|
|
|
|
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
2017-09-23 13:55:40 +00:00
|
|
|
|
|
|
|
TEST(SamplingHeapProfilerPretenuredInlineAllocations) {
|
|
|
|
i::FLAG_allow_natives_syntax = true;
|
|
|
|
i::FLAG_expose_gc = true;
|
|
|
|
|
|
|
|
CcTest::InitializeVM();
|
|
|
|
if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
|
|
|
|
if (i::FLAG_gc_global || i::FLAG_stress_compaction ||
|
2020-09-03 14:30:07 +00:00
|
|
|
i::FLAG_stress_incremental_marking ||
|
|
|
|
i::FLAG_stress_concurrent_allocation) {
|
2017-09-23 13:55:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2017-09-23 13:55:40 +00:00
|
|
|
LocalContext env;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
// Suppress randomness to avoid flakiness in tests.
|
|
|
|
v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
|
|
|
|
|
2020-09-24 15:27:07 +00:00
|
|
|
GrowNewSpaceToMaximumCapacity(CcTest::heap());
|
2017-09-23 13:55:40 +00:00
|
|
|
|
|
|
|
i::ScopedVector<char> source(1024);
|
|
|
|
i::SNPrintF(source,
|
|
|
|
"var number_elements = %d;"
|
|
|
|
"var elements = new Array(number_elements);"
|
|
|
|
"function f() {"
|
|
|
|
" for (var i = 0; i < number_elements; i++) {"
|
|
|
|
" elements[i] = [{}, {}, {}];"
|
|
|
|
" }"
|
|
|
|
" return elements[number_elements - 1];"
|
|
|
|
"};"
|
2019-04-30 11:04:41 +00:00
|
|
|
"%%PrepareFunctionForOptimization(f);"
|
2017-09-23 13:55:40 +00:00
|
|
|
"f(); gc();"
|
|
|
|
"f(); f();"
|
|
|
|
"%%OptimizeFunctionOnNextCall(f);"
|
|
|
|
"f();"
|
|
|
|
"f;",
|
|
|
|
i::AllocationSite::kPretenureMinimumCreated + 1);
|
|
|
|
|
|
|
|
v8::Local<v8::Function> f =
|
2019-04-29 11:06:49 +00:00
|
|
|
v8::Local<v8::Function>::Cast(CompileRun(source.begin()));
|
2017-09-23 13:55:40 +00:00
|
|
|
|
|
|
|
// Make sure the function is producing pre-tenured objects.
|
2017-10-13 16:33:03 +00:00
|
|
|
auto res = f->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked();
|
2017-09-23 13:55:40 +00:00
|
|
|
i::Handle<i::JSObject> o = i::Handle<i::JSObject>::cast(
|
|
|
|
v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(res)));
|
|
|
|
CHECK(CcTest::heap()->InOldSpace(o->elements()));
|
|
|
|
CHECK(CcTest::heap()->InOldSpace(*o));
|
|
|
|
|
|
|
|
// Call the function and profile it.
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(64);
|
2017-11-09 10:51:12 +00:00
|
|
|
for (int i = 0; i < 80; ++i) {
|
2017-10-13 16:33:03 +00:00
|
|
|
f->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked();
|
2017-09-23 13:55:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
|
|
|
heap_profiler->GetAllocationProfile());
|
|
|
|
CHECK(profile);
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
|
|
|
|
const char* names[] = {"f"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node_f = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2017-09-23 13:55:40 +00:00
|
|
|
ArrayVector(names));
|
|
|
|
CHECK(node_f);
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
for (auto allocation : node_f->allocations) {
|
|
|
|
count += allocation.count;
|
|
|
|
}
|
|
|
|
|
2017-11-09 10:51:12 +00:00
|
|
|
CHECK_GE(count, 8000);
|
2017-09-23 13:55:40 +00:00
|
|
|
}
|
2017-11-30 00:51:34 +00:00
|
|
|
|
|
|
|
TEST(SamplingHeapProfilerLargeInterval) {
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2017-11-30 00:51:34 +00:00
|
|
|
LocalContext env;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
// Suppress randomness to avoid flakiness in tests.
|
|
|
|
v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
|
|
|
|
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(512 * 1024);
|
|
|
|
|
|
|
|
for (int i = 0; i < 8 * 1024; ++i) {
|
|
|
|
CcTest::i_isolate()->factory()->NewFixedArray(1024);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
|
|
|
heap_profiler->GetAllocationProfile());
|
|
|
|
CHECK(profile);
|
|
|
|
const char* names[] = {"(EXTERNAL)"};
|
2019-09-10 01:19:59 +00:00
|
|
|
auto node = FindAllocationProfileNode(env->GetIsolate(), profile.get(),
|
2017-11-30 00:51:34 +00:00
|
|
|
ArrayVector(names));
|
|
|
|
CHECK(node);
|
|
|
|
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
2018-01-18 21:22:55 +00:00
|
|
|
|
|
|
|
TEST(HeapSnapshotPrototypeNotJSReceiver) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
CompileRun(
|
|
|
|
"function object() {}"
|
|
|
|
"object.prototype = 42;");
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
}
|
2018-01-22 19:16:21 +00:00
|
|
|
|
|
|
|
TEST(SamplingHeapProfilerSampleDuringDeopt) {
|
|
|
|
i::FLAG_allow_natives_syntax = true;
|
|
|
|
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2018-01-22 19:16:21 +00:00
|
|
|
LocalContext env;
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
// Suppress randomness to avoid flakiness in tests.
|
|
|
|
v8::internal::FLAG_sampling_heap_profiler_suppress_randomness = true;
|
|
|
|
|
|
|
|
// Small sample interval to force each object to be sampled.
|
2018-12-19 19:10:21 +00:00
|
|
|
heap_profiler->StartSamplingHeapProfiler(i::kTaggedSize);
|
2018-01-22 19:16:21 +00:00
|
|
|
|
|
|
|
// Lazy deopt from runtime call from inlined callback function.
|
|
|
|
const char* source =
|
|
|
|
"var b = "
|
|
|
|
" [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];"
|
|
|
|
"(function f() {"
|
|
|
|
" var result = 0;"
|
|
|
|
" var lazyDeopt = function(deopt) {"
|
|
|
|
" var callback = function(v,i,o) {"
|
|
|
|
" result += i;"
|
|
|
|
" if (i == 13 && deopt) {"
|
|
|
|
" %DeoptimizeNow();"
|
|
|
|
" }"
|
|
|
|
" return v;"
|
|
|
|
" };"
|
|
|
|
" b.map(callback);"
|
|
|
|
" };"
|
2019-04-30 11:04:41 +00:00
|
|
|
" %PrepareFunctionForOptimization(lazyDeopt);"
|
2018-01-22 19:16:21 +00:00
|
|
|
" lazyDeopt();"
|
|
|
|
" lazyDeopt();"
|
|
|
|
" %OptimizeFunctionOnNextCall(lazyDeopt);"
|
|
|
|
" lazyDeopt();"
|
|
|
|
" lazyDeopt(true);"
|
|
|
|
" lazyDeopt();"
|
|
|
|
"})();";
|
|
|
|
|
|
|
|
CompileRun(source);
|
|
|
|
// Should not crash.
|
|
|
|
|
|
|
|
std::unique_ptr<v8::AllocationProfile> profile(
|
|
|
|
heap_profiler->GetAllocationProfile());
|
|
|
|
CHECK(profile);
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
2018-03-05 13:18:51 +00:00
|
|
|
|
|
|
|
TEST(WeakReference) {
|
|
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
|
|
i::Isolate* i_isolate = CcTest::i_isolate();
|
|
|
|
i::Factory* factory = i_isolate->factory();
|
|
|
|
i::HandleScope scope(i_isolate);
|
|
|
|
LocalContext env;
|
|
|
|
|
|
|
|
// Create a FeedbackVector.
|
|
|
|
v8::Local<v8::Script> script =
|
2020-03-09 10:41:45 +00:00
|
|
|
v8::Script::Compile(
|
|
|
|
isolate->GetCurrentContext(),
|
|
|
|
v8::String::NewFromUtf8Literal(isolate, "function foo() {}"))
|
2018-03-05 13:18:51 +00:00
|
|
|
.ToLocalChecked();
|
|
|
|
v8::MaybeLocal<v8::Value> value = script->Run(isolate->GetCurrentContext());
|
|
|
|
CHECK(!value.IsEmpty());
|
|
|
|
|
|
|
|
i::Handle<i::Object> obj = v8::Utils::OpenHandle(*script);
|
|
|
|
i::Handle<i::SharedFunctionInfo> shared_function =
|
2019-05-23 07:47:44 +00:00
|
|
|
i::Handle<i::SharedFunctionInfo>(i::JSFunction::cast(*obj).shared(),
|
2018-06-23 09:05:50 +00:00
|
|
|
i_isolate);
|
2019-03-25 15:05:47 +00:00
|
|
|
i::Handle<i::ClosureFeedbackCellArray> feedback_cell_array =
|
|
|
|
i::ClosureFeedbackCellArray::New(i_isolate, shared_function);
|
|
|
|
i::Handle<i::FeedbackVector> fv =
|
|
|
|
factory->NewFeedbackVector(shared_function, feedback_cell_array);
|
2018-03-05 13:18:51 +00:00
|
|
|
|
|
|
|
// Create a Code.
|
2019-01-16 18:29:52 +00:00
|
|
|
i::Assembler assm(i::AssemblerOptions{});
|
2018-03-05 13:18:51 +00:00
|
|
|
assm.nop(); // supported on all architectures
|
|
|
|
i::CodeDesc desc;
|
|
|
|
assm.GetCode(i_isolate, &desc);
|
|
|
|
i::Handle<i::Code> code =
|
Reland "Reland "[deoptimizer] Change deopt entries into builtins""
This is a reland of fbfa9bf4ec72b1b73a96b70ccb68cd98c321511b
The arm64 was missing proper codegen for CFI, thus sizes were off.
Original change's description:
> Reland "[deoptimizer] Change deopt entries into builtins"
>
> This is a reland of 7f58ced72eb65b6b5530ccabaf2eaebe45bf9d33
>
> It fixes the different exit size emitted on x64/Atom CPUs due to
> performance tuning in TurboAssembler::Call. Additionally, add
> cctests to verify the fixed size exits.
>
> Original change's description:
> > [deoptimizer] Change deopt entries into builtins
> >
> > While the overall goal of this commit is to change deoptimization
> > entries into builtins, there are multiple related things happening:
> >
> > - Deoptimization entries, formerly stubs (i.e. Code objects generated
> > at runtime, guaranteed to be immovable), have been converted into
> > builtins. The major restriction is that we now need to preserve the
> > kRootRegister, which was formerly used on most architectures to pass
> > the deoptimization id. The solution differs based on platform.
> > - Renamed DEOPT_ENTRIES_OR_FOR_TESTING code kind to FOR_TESTING.
> > - Removed heap/ support for immovable Code generation.
> > - Removed the DeserializerData class (no longer needed).
> > - arm64: to preserve 4-byte deopt exits, introduced a new optimization
> > in which the final jump to the deoptimization entry is generated
> > once per Code object, and deopt exits can continue to emit a
> > near-call.
> > - arm,ia32,x64: change to fixed-size deopt exits. This reduces exit
> > sizes by 4/8, 5, and 5 bytes, respectively.
> >
> > On arm the deopt exit size is reduced from 12 (or 16) bytes to 8 bytes
> > by using the same strategy as on arm64 (recalc deopt id from return
> > address). Before:
> >
> > e300a002 movw r10, <id>
> > e59fc024 ldr ip, [pc, <entry offset>]
> > e12fff3c blx ip
> >
> > After:
> >
> > e59acb35 ldr ip, [r10, <entry offset>]
> > e12fff3c blx ip
> >
> > On arm64 the deopt exit size remains 4 bytes (or 8 bytes in same cases
> > with CFI). Additionally, up to 4 builtin jumps are emitted per Code
> > object (max 32 bytes added overhead per Code object). Before:
> >
> > 9401cdae bl <entry offset>
> >
> > After:
> >
> > # eager deoptimization entry jump.
> > f95b1f50 ldr x16, [x26, <eager entry offset>]
> > d61f0200 br x16
> > # lazy deoptimization entry jump.
> > f95b2b50 ldr x16, [x26, <lazy entry offset>]
> > d61f0200 br x16
> > # the deopt exit.
> > 97fffffc bl <eager deoptimization entry jump offset>
> >
> > On ia32 the deopt exit size is reduced from 10 to 5 bytes. Before:
> >
> > bb00000000 mov ebx,<id>
> > e825f5372b call <entry>
> >
> > After:
> >
> > e8ea2256ba call <entry>
> >
> > On x64 the deopt exit size is reduced from 12 to 7 bytes. Before:
> >
> > 49c7c511000000 REX.W movq r13,<id>
> > e8ea2f0700 call <entry>
> >
> > After:
> >
> > 41ff9560360000 call [r13+<entry offset>]
> >
> > Bug: v8:8661,v8:8768
> > Change-Id: I13e30aedc360474dc818fecc528ce87c3bfeed42
> > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2465834
> > Commit-Queue: Jakob Gruber <jgruber@chromium.org>
> > Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
> > Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#70597}
>
> Tbr: ulan@chromium.org, tebbi@chromium.org, rmcilroy@chromium.org
> Bug: v8:8661,v8:8768,chromium:1140165
> Change-Id: Ibcd5c39c58a70bf2b2ac221aa375fc68d495e144
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2485506
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> Commit-Queue: Jakob Gruber <jgruber@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#70655}
Tbr: ulan@chromium.org, tebbi@chromium.org, rmcilroy@chromium.org
Bug: v8:8661
Bug: v8:8768
Bug: chromium:1140165
Change-Id: I471cc94fc085e527dc9bfb5a84b96bd907c2333f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2488682
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70672}
2020-10-21 05:12:25 +00:00
|
|
|
i::Factory::CodeBuilder(i_isolate, desc, i::CodeKind::FOR_TESTING)
|
2020-09-30 11:28:28 +00:00
|
|
|
.Build();
|
2018-03-05 13:18:51 +00:00
|
|
|
CHECK(code->IsCode());
|
|
|
|
|
2021-02-19 13:32:39 +00:00
|
|
|
fv->set_maybe_optimized_code(i::HeapObjectReference::Weak(*code),
|
|
|
|
v8::kReleaseStore);
|
2020-10-28 10:16:29 +00:00
|
|
|
fv->set_flags(i::FeedbackVector::OptimizationTierBits::encode(
|
|
|
|
i::OptimizationTier::kTopTier) |
|
|
|
|
i::FeedbackVector::OptimizationMarkerBits::encode(
|
|
|
|
i::OptimizationMarker::kNone));
|
2018-03-05 13:18:51 +00:00
|
|
|
|
|
|
|
v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
|
|
|
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
|
|
|
|
CHECK(ValidateSnapshot(snapshot));
|
|
|
|
}
|
2018-10-30 09:33:26 +00:00
|
|
|
|
|
|
|
TEST(Bug8373_1) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
heap_profiler->StartSamplingHeapProfiler(100);
|
|
|
|
|
|
|
|
heap_profiler->TakeHeapSnapshot();
|
|
|
|
// Causes the StringsStorage to be deleted.
|
|
|
|
heap_profiler->DeleteAllHeapSnapshots();
|
|
|
|
|
|
|
|
// Triggers an allocation sample that tries to use the StringsStorage.
|
|
|
|
for (int i = 0; i < 2 * 1024; ++i) {
|
|
|
|
CompileRun(
|
|
|
|
"new Array(64);"
|
|
|
|
"new Uint8Array(16);");
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_profiler->StopSamplingHeapProfiler();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Bug8373_2) {
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
|
|
|
|
heap_profiler->StartTrackingHeapObjects(true);
|
|
|
|
|
|
|
|
heap_profiler->TakeHeapSnapshot();
|
|
|
|
// Causes the StringsStorage to be deleted.
|
|
|
|
heap_profiler->DeleteAllHeapSnapshots();
|
|
|
|
|
|
|
|
// Triggers an allocations that try to use the StringsStorage.
|
|
|
|
for (int i = 0; i < 2 * 1024; ++i) {
|
|
|
|
CompileRun(
|
|
|
|
"new Array(64);"
|
|
|
|
"new Uint8Array(16);");
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_profiler->StopTrackingHeapObjects();
|
|
|
|
}
|
2020-10-12 11:32:23 +00:00
|
|
|
|
|
|
|
TEST(HeapSnapshotDeleteDuringTakeSnapshot) {
|
|
|
|
// Check that a heap snapshot can be deleted during GC while another one
|
|
|
|
// is being taken.
|
|
|
|
|
|
|
|
LocalContext env;
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
|
|
|
int gc_calls = 0;
|
|
|
|
v8::Global<v8::Object> handle;
|
|
|
|
|
|
|
|
{
|
|
|
|
struct WeakData {
|
|
|
|
const v8::HeapSnapshot* snapshot;
|
|
|
|
int* gc_calls;
|
|
|
|
v8::Global<v8::Object>* handle;
|
|
|
|
};
|
|
|
|
WeakData* data =
|
|
|
|
new WeakData{heap_profiler->TakeHeapSnapshot(), &gc_calls, &handle};
|
|
|
|
|
|
|
|
v8::HandleScope scope(env->GetIsolate());
|
|
|
|
handle.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
|
|
|
|
handle.SetWeak(
|
|
|
|
data,
|
|
|
|
[](const v8::WeakCallbackInfo<WeakData>& data) {
|
|
|
|
std::unique_ptr<WeakData> weakdata{data.GetParameter()};
|
|
|
|
const_cast<v8::HeapSnapshot*>(weakdata->snapshot)->Delete();
|
|
|
|
++*weakdata->gc_calls;
|
|
|
|
weakdata->handle->Reset();
|
|
|
|
},
|
|
|
|
v8::WeakCallbackType::kParameter);
|
|
|
|
}
|
|
|
|
CHECK_EQ(gc_calls, 0);
|
|
|
|
|
|
|
|
CHECK(ValidateSnapshot(heap_profiler->TakeHeapSnapshot()));
|
|
|
|
CHECK_EQ(gc_calls, 1);
|
|
|
|
}
|