| Index: test/cctest/test-heap-profiler.cc
|
| diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc
|
| index 6dc49c0e54911adba3981d9fb565dda9e2d087b0..606336c906af817a3464d9e6925b2ca6eca0cb14 100644
|
| --- a/test/cctest/test-heap-profiler.cc
|
| +++ b/test/cctest/test-heap-profiler.cc
|
| @@ -989,4 +989,137 @@ TEST(AggregatedHeapSnapshot) {
|
| CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A.
|
| }
|
|
|
| +namespace {
|
| +
|
| +class TestJSONStream : public v8::OutputStream {
|
| + public:
|
| + TestJSONStream() : eos_signaled_(0) {}
|
| + virtual ~TestJSONStream() {}
|
| + virtual void EndOfStream() { ++eos_signaled_; }
|
| + virtual void WriteAsciiChunk(char* buffer, int chars_written) {
|
| + CHECK_GT(chars_written, 0);
|
| + i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
|
| + memcpy(chunk.start(), buffer, chars_written);
|
| + }
|
| + void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
|
| + int eos_signaled() { return eos_signaled_; }
|
| + int size() { return buffer_.size(); }
|
| + private:
|
| + i::Collector<char> buffer_;
|
| + int eos_signaled_;
|
| +};
|
| +
|
| +class AsciiResource: public v8::String::ExternalAsciiStringResource {
|
| + public:
|
| + explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
|
| + length_ = string.length();
|
| + }
|
| + virtual const char* data() const { return data_; }
|
| + virtual size_t length() const { return length_; }
|
| + private:
|
| + const char* data_;
|
| + size_t length_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +TEST(HeapSnapshotJSONSerialization) {
|
| + v8::HandleScope scope;
|
| + LocalContext env;
|
| +
|
| +#define STRING_LITERAL_FOR_TEST \
|
| + "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
|
| + CompileAndRunScript(
|
| + "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);");
|
| + const v8::HeapSnapshot* snapshot =
|
| + v8::HeapProfiler::TakeSnapshot(v8::String::New("json"));
|
| + 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.
|
| + AsciiResource json_res(json);
|
| + v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
|
| + env->Global()->Set(v8::String::New("json_snapshot"), json_string);
|
| + 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 =
|
| + env->Global()->Get(v8::String::New("parsed"))->ToObject();
|
| + CHECK(parsed_snapshot->Has(v8::String::New("snapshot")));
|
| + CHECK(parsed_snapshot->Has(v8::String::New("nodes")));
|
| + CHECK(parsed_snapshot->Has(v8::String::New("strings")));
|
| +
|
| + // Verify that nodes meta-info is valid JSON.
|
| + v8::Local<v8::Value> nodes_meta_parse_result = CompileRun(
|
| + "var parsed_meta = JSON.parse(parsed.nodes[0]); true;");
|
| + CHECK(!nodes_meta_parse_result.IsEmpty());
|
| +
|
| + // Get node and edge "member" offsets.
|
| + v8::Local<v8::Value> meta_analysis_result = CompileRun(
|
| + "var children_count_offset ="
|
| + " parsed_meta.fields.indexOf('children_count');\n"
|
| + "var children_offset ="
|
| + " parsed_meta.fields.indexOf('children');\n"
|
| + "var children_meta ="
|
| + " parsed_meta.types[children_offset];\n"
|
| + "var child_fields_count = children_meta.fields.length;\n"
|
| + "var child_type_offset ="
|
| + " children_meta.fields.indexOf('type');\n"
|
| + "var child_name_offset ="
|
| + " children_meta.fields.indexOf('name_or_index');\n"
|
| + "var child_to_node_offset ="
|
| + " children_meta.fields.indexOf('to_node');\n"
|
| + "var property_type ="
|
| + " children_meta.types[child_type_offset].indexOf('property');");
|
| + CHECK(!meta_analysis_result.IsEmpty());
|
| +
|
| + // A helper function for processing encoded nodes.
|
| + CompileRun(
|
| + "function GetChildPosByProperty(pos, prop_name) {\n"
|
| + " var nodes = parsed.nodes;\n"
|
| + " var strings = parsed.strings;\n"
|
| + " for (var i = 0,\n"
|
| + " count = nodes[pos + children_count_offset] * child_fields_count;\n"
|
| + " i < count; i += child_fields_count) {\n"
|
| + " var child_pos = pos + children_offset + i;\n"
|
| + " if (nodes[child_pos + child_type_offset] === property_type\n"
|
| + " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n"
|
| + " return nodes[child_pos + child_to_node_offset];\n"
|
| + " }\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("
|
| + " parsed.nodes[1 + children_offset + child_to_node_offset],\"b\"),\n"
|
| + " \"x\"),"
|
| + " \"s\")");
|
| + CHECK(!string_obj_pos_val.IsEmpty());
|
| + int string_obj_pos = string_obj_pos_val->ToNumber()->Value();
|
| + v8::Local<v8::Object> nodes_array =
|
| + parsed_snapshot->Get(v8::String::New("nodes"))->ToObject();
|
| + int string_index =
|
| + nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value();
|
| + CHECK_GT(string_index, 0);
|
| + v8::Local<v8::Object> strings_array =
|
| + parsed_snapshot->Get(v8::String::New("strings"))->ToObject();
|
| + v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
|
| + v8::Local<v8::String> ref_string =
|
| + CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
|
| +#undef STRING_LITERAL_FOR_TEST
|
| + CHECK_EQ(*v8::String::Utf8Value(ref_string),
|
| + *v8::String::Utf8Value(string));
|
| +}
|
| +
|
| #endif // ENABLE_LOGGING_AND_PROFILING
|
|
|