#include <ctype.h>
-#include "v8.h"
-
-#include "allocation-tracker.h"
-#include "cctest.h"
-#include "hashmap.h"
-#include "heap-profiler.h"
-#include "snapshot.h"
-#include "debug.h"
-#include "utils-inl.h"
-#include "../include/v8-profiler.h"
+#include "src/v8.h"
+
+#include "include/v8-profiler.h"
+#include "src/allocation-tracker.h"
+#include "src/debug.h"
+#include "src/hashmap.h"
+#include "src/heap-profiler.h"
+#include "src/snapshot.h"
+#include "src/utils-inl.h"
+#include "src/xdk-utils.h"
+#include "test/cctest/cctest.h"
using i::AllocationTraceNode;
using i::AllocationTraceTree;
CHECK_EQ(1, global->InternalFieldCount());
i::Factory* factory = CcTest::i_isolate()->factory();
- i::Handle<i::String> first =
- factory->NewStringFromAscii(i::CStrVector("0123456789"));
- i::Handle<i::String> second =
- factory->NewStringFromAscii(i::CStrVector("0123456789"));
- i::Handle<i::String> cons_string = factory->NewConsString(first, second);
+ i::Handle<i::String> first = factory->NewStringFromStaticChars("0123456789");
+ i::Handle<i::String> second = factory->NewStringFromStaticChars("0123456789");
+ i::Handle<i::String> cons_string =
+ factory->NewConsString(first, second).ToHandleChecked();
global->SetInternalField(0, v8::ToApiHandle<v8::String>(cons_string));
}
+TEST(HeapSnapshotSymbol) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun("a = Symbol('mySymbol');\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("Symbol"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* a =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "a");
+ CHECK_NE(NULL, a);
+ CHECK_EQ(a->GetType(), v8::HeapGraphNode::kSymbol);
+ CHECK_EQ(v8_str("symbol"), a->GetName());
+ const v8::HeapGraphNode* name =
+ GetProperty(a, v8::HeapGraphEdge::kInternal, "name");
+ CHECK_NE(NULL, name);
+ CHECK_EQ(v8_str("mySymbol"), name->GetName());
+}
+
+
+TEST(HeapSnapshotWeakCollection) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ 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");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("WeakCollections"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* k =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
+ CHECK_NE(NULL, k);
+ const v8::HeapGraphNode* v =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
+ CHECK_NE(NULL, v);
+ const v8::HeapGraphNode* s =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
+ CHECK_NE(NULL, s);
+
+ const v8::HeapGraphNode* ws =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "ws");
+ CHECK_NE(NULL, ws);
+ CHECK_EQ(v8::HeapGraphNode::kObject, ws->GetType());
+ CHECK_EQ(v8_str("WeakSet"), ws->GetName());
+
+ const v8::HeapGraphNode* ws_table =
+ GetProperty(ws, v8::HeapGraphEdge::kInternal, "table");
+ 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);
+ const v8::HeapGraphNode* ws_s =
+ GetProperty(ws, v8::HeapGraphEdge::kProperty, "str");
+ CHECK_NE(NULL, ws_s);
+ CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(ws_s->GetId()));
+
+ const v8::HeapGraphNode* wm =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "wm");
+ CHECK_NE(NULL, wm);
+ CHECK_EQ(v8::HeapGraphNode::kObject, wm->GetType());
+ CHECK_EQ(v8_str("WeakMap"), wm->GetName());
+
+ const v8::HeapGraphNode* wm_table =
+ GetProperty(wm, v8::HeapGraphEdge::kInternal, "table");
+ 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;
+ }
+ }
+ CHECK_EQ(2, weak_entries);
+ const v8::HeapGraphNode* wm_s =
+ GetProperty(wm, v8::HeapGraphEdge::kProperty, "str");
+ CHECK_NE(NULL, wm_s);
+ CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(wm_s->GetId()));
+}
+
+
+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");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("Collections"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* k =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "k");
+ CHECK_NE(NULL, k);
+ const v8::HeapGraphNode* v =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "v");
+ CHECK_NE(NULL, v);
+ const v8::HeapGraphNode* s =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "s");
+ CHECK_NE(NULL, s);
+
+ const v8::HeapGraphNode* set =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "set");
+ CHECK_NE(NULL, set);
+ CHECK_EQ(v8::HeapGraphNode::kObject, set->GetType());
+ CHECK_EQ(v8_str("Set"), set->GetName());
+
+ const v8::HeapGraphNode* set_table =
+ GetProperty(set, v8::HeapGraphEdge::kInternal, "table");
+ 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 =
+ GetProperty(set, v8::HeapGraphEdge::kProperty, "str");
+ CHECK_NE(NULL, set_s);
+ CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(set_s->GetId()));
+
+ const v8::HeapGraphNode* map =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "map");
+ CHECK_NE(NULL, map);
+ CHECK_EQ(v8::HeapGraphNode::kObject, map->GetType());
+ CHECK_EQ(v8_str("Map"), map->GetName());
+
+ const v8::HeapGraphNode* map_table =
+ GetProperty(map, v8::HeapGraphEdge::kInternal, "table");
+ 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 =
+ GetProperty(map, v8::HeapGraphEdge::kProperty, "str");
+ CHECK_NE(NULL, map_s);
+ CHECK_EQ(static_cast<int>(s->GetId()), static_cast<int>(map_s->GetId()));
+}
+
TEST(HeapSnapshotInternalReferences) {
v8::Isolate* isolate = CcTest::isolate();
if (abort_countdown_ == 0) return kAbort;
CHECK_GT(chars_written, 0);
i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
- i::OS::MemCopy(chunk.start(), buffer, chars_written);
+ i::MemCopy(chunk.start(), buffer, chars_written);
return kContinue;
}
virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) {
- ASSERT(false);
+ DCHECK(false);
return kAbort;
}
void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
int abort_countdown_;
};
-class AsciiResource: public v8::String::ExternalAsciiStringResource {
+class OneByteResource : public v8::String::ExternalOneByteStringResource {
public:
- explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
+ explicit OneByteResource(i::Vector<char> string) : data_(string.start()) {
length_ = string.length();
}
virtual const char* data() const { return data_; }
stream.WriteTo(json);
// Verify that snapshot string is valid JSON.
- AsciiResource* json_res = new AsciiResource(json);
+ OneByteResource* json_res = new OneByteResource(json);
v8::Local<v8::String> json_string =
v8::String::NewExternal(env->GetIsolate(), json_res);
env->Global()->Set(v8_str("json_snapshot"), json_string);
virtual ~TestStatsStream() {}
virtual void EndOfStream() { ++eos_signaled_; }
virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
- ASSERT(false);
+ DCHECK(false);
return kAbort;
}
virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* buffer,
int updates_written) {
++intervals_count_;
- ASSERT(updates_written);
+ DCHECK(updates_written);
updates_written_ += updates_written;
entries_count_ = 0;
if (first_interval_index_ == -1 && updates_written != 0)
const v8::HeapGraphNode* global_context =
GetProperty(global, v8::HeapGraphEdge::kInternal, "global_context");
CHECK_NE(NULL, global_context);
- const v8::HeapGraphNode* global_receiver =
- GetProperty(global, v8::HeapGraphEdge::kInternal, "global_receiver");
- CHECK_NE(NULL, global_receiver);
+ const v8::HeapGraphNode* global_proxy =
+ GetProperty(global, v8::HeapGraphEdge::kInternal, "global_proxy");
+ CHECK_NE(NULL, global_proxy);
}
v8::HandleScope scope(env->GetIsolate());
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };");
+ CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
const v8::HeapSnapshot* snapshot =
heap_profiler->TakeHeapSnapshot(v8_str("value"));
CHECK(ValidateSnapshot(snapshot));
CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId()));
const v8::HeapGraphNode* n_prop =
GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
- v8::Local<v8::Number> js_n_prop =
- js_obj->Get(v8_str("n_prop")).As<v8::Number>();
- CHECK(js_n_prop->NumberValue() ==
- heap_profiler->FindObjectById(n_prop->GetId())->NumberValue());
+ v8::Local<v8::String> js_n_prop =
+ js_obj->Get(v8_str("n_prop")).As<v8::String>();
+ CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId()));
}
"Constructor2", i::V8HeapExplorer::GetConstructorName(*js_obj2)));
v8::Local<v8::Object> obj3 = js_global->Get(v8_str("obj3")).As<v8::Object>();
i::Handle<i::JSObject> js_obj3 = v8::Utils::OpenHandle(*obj3);
- CHECK_EQ(0, StringCmp(
- "Constructor3", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
+ // TODO(verwaest): Restore to Constructor3 once supported by the
+ // heap-snapshot-generator.
+ CHECK_EQ(
+ 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj3)));
v8::Local<v8::Object> obj4 = js_global->Get(v8_str("obj4")).As<v8::Object>();
i::Handle<i::JSObject> js_obj4 = v8::Utils::OpenHandle(*obj4);
- CHECK_EQ(0, StringCmp(
- "Constructor4", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
+ // TODO(verwaest): Restore to Constructor4 once supported by the
+ // heap-snapshot-generator.
+ CHECK_EQ(
+ 0, StringCmp("Object", i::V8HeapExplorer::GetConstructorName(*js_obj4)));
v8::Local<v8::Object> obj5 = js_global->Get(v8_str("obj5")).As<v8::Object>();
i::Handle<i::JSObject> js_obj5 = v8::Utils::OpenHandle(*obj5);
CHECK_EQ(0, StringCmp(
}
+TEST(AccessorInfo) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun("function foo(x) { }\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("AccessorInfoTest"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* foo =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "foo");
+ CHECK_NE(NULL, foo);
+ const v8::HeapGraphNode* map =
+ GetProperty(foo, v8::HeapGraphEdge::kInternal, "map");
+ CHECK_NE(NULL, map);
+ const v8::HeapGraphNode* descriptors =
+ GetProperty(map, v8::HeapGraphEdge::kInternal, "descriptors");
+ CHECK_NE(NULL, descriptors);
+ const v8::HeapGraphNode* length_name =
+ GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "2");
+ CHECK_NE(NULL, length_name);
+ CHECK_EQ("length", *v8::String::Utf8Value(length_name->GetName()));
+ const v8::HeapGraphNode* length_accessor =
+ GetProperty(descriptors, v8::HeapGraphEdge::kInternal, "4");
+ CHECK_NE(NULL, length_accessor);
+ CHECK_EQ("system / ExecutableAccessorInfo",
+ *v8::String::Utf8Value(length_accessor->GetName()));
+ const v8::HeapGraphNode* name =
+ GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "name");
+ CHECK_NE(NULL, name);
+ const v8::HeapGraphNode* getter =
+ GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "getter");
+ CHECK_NE(NULL, getter);
+ const v8::HeapGraphNode* setter =
+ GetProperty(length_accessor, v8::HeapGraphEdge::kInternal, "setter");
+ CHECK_NE(NULL, setter);
+}
+
+
bool HasWeakEdge(const v8::HeapGraphNode* node) {
for (int i = 0; i < node->GetChildrenCount(); ++i) {
const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
}
-#ifdef ENABLE_DEBUGGER_SUPPORT
TEST(NoDebugObjectInSnapshot) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- CcTest::i_isolate()->debug()->Load();
+ CHECK(CcTest::i_isolate()->debug()->Load());
CompileRun("foo = {};");
const v8::HeapSnapshot* snapshot =
heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
}
CHECK_EQ(1, globals_count);
}
-#endif // ENABLE_DEBUGGER_SUPPORT
TEST(AllStrongGcRootsHaveNames) {
// ... well check just every 15th because otherwise it's too slow in debug.
for (int i = 0; i < num_objects - 1; i += 15) {
i::EmbeddedVector<char, 100> var_name;
- i::OS::SNPrintF(var_name, "f_%d", i);
+ i::SNPrintF(var_name, "f_%d", i);
const v8::HeapGraphNode* f_object = GetProperty(
context_object, v8::HeapGraphEdge::kContextVariable, var_name.start());
CHECK_NE(NULL, f_object);
v8::String::Utf8Value edge_name(edge->GetName());
v8::String::Utf8Value node_name(to_node->GetName());
i::EmbeddedVector<char, 100> name;
- i::OS::SNPrintF(name, "%s::%s", *edge_name, *node_name);
+ i::SNPrintF(name, "%s::%s", *edge_name, *node_name);
if (strstr(name.start(), path[current_depth])) {
node = to_node;
break;
"::(ArraySingleArgumentConstructorStub code)"
};
const v8::HeapGraphNode* node = GetNodeByPath(snapshot,
- stub_path, ARRAY_SIZE(stub_path));
+ stub_path, arraysize(stub_path));
CHECK_NE(NULL, node);
const char* builtin_path1[] = {
"::(Builtins)",
"::(KeyedLoadIC_Generic builtin)"
};
- node = GetNodeByPath(snapshot, builtin_path1, ARRAY_SIZE(builtin_path1));
+ node = GetNodeByPath(snapshot, builtin_path1, arraysize(builtin_path1));
CHECK_NE(NULL, node);
- const char* builtin_path2[] = {
- "::(GC roots)",
- "::(Builtins)",
- "::(CompileUnoptimized builtin)"
- };
- node = GetNodeByPath(snapshot, builtin_path2, ARRAY_SIZE(builtin_path2));
+ const char* builtin_path2[] = {"::(GC roots)", "::(Builtins)",
+ "::(CompileLazy builtin)"};
+ node = GetNodeByPath(snapshot, builtin_path2, arraysize(builtin_path2));
CHECK_NE(NULL, node);
v8::String::Utf8Value node_name(node->GetName());
- CHECK_EQ("(CompileUnoptimized builtin)", *node_name);
+ CHECK_EQ("(CompileLazy builtin)", *node_name);
}
"for (var i = 0; i < 3; ++i)\n"
" a.shift();\n");
- const char* names[] = { "(anonymous function)" };
+ const char* names[] = {""};
AllocationTracker* tracker =
reinterpret_cast<i::HeapProfiler*>(heap_profiler)->allocation_tracker();
CHECK_NE(NULL, tracker);
tracker->trace_tree()->Print(tracker);
AllocationTraceNode* node =
- FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names)));
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
CHECK_NE(NULL, node);
CHECK_GE(node->allocation_count(), 2);
CHECK_GE(node->allocation_size(), 4 * 5);
// Print for better diagnostics in case of failure.
tracker->trace_tree()->Print(tracker);
- const char* names[] =
- { "(anonymous function)", "start", "f_0_0", "f_0_1", "f_0_2" };
+ const char* names[] = {"", "start", "f_0_0", "f_0_1", "f_0_2"};
AllocationTraceNode* node =
- FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names)));
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
CHECK_NE(NULL, node);
CHECK_GE(node->allocation_count(), 100);
CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
LocalContext env;
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- const char* names[] = { "(anonymous function)", "start", "f_0", "f_1" };
+ const char* names[] = {"", "start", "f_0", "f_1"};
// First check that normally all allocations are recorded.
{
heap_profiler->StartTrackingHeapObjects(true);
tracker->trace_tree()->Print(tracker);
AllocationTraceNode* node =
- FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names)));
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
CHECK_NE(NULL, node);
CHECK_GE(node->allocation_count(), 100);
CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
tracker->trace_tree()->Print(tracker);
AllocationTraceNode* node =
- FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names)));
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
CHECK_NE(NULL, node);
CHECK_LT(node->allocation_count(), 100);
tracker->trace_tree()->Print(tracker);
AllocationTraceNode* node =
- FindNode(tracker, Vector<const char*>(names, ARRAY_SIZE(names)));
+ FindNode(tracker, Vector<const char*>(names, arraysize(names)));
CHECK_NE(NULL, node);
CHECK_GE(node->allocation_count(), 2);
CHECK_GE(node->allocation_size(), 4 * node->allocation_count());
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
void* data = ab_contents.Data();
- ASSERT(data != NULL);
+ DCHECK(data != NULL);
v8::Local<v8::ArrayBuffer> ab2 =
v8::ArrayBuffer::New(isolate, data, ab_contents.ByteLength());
CHECK(ab2->IsExternal());
v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
i::Factory* factory = CcTest::i_isolate()->factory();
- i::Handle<i::String> string =
- factory->NewStringFromAscii(i::CStrVector("string"));
+ i::Handle<i::String> string = factory->NewStringFromStaticChars("string");
i::Handle<i::Object> box = factory->NewBox(string);
global->Set(0, v8::ToApiHandle<v8::Object>(box));
}
+TEST(WeakContainers) {
+ i::FLAG_allow_natives_syntax = true;
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ if (!CcTest::i_isolate()->use_crankshaft()) return;
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ CompileRun(
+ "function foo(a) { return a.x; }\n"
+ "obj = {x : 123};\n"
+ "foo(obj);\n"
+ "foo(obj);\n"
+ "%OptimizeFunctionOnNextCall(foo);\n"
+ "foo(obj);\n");
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ const v8::HeapGraphNode* obj =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "obj");
+ CHECK_NE(NULL, obj);
+ const v8::HeapGraphNode* map =
+ GetProperty(obj, v8::HeapGraphEdge::kInternal, "map");
+ CHECK_NE(NULL, map);
+ const v8::HeapGraphNode* dependent_code =
+ GetProperty(map, v8::HeapGraphEdge::kInternal, "dependent_code");
+ 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);
+ CHECK_EQ(v8::HeapGraphEdge::kWeak, prop->GetType());
+ }
+}
+
+
static inline i::Address ToAddress(int n) {
return reinterpret_cast<i::Address>(n);
}
CHECK_EQ(0, static_cast<int>(map.size()));
CHECK_EQ(0, map.GetTraceNodeId(ToAddress(0x400)));
}
+
+struct TestObjectInfo {
+ std::vector<std::string> bu_call_stack_;
+ std::string type_;
+ unsigned number_of_objects_;
+};
+
+struct TestFrameInfo {
+ unsigned frame_id_;
+ unsigned callsite_;
+ unsigned parent_;
+};
+
+struct Chunk {
+ unsigned time_begin_;
+ unsigned time_end_;
+ unsigned frame_id_;
+ unsigned type_id_;
+ unsigned size_;
+ unsigned number_of_objects_;
+};
+
+class XDKHPOutputChecker {
+ public:
+ // If info.number_of_objects_ is not eq 0, then it participates in the search
+ // and we look for the record by 3 parameters. In other case we look for the
+ // chunk by call stack and type id only
+ bool checkObjectsExists(const TestObjectInfo& info, std::string chunk) {
+ std::vector<Chunk> chunks = parseChunk(chunk);
+ // look for the frame id, which correspond to the passed stack
+ // get the type id:
+ std::vector<unsigned> frames = findFrame(info.bu_call_stack_);
+ unsigned type_id = types_[info.type_];
+ for (size_t i = 0; i < chunks.size(); i++) {
+ for (size_t j = 0; j < frames.size(); j++) {
+ if (chunks[i].frame_id_ == frames[j] && chunks[i].type_id_ == type_id &&
+ (info.number_of_objects_ ? chunks[i].number_of_objects_ ==
+ info.number_of_objects_ : true)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ void parse(const char* symbols, const char* frames, const char* types) {
+ std::string symbols_std_ = symbols;
+ std::string frames_std_ = frames;
+ std::string types_std_ = types;
+ {
+ // parse symbols, don't care of line and column
+ size_t s1_pos = 0, s2_pos = 0;
+ int sym_id;
+ std::string function_name;
+ while (s2_pos != symbols_std_.npos) {
+ // look for the \n symbol
+ // format: symId, funcId, funcName, line, column
+ s2_pos = symbols_std_.find("\n", s1_pos);
+ if (s2_pos != symbols_std_.npos) {
+ int sym_id_e = symbols_std_.find(",", s1_pos);
+ std::string sym_id_s = symbols_std_.substr(s1_pos, sym_id_e - s1_pos);
+ sym_id = atoi(sym_id_s.c_str());
+ int func_id_e = symbols_std_.find(",", sym_id_e + 1);
+ int finc_name_e = symbols_std_.find(",", func_id_e + 1);
+ function_name = symbols_std_.substr(func_id_e + 1, finc_name_e -
+ func_id_e - 1);
+ symbols_[function_name] = sym_id;
+ s1_pos = s2_pos + 1;
+ }
+ }
+ }
+ {
+ // parse types
+ size_t s1_pos = 0, s2_pos = 0;
+ unsigned type_id;
+ std::string type_name;
+ while (s2_pos != types_std_.npos) {
+ // look for the \n symbol
+ // format: typeId, typeName
+ s2_pos = types_std_.find("\n", s1_pos);
+ if (s2_pos != types_std_.npos) {
+ int type_id_e = types_std_.find(",", s1_pos);
+ std::string sym_id_s = types_std_.substr(s1_pos, type_id_e - s1_pos);
+ type_id = atoi(sym_id_s.c_str());
+ type_name = types_std_.substr(type_id_e + 1, s2_pos - type_id_e - 1);
+
+ types_[type_name] = type_id;
+ s1_pos = s2_pos + 1;
+ }
+ }
+ }
+ {
+ // parse frames
+ size_t s1_pos = 0, s2_pos = 0;
+ int frame_id, symbol_id, parent_id;
+ while (s2_pos != frames_std_.npos) {
+ // look for the \n symbol
+ // format: frameId, symbolId, parentId
+ s2_pos = frames_std_.find("\n", s1_pos);
+ if (s2_pos != frames_std_.npos) {
+ int frame_id_e = frames_std_.find(",", s1_pos);
+ std::string frame_id_s = frames_std_.substr(s1_pos,
+ frame_id_e - s1_pos);
+ frame_id = atoi(frame_id_s.c_str());
+
+ int symb_id_e = frames_std_.find(",", frame_id_e + 1);
+ std::string symb_id_s = frames_std_.substr(frame_id_e + 1,
+ symb_id_e - frame_id_e - 1);
+ symbol_id = atoi(symb_id_s.c_str());
+
+ int parent_id_e = frames_std_.find(",", symb_id_e + 1);
+ std::string parent_id_s = frames_std_.substr(symb_id_e + 1,
+ s2_pos - parent_id_e - 1);
+ parent_id = atoi(parent_id_s.c_str());
+ TestFrameInfo info;
+ info.callsite_ = symbol_id;
+ info.frame_id_ = frame_id;
+ info.parent_ = parent_id;
+ frames_.push_back(info);
+ s1_pos = s2_pos + 1;
+ }
+ }
+ }
+ }
+
+ std::vector<Chunk> parseChunk(const std::string& chunk_std) {
+ std::vector<Chunk> chunks;
+ {
+ // parse chunks
+ size_t s1_pos = 0, s2_pos = 0;
+ unsigned time_begin, time_end, frame_id, type_id, size, number_of_objects;
+
+ while (s2_pos != chunk_std.npos) {
+ // look for the \n symbol
+ // format: frameId, symbolId, parentId
+ s2_pos = chunk_std.find("\n", s1_pos);
+ if (s2_pos != chunk_std.npos) {
+ int c1_e = chunk_std.find(",", s1_pos);
+ std::string c1_s = chunk_std.substr(s1_pos, c1_e - s1_pos);
+ time_begin = atoi(c1_s.c_str());
+
+ int c2_e = chunk_std.find(",", c1_e + 1);
+ std::string c2_s = chunk_std.substr(c1_e + 1, c2_e - c1_e - 1);
+ time_end = atoi(c2_s.c_str());
+
+ int c3_e = chunk_std.find(",", c2_e + 1);
+ std::string c3_s = chunk_std.substr(c2_e + 1, c3_e - c2_e - 1);
+ frame_id = atoi(c3_s.c_str());
+
+ int c4_e = chunk_std.find(",", c3_e + 1);
+ std::string c4_s = chunk_std.substr(c3_e + 1, c4_e - c3_e - 1);
+ type_id = atoi(c4_s.c_str());
+
+ int c5_e = chunk_std.find(",", c4_e + 1);
+ std::string c5_s = chunk_std.substr(c4_e + 1, c5_e - c4_e - 1);
+ size = atoi(c5_s.c_str());
+
+ int c6_e = chunk_std.find(",", c5_e + 1);
+ std::string c6_s = chunk_std.substr(c5_e + 1, c6_e - c5_e - 1);
+ number_of_objects = atoi(c6_s.c_str());
+
+ Chunk chunk;
+ chunk.frame_id_ = frame_id;
+ chunk.number_of_objects_ = number_of_objects;
+ chunk.size_ = size;
+ chunk.time_begin_ = time_begin;
+ chunk.time_end_ = time_end;
+ chunk.type_id_ = type_id;
+ chunks.push_back(chunk);
+ s1_pos = s2_pos + 1;
+ }
+ }
+ }
+ return chunks;
+ }
+
+ private:
+ size_t getFrameIdx(unsigned frame_id) {
+ for (size_t i = 0; i < frames_.size(); i++) {
+ if (frames_[i].frame_id_ == frame_id) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ std::vector<unsigned> findFrame(std::vector<std::string> bu_call_stack) {
+ std::vector<unsigned> frames;
+
+ std::map<std::string, unsigned>::const_iterator cit =
+ symbols_.find(bu_call_stack[0]);
+ if (cit != symbols_.end()) {
+ // take the cit->second and look for it in the frames
+ for (size_t j = 0; j < frames_.size(); j++) {
+ if (frames_[j].callsite_ == cit->second) {
+ bool good_frame = true;
+ // check all other frames iterating by parents
+ unsigned parent_frame = frames_[j].parent_;
+ for (size_t i = 1; i < bu_call_stack.size() && good_frame; i++) {
+ size_t idx = getFrameIdx(parent_frame);
+ if (idx != (size_t)-1) {
+ TestFrameInfo& parent = frames_[idx];
+ std::map<std::string, unsigned>::const_iterator cit2 =
+ symbols_.find(bu_call_stack[i]);
+ if (cit2 != symbols_.end()) {
+ if (cit2->second == parent.callsite_) {
+ parent_frame = parent.parent_;
+ } else {
+ good_frame = false;
+ }
+ } else {
+ good_frame = false;
+ }
+ } else {
+ good_frame = false;
+ }
+ }
+ if (good_frame) {
+ frames.push_back(frames_[j].frame_id_);
+ }
+ }
+ }
+ }
+ return frames;
+ }
+ std::map<std::string, unsigned> symbols_;
+ std::map<std::string, unsigned> types_;
+ // no need to have fast version, it will not be many frames
+ std::vector<TestFrameInfo> frames_;
+};
+
+class TestStatsStreamXDK : public v8::OutputStream {
+ public:
+ explicit TestStatsStreamXDK(XDKHPOutputChecker* checker) :
+ checker_(checker) {}
+ virtual ~TestStatsStreamXDK() {}
+ virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
+ DCHECK(false);
+ return kAbort;
+ }
+ virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* data,
+ int count) {
+ DCHECK(false);
+ return kAbort;
+ }
+ virtual WriteResult WriteHeapXDKChunk(const char* symbols, int symbolsSize,
+ const char* frames, int framesSize,
+ const char* types, int typesSize,
+ const char* chunks, int chunksSize,
+ const char* retentions, int retentionSize) {
+ checker_->parse(symbols, frames, types);
+ chunk_ = chunks;
+ return kContinue;
+ }
+ void EndOfStream() {}
+
+ std::string GetChunk() {
+ return chunk_;
+ }
+
+ private:
+ XDKHPOutputChecker* checker_;
+ std::string chunk_;
+};
+
+
+TEST(HeapProfilerXDK) {
+ XDKHPOutputChecker checker;
+ LocalContext env2;
+ v8::HandleScope scope(env2->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env2->GetIsolate()->GetHeapProfiler();
+ TestStatsStreamXDK stream(&checker);
+ heap_profiler->StartTrackingHeapObjectsXDK(8, false, true);
+
+ // To have repeatable test we need to warm-up the heap and optimization v8
+ // techniques (like inlining). So, we create 100 objects, not the only one
+ CompileRun(
+ "function A1() { this.string = 'This is a string';}\n"
+ "function object2() {\n"
+ " this.elem = [];\n"
+ " this.third = [];\n"
+ "}"
+ "var globalA2 = [];\n"
+ "function allocFunction2() {\n"
+ " globalA2.push(new object2());\n"
+ "}\n"
+ "for (var i=0; i<100; i++) allocFunction2();\n");
+
+ heap_profiler->GetHeapXDKStats(&stream);
+ CompileRun("allocFunction2();\n");
+ heap_profiler->GetHeapXDKStats(&stream);
+ CompileRun("delete globalA2[99];\n");
+ heap_profiler->GetHeapXDKStats(&stream);
+ std::string chunk_deleted_globalA2_0_array = stream.GetChunk();
+ v8::HeapEventXDK* event = heap_profiler->StopTrackingHeapObjectsXDK();
+
+ // adding the latest info:
+ checker.parse(event->getSymbols(), event->getFrames(), event->getTypes());
+
+ // here should be 2 arrays and 1 object2
+ TestObjectInfo info_deleted_globalA2_0_array;
+ info_deleted_globalA2_0_array.bu_call_stack_.push_back("object2");
+ info_deleted_globalA2_0_array.bu_call_stack_.push_back("allocFunction2");
+ info_deleted_globalA2_0_array.type_ = "Array";
+ info_deleted_globalA2_0_array.number_of_objects_ = 0;
+ bool idg_arr_jad = checker.checkObjectsExists(
+ info_deleted_globalA2_0_array, chunk_deleted_globalA2_0_array);
+
+ TestObjectInfo info_deleted_globalA2_0;
+ info_deleted_globalA2_0.bu_call_stack_.push_back("allocFunction2");
+ info_deleted_globalA2_0.type_ = "object2";
+ info_deleted_globalA2_0.number_of_objects_ = 1;
+ bool idg_obj_jad = checker.checkObjectsExists(
+ info_deleted_globalA2_0, chunk_deleted_globalA2_0_array);
+
+ // here should be 2 arrays and 1 object2
+ TestObjectInfo info_deleted_globalA2_1_array;
+ info_deleted_globalA2_1_array.bu_call_stack_.push_back("object2");
+ info_deleted_globalA2_1_array.bu_call_stack_.push_back("allocFunction2");
+ info_deleted_globalA2_1_array.type_ = "Array";
+ info_deleted_globalA2_1_array.number_of_objects_ = 2;
+ bool idg_arr_end = checker.checkObjectsExists(
+ info_deleted_globalA2_1_array, event->getChunks());
+
+ TestObjectInfo info_deleted_globalA2_1;
+ info_deleted_globalA2_1.bu_call_stack_.push_back("allocFunction2");
+ info_deleted_globalA2_1.type_ = "object2";
+ info_deleted_globalA2_1.number_of_objects_ = 1;
+ bool idg_obj_end = checker.checkObjectsExists(
+ info_deleted_globalA2_1, event->getChunks());
+ // find objects anywhere
+ CHECK_EQ(true, idg_obj_end || idg_obj_jad);
+ CHECK_EQ(true, idg_arr_end || idg_arr_jad);
+}
+
+
+TEST(HeapProfilerXDKRetentionStorage) {
+ v8::internal::RefId parent;
+ v8::internal::RefId rf11, rf12, rf13, rf21, rf22, rf23;
+ v8::internal::RefSet set1, set2, set3;
+ v8::internal::References refs;
+
+ parent.stackId_ = 99;
+ parent.classId_ = 99;
+
+ rf11.stackId_ = 10; rf11.classId_ = 1; rf11.field_ = "one_";
+ set1.references_.insert(rf11);
+ rf12.stackId_ = 20; rf12.classId_ = 1; rf12.field_ = "two_";
+ set1.references_.insert(rf12);
+ rf13.stackId_ = 30; rf13.classId_ = 2; rf13.field_ = "three_";
+ set1.references_.insert(rf13);
+ refs.addReference(parent, set1, 0);
+
+ rf21.stackId_ = 10; rf21.classId_ = 1; rf21.field_ = "eno_";
+ set2.references_.insert(rf21);
+ rf22.stackId_ = 15; rf22.classId_ = 1; rf22.field_ = "owt_";
+ set2.references_.insert(rf22);
+ rf23.stackId_ = 30; rf23.classId_ = 2; rf23.field_ = "eerht_";
+ set2.references_.insert(rf23);
+ refs.addReference(parent, set2, 0);
+
+ set3.references_.insert(rf11);
+ set3.references_.insert(rf12);
+ set3.references_.insert(rf13);
+ refs.addReference(parent, set3, 0);
+
+ // there should be two records by set1 and one by set2
+ std::string str = refs.serialize();
+
+ CHECK_EQ(true,
+ str.find("99,99,1,0,2,10,1,one_,20,1,two_,30,2,three_") != str.npos &&
+ str.find("99,99,1,0,1,10,1,eno_,15,1,owt_,30,2,eerht_") != str.npos);
+}