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:
mikhail.naganov@gmail.com 2010-10-18 09:15:38 +00:00
parent a8455f0424
commit d4bc8e1585
4 changed files with 126 additions and 46 deletions

View File

@ -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). */

View File

@ -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")

View File

@ -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,

View File

@ -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"