| OLD | NEW |
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 827 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 838 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); | 838 const v8::HeapGraphNode* root1 = snapshot->GetRoot(); |
| 839 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( | 839 const_cast<i::HeapSnapshot*>(reinterpret_cast<const i::HeapSnapshot*>( |
| 840 snapshot))->GetSortedEntriesList(); | 840 snapshot))->GetSortedEntriesList(); |
| 841 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); | 841 const v8::HeapGraphNode* root2 = snapshot->GetRoot(); |
| 842 CHECK_EQ(root1, root2); | 842 CHECK_EQ(root1, root2); |
| 843 } | 843 } |
| 844 | 844 |
| 845 | 845 |
| 846 namespace { | 846 namespace { |
| 847 | 847 |
| 848 class OneByteResource : public v8::String::ExternalOneByteStringResource { |
| 849 public: |
| 850 explicit OneByteResource(i::Vector<char> string) : data_(string.start()) { |
| 851 length_ = string.length(); |
| 852 } |
| 853 virtual const char* data() const { return data_; } |
| 854 virtual size_t length() const { return length_; } |
| 855 |
| 856 private: |
| 857 const char* data_; |
| 858 size_t length_; |
| 859 }; |
| 860 |
| 848 class TestJSONStream : public v8::OutputStream { | 861 class TestJSONStream : public v8::OutputStream { |
| 849 public: | 862 public: |
| 850 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {} | 863 TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {} |
| 851 explicit TestJSONStream(int abort_countdown) | 864 explicit TestJSONStream(int abort_countdown) |
| 852 : eos_signaled_(0), abort_countdown_(abort_countdown) {} | 865 : eos_signaled_(0), abort_countdown_(abort_countdown) {} |
| 853 virtual ~TestJSONStream() {} | 866 virtual ~TestJSONStream() {} |
| 854 virtual void EndOfStream() { ++eos_signaled_; } | 867 virtual void EndOfStream() { ++eos_signaled_; } |
| 855 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) { | 868 virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) { |
| 856 if (abort_countdown_ > 0) --abort_countdown_; | 869 if (abort_countdown_ > 0) --abort_countdown_; |
| 857 if (abort_countdown_ == 0) return kAbort; | 870 if (abort_countdown_ == 0) return kAbort; |
| 858 CHECK_GT(chars_written, 0); | 871 CHECK_GT(chars_written, 0); |
| 859 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0'); | 872 i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0'); |
| 860 i::MemCopy(chunk.start(), buffer, chars_written); | 873 i::MemCopy(chunk.start(), buffer, chars_written); |
| 861 return kContinue; | 874 return kContinue; |
| 862 } | 875 } |
| 863 virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) { | 876 virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int chars_written) { |
| 864 CHECK(false); | 877 CHECK(false); |
| 865 return kAbort; | 878 return kAbort; |
| 866 } | 879 } |
| 867 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); } | 880 void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); } |
| 868 int eos_signaled() { return eos_signaled_; } | 881 int eos_signaled() { return eos_signaled_; } |
| 869 int size() { return buffer_.size(); } | 882 int size() { return buffer_.size(); } |
| 870 | 883 |
| 884 // Verifies that the stream contains JSON, parses, and returns the parsed |
| 885 // result. |
| 886 v8::Local<v8::Object> verifyAndParseJSON(LocalContext& env) { |
| 887 i::ScopedVector<char> json(size()); |
| 888 WriteTo(json); |
| 889 |
| 890 // Verify that string is valid JSON. |
| 891 OneByteResource* json_res = new OneByteResource(json); |
| 892 v8::Local<v8::String> json_string = |
| 893 v8::String::NewExternalOneByte(env->GetIsolate(), json_res) |
| 894 .ToLocalChecked(); |
| 895 env->Global() |
| 896 ->Set(env.local(), v8_str("json_snapshot"), json_string) |
| 897 .FromJust(); |
| 898 v8::Local<v8::Value> snapshot_parse_result = |
| 899 CompileRun("var parsed = JSON.parse(json_snapshot); true;"); |
| 900 CHECK(!snapshot_parse_result.IsEmpty()); |
| 901 |
| 902 v8::Local<v8::Object> parsed = env->Global() |
| 903 ->Get(env.local(), v8_str("parsed")) |
| 904 .ToLocalChecked() |
| 905 ->ToObject(env.local()) |
| 906 .ToLocalChecked(); |
| 907 return parsed; |
| 908 } |
| 909 |
| 871 private: | 910 private: |
| 872 i::Collector<char> buffer_; | 911 i::Collector<char> buffer_; |
| 873 int eos_signaled_; | 912 int eos_signaled_; |
| 874 int abort_countdown_; | 913 int abort_countdown_; |
| 875 }; | 914 }; |
| 876 | 915 |
| 877 class OneByteResource : public v8::String::ExternalOneByteStringResource { | |
| 878 public: | |
| 879 explicit OneByteResource(i::Vector<char> string) : data_(string.start()) { | |
| 880 length_ = string.length(); | |
| 881 } | |
| 882 virtual const char* data() const { return data_; } | |
| 883 virtual size_t length() const { return length_; } | |
| 884 private: | |
| 885 const char* data_; | |
| 886 size_t length_; | |
| 887 }; | |
| 888 | |
| 889 } // namespace | 916 } // namespace |
| 890 | 917 |
| 891 TEST(HeapSnapshotJSONSerialization) { | 918 TEST(HeapSnapshotJSONSerialization) { |
| 892 v8::Isolate* isolate = CcTest::isolate(); | 919 v8::Isolate* isolate = CcTest::isolate(); |
| 893 LocalContext env; | 920 LocalContext env; |
| 894 v8::HandleScope scope(isolate); | 921 v8::HandleScope scope(isolate); |
| 895 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); | 922 v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler(); |
| 896 | 923 |
| 897 #define STRING_LITERAL_FOR_TEST \ | 924 #define STRING_LITERAL_FOR_TEST \ |
| 898 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\"" | 925 "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\"" |
| 899 CompileRun( | 926 CompileRun( |
| 900 "function A(s) { this.s = s; }\n" | 927 "function A(s) { this.s = s; }\n" |
| 901 "function B(x) { this.x = x; }\n" | 928 "function B(x) { this.x = x; }\n" |
| 902 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n" | 929 "var a = new A(" STRING_LITERAL_FOR_TEST ");\n" |
| 903 "var b = new B(a);"); | 930 "var b = new B(a);"); |
| 904 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(); | 931 const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot(); |
| 905 CHECK(ValidateSnapshot(snapshot)); | 932 CHECK(ValidateSnapshot(snapshot)); |
| 906 | 933 |
| 907 TestJSONStream stream; | 934 TestJSONStream stream; |
| 908 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); | 935 snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); |
| 909 CHECK_GT(stream.size(), 0); | 936 CHECK_GT(stream.size(), 0); |
| 910 CHECK_EQ(1, stream.eos_signaled()); | 937 CHECK_EQ(1, stream.eos_signaled()); |
| 911 i::ScopedVector<char> json(stream.size()); | |
| 912 stream.WriteTo(json); | |
| 913 | 938 |
| 914 // Verify that snapshot string is valid JSON. | 939 v8::Local<v8::Object> parsed_snapshot = stream.verifyAndParseJSON(env); |
| 915 OneByteResource* json_res = new OneByteResource(json); | |
| 916 v8::Local<v8::String> json_string = | |
| 917 v8::String::NewExternalOneByte(env->GetIsolate(), json_res) | |
| 918 .ToLocalChecked(); | |
| 919 env->Global() | |
| 920 ->Set(env.local(), v8_str("json_snapshot"), json_string) | |
| 921 .FromJust(); | |
| 922 v8::Local<v8::Value> snapshot_parse_result = CompileRun( | |
| 923 "var parsed = JSON.parse(json_snapshot); true;"); | |
| 924 CHECK(!snapshot_parse_result.IsEmpty()); | |
| 925 | |
| 926 // Verify that snapshot object has required fields. | |
| 927 v8::Local<v8::Object> parsed_snapshot = | |
| 928 env->Global() | |
| 929 ->Get(env.local(), v8_str("parsed")) | |
| 930 .ToLocalChecked() | |
| 931 ->ToObject(env.local()) | |
| 932 .ToLocalChecked(); | |
| 933 CHECK(parsed_snapshot->Has(env.local(), v8_str("snapshot")).FromJust()); | 940 CHECK(parsed_snapshot->Has(env.local(), v8_str("snapshot")).FromJust()); |
| 934 CHECK(parsed_snapshot->Has(env.local(), v8_str("nodes")).FromJust()); | 941 CHECK(parsed_snapshot->Has(env.local(), v8_str("nodes")).FromJust()); |
| 935 CHECK(parsed_snapshot->Has(env.local(), v8_str("edges")).FromJust()); | 942 CHECK(parsed_snapshot->Has(env.local(), v8_str("edges")).FromJust()); |
| 936 CHECK(parsed_snapshot->Has(env.local(), v8_str("strings")).FromJust()); | 943 CHECK(parsed_snapshot->Has(env.local(), v8_str("strings")).FromJust()); |
| 937 | 944 |
| 938 // Get node and edge "member" offsets. | 945 // Get node and edge "member" offsets. |
| 939 v8::Local<v8::Value> meta_analysis_result = CompileRun( | 946 v8::Local<v8::Value> meta_analysis_result = CompileRun( |
| 940 "var meta = parsed.snapshot.meta;\n" | 947 "var meta = parsed.snapshot.meta;\n" |
| 941 "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n" | 948 "var edge_count_offset = meta.node_fields.indexOf('edge_count');\n" |
| 942 "var node_fields_count = meta.node_fields.length;\n" | 949 "var node_fields_count = meta.node_fields.length;\n" |
| (...skipping 1902 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2845 map.AddRange(ToAddress(0x180), 0x80, 6U); | 2852 map.AddRange(ToAddress(0x180), 0x80, 6U); |
| 2846 map.AddRange(ToAddress(0x180), 0x80, 7U); | 2853 map.AddRange(ToAddress(0x180), 0x80, 7U); |
| 2847 CHECK_EQ(7u, map.GetTraceNodeId(ToAddress(0x180))); | 2854 CHECK_EQ(7u, map.GetTraceNodeId(ToAddress(0x180))); |
| 2848 CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200))); | 2855 CHECK_EQ(5u, map.GetTraceNodeId(ToAddress(0x200))); |
| 2849 CHECK_EQ(3u, map.size()); | 2856 CHECK_EQ(3u, map.size()); |
| 2850 | 2857 |
| 2851 map.Clear(); | 2858 map.Clear(); |
| 2852 CHECK_EQ(0u, map.size()); | 2859 CHECK_EQ(0u, map.size()); |
| 2853 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x400))); | 2860 CHECK_EQ(0u, map.GetTraceNodeId(ToAddress(0x400))); |
| 2854 } | 2861 } |
| 2862 |
| 2863 |
| 2864 TEST(SamplingHeapProfiler) { |
| 2865 v8::HandleScope scope(v8::Isolate::GetCurrent()); |
| 2866 LocalContext env; |
| 2867 v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler(); |
| 2868 |
| 2869 // GetHeapSample should return an empty stream when called before starting |
| 2870 // the sampling heap profiler. |
| 2871 TestJSONStream stream0; |
| 2872 heap_profiler->GetHeapSample(&stream0); |
| 2873 CHECK_EQ(stream0.size(), 0); |
| 2874 CHECK_EQ(1, stream0.eos_signaled()); |
| 2875 |
| 2876 heap_profiler->StartSamplingHeapProfiler(); |
| 2877 CompileRun( |
| 2878 "var A = [];\n" |
| 2879 "function bar(size) { return new Array(size); }\n" |
| 2880 "var foo = function() {\n" |
| 2881 " for (var i = 0; i < 1024; ++i) {\n" |
| 2882 " A[i] = bar(1024);\n" |
| 2883 " }\n" |
| 2884 "}\n" |
| 2885 "foo();"); |
| 2886 |
| 2887 TestJSONStream stream1; |
| 2888 heap_profiler->GetHeapSample(&stream1); |
| 2889 heap_profiler->StopSamplingHeapProfiler(); |
| 2890 |
| 2891 CHECK_GT(stream1.size(), 0); |
| 2892 CHECK_EQ(1, stream1.eos_signaled()); |
| 2893 |
| 2894 v8::Local<v8::Object> sample = stream1.verifyAndParseJSON(env); |
| 2895 CHECK(sample->IsArray()); |
| 2896 v8::Local<v8::Value> validation_result = CompileRun( |
| 2897 "parsed.every(allocation => {\n" |
| 2898 " return allocation.size && Array.isArray(allocation.stack) &&\n" |
| 2899 " allocation.stack.every(frame => {\n" |
| 2900 " return frame.hasOwnProperty('name') &&\n" |
| 2901 " frame.hasOwnProperty('scriptName') &&\n" |
| 2902 " frame.hasOwnProperty('line') &&\n" |
| 2903 " frame.hasOwnProperty('column');\n" |
| 2904 " });\n" |
| 2905 "});"); |
| 2906 CHECK(validation_result->IsTrue()); |
| 2907 |
| 2908 // Samples are reset once sampling heap profiler is stopped. |
| 2909 TestJSONStream stream2; |
| 2910 heap_profiler->GetHeapSample(&stream2); |
| 2911 CHECK_EQ(stream2.size(), 0); |
| 2912 CHECK_EQ(1, stream2.eos_signaled()); |
| 2913 } |
| OLD | NEW |