| Index: runtime/vm/dart_api_impl.cc
 | 
| diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
 | 
| index b7d4325f28c8bca6131a78fca83c6a2d69f7e26d..3aa65160d8cd131307c1bbb8d71e3d38085064dc 100644
 | 
| --- a/runtime/vm/dart_api_impl.cc
 | 
| +++ b/runtime/vm/dart_api_impl.cc
 | 
| @@ -5668,6 +5668,8 @@ DART_EXPORT void Dart_TimelineSetRecordedStreams(int64_t stream_mask) {
 | 
|        (stream_mask & DART_TIMELINE_STREAM_API) != 0);
 | 
|    isolate->GetCompilerStream()->set_enabled(
 | 
|        (stream_mask & DART_TIMELINE_STREAM_COMPILER) != 0);
 | 
| +  isolate->GetDartStream()->set_enabled(
 | 
| +      (stream_mask & DART_TIMELINE_STREAM_DART) != 0);
 | 
|    isolate->GetEmbedderStream()->set_enabled(
 | 
|        (stream_mask & DART_TIMELINE_STREAM_EMBEDDER) != 0);
 | 
|    isolate->GetGCStream()->set_enabled(
 | 
| @@ -5682,6 +5684,8 @@ DART_EXPORT void Dart_GlobalTimelineSetRecordedStreams(int64_t stream_mask) {
 | 
|    const bool api_enabled = (stream_mask & DART_TIMELINE_STREAM_API) != 0;
 | 
|    const bool compiler_enabled =
 | 
|        (stream_mask & DART_TIMELINE_STREAM_COMPILER) != 0;
 | 
| +  const bool dart_enabled =
 | 
| +      (stream_mask & DART_TIMELINE_STREAM_DART) != 0;
 | 
|    const bool embedder_enabled =
 | 
|        (stream_mask & DART_TIMELINE_STREAM_EMBEDDER) != 0;
 | 
|    const bool gc_enabled = (stream_mask & DART_TIMELINE_STREAM_GC) != 0;
 | 
| @@ -5689,6 +5693,7 @@ DART_EXPORT void Dart_GlobalTimelineSetRecordedStreams(int64_t stream_mask) {
 | 
|        (stream_mask & DART_TIMELINE_STREAM_ISOLATE) != 0;
 | 
|    Timeline::SetStreamAPIEnabled(api_enabled);
 | 
|    Timeline::SetStreamCompilerEnabled(compiler_enabled);
 | 
| +  Timeline::SetStreamDartEnabled(dart_enabled);
 | 
|    Timeline::SetStreamEmbedderEnabled(embedder_enabled);
 | 
|    Timeline::SetStreamGCEnabled(gc_enabled);
 | 
|    Timeline::SetStreamIsolateEnabled(isolate_enabled);
 | 
| @@ -5702,40 +5707,71 @@ DART_EXPORT void Dart_GlobalTimelineSetRecordedStreams(int64_t stream_mask) {
 | 
|  // '[' + ']' + '\0'.
 | 
|  #define MINIMUM_OUTPUT_LENGTH 3
 | 
|  
 | 
| -static void StreamToConsumer(Dart_StreamConsumer consumer,
 | 
| -                             void* user_data,
 | 
| -                             char* output,
 | 
| -                             intptr_t output_length) {
 | 
| -  if (output == NULL) {
 | 
| -    return;
 | 
| -  }
 | 
| -  if (output_length <= MINIMUM_OUTPUT_LENGTH) {
 | 
| -    return;
 | 
| -  }
 | 
| +// Trims the '[' and ']' characters and, depending on whether or not more events
 | 
| +// will follow, adjusts the last character of the string to either a '\0' or
 | 
| +// ','.
 | 
| +static char* TrimOutput(char* output,
 | 
| +                        intptr_t* output_length,
 | 
| +                        bool events_will_follow) {
 | 
| +  ASSERT(output != NULL);
 | 
| +  ASSERT(output_length != NULL);
 | 
| +  ASSERT(*output_length > MINIMUM_OUTPUT_LENGTH);
 | 
|    // 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 - 2] == ']');
 | 
| +  ASSERT(output[*output_length - 2] == ']');
 | 
| +  if (events_will_follow) {
 | 
| +    // Replace array closing character (']') with ','.
 | 
| +    output[*output_length - 2] = ',';
 | 
| +  } else {
 | 
| +    // Replace array closing character (']') with '\0'.
 | 
| +    output[*output_length - 2] = '\0';
 | 
| +  }
 | 
| +  // Skip the array opening character ('[').
 | 
| +  *output_length -= 2;
 | 
| +  return &output[1];
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void StartStreamToConsumer(Dart_StreamConsumer consumer,
 | 
| +                                  void* user_data,
 | 
| +                                  const char* stream_name) {
 | 
|    // Start stream.
 | 
| -  const char* kStreamName = "timeline";
 | 
| -  const intptr_t kDataSize = 64 * KB;
 | 
|    consumer(Dart_StreamConsumer_kStart,
 | 
| -           kStreamName,
 | 
| +           stream_name,
 | 
| +           NULL,
 | 
| +           0,
 | 
| +           user_data);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void FinishStreamToConsumer(Dart_StreamConsumer consumer,
 | 
| +                                   void* user_data,
 | 
| +                                   const char* stream_name) {
 | 
| +  // Finish stream.
 | 
| +  consumer(Dart_StreamConsumer_kFinish,
 | 
| +           stream_name,
 | 
|             NULL,
 | 
|             0,
 | 
|             user_data);
 | 
| +}
 | 
|  
 | 
| -  // Stream out data. Skipping the array characters.
 | 
| -  // Replace array close with '\0'.
 | 
| -  output[output_length - 2] = '\0';
 | 
| -  intptr_t cursor = 1;
 | 
| -  output_length -= 1;
 | 
| -  intptr_t remaining = output_length - 1;
 | 
|  
 | 
| +static void DataStreamToConsumer(Dart_StreamConsumer consumer,
 | 
| +                                 void* user_data,
 | 
| +                                 const char* output,
 | 
| +                                 intptr_t output_length,
 | 
| +                                 const char* stream_name) {
 | 
| +  if (output == NULL) {
 | 
| +    return;
 | 
| +  }
 | 
| +  const intptr_t kDataSize = 64 * KB;
 | 
| +  intptr_t cursor = 0;
 | 
| +  intptr_t remaining = output_length;
 | 
|    while (remaining >= kDataSize) {
 | 
|      consumer(Dart_StreamConsumer_kData,
 | 
| -             kStreamName,
 | 
| -             reinterpret_cast<uint8_t*>(&output[cursor]),
 | 
| +             stream_name,
 | 
| +             reinterpret_cast<const uint8_t*>(&output[cursor]),
 | 
|               kDataSize,
 | 
|               user_data);
 | 
|      cursor += kDataSize;
 | 
| @@ -5744,8 +5780,8 @@ static void StreamToConsumer(Dart_StreamConsumer consumer,
 | 
|    if (remaining > 0) {
 | 
|      ASSERT(remaining < kDataSize);
 | 
|      consumer(Dart_StreamConsumer_kData,
 | 
| -             kStreamName,
 | 
| -             reinterpret_cast<uint8_t*>(&output[cursor]),
 | 
| +             stream_name,
 | 
| +             reinterpret_cast<const uint8_t*>(&output[cursor]),
 | 
|               remaining,
 | 
|               user_data);
 | 
|      cursor += remaining;
 | 
| @@ -5753,13 +5789,53 @@ static void StreamToConsumer(Dart_StreamConsumer consumer,
 | 
|    }
 | 
|    ASSERT(cursor == output_length);
 | 
|    ASSERT(remaining == 0);
 | 
| +}
 | 
|  
 | 
| -  // Finish stream.
 | 
| -  consumer(Dart_StreamConsumer_kFinish,
 | 
| -           kStreamName,
 | 
| -           NULL,
 | 
| -           0,
 | 
| -           user_data);
 | 
| +
 | 
| +static bool StreamTraceEvents(Dart_StreamConsumer consumer,
 | 
| +                              void* user_data,
 | 
| +                              JSONStream* js,
 | 
| +                              const char* dart_events) {
 | 
| +  ASSERT(js != NULL);
 | 
| +  // Steal output from JSONStream.
 | 
| +  char* output = NULL;
 | 
| +  intptr_t output_length = 0;
 | 
| +  js->Steal(const_cast<const char**>(&output), &output_length);
 | 
| +
 | 
| +  const bool output_vm = output_length > MINIMUM_OUTPUT_LENGTH;
 | 
| +  const bool output_dart = dart_events != NULL;
 | 
| +
 | 
| +  if (!output_vm && !output_dart) {
 | 
| +    // We stole the JSONStream's output buffer, free it.
 | 
| +    free(output);
 | 
| +    // Nothing will be emitted.
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  // Start the stream.
 | 
| +  StartStreamToConsumer(consumer, user_data, "timeline");
 | 
| +
 | 
| +  // Send events from the VM.
 | 
| +  if (output_vm) {
 | 
| +    // Add one for the '\0' character.
 | 
| +    output_length++;
 | 
| +    char* trimmed_output = TrimOutput(output, &output_length, output_dart);
 | 
| +    DataStreamToConsumer(consumer, user_data,
 | 
| +                         trimmed_output, output_length, "timeline");
 | 
| +  }
 | 
| +  // We stole the JSONStream's output buffer, free it.
 | 
| +  free(output);
 | 
| +
 | 
| +  // Send events from dart.
 | 
| +  if (output_dart) {
 | 
| +    const intptr_t dart_events_len = strlen(dart_events) + 1;  // +1 for '\0'.
 | 
| +    DataStreamToConsumer(consumer, user_data,
 | 
| +                         dart_events, dart_events_len, "timeline");
 | 
| +  }
 | 
| +
 | 
| +  // Finish the stream.
 | 
| +  FinishStreamToConsumer(consumer, user_data, "timeline");
 | 
| +  return true;
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -5775,25 +5851,18 @@ DART_EXPORT bool Dart_TimelineGetTrace(Dart_StreamConsumer consumer,
 | 
|      // Nothing has been recorded.
 | 
|      return false;
 | 
|    }
 | 
| +  Thread* T = Thread::Current();
 | 
| +  StackZone zone(T);
 | 
|    // Reclaim all blocks cached by isolate.
 | 
|    Timeline::ReclaimIsolateBlocks();
 | 
|    JSONStream js;
 | 
|    IsolateTimelineEventFilter filter(isolate);
 | 
|    timeline_recorder->PrintTraceEvent(&js, &filter);
 | 
| -
 | 
| -  // Copy output.
 | 
| -  char* output = NULL;
 | 
| -  intptr_t output_length = 0;
 | 
| -  js.Steal(const_cast<const char**>(&output), &output_length);
 | 
| -  if (output != NULL) {
 | 
| -    // Add one for the '\0' character.
 | 
| -    output_length++;
 | 
| -    StreamToConsumer(consumer, user_data, output, output_length);
 | 
| -    // We stole the JSONStream's output buffer, free it.
 | 
| -    free(output);
 | 
| -    return output_length > MINIMUM_OUTPUT_LENGTH;
 | 
| -  }
 | 
| -  return false;
 | 
| +  const char* dart_events =
 | 
| +      DartTimelineEventIterator::PrintTraceEvents(timeline_recorder,
 | 
| +                                                  zone.GetZone(),
 | 
| +                                                  isolate);
 | 
| +  return StreamTraceEvents(consumer, user_data, &js, dart_events);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -5807,26 +5876,18 @@ DART_EXPORT bool Dart_GlobalTimelineGetTrace(Dart_StreamConsumer consumer,
 | 
|      // Nothing has been recorded.
 | 
|      return false;
 | 
|    }
 | 
| -
 | 
| +  Thread* T = Thread::Current();
 | 
| +  StackZone zone(T);
 | 
|    // Reclaim all blocks cached in the system.
 | 
|    Timeline::ReclaimAllBlocks();
 | 
|    JSONStream js;
 | 
|    TimelineEventFilter filter;
 | 
|    timeline_recorder->PrintTraceEvent(&js, &filter);
 | 
| -
 | 
| -  // Copy output.
 | 
| -  char* output = NULL;
 | 
| -  intptr_t output_length = 0;
 | 
| -  js.Steal(const_cast<const char**>(&output), &output_length);
 | 
| -  if (output != NULL) {
 | 
| -    // Add one for the '\0' character.
 | 
| -    output_length++;
 | 
| -    StreamToConsumer(consumer, user_data, output, output_length);
 | 
| -    // We stole the JSONStream's output buffer, free it.
 | 
| -    free(output);
 | 
| -    return output_length > MINIMUM_OUTPUT_LENGTH;
 | 
| -  }
 | 
| -  return false;
 | 
| +  const char* dart_events =
 | 
| +      DartTimelineEventIterator::PrintTraceEvents(timeline_recorder,
 | 
| +                                                  zone.GetZone(),
 | 
| +                                                  NULL);
 | 
| +  return StreamTraceEvents(consumer, user_data, &js, dart_events);
 | 
|  }
 | 
|  
 | 
|  
 | 
| 
 |