New Heap profiler: add dumping HeapNumbers and InternalFields to snapshot.
HeapNumbers do consume memory, so it's worth dumping them. However, we don't dump their values, as they are not as self-descriptive as values of strings, and they will increase snapshot size. Storing heap numbers values can be added if we will feel a sufficient demand for that. InternalFields are used, e.g. for storing references to DOM nodes event handlers. Review URL: http://codereview.chromium.org/3769007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5635 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
a8455f0424
commit
d4bc8e1585
@ -246,7 +246,8 @@ class V8EXPORT HeapGraphNode {
|
||||
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). */
|
||||
|
@ -94,12 +94,18 @@ StringsStorage::StringsStorage()
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -120,6 +126,22 @@ const char* StringsStorage::GetName(String* name) {
|
||||
}
|
||||
|
||||
|
||||
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 = "";
|
||||
|
||||
|
||||
@ -485,11 +507,6 @@ CpuProfilesCollection::CpuProfilesCollection()
|
||||
}
|
||||
|
||||
|
||||
static void DeleteArgsCountName(char** name_ptr) {
|
||||
DeleteArray(*name_ptr);
|
||||
}
|
||||
|
||||
|
||||
static void DeleteCodeEntry(CodeEntry** entry_ptr) {
|
||||
delete *entry_ptr;
|
||||
}
|
||||
@ -508,7 +525,6 @@ CpuProfilesCollection::~CpuProfilesCollection() {
|
||||
current_profiles_.Iterate(DeleteCpuProfile);
|
||||
profiles_by_token_.Iterate(DeleteProfilesList);
|
||||
code_entries_.Iterate(DeleteCodeEntry);
|
||||
args_count_names_.Iterate(DeleteArgsCountName);
|
||||
}
|
||||
|
||||
|
||||
@ -706,22 +722,6 @@ CodeEntry* CpuProfilesCollection::NewCodeEntry(int security_token_id) {
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@ -1002,6 +1002,7 @@ const char* HeapEntry::TypeAsString() {
|
||||
case kCode: return "/code/";
|
||||
case kArray: return "/array/";
|
||||
case kRegExp: return "/regexp/";
|
||||
case kHeapNumber: return "/number/";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
@ -1339,6 +1340,12 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
|
||||
"",
|
||||
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;
|
||||
@ -1354,7 +1361,8 @@ bool HeapSnapshot::WillAddEntry(HeapObject* object) {
|
||||
|| object->IsCode()
|
||||
|| object->IsSharedFunctionInfo()
|
||||
|| object->IsScript()
|
||||
|| object->IsFixedArray();
|
||||
|| object->IsFixedArray()
|
||||
|| object->IsHeapNumber();
|
||||
}
|
||||
|
||||
|
||||
@ -1911,6 +1919,7 @@ void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) {
|
||||
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()) {
|
||||
@ -2019,6 +2028,16 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
@ -2063,6 +2082,22 @@ void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj,
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
@ -2368,7 +2403,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() {
|
||||
"," 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")
|
||||
|
@ -67,6 +67,7 @@ class StringsStorage {
|
||||
~StringsStorage();
|
||||
|
||||
const char* GetName(String* name);
|
||||
const char* GetName(int index);
|
||||
inline const char* GetFunctionName(String* name);
|
||||
inline const char* GetFunctionName(const char* name);
|
||||
|
||||
@ -78,6 +79,8 @@ class StringsStorage {
|
||||
|
||||
// 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);
|
||||
};
|
||||
@ -284,6 +287,9 @@ class CpuProfilesCollection {
|
||||
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);
|
||||
|
||||
@ -302,7 +308,6 @@ class CpuProfilesCollection {
|
||||
static const int kMaxSimultaneousProfiles = 100;
|
||||
|
||||
private:
|
||||
const char* GetName(int args_count);
|
||||
const char* GetFunctionName(String* name) {
|
||||
return function_and_resource_names_.GetFunctionName(name);
|
||||
}
|
||||
@ -317,8 +322,6 @@ class CpuProfilesCollection {
|
||||
}
|
||||
|
||||
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
|
||||
@ -503,7 +506,8 @@ class HeapEntry BASE_EMBEDDED {
|
||||
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() { }
|
||||
@ -825,6 +829,7 @@ class HeapSnapshotsCollection {
|
||||
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);
|
||||
}
|
||||
@ -949,6 +954,7 @@ class HeapSnapshotGenerator {
|
||||
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,
|
||||
@ -961,6 +967,10 @@ class HeapSnapshotGenerator {
|
||||
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,
|
||||
|
@ -20,11 +20,6 @@ using i::JSObjectsClusterTree;
|
||||
using i::RetainerHeapProfile;
|
||||
|
||||
|
||||
static void CompileAndRunScript(const char *src) {
|
||||
v8::Script::Compile(v8::String::New(src))->Run();
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile {
|
||||
@ -58,7 +53,7 @@ TEST(ConstructorProfile) {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
||||
CompileAndRunScript(
|
||||
CompileRun(
|
||||
"function F() {} // A constructor\n"
|
||||
"var f1 = new F();\n"
|
||||
"var f2 = new F();\n");
|
||||
@ -359,7 +354,7 @@ TEST(RetainerProfile) {
|
||||
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"
|
||||
@ -473,7 +468,7 @@ TEST(HeapSnapshot) {
|
||||
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"
|
||||
@ -485,7 +480,7 @@ TEST(HeapSnapshot) {
|
||||
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"
|
||||
@ -583,7 +578,7 @@ TEST(HeapSnapshotObjectSizes) {
|
||||
|
||||
// -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;");
|
||||
@ -624,7 +619,7 @@ TEST(HeapSnapshotEntryChildren) {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
||||
CompileAndRunScript(
|
||||
CompileRun(
|
||||
"function A() { }\n"
|
||||
"a = new A;");
|
||||
const v8::HeapSnapshot* snapshot =
|
||||
@ -648,7 +643,7 @@ TEST(HeapSnapshotCodeObjects) {
|
||||
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"
|
||||
@ -709,6 +704,44 @@ TEST(HeapSnapshotCodeObjects) {
|
||||
}
|
||||
|
||||
|
||||
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.
|
||||
@ -721,7 +754,7 @@ TEST(HeapEntryIdsAndGC) {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
||||
CompileAndRunScript(
|
||||
CompileRun(
|
||||
"function A() {}\n"
|
||||
"function B(x) { this.x = x; }\n"
|
||||
"var a = new A();\n"
|
||||
@ -777,7 +810,7 @@ TEST(HeapSnapshotsDiff) {
|
||||
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"
|
||||
@ -786,7 +819,7 @@ TEST(HeapSnapshotsDiff) {
|
||||
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"
|
||||
@ -921,7 +954,7 @@ TEST(AggregatedHeapSnapshot) {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
||||
CompileAndRunScript(
|
||||
CompileRun(
|
||||
"function A() {}\n"
|
||||
"function B(x) { this.x = x; }\n"
|
||||
"var a = new A();\n"
|
||||
@ -1042,7 +1075,7 @@ TEST(HeapSnapshotJSONSerialization) {
|
||||
|
||||
#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"
|
||||
|
Loading…
Reference in New Issue
Block a user