enum OutputEncoding {
kAscii = 0 // 7-bit ASCII.
};
+ enum WriteResult {
+ kContinue = 0,
+ kAbort = 1
+ };
virtual ~OutputStream() {}
/** Notify about the end of stream. */
virtual void EndOfStream() = 0;
virtual int GetChunkSize() { return 1024; }
/** Get preferred output encoding. Called only once. */
virtual OutputEncoding GetOutputEncoding() { return kAscii; }
- /** Writes the next chunk of snapshot data into the stream. */
- virtual void WriteAsciiChunk(char* data, int size) = 0;
+ /**
+ * Writes the next chunk of snapshot data into the stream. Writing
+ * can be stopped by returning kAbort as function result. EndOfStream
+ * will not be called in case writing was aborted.
+ */
+ virtual WriteResult WriteAsciiChunk(char* data, int size) = 0;
};
: stream_(stream),
chunk_size_(stream->GetChunkSize()),
chunk_(chunk_size_),
- chunk_pos_(0) {
+ chunk_pos_(0),
+ aborted_(false) {
ASSERT(chunk_size_ > 0);
}
+ bool aborted() { return aborted_; }
void AddCharacter(char c) {
ASSERT(c != '\0');
ASSERT(chunk_pos_ < chunk_size_);
void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
void AddNumber(uint64_t n) { AddNumberImpl<uint64_t>(n, "%llu"); }
void Finalize() {
+ if (aborted_) return;
ASSERT(chunk_pos_ < chunk_size_);
if (chunk_pos_ != 0) {
WriteChunk();
}
}
void WriteChunk() {
- stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_);
+ if (aborted_) return;
+ if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
+ v8::OutputStream::kAbort) aborted_ = true;
}
v8::OutputStream* stream_;
int chunk_size_;
ScopedVector<char> chunk_;
int chunk_pos_;
+ bool aborted_;
};
void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
// Since nodes graph is cyclic, we need the first pass to enumerate
// them. Strings can be serialized in one pass.
EnumerateNodes();
+ SerializeImpl();
+ delete writer_;
+ writer_ = NULL;
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeImpl() {
writer_->AddCharacter('{');
writer_->AddString("\"snapshot\":{");
SerializeSnapshot();
+ if (writer_->aborted()) return;
writer_->AddString("},\n");
writer_->AddString("\"nodes\":[");
SerializeNodes();
+ if (writer_->aborted()) return;
writer_->AddString("],\n");
writer_->AddString("\"strings\":[");
SerializeStrings();
+ if (writer_->aborted()) return;
writer_->AddCharacter(']');
writer_->AddCharacter('}');
writer_->Finalize();
-
- delete writer_;
- writer_ = NULL;
}
writer_->AddNumber(children.length());
for (int i = 0; i < children.length(); ++i) {
SerializeEdge(&children[i]);
+ if (writer_->aborted()) return;
}
}
}
for (int i = 0; i < sorted_nodes.length(); ++i) {
SerializeNode(reinterpret_cast<HeapEntry*>(sorted_nodes[i]->key));
+ if (writer_->aborted()) return;
}
}
writer_->AddCharacter(',');
SerializeString(
reinterpret_cast<const unsigned char*>(sorted_strings[i]->key));
+ if (writer_->aborted()) return;
}
}
int GetNodeId(HeapEntry* entry);
int GetStringId(const char* s);
void SerializeEdge(HeapGraphEdge* edge);
+ void SerializeImpl();
void SerializeNode(HeapEntry* entry);
void SerializeNodes();
void SerializeSnapshot();
class TestJSONStream : public v8::OutputStream {
public:
- TestJSONStream() : eos_signaled_(0) {}
+ TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
+ explicit TestJSONStream(int abort_countdown)
+ : eos_signaled_(0), abort_countdown_(abort_countdown) {}
virtual ~TestJSONStream() {}
virtual void EndOfStream() { ++eos_signaled_; }
- virtual void WriteAsciiChunk(char* buffer, int chars_written) {
+ virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
+ if (abort_countdown_ > 0) --abort_countdown_;
+ if (abort_countdown_ == 0) return kAbort;
CHECK_GT(chars_written, 0);
i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
memcpy(chunk.start(), buffer, chars_written);
+ return kContinue;
}
void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
int eos_signaled() { return eos_signaled_; }
private:
i::Collector<char> buffer_;
int eos_signaled_;
+ int abort_countdown_;
};
class AsciiResource: public v8::String::ExternalAsciiStringResource {
*v8::String::Utf8Value(string));
}
+
+TEST(HeapSnapshotJSONSerializationAborting) {
+ v8::HandleScope scope;
+ LocalContext env;
+ const v8::HeapSnapshot* snapshot =
+ v8::HeapProfiler::TakeSnapshot(v8::String::New("abort"));
+ TestJSONStream stream(5);
+ snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
+ CHECK_GT(stream.size(), 0);
+ CHECK_EQ(0, stream.eos_signaled());
+}
+
#endif // ENABLE_LOGGING_AND_PROFILING