Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(786)

Unified Diff: src/profile-generator.cc

Issue 3311028: Implement heap snapshots serialization into JSON. API is designed (Closed)
Patch Set: Comments addressed Created 10 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/profile-generator.h ('k') | src/unicode.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/profile-generator.cc
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index 2de7a2fb912ae8a444524a9ca5ce7f27ee207d65..d9740011ec020001c4c5319f65d2d99a38358936 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -31,6 +31,7 @@
#include "global-handles.h"
#include "scopeinfo.h"
#include "top.h"
+#include "unicode.h"
#include "zone-inl.h"
#include "profile-generator-inl.h"
@@ -2132,6 +2133,333 @@ HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1,
return diff;
}
+
+class OutputStreamWriter {
+ public:
+ explicit OutputStreamWriter(v8::OutputStream* stream)
+ : stream_(stream),
+ chunk_size_(stream->GetChunkSize()),
+ chunk_(chunk_size_),
+ chunk_pos_(0) {
+ ASSERT(chunk_size_ > 0);
+ }
+ void AddCharacter(char c) {
+ ASSERT(c != '\0');
+ ASSERT(chunk_pos_ < chunk_size_);
+ chunk_[chunk_pos_++] = c;
+ MaybeWriteChunk();
+ }
+ void AddString(const char* s) {
+ AddSubstring(s, StrLength(s));
+ }
+ void AddSubstring(const char* s, int n) {
+ if (n <= 0) return;
+ ASSERT(static_cast<size_t>(n) <= strlen(s));
+ const char* s_end = s + n;
+ while (s < s_end) {
+ int s_chunk_size = Min(
+ chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
+ ASSERT(s_chunk_size > 0);
+ memcpy(chunk_.start() + chunk_pos_, s, s_chunk_size);
+ s += s_chunk_size;
+ chunk_pos_ += s_chunk_size;
+ MaybeWriteChunk();
+ }
+ }
+ void AddNumber(int n) { AddNumberImpl<int>(n, "%d"); }
+ void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
+ void AddNumber(uint64_t n) { AddNumberImpl<uint64_t>(n, "%llu"); }
+ void Finalize() {
+ ASSERT(chunk_pos_ < chunk_size_);
+ if (chunk_pos_ != 0) {
+ WriteChunk();
+ }
+ stream_->EndOfStream();
+ }
+
+ private:
+ template<typename T>
+ void AddNumberImpl(T n, const char* format) {
+ ScopedVector<char> buffer(32);
+ int result = OS::SNPrintF(buffer, format, n);
+ USE(result);
+ ASSERT(result != -1);
+ AddString(buffer.start());
+ }
+ void MaybeWriteChunk() {
+ ASSERT(chunk_pos_ <= chunk_size_);
+ if (chunk_pos_ == chunk_size_) {
+ WriteChunk();
+ chunk_pos_ = 0;
+ }
+ }
+ void WriteChunk() {
+ stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_);
+ }
+
+ v8::OutputStream* stream_;
+ int chunk_size_;
+ ScopedVector<char> chunk_;
+ int chunk_pos_;
+};
+
+void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
+ ASSERT(writer_ == NULL);
+ writer_ = new OutputStreamWriter(stream);
+
+ // Since nodes graph is cyclic, we need the first pass to enumerate
+ // them. Strings can be serialized in one pass.
+ EnumerateNodes();
+
+ writer_->AddCharacter('{');
+ writer_->AddString("\"snapshot\":{");
+ SerializeSnapshot();
+ writer_->AddString("},\n");
+ writer_->AddString("\"nodes\":[");
+ SerializeNodes();
+ writer_->AddString("],\n");
+ writer_->AddString("\"strings\":[");
+ SerializeStrings();
+ writer_->AddCharacter(']');
+ writer_->AddCharacter('}');
+ writer_->Finalize();
+
+ delete writer_;
+ writer_ = NULL;
+}
+
+
+class HeapSnapshotJSONSerializerEnumerator {
+ public:
+ explicit HeapSnapshotJSONSerializerEnumerator(HeapSnapshotJSONSerializer* s)
+ : s_(s) {
+ }
+ void Apply(HeapEntry** entry) {
+ s_->GetNodeId(*entry);
+ }
+ private:
+ HeapSnapshotJSONSerializer* s_;
+};
+
+void HeapSnapshotJSONSerializer::EnumerateNodes() {
+ GetNodeId(snapshot_->root()); // Make sure root gets the first id.
+ HeapSnapshotJSONSerializerEnumerator iter(this);
+ snapshot_->IterateEntries(&iter);
+}
+
+
+int HeapSnapshotJSONSerializer::GetNodeId(HeapEntry* entry) {
+ HashMap::Entry* cache_entry = nodes_.Lookup(entry, ObjectHash(entry), true);
+ if (cache_entry->value == NULL) {
+ cache_entry->value = reinterpret_cast<void*>(next_node_id_++);
+ }
+ return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
+}
+
+
+int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
+ HashMap::Entry* cache_entry = strings_.Lookup(
+ const_cast<char*>(s), ObjectHash(s), true);
+ if (cache_entry->value == NULL) {
+ cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
+ }
+ return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) {
+ writer_->AddCharacter(',');
+ writer_->AddNumber(edge->type());
+ writer_->AddCharacter(',');
+ if (edge->type() == HeapGraphEdge::kElement) {
+ writer_->AddNumber(edge->index());
+ } else {
+ writer_->AddNumber(GetStringId(edge->name()));
+ }
+ writer_->AddCharacter(',');
+ writer_->AddNumber(GetNodeId(edge->to()));
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
+ writer_->AddCharacter('\n');
+ writer_->AddCharacter(',');
+ writer_->AddNumber(entry->type());
+ writer_->AddCharacter(',');
+ writer_->AddNumber(GetStringId(entry->name()));
+ writer_->AddCharacter(',');
+ writer_->AddNumber(entry->id());
+ writer_->AddCharacter(',');
+ writer_->AddNumber(entry->self_size());
+ Vector<HeapGraphEdge> children = entry->children();
+ writer_->AddCharacter(',');
+ writer_->AddNumber(children.length());
+ for (int i = 0; i < children.length(); ++i) {
+ SerializeEdge(&children[i]);
+ }
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeNodes() {
+ // The first (zero) item of nodes array is a JSON-ified object
+ // describing node serialization layout.
+ // We use a set of macros to improve readability.
+#define JSON_A(s) "["s"]"
+#define JSON_O(s) "{"s"}"
+#define JSON_S(s) "\\\""s"\\\""
+ writer_->AddString("\"" JSON_O(
+ JSON_S("fields") ":" JSON_A(
+ JSON_S("type")
+ "," JSON_S("name")
+ "," JSON_S("id")
+ "," JSON_S("self_size")
+ "," JSON_S("children_count")
+ "," JSON_S("children"))
+ "," JSON_S("types") ":" JSON_A(
+ JSON_A(
+ JSON_S("internal")
+ "," JSON_S("array")
+ "," JSON_S("string")
+ "," JSON_S("object")
+ "," JSON_S("code")
+ "," JSON_S("closure"))
+ "," JSON_S("string")
+ "," JSON_S("number")
+ "," JSON_S("number")
+ "," JSON_S("number")
+ "," JSON_O(
+ JSON_S("fields") ":" JSON_A(
+ JSON_S("type")
+ "," JSON_S("name_or_index")
+ "," JSON_S("to_node"))
+ "," JSON_S("types") ":" JSON_A(
+ JSON_A(
+ JSON_S("context")
+ "," JSON_S("element")
+ "," JSON_S("property")
+ "," JSON_S("internal"))
+ "," JSON_S("string_or_number")
+ "," JSON_S("node"))))) "\"");
+#undef JSON_S
+#undef JSON_O
+#undef JSON_A
+
+ const int node_fields_count = 5; // type,name,id,self_size,children_count.
+ const int edge_fields_count = 3; // type,name|index,to_node.
+ List<HashMap::Entry*> sorted_nodes;
+ SortHashMap(&nodes_, &sorted_nodes);
+ // Rewrite node ids, so they refer to actual array positions.
+ if (sorted_nodes.length() > 1) {
+ // Nodes start from array index 1.
+ int prev_value = 1;
+ sorted_nodes[0]->value = reinterpret_cast<void*>(prev_value);
+ for (int i = 1; i < sorted_nodes.length(); ++i) {
+ HeapEntry* prev_heap_entry =
+ reinterpret_cast<HeapEntry*>(sorted_nodes[i-1]->key);
+ prev_value += node_fields_count +
+ prev_heap_entry->children().length() * edge_fields_count;
+ sorted_nodes[i]->value = reinterpret_cast<void*>(prev_value);
+ }
+ }
+ for (int i = 0; i < sorted_nodes.length(); ++i) {
+ SerializeNode(reinterpret_cast<HeapEntry*>(sorted_nodes[i]->key));
+ }
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeSnapshot() {
+ writer_->AddString("\"title\":\"");
+ writer_->AddString(snapshot_->title());
+ writer_->AddString("\"");
+ writer_->AddString(",\"uid\":");
+ writer_->AddNumber(snapshot_->uid());
+}
+
+
+static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
+ static const char hex_chars[] = "0123456789ABCDEF";
+ w->AddString("\\u");
+ w->AddCharacter(hex_chars[(u >> 12) & 0xf]);
+ w->AddCharacter(hex_chars[(u >> 8) & 0xf]);
+ w->AddCharacter(hex_chars[(u >> 4) & 0xf]);
+ w->AddCharacter(hex_chars[u & 0xf]);
+}
+
+void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
+ writer_->AddCharacter('\n');
+ writer_->AddCharacter('\"');
+ for ( ; *s != '\0'; ++s) {
+ switch (*s) {
+ case '\b':
+ writer_->AddString("\\b");
+ continue;
+ case '\f':
+ writer_->AddString("\\f");
+ continue;
+ case '\n':
+ writer_->AddString("\\n");
+ continue;
+ case '\r':
+ writer_->AddString("\\r");
+ continue;
+ case '\t':
+ writer_->AddString("\\t");
+ continue;
+ case '\"':
+ case '\\':
+ writer_->AddCharacter('\\');
+ writer_->AddCharacter(*s);
+ continue;
+ default:
+ if (*s > 31 && *s < 128) {
+ writer_->AddCharacter(*s);
+ } else if (*s <= 31) {
+ // Special character with no dedicated literal.
+ WriteUChar(writer_, *s);
+ } else {
+ // Convert UTF-8 into \u UTF-16 literal.
+ unsigned length = 1, cursor = 0;
+ for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
+ unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
+ if (c != unibrow::Utf8::kBadChar) {
+ WriteUChar(writer_, c);
+ ASSERT(cursor != 0);
+ s += cursor - 1;
+ } else {
+ writer_->AddCharacter('?');
+ }
+ }
+ }
+ }
+ writer_->AddCharacter('\"');
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeStrings() {
+ List<HashMap::Entry*> sorted_strings;
+ SortHashMap(&strings_, &sorted_strings);
+ writer_->AddString("\"<dummy>\"");
+ for (int i = 0; i < sorted_strings.length(); ++i) {
+ writer_->AddCharacter(',');
+ SerializeString(
+ reinterpret_cast<const unsigned char*>(sorted_strings[i]->key));
+ }
+}
+
+
+template<typename T>
+inline static int SortUsingEntryValue(const T* x, const T* y) {
+ return reinterpret_cast<intptr_t>((*x)->value) -
+ reinterpret_cast<intptr_t>((*y)->value);
+}
+
+void HeapSnapshotJSONSerializer::SortHashMap(
+ HashMap* map, List<HashMap::Entry*>* sorted_entries) {
+ for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p))
+ sorted_entries->Add(p);
+ sorted_entries->Sort(SortUsingEntryValue);
+}
+
} } // namespace v8::internal
#endif // ENABLE_LOGGING_AND_PROFILING
« no previous file with comments | « src/profile-generator.h ('k') | src/unicode.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698