diff --git a/runtime/include/dart_tools_api.h b/runtime/include/dart_tools_api.h index 64991285f7b..0ed7cd28aee 100644 --- a/runtime/include/dart_tools_api.h +++ b/runtime/include/dart_tools_api.h @@ -976,6 +976,23 @@ typedef void (*Dart_StreamConsumer)( DART_EXPORT bool Dart_TimelineGetTrace(Dart_StreamConsumer consumer, void* user_data); + +/** + * Get the timeline for entire VM (including all isolates). + * + * NOTE: The timeline retrieved from this API call may not include the most + * recent events. + * + * \param consumer A Dart_StreamConsumer. + * \param user_data User data passed into consumer. + * + * NOTE: The trace-event format is documented here: https://goo.gl/hDZw5M + * + * \return True if a stream was output. + */ +DART_EXPORT bool Dart_GlobalTimelineGetTrace(Dart_StreamConsumer consumer, + void* user_data); + /** * Add a duration timeline event to the embedder stream for the current isolate. * diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index 10b9f95e29d..e47d14e0f75 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -5714,34 +5714,10 @@ DART_EXPORT void Dart_GlobalTimelineSetRecordedStreams(int64_t stream_mask) { } -DART_EXPORT bool Dart_TimelineGetTrace(Dart_StreamConsumer consumer, - void* user_data) { - Isolate* isolate = Isolate::Current(); - CHECK_ISOLATE(isolate); - if (consumer == NULL) { - return false; - } - TimelineEventRecorder* timeline_recorder = Timeline::recorder(); - if (timeline_recorder == NULL) { - // Nothing has been recorded. - return false; - } - // Suspend execution of other threads while serializing to JSON. - isolate->thread_registry()->SafepointThreads(); - JSONStream js; - IsolateTimelineEventFilter filter(isolate); - timeline_recorder->PrintJSON(&js, &filter); - // Resume execution of other threads. - isolate->thread_registry()->ResumeAllThreads(); - - // Copy output. - char* output = NULL; - intptr_t output_length = 0; - js.Steal(const_cast(&output), &output_length); - if (output != NULL) { - // Add one for the '\0' character. - output_length++; - } +static void StreamToConsumer(Dart_StreamConsumer consumer, + void* user_data, + char* output, + intptr_t output_length) { // Start stream. const char* kStreamName = "timeline"; const intptr_t kDataSize = 64 * KB; @@ -5775,8 +5751,6 @@ DART_EXPORT bool Dart_TimelineGetTrace(Dart_StreamConsumer consumer, } ASSERT(cursor == output_length); ASSERT(remaining == 0); - // We stole the JSONStream's output buffer, free it. - free(output); // Finish stream. consumer(Dart_StreamConsumer_kFinish, @@ -5784,6 +5758,76 @@ DART_EXPORT bool Dart_TimelineGetTrace(Dart_StreamConsumer consumer, NULL, 0, user_data); +} + + +DART_EXPORT bool Dart_TimelineGetTrace(Dart_StreamConsumer consumer, + void* user_data) { + Isolate* isolate = Isolate::Current(); + CHECK_ISOLATE(isolate); + if (consumer == NULL) { + return false; + } + TimelineEventRecorder* timeline_recorder = Timeline::recorder(); + if (timeline_recorder == NULL) { + // Nothing has been recorded. + return false; + } + // Suspend execution of other threads while serializing to JSON. + isolate->thread_registry()->SafepointThreads(); + // TODO(johnmccutchan): Reclaim open blocks from isolate so we have a complete + // timeline. + JSONStream js; + IsolateTimelineEventFilter filter(isolate); + timeline_recorder->PrintJSON(&js, &filter); + // Resume execution of other threads. + isolate->thread_registry()->ResumeAllThreads(); + + // Copy output. + char* output = NULL; + intptr_t output_length = 0; + js.Steal(const_cast(&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 true; +} + + +DART_EXPORT bool Dart_GlobalTimelineGetTrace(Dart_StreamConsumer consumer, + void* user_data) { + if (consumer == NULL) { + return false; + } + TimelineEventRecorder* timeline_recorder = Timeline::recorder(); + if (timeline_recorder == NULL) { + // Nothing has been recorded. + return false; + } + + // TODO(johnmccutchan): Reclaim all open blocks from the system so we have + // a complete timeline. + JSONStream js; + TimelineEventFilter filter; + timeline_recorder->PrintJSON(&js, &filter); + + // Copy output. + char* output = NULL; + intptr_t output_length = 0; + js.Steal(const_cast(&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 true; } diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc index 92ace20d1cd..39a7f3e1af6 100644 --- a/runtime/vm/dart_api_impl_test.cc +++ b/runtime/vm/dart_api_impl_test.cc @@ -9483,4 +9483,58 @@ TEST_CASE(Timeline_Dart_TimelineGetTraceGlobalOverride) { free(data.buffer); } + +UNIT_TEST_CASE(Timeline_Dart_GlobalTimelineGetTrace) { + const char* buffer = NULL; + intptr_t buffer_length = 0; + bool success = false; + + // Enable all streams. + Dart_GlobalTimelineSetRecordedStreams(DART_TIMELINE_STREAM_ALL | + DART_TIMELINE_STREAM_VM); + { + // Create isolate. + TestIsolateScope __test_isolate__; + Thread* __thread__ = Thread::Current(); + ASSERT(__thread__->isolate() == __test_isolate__.isolate()); + StackZone __zone__(__thread__); + HandleScope __hs__(__thread__); + + // Load test script. + const char* kScriptChars = + "foo() => 'a';\n" + "main() => foo();\n"; + + Dart_Handle lib = + TestCase::LoadTestScript(kScriptChars, NULL); + + // Invoke main, which will be compiled resulting in a compiler event in + // the timeline. + Dart_Handle result = Dart_Invoke(lib, + NewString("main"), + 0, + NULL); + EXPECT_VALID(result); + } + + // Isolate is shutdown now. + // Grab the global trace. + AppendData data; + success = Dart_GlobalTimelineGetTrace(AppendStreamConsumer, &data); + EXPECT(success); + buffer = reinterpret_cast(data.buffer); + buffer_length = data.buffer_length; + EXPECT(buffer_length > 0); + EXPECT(buffer != NULL); + + // Heartbeat test. + EXPECT_SUBSTRING("\"name\":\"InitializeIsolate\"", buffer); + EXPECT_SUBSTRING("\"cat\":\"Compiler\"", buffer); + EXPECT_SUBSTRING("\"name\":\"CompileFunction\"", buffer); + EXPECT_SUBSTRING("\"function\":\"::_main\"", buffer); + + // Free buffer allocated by AppendStreamConsumer + free(data.buffer); +} + } // namespace dart