Index: runtime/vm/timeline.cc |
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc |
index d55a8dcd9b6ff949ade135d668c4213168268963..1a8536a8c0d5c39832ed1da5c48e5078e336cf64 100644 |
--- a/runtime/vm/timeline.cc |
+++ b/runtime/vm/timeline.cc |
@@ -479,6 +479,36 @@ IsolateTimelineEventFilter::IsolateTimelineEventFilter(Isolate* isolate) |
} |
+DartTimelineEvent::DartTimelineEvent() |
+ : isolate_(NULL), |
+ event_as_json_(NULL) { |
+} |
+ |
+ |
+DartTimelineEvent::~DartTimelineEvent() { |
+ Clear(); |
+} |
+ |
+ |
+void DartTimelineEvent::Clear() { |
+ if (isolate_ != NULL) { |
+ isolate_ = NULL; |
+ } |
+ if (event_as_json_ != NULL) { |
+ free(event_as_json_); |
+ event_as_json_ = NULL; |
+ } |
+} |
+ |
+ |
+void DartTimelineEvent::Init(Isolate* isolate, const char* event) { |
+ ASSERT(isolate_ == NULL); |
+ ASSERT(event != NULL); |
+ isolate_ = isolate; |
+ event_as_json_ = strdup(event); |
+} |
+ |
+ |
TimelineEventRecorder::TimelineEventRecorder() |
: global_block_(NULL), |
async_id_(0) { |
@@ -550,6 +580,21 @@ TimelineEvent* TimelineEventRecorder::GlobalBlockStartEvent() { |
} |
+// Trims the ']' character. |
+static void TrimOutput(char* output, |
+ intptr_t* output_length) { |
+ ASSERT(output != NULL); |
+ ASSERT(output_length != NULL); |
+ ASSERT(*output_length >= 2); |
+ // We expect the first character to be the opening of an array. |
+ ASSERT(output[0] == '['); |
+ // We expect the last character to be the closing of an array. |
+ ASSERT(output[*output_length - 1] == ']'); |
+ // Skip the ]. |
+ *output_length -= 1; |
+} |
+ |
+ |
void TimelineEventRecorder::WriteTo(const char* directory) { |
Dart_FileOpenCallback file_open = Isolate::file_open_callback(); |
Dart_FileWriteCallback file_write = Isolate::file_write_callback(); |
@@ -557,13 +602,11 @@ void TimelineEventRecorder::WriteTo(const char* directory) { |
if ((file_open == NULL) || (file_write == NULL) || (file_close == NULL)) { |
return; |
} |
+ Thread* T = Thread::Current(); |
+ StackZone zone(T); |
Timeline::ReclaimAllBlocks(); |
- JSONStream js; |
- TimelineEventFilter filter; |
- PrintJSON(&js, &filter); |
- |
intptr_t pid = OS::ProcessId(); |
char* filename = OS::SCreate(NULL, |
"%s/dart-timeline-%" Pd ".json", directory, pid); |
@@ -574,8 +617,43 @@ void TimelineEventRecorder::WriteTo(const char* directory) { |
return; |
} |
free(filename); |
- (*file_write)(js.buffer()->buf(), js.buffer()->length(), file); |
+ |
+ JSONStream js; |
+ TimelineEventFilter filter; |
+ PrintTraceEvent(&js, &filter); |
+ // Steal output from JSONStream. |
+ char* output = NULL; |
+ intptr_t output_length = 0; |
+ js.Steal(const_cast<const char**>(&output), &output_length); |
+ TrimOutput(output, &output_length); |
+ ASSERT(output_length >= 1); |
+ (*file_write)(output, output_length, file); |
+ // Free the stolen output. |
+ free(output); |
+ |
+ const char* dart_events = |
+ DartTimelineEventIterator::PrintTraceEvents(this, |
+ zone.GetZone(), |
+ NULL); |
+ |
+ // If we wrote out vm events and have dart events, write out the comma. |
+ if ((output_length > 1) && (dart_events != NULL)) { |
+ // Write out the ',' character. |
+ const char* comma = ","; |
+ (*file_write)(comma, 1, file); |
+ } |
+ |
+ // Write out the Dart events. |
+ if (dart_events != NULL) { |
+ (*file_write)(dart_events, strlen(dart_events), file); |
+ } |
+ |
+ // Write out the ']' character. |
+ const char* array_close = "]"; |
+ (*file_write)(array_close, 1, file); |
(*file_close)(file); |
+ |
+ return; |
} |
@@ -615,7 +693,10 @@ TimelineEventRingRecorder::TimelineEventRingRecorder(intptr_t capacity) |
: blocks_(NULL), |
capacity_(capacity), |
num_blocks_(0), |
- block_cursor_(0) { |
+ block_cursor_(0), |
+ dart_events_(NULL), |
+ dart_events_capacity_(capacity), |
+ dart_events_cursor_(0) { |
// Capacity must be a multiple of TimelineEventBlock::kBlockSize |
ASSERT((capacity % TimelineEventBlock::kBlockSize) == 0); |
// Allocate blocks array. |
@@ -631,6 +712,13 @@ TimelineEventRingRecorder::TimelineEventRingRecorder(intptr_t capacity) |
for (intptr_t i = 0; i < num_blocks_ - 1; i++) { |
blocks_[i]->set_next(blocks_[i + 1]); |
} |
+ // Pre-allocate DartTimelineEvents. |
+ dart_events_ = |
+ reinterpret_cast<DartTimelineEvent**>( |
+ calloc(dart_events_capacity_, sizeof(DartTimelineEvent*))); |
+ for (intptr_t i = 0; i < dart_events_capacity_; i++) { |
+ dart_events_[i] = new DartTimelineEvent(); |
+ } |
} |
@@ -641,6 +729,12 @@ TimelineEventRingRecorder::~TimelineEventRingRecorder() { |
delete block; |
} |
free(blocks_); |
+ // Delete all DartTimelineEvents. |
+ for (intptr_t i = 0; i < dart_events_capacity_; i++) { |
+ DartTimelineEvent* event = dart_events_[i]; |
+ delete event; |
+ } |
+ free(dart_events_); |
} |
@@ -681,6 +775,33 @@ void TimelineEventRingRecorder::PrintJSON(JSONStream* js, |
} |
+void TimelineEventRingRecorder::AppendDartEvent(Isolate* isolate, |
+ const char* event) { |
+ MutexLocker ml(&lock_); |
+ // TODO(johnmccutchan): If locking becomes an issue, use the Isolate to store |
+ // the events. |
+ if (dart_events_cursor_ == dart_events_capacity_) { |
+ dart_events_cursor_ = 0; |
+ } |
+ ASSERT(dart_events_[dart_events_cursor_] != NULL); |
+ dart_events_[dart_events_cursor_]->Clear(); |
+ dart_events_[dart_events_cursor_]->Init(isolate, event); |
+ dart_events_cursor_++; |
+} |
+ |
+ |
+intptr_t TimelineEventRingRecorder::NumDartEventsLocked() { |
+ return dart_events_capacity_; |
+} |
+ |
+ |
+DartTimelineEvent* TimelineEventRingRecorder::DartEventAtLocked(intptr_t i) { |
+ ASSERT(i >= 0); |
+ ASSERT(i < dart_events_capacity_); |
+ return dart_events_[i]; |
+} |
+ |
+ |
void TimelineEventRingRecorder::PrintTraceEvent(JSONStream* js, |
TimelineEventFilter* filter) { |
JSONArray events(js); |
@@ -761,6 +882,25 @@ void TimelineEventStreamingRecorder::PrintTraceEvent( |
} |
+void TimelineEventStreamingRecorder::AppendDartEvent(Isolate* isolate, |
+ const char* event) { |
+ if (event != NULL) { |
+ StreamDartEvent(event); |
+ } |
+} |
+ |
+ |
+intptr_t TimelineEventStreamingRecorder::NumDartEventsLocked() { |
+ return 0; |
+} |
+ |
+ |
+DartTimelineEvent* TimelineEventStreamingRecorder::DartEventAtLocked( |
+ intptr_t i) { |
+ return NULL; |
+} |
+ |
+ |
TimelineEvent* TimelineEventStreamingRecorder::StartEvent() { |
TimelineEvent* event = new TimelineEvent(); |
return event; |
@@ -775,7 +915,10 @@ void TimelineEventStreamingRecorder::CompleteEvent(TimelineEvent* event) { |
TimelineEventEndlessRecorder::TimelineEventEndlessRecorder() |
: head_(NULL), |
- block_index_(0) { |
+ block_index_(0), |
+ dart_events_(NULL), |
+ dart_events_capacity_(0), |
+ dart_events_cursor_(0) { |
} |
@@ -800,6 +943,31 @@ void TimelineEventEndlessRecorder::PrintTraceEvent( |
} |
+void TimelineEventEndlessRecorder::AppendDartEvent(Isolate* isolate, |
+ const char* event) { |
+ MutexLocker ml(&lock_); |
+ // TODO(johnmccutchan): If locking becomes an issue, use the Isolate to store |
+ // the events. |
+ if (dart_events_cursor_ == dart_events_capacity_) { |
+ // Grow. |
+ intptr_t new_capacity = |
+ (dart_events_capacity_ == 0) ? 16 : dart_events_capacity_ * 2; |
+ dart_events_ = reinterpret_cast<DartTimelineEvent**>( |
+ realloc(dart_events_, new_capacity * sizeof(DartTimelineEvent*))); |
+ for (intptr_t i = dart_events_capacity_; i < new_capacity; i++) { |
+ // Fill with NULLs. |
+ dart_events_[i] = NULL; |
+ } |
+ dart_events_capacity_ = new_capacity; |
+ } |
+ ASSERT(dart_events_cursor_ < dart_events_capacity_); |
+ DartTimelineEvent* dart_event = new DartTimelineEvent(); |
+ dart_event->Init(isolate, event); |
+ ASSERT(dart_events_[dart_events_cursor_] == NULL); |
+ dart_events_[dart_events_cursor_++] = dart_event; |
+} |
+ |
+ |
TimelineEventBlock* TimelineEventEndlessRecorder::GetHeadBlockLocked() { |
return head_; |
} |
@@ -833,6 +1001,19 @@ TimelineEventBlock* TimelineEventEndlessRecorder::GetNewBlockLocked( |
} |
+intptr_t TimelineEventEndlessRecorder::NumDartEventsLocked() { |
+ return dart_events_cursor_; |
+} |
+ |
+ |
+DartTimelineEvent* TimelineEventEndlessRecorder::DartEventAtLocked( |
+ intptr_t i) { |
+ ASSERT(i >= 0); |
+ ASSERT(i < dart_events_cursor_); |
+ return dart_events_[i]; |
+} |
+ |
+ |
void TimelineEventEndlessRecorder::PrintJSONEvents( |
JSONArray* events, |
TimelineEventFilter* filter) const { |
@@ -1000,4 +1181,80 @@ TimelineEventBlock* TimelineEventBlockIterator::Next() { |
return r; |
} |
+ |
+DartTimelineEventIterator::DartTimelineEventIterator( |
+ TimelineEventRecorder* recorder) |
+ : cursor_(0), |
+ num_events_(0), |
+ recorder_(NULL) { |
+ Reset(recorder); |
+} |
+ |
+ |
+DartTimelineEventIterator::~DartTimelineEventIterator() { |
+ Reset(NULL); |
+} |
+ |
+ |
+void DartTimelineEventIterator::Reset(TimelineEventRecorder* recorder) { |
+ // Clear state. |
+ cursor_ = 0; |
+ num_events_ = 0; |
+ if (recorder_ != NULL) { |
+ // Unlock old recorder. |
+ recorder_->lock_.Unlock(); |
+ } |
+ recorder_ = recorder; |
+ if (recorder_ == NULL) { |
+ return; |
+ } |
+ // Lock new recorder. |
+ recorder_->lock_.Lock(); |
+ cursor_ = 0; |
+ num_events_ = recorder_->NumDartEventsLocked(); |
+} |
+ |
+ |
+bool DartTimelineEventIterator::HasNext() const { |
+ return cursor_ < num_events_; |
+} |
+ |
+ |
+DartTimelineEvent* DartTimelineEventIterator::Next() { |
+ ASSERT(cursor_ < num_events_); |
+ DartTimelineEvent* r = recorder_->DartEventAtLocked(cursor_); |
+ cursor_++; |
+ return r; |
+} |
+ |
+const char* DartTimelineEventIterator::PrintTraceEvents( |
+ TimelineEventRecorder* recorder, |
+ Zone* zone, |
+ Isolate* isolate) { |
+ if (recorder == NULL) { |
+ return NULL; |
+ } |
+ |
+ if (zone == NULL) { |
+ return NULL; |
+ } |
+ |
+ char* result = NULL; |
+ DartTimelineEventIterator iterator(recorder); |
+ while (iterator.HasNext()) { |
+ DartTimelineEvent* event = iterator.Next(); |
+ if (!event->IsValid()) { |
+ // Skip invalid |
+ continue; |
+ } |
+ if ((isolate != NULL) && (isolate != event->isolate())) { |
+ // If an isolate was specified, skip events from other isolates. |
+ continue; |
+ } |
+ ASSERT(event->event_as_json() != NULL); |
+ result = zone->ConcatStrings(result, event->event_as_json()); |
+ } |
+ return result; |
+} |
+ |
} // namespace dart |