kObject = 3, // A JS object (except for arrays and strings).
kCode = 4, // Compiled code.
kClosure = 5, // Function closure.
- kRegExp = 6 // RegExp.
+ kRegExp = 6, // RegExp.
+ kHeapNumber = 7 // Number stored in the heap.
};
/** Returns node type (see HeapGraphNode::Type). */
}
+static void DeleteIndexName(char** name_ptr) {
+ DeleteArray(*name_ptr);
+}
+
+
StringsStorage::~StringsStorage() {
for (HashMap::Entry* p = names_.Start();
p != NULL;
p = names_.Next(p)) {
DeleteArray(reinterpret_cast<const char*>(p->value));
}
+ index_names_.Iterate(DeleteIndexName);
}
}
+const char* StringsStorage::GetName(int index) {
+ ASSERT(index >= 0);
+ if (index_names_.length() <= index) {
+ index_names_.AddBlock(
+ NULL, index - index_names_.length() + 1);
+ }
+ if (index_names_[index] == NULL) {
+ const int kMaximumNameLength = 32;
+ char* name = NewArray<char>(kMaximumNameLength);
+ OS::SNPrintF(Vector<char>(name, kMaximumNameLength), "%d", index);
+ index_names_[index] = name;
+ }
+ return index_names_[index];
+}
+
+
const char* CodeEntry::kEmptyNamePrefix = "";
}
-static void DeleteArgsCountName(char** name_ptr) {
- DeleteArray(*name_ptr);
-}
-
-
static void DeleteCodeEntry(CodeEntry** entry_ptr) {
delete *entry_ptr;
}
current_profiles_.Iterate(DeleteCpuProfile);
profiles_by_token_.Iterate(DeleteProfilesList);
code_entries_.Iterate(DeleteCodeEntry);
- args_count_names_.Iterate(DeleteArgsCountName);
}
}
-const char* CpuProfilesCollection::GetName(int args_count) {
- ASSERT(args_count >= 0);
- if (args_count_names_.length() <= args_count) {
- args_count_names_.AddBlock(
- NULL, args_count - args_count_names_.length() + 1);
- }
- if (args_count_names_[args_count] == NULL) {
- const int kMaximumNameLength = 32;
- char* name = NewArray<char>(kMaximumNameLength);
- OS::SNPrintF(Vector<char>(name, kMaximumNameLength), "%d", args_count);
- args_count_names_[args_count] = name;
- }
- return args_count_names_[args_count];
-}
-
-
void CpuProfilesCollection::AddPathToCurrentProfiles(
const Vector<CodeEntry*>& path) {
// As starting / stopping profiles is rare relatively to this
case kCode: return "/code/";
case kArray: return "/array/";
case kRegExp: return "/regexp/";
+ case kHeapNumber: return "/number/";
default: return "???";
}
}
"",
children_count,
retainers_count);
+ } else if (object->IsHeapNumber()) {
+ return AddEntry(object,
+ HeapEntry::kHeapNumber,
+ "number",
+ children_count,
+ retainers_count);
}
// No interest in this object.
return NULL;
|| object->IsCode()
|| object->IsSharedFunctionInfo()
|| object->IsScript()
- || object->IsFixedArray();
+ || object->IsFixedArray()
+ || object->IsHeapNumber();
}
ExtractClosureReferences(js_obj, entry);
ExtractPropertyReferences(js_obj, entry);
ExtractElementReferences(js_obj, entry);
+ ExtractInternalReferences(js_obj, entry);
SetPropertyReference(
obj, entry, Heap::Proto_symbol(), js_obj->GetPrototype());
if (obj->IsJSFunction()) {
}
+void HeapSnapshotGenerator::ExtractInternalReferences(JSObject* js_obj,
+ HeapEntry* entry) {
+ int length = js_obj->GetInternalFieldCount();
+ for (int i = 0; i < length; ++i) {
+ Object* o = js_obj->GetInternalField(i);
+ SetInternalReference(js_obj, entry, i, o);
+ }
+}
+
+
void HeapSnapshotGenerator::SetClosureReference(HeapObject* parent_obj,
HeapEntry* parent_entry,
String* reference_name,
}
+void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj,
+ HeapEntry* parent_entry,
+ int index,
+ Object* child_obj) {
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry != NULL) {
+ filler_->SetNamedReference(HeapGraphEdge::kInternal,
+ parent_obj,
+ parent_entry,
+ collection_->GetName(index),
+ child_obj,
+ child_entry);
+ }
+}
+
+
void HeapSnapshotGenerator::SetPropertyReference(HeapObject* parent_obj,
HeapEntry* parent_entry,
String* reference_name,
"," JSON_S("object")
"," JSON_S("code")
"," JSON_S("closure")
- "," JSON_S("regexp"))
+ "," JSON_S("regexp")
+ "," JSON_S("number"))
"," JSON_S("string")
"," JSON_S("number")
"," JSON_S("number")
~StringsStorage();
const char* GetName(String* name);
+ const char* GetName(int index);
inline const char* GetFunctionName(String* name);
inline const char* GetFunctionName(const char* name);
// Mapping of strings by String::Hash to const char* strings.
HashMap names_;
+ // Mapping from ints to char* strings.
+ List<char*> index_names_;
DISALLOW_COPY_AND_ASSIGN(StringsStorage);
};
const char* GetName(String* name) {
return function_and_resource_names_.GetName(name);
}
+ const char* GetName(int args_count) {
+ return function_and_resource_names_.GetName(args_count);
+ }
CpuProfile* GetProfile(int security_token_id, unsigned uid);
bool IsLastProfile(const char* title);
static const int kMaxSimultaneousProfiles = 100;
private:
- const char* GetName(int args_count);
const char* GetFunctionName(String* name) {
return function_and_resource_names_.GetFunctionName(name);
}
}
StringsStorage function_and_resource_names_;
- // Mapping from args_count (int) to char* strings.
- List<char*> args_count_names_;
List<CodeEntry*> code_entries_;
List<List<CpuProfile*>* > profiles_by_token_;
// Mapping from profiles' uids to indexes in the second nested list
kObject = v8::HeapGraphNode::kObject,
kCode = v8::HeapGraphNode::kCode,
kClosure = v8::HeapGraphNode::kClosure,
- kRegExp = v8::HeapGraphNode::kRegExp
+ kRegExp = v8::HeapGraphNode::kRegExp,
+ kHeapNumber = v8::HeapGraphNode::kHeapNumber
};
HeapEntry() { }
HeapSnapshot* GetSnapshot(unsigned uid);
const char* GetName(String* name) { return names_.GetName(name); }
+ const char* GetName(int index) { return names_.GetName(index); }
const char* GetFunctionName(String* name) {
return names_.GetFunctionName(name);
}
void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry);
void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry);
void ExtractElementReferences(JSObject* js_obj, HeapEntry* entry);
+ void ExtractInternalReferences(JSObject* js_obj, HeapEntry* entry);
void SetClosureReference(HeapObject* parent_obj,
HeapEntry* parent,
String* reference_name,
HeapEntry* parent,
const char* reference_name,
Object* child);
+ void SetInternalReference(HeapObject* parent_obj,
+ HeapEntry* parent,
+ int index,
+ Object* child);
void SetPropertyReference(HeapObject* parent_obj,
HeapEntry* parent,
String* reference_name,
using i::RetainerHeapProfile;
-static void CompileAndRunScript(const char *src) {
- v8::Script::Compile(v8::String::New(src))->Run();
-}
-
-
namespace {
class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile {
v8::HandleScope scope;
LocalContext env;
- CompileAndRunScript(
+ CompileRun(
"function F() {} // A constructor\n"
"var f1 = new F();\n"
"var f2 = new F();\n");
v8::HandleScope scope;
LocalContext env;
- CompileAndRunScript(
+ CompileRun(
"function A() {}\n"
"function B(x) { this.x = x; }\n"
"function C(x) { this.x1 = x; this.x2 = x; }\n"
LocalContext env1;
env1->SetSecurityToken(token1);
- CompileAndRunScript(
+ CompileRun(
"function A1() {}\n"
"function B1(x) { this.x = x; }\n"
"function C1(x) { this.x1 = x; this.x2 = x; }\n"
LocalContext env2;
env2->SetSecurityToken(token2);
- CompileAndRunScript(
+ CompileRun(
"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"
// -a-> X1 --a
// x -b-> X2 <-|
- CompileAndRunScript(
+ CompileRun(
"function X(a, b) { this.a = a; this.b = b; }\n"
"x = new X(new X(), new X());\n"
"x.a.a = x.b;");
v8::HandleScope scope;
LocalContext env;
- CompileAndRunScript(
+ CompileRun(
"function A() { }\n"
"a = new A;");
const v8::HeapSnapshot* snapshot =
v8::HandleScope scope;
LocalContext env;
- CompileAndRunScript(
+ CompileRun(
"function lazy(x) { return x - 1; }\n"
"function compiled(x) { return x + 1; }\n"
"var anonymous = (function() { return function() { return 0; } })();\n"
}
+TEST(HeapSnapshotHeapNumbers) {
+ v8::HandleScope scope;
+ LocalContext env;
+ CompileRun(
+ "a = 1; // a is Smi\n"
+ "b = 2.5; // b is HeapNumber");
+ const v8::HeapSnapshot* snapshot =
+ v8::HeapProfiler::TakeSnapshot(v8::String::New("numbers"));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a"));
+ const v8::HeapGraphNode* b =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "b");
+ CHECK_NE(NULL, b);
+ CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType());
+}
+
+
+TEST(HeapSnapshotInternalReferences) {
+ v8::HandleScope scope;
+ v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+ global_template->SetInternalFieldCount(2);
+ LocalContext env(NULL, global_template);
+ v8::Handle<v8::Object> global_proxy = env->Global();
+ v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>();
+ CHECK_EQ(2, global->InternalFieldCount());
+ v8::Local<v8::Object> obj = v8::Object::New();
+ global->SetInternalField(0, v8_num(17));
+ global->SetInternalField(1, obj);
+ const v8::HeapSnapshot* snapshot =
+ v8::HeapProfiler::TakeSnapshot(v8::String::New("internals"));
+ const v8::HeapGraphNode* global_node = GetGlobalObject(snapshot);
+ // The first reference will not present, because it's a Smi.
+ CHECK_EQ(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "0"));
+ // The second reference is to an object.
+ CHECK_NE(NULL, GetProperty(global_node, v8::HeapGraphEdge::kInternal, "1"));
+}
+
+
// Trying to introduce a check helper for uint64_t causes many
// overloading ambiguities, so it seems easier just to cast
// them to a signed type.
v8::HandleScope scope;
LocalContext env;
- CompileAndRunScript(
+ CompileRun(
"function A() {}\n"
"function B(x) { this.x = x; }\n"
"var a = new A();\n"
v8::HandleScope scope;
LocalContext env;
- CompileAndRunScript(
+ CompileRun(
"function A() {}\n"
"function B(x) { this.x = x; }\n"
"function A2(a) { for (var i = 0; i < a; ++i) this[i] = i; }\n"
const v8::HeapSnapshot* snapshot1 =
v8::HeapProfiler::TakeSnapshot(v8::String::New("s1"));
- CompileAndRunScript(
+ CompileRun(
"delete a;\n"
"b.x = null;\n"
"var a = new A2(20);\n"
v8::HandleScope scope;
LocalContext env;
- CompileAndRunScript(
+ CompileRun(
"function A() {}\n"
"function B(x) { this.x = x; }\n"
"var a = new A();\n"
#define STRING_LITERAL_FOR_TEST \
"\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
- CompileAndRunScript(
+ CompileRun(
"function A(s) { this.s = s; }\n"
"function B(x) { this.x = x; }\n"
"var a = new A(" STRING_LITERAL_FOR_TEST ");\n"