1// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/globals.h"
6#if defined(SUPPORT_TIMELINE)
7
8#include "vm/timeline.h"
9
10#include <errno.h>
11#include <fcntl.h>
12
13#include <cstdlib>
14
15#include "platform/atomic.h"
16#include "vm/isolate.h"
17#include "vm/json_stream.h"
18#include "vm/lockers.h"
19#include "vm/log.h"
20#include "vm/object.h"
21#include "vm/service.h"
22#include "vm/service_event.h"
23#include "vm/thread.h"
24
25namespace dart {
26
27DEFINE_FLAG(bool, complete_timeline, false, "Record the complete timeline");
28DEFINE_FLAG(bool, startup_timeline, false, "Record the startup timeline");
29DEFINE_FLAG(
30 bool,
31 systrace_timeline,
32 false,
33 "Record the timeline to the platform's tracing service if there is one");
34DEFINE_FLAG(bool, trace_timeline, false, "Trace timeline backend");
35DEFINE_FLAG(bool,
36 trace_timeline_analysis,
37 false,
38 "Trace timeline analysis backend");
39DEFINE_FLAG(bool,
40 timing,
41 false,
42 "Dump isolate timing information from timeline.");
43DEFINE_FLAG(charp,
44 timeline_dir,
45 NULL,
46 "Enable all timeline trace streams and output VM global trace "
47 "into specified directory.");
48DEFINE_FLAG(charp,
49 timeline_streams,
50 NULL,
51 "Comma separated list of timeline streams to record. "
52 "Valid values: all, API, Compiler, CompilerVerbose, Dart, "
53 "Debugger, Embedder, GC, Isolate, and VM.");
54DEFINE_FLAG(charp,
55 timeline_recorder,
56 "ring",
57 "Select the timeline recorder used. "
58 "Valid values: ring, endless, startup, and systrace.")
59
60// Implementation notes:
61//
62// Writing events:
63// |TimelineEvent|s are written into |TimelineEventBlock|s. Each |Thread| caches
64// a |TimelineEventBlock| object so that it can write events without
65// synchronizing with other threads in the system. Even though the |Thread| owns
66// the |TimelineEventBlock| the block may need to be reclaimed by the reporting
67// system. To support that, a |Thread| must hold its |timeline_block_lock_|
68// when operating on the |TimelineEventBlock|. This lock will only ever be
69// busy if blocks are being reclaimed by the reporting system.
70//
71// Reporting:
72// When requested, the timeline is serialized in the trace-event format
73// (https://goo.gl/hDZw5M). The request can be for a VM-wide timeline or an
74// isolate specific timeline. In both cases it may be that a thread has
75// a |TimelineEventBlock| cached in TLS partially filled with events. In order
76// to report a complete timeline the cached |TimelineEventBlock|s need to be
77// reclaimed.
78//
79// Reclaiming open |TimelineEventBlock|s from threads:
80//
81// Each |Thread| can have one |TimelineEventBlock| cached in it.
82//
83// To reclaim blocks, we iterate over all threads and remove the cached
84// |TimelineEventBlock| from each thread. This is safe because we hold the
85// |Thread|'s |timeline_block_lock_| meaning the block can't be being modified.
86//
87// Locking notes:
88// The following locks are used by the timeline system:
89// - |TimelineEventRecorder::lock_| This lock is held whenever a
90// |TimelineEventBlock| is being requested or reclaimed.
91// - |Thread::timeline_block_lock_| This lock is held whenever a |Thread|'s
92// cached block is being operated on.
93// - |Thread::thread_list_lock_| This lock is held when iterating over
94// |Thread|s.
95//
96// Locks must always be taken in the following order:
97// |Thread::thread_list_lock_|
98// |Thread::timeline_block_lock_|
99// |TimelineEventRecorder::lock_|
100//
101
102static TimelineEventRecorder* CreateTimelineRecorder() {
103 // Some flags require that we use the endless recorder.
104 const bool use_endless_recorder =
105 (FLAG_timeline_dir != NULL) || FLAG_timing || FLAG_complete_timeline;
106
107 const bool use_startup_recorder = FLAG_startup_timeline;
108 const bool use_systrace_recorder = FLAG_systrace_timeline;
109 const char* flag = FLAG_timeline_recorder;
110
111 if (use_systrace_recorder || (flag != NULL)) {
112 if (use_systrace_recorder || (strcmp("systrace", flag) == 0)) {
113 if (FLAG_trace_timeline) {
114 THR_Print("Using the Systrace timeline recorder.\n");
115 }
116
117#if defined(HOST_OS_LINUX) || defined(HOST_OS_ANDROID)
118 return new TimelineEventSystraceRecorder();
119#elif defined(HOST_OS_MACOS)
120 if (__builtin_available(iOS 12.0, macOS 10.14, *)) {
121 return new TimelineEventMacosRecorder();
122 }
123#elif defined(HOST_OS_FUCHSIA)
124 return new TimelineEventFuchsiaRecorder();
125#else
126 OS::PrintErr(
127 "Warning: The systrace timeline recorder is equivalent to the"
128 "ring recorder on this platform.");
129 return new TimelineEventRingRecorder();
130#endif
131 }
132 }
133
134 if (use_endless_recorder || (flag != NULL)) {
135 if (use_endless_recorder || (strcmp("endless", flag) == 0)) {
136 if (FLAG_trace_timeline) {
137 THR_Print("Using the endless timeline recorder.\n");
138 }
139 return new TimelineEventEndlessRecorder();
140 }
141 }
142
143 if (use_startup_recorder || (flag != NULL)) {
144 if (use_startup_recorder || (strcmp("startup", flag) == 0)) {
145 if (FLAG_trace_timeline) {
146 THR_Print("Using the startup recorder.\n");
147 }
148 return new TimelineEventStartupRecorder();
149 }
150 }
151
152 if (FLAG_trace_timeline) {
153 THR_Print("Using the ring timeline recorder.\n");
154 }
155
156 // Always fall back to the ring recorder.
157 return new TimelineEventRingRecorder();
158}
159
160// Returns a caller freed array of stream names in FLAG_timeline_streams.
161static MallocGrowableArray<char*>* GetEnabledByDefaultTimelineStreams() {
162 MallocGrowableArray<char*>* result = new MallocGrowableArray<char*>();
163 if (FLAG_timeline_streams == NULL) {
164 // Nothing set.
165 return result;
166 }
167 char* save_ptr; // Needed for strtok_r.
168 // strtok modifies arg 1 so we make a copy of it.
169 char* streams = Utils::StrDup(FLAG_timeline_streams);
170 char* token = strtok_r(streams, ",", &save_ptr);
171 while (token != NULL) {
172 result->Add(Utils::StrDup(token));
173 token = strtok_r(NULL, ",", &save_ptr);
174 }
175 free(streams);
176 return result;
177}
178
179// Frees the result of |GetEnabledByDefaultTimelineStreams|.
180static void FreeEnabledByDefaultTimelineStreams(
181 MallocGrowableArray<char*>* streams) {
182 if (streams == NULL) {
183 return;
184 }
185 for (intptr_t i = 0; i < streams->length(); i++) {
186 free((*streams)[i]);
187 }
188 delete streams;
189}
190
191// Returns true if |streams| contains |stream| or "all". Not case sensitive.
192static bool HasStream(MallocGrowableArray<char*>* streams, const char* stream) {
193 if ((FLAG_timeline_dir != NULL) || FLAG_timing || FLAG_complete_timeline ||
194 FLAG_startup_timeline) {
195 return true;
196 }
197 for (intptr_t i = 0; i < streams->length(); i++) {
198 const char* checked_stream = (*streams)[i];
199 if ((strstr(checked_stream, "all") != NULL) ||
200 (strstr(checked_stream, stream) != NULL)) {
201 return true;
202 }
203 }
204 return false;
205}
206
207void Timeline::Init() {
208 ASSERT(recorder_ == NULL);
209 recorder_ = CreateTimelineRecorder();
210 ASSERT(recorder_ != NULL);
211 enabled_streams_ = GetEnabledByDefaultTimelineStreams();
212// Global overrides.
213#define TIMELINE_STREAM_FLAG_DEFAULT(name, fuchsia_name) \
214 stream_##name##_.set_enabled(HasStream(enabled_streams_, #name));
215 TIMELINE_STREAM_LIST(TIMELINE_STREAM_FLAG_DEFAULT)
216#undef TIMELINE_STREAM_FLAG_DEFAULT
217}
218
219void Timeline::Cleanup() {
220 ASSERT(recorder_ != NULL);
221
222#ifndef PRODUCT
223 if (FLAG_timeline_dir != NULL) {
224 recorder_->WriteTo(FLAG_timeline_dir);
225 }
226#endif
227
228// Disable global streams.
229#define TIMELINE_STREAM_DISABLE(name, fuchsia_name) \
230 Timeline::stream_##name##_.set_enabled(false);
231 TIMELINE_STREAM_LIST(TIMELINE_STREAM_DISABLE)
232#undef TIMELINE_STREAM_DISABLE
233 delete recorder_;
234 recorder_ = NULL;
235 if (enabled_streams_ != NULL) {
236 FreeEnabledByDefaultTimelineStreams(enabled_streams_);
237 enabled_streams_ = NULL;
238 }
239}
240
241TimelineEventRecorder* Timeline::recorder() {
242 return recorder_;
243}
244
245void Timeline::ReclaimCachedBlocksFromThreads() {
246 TimelineEventRecorder* recorder = Timeline::recorder();
247 if (recorder == NULL) {
248 return;
249 }
250
251 // Iterate over threads.
252 OSThreadIterator it;
253 while (it.HasNext()) {
254 OSThread* thread = it.Next();
255 MutexLocker ml(thread->timeline_block_lock());
256 // Grab block and clear it.
257 TimelineEventBlock* block = thread->timeline_block();
258 thread->set_timeline_block(NULL);
259 // TODO(johnmccutchan): Consider dropping the timeline_block_lock here
260 // if we can do it everywhere. This would simplify the lock ordering
261 // requirements.
262 recorder->FinishBlock(block);
263 }
264}
265
266#ifndef PRODUCT
267void Timeline::PrintFlagsToJSONArray(JSONArray* arr) {
268#define ADD_RECORDED_STREAM_NAME(name, fuchsia_name) \
269 if (stream_##name##_.enabled()) { \
270 arr->AddValue(#name); \
271 }
272 TIMELINE_STREAM_LIST(ADD_RECORDED_STREAM_NAME);
273#undef ADD_RECORDED_STREAM_NAME
274}
275
276void Timeline::PrintFlagsToJSON(JSONStream* js) {
277 JSONObject obj(js);
278 obj.AddProperty("type", "TimelineFlags");
279 TimelineEventRecorder* recorder = Timeline::recorder();
280 if (recorder == NULL) {
281 obj.AddProperty("recorderName", "null");
282 } else {
283 obj.AddProperty("recorderName", recorder->name());
284 }
285 {
286 JSONArray availableStreams(&obj, "availableStreams");
287#define ADD_STREAM_NAME(name, fuchsia_name) availableStreams.AddValue(#name);
288 TIMELINE_STREAM_LIST(ADD_STREAM_NAME);
289#undef ADD_STREAM_NAME
290 }
291 {
292 JSONArray recordedStreams(&obj, "recordedStreams");
293#define ADD_RECORDED_STREAM_NAME(name, fuchsia_name) \
294 if (stream_##name##_.enabled()) { \
295 recordedStreams.AddValue(#name); \
296 }
297 TIMELINE_STREAM_LIST(ADD_RECORDED_STREAM_NAME);
298#undef ADD_RECORDED_STREAM_NAME
299 }
300}
301#endif
302
303void Timeline::Clear() {
304 TimelineEventRecorder* recorder = Timeline::recorder();
305 if (recorder == NULL) {
306 return;
307 }
308 ReclaimCachedBlocksFromThreads();
309 recorder->Clear();
310}
311
312void TimelineEventArguments::SetNumArguments(intptr_t length) {
313 if (length == length_) {
314 return;
315 }
316 if (length == 0) {
317 Free();
318 return;
319 }
320 if (buffer_ == NULL) {
321 // calloc already nullifies
322 buffer_ = reinterpret_cast<TimelineEventArgument*>(
323 calloc(sizeof(TimelineEventArgument), length));
324 } else {
325 for (intptr_t i = length; i < length_; ++i) {
326 free(buffer_[i].value);
327 }
328 buffer_ = reinterpret_cast<TimelineEventArgument*>(
329 realloc(buffer_, sizeof(TimelineEventArgument) * length));
330 if (length > length_) {
331 memset(buffer_ + length_, 0,
332 sizeof(TimelineEventArgument) * (length - length_));
333 }
334 }
335 length_ = length;
336}
337
338void TimelineEventArguments::SetArgument(intptr_t i,
339 const char* name,
340 char* argument) {
341 ASSERT(i >= 0);
342 ASSERT(i < length_);
343 buffer_[i].name = name;
344 buffer_[i].value = argument;
345}
346
347void TimelineEventArguments::CopyArgument(intptr_t i,
348 const char* name,
349 const char* argument) {
350 SetArgument(i, name, Utils::StrDup(argument));
351}
352
353void TimelineEventArguments::FormatArgument(intptr_t i,
354 const char* name,
355 const char* fmt,
356 va_list args) {
357 ASSERT(i >= 0);
358 ASSERT(i < length_);
359 va_list args2;
360 va_copy(args2, args);
361 intptr_t len = Utils::VSNPrint(NULL, 0, fmt, args);
362 va_end(args);
363
364 char* buffer = reinterpret_cast<char*>(malloc(len + 1));
365 Utils::VSNPrint(buffer, (len + 1), fmt, args2);
366 va_end(args2);
367
368 SetArgument(i, name, buffer);
369}
370
371void TimelineEventArguments::StealArguments(TimelineEventArguments* arguments) {
372 Free();
373 length_ = arguments->length_;
374 buffer_ = arguments->buffer_;
375 arguments->length_ = 0;
376 arguments->buffer_ = NULL;
377}
378
379void TimelineEventArguments::Free() {
380 if (buffer_ == NULL) {
381 return;
382 }
383 for (intptr_t i = 0; i < length_; i++) {
384 free(buffer_[i].value);
385 }
386 free(buffer_);
387 buffer_ = NULL;
388 length_ = 0;
389}
390
391TimelineEventRecorder* Timeline::recorder_ = NULL;
392MallocGrowableArray<char*>* Timeline::enabled_streams_ = NULL;
393
394#define TIMELINE_STREAM_DEFINE(name, fuchsia_name) \
395 TimelineStream Timeline::stream_##name##_(#name, fuchsia_name, false);
396TIMELINE_STREAM_LIST(TIMELINE_STREAM_DEFINE)
397#undef TIMELINE_STREAM_DEFINE
398
399TimelineEvent::TimelineEvent()
400 : timestamp0_(0),
401 timestamp1_(0),
402 thread_timestamp0_(-1),
403 thread_timestamp1_(-1),
404 state_(0),
405 label_(NULL),
406 stream_(NULL),
407 thread_(OSThread::kInvalidThreadId),
408 isolate_id_(ILLEGAL_PORT),
409 isolate_group_id_(0) {}
410
411TimelineEvent::~TimelineEvent() {
412 Reset();
413}
414
415void TimelineEvent::Reset() {
416 if (owns_label() && label_ != NULL) {
417 free(const_cast<char*>(label_));
418 }
419 state_ = 0;
420 thread_ = OSThread::kInvalidThreadId;
421 isolate_id_ = ILLEGAL_PORT;
422 isolate_group_id_ = 0;
423 stream_ = NULL;
424 label_ = NULL;
425 arguments_.Free();
426 set_event_type(kNone);
427 set_pre_serialized_args(false);
428 set_owns_label(false);
429}
430
431void TimelineEvent::AsyncBegin(const char* label,
432 int64_t async_id,
433 int64_t micros) {
434 Init(kAsyncBegin, label);
435 set_timestamp0(micros);
436 // Overload timestamp1_ with the async_id.
437 set_timestamp1(async_id);
438}
439
440void TimelineEvent::AsyncInstant(const char* label,
441 int64_t async_id,
442 int64_t micros) {
443 Init(kAsyncInstant, label);
444 set_timestamp0(micros);
445 // Overload timestamp1_ with the async_id.
446 set_timestamp1(async_id);
447}
448
449void TimelineEvent::AsyncEnd(const char* label,
450 int64_t async_id,
451 int64_t micros) {
452 Init(kAsyncEnd, label);
453 set_timestamp0(micros);
454 // Overload timestamp1_ with the async_id.
455 set_timestamp1(async_id);
456}
457
458void TimelineEvent::DurationBegin(const char* label,
459 int64_t micros,
460 int64_t thread_micros) {
461 Init(kDuration, label);
462 set_timestamp0(micros);
463 set_thread_timestamp0(thread_micros);
464}
465
466void TimelineEvent::DurationEnd(int64_t micros, int64_t thread_micros) {
467 ASSERT(timestamp1_ == 0);
468 set_timestamp1(micros);
469 set_thread_timestamp1(thread_micros);
470}
471
472void TimelineEvent::Instant(const char* label, int64_t micros) {
473 Init(kInstant, label);
474 set_timestamp0(micros);
475}
476
477void TimelineEvent::Duration(const char* label,
478 int64_t start_micros,
479 int64_t end_micros,
480 int64_t thread_start_micros,
481 int64_t thread_end_micros) {
482 Init(kDuration, label);
483 set_timestamp0(start_micros);
484 set_timestamp1(end_micros);
485 set_thread_timestamp0(thread_start_micros);
486 set_thread_timestamp1(thread_end_micros);
487}
488
489void TimelineEvent::Begin(const char* label,
490 int64_t micros,
491 int64_t thread_micros) {
492 Init(kBegin, label);
493 set_timestamp0(micros);
494 set_thread_timestamp0(thread_micros);
495}
496
497void TimelineEvent::End(const char* label,
498 int64_t micros,
499 int64_t thread_micros) {
500 Init(kEnd, label);
501 set_timestamp0(micros);
502 set_thread_timestamp0(thread_micros);
503}
504
505void TimelineEvent::Counter(const char* label, int64_t micros) {
506 Init(kCounter, label);
507 set_timestamp0(micros);
508}
509
510void TimelineEvent::FlowBegin(const char* label,
511 int64_t async_id,
512 int64_t micros) {
513 Init(kFlowBegin, label);
514 set_timestamp0(micros);
515 // Overload timestamp1_ with the async_id.
516 set_timestamp1(async_id);
517}
518
519void TimelineEvent::FlowStep(const char* label,
520 int64_t async_id,
521 int64_t micros) {
522 Init(kFlowStep, label);
523 set_timestamp0(micros);
524 // Overload timestamp1_ with the async_id.
525 set_timestamp1(async_id);
526}
527
528void TimelineEvent::FlowEnd(const char* label,
529 int64_t async_id,
530 int64_t micros) {
531 Init(kFlowEnd, label);
532 set_timestamp0(micros);
533 // Overload timestamp1_ with the async_id.
534 set_timestamp1(async_id);
535}
536
537void TimelineEvent::Metadata(const char* label, int64_t micros) {
538 Init(kMetadata, label);
539 set_timestamp0(micros);
540}
541
542void TimelineEvent::CompleteWithPreSerializedArgs(char* args_json) {
543 set_pre_serialized_args(true);
544 SetNumArguments(1);
545 SetArgument(0, "Dart Arguments", args_json);
546 Complete();
547}
548
549void TimelineEvent::FormatArgument(intptr_t i,
550 const char* name,
551 const char* fmt,
552 ...) {
553 va_list args;
554 va_start(args, fmt);
555 arguments_.FormatArgument(i, name, fmt, args);
556}
557
558void TimelineEvent::Complete() {
559 TimelineEventRecorder* recorder = Timeline::recorder();
560 if (recorder != NULL) {
561 recorder->CompleteEvent(this);
562 }
563}
564
565void TimelineEvent::Init(EventType event_type, const char* label) {
566 ASSERT(label != NULL);
567 state_ = 0;
568 timestamp0_ = 0;
569 timestamp1_ = 0;
570 thread_timestamp0_ = -1;
571 thread_timestamp1_ = -1;
572 OSThread* os_thread = OSThread::Current();
573 ASSERT(os_thread != NULL);
574 thread_ = os_thread->trace_id();
575 auto thread = Thread::Current();
576 auto isolate = thread != nullptr ? thread->isolate() : nullptr;
577 auto isolate_group = thread != nullptr ? thread->isolate_group() : nullptr;
578 isolate_id_ = (isolate != nullptr) ? isolate->main_port() : ILLEGAL_PORT;
579 isolate_group_id_ = (isolate_group != nullptr) ? isolate_group->id() : 0;
580 label_ = label;
581 arguments_.Free();
582 set_event_type(event_type);
583 set_pre_serialized_args(false);
584 set_owns_label(false);
585}
586
587bool TimelineEvent::Within(int64_t time_origin_micros,
588 int64_t time_extent_micros) {
589 if ((time_origin_micros == -1) || (time_extent_micros == -1)) {
590 // No time range specified.
591 return true;
592 }
593 if (IsFinishedDuration()) {
594 // Event is from e_t0 to e_t1.
595 int64_t e_t0 = TimeOrigin();
596 int64_t e_t1 = TimeEnd();
597 ASSERT(e_t0 <= e_t1);
598 // Range is from r_t0 to r_t1.
599 int64_t r_t0 = time_origin_micros;
600 int64_t r_t1 = time_origin_micros + time_extent_micros;
601 ASSERT(r_t0 <= r_t1);
602 return !((r_t1 < e_t0) || (e_t1 < r_t0));
603 }
604 int64_t delta = TimeOrigin() - time_origin_micros;
605 return (delta >= 0) && (delta <= time_extent_micros);
606}
607
608#ifndef PRODUCT
609void TimelineEvent::PrintJSON(JSONStream* stream) const {
610 JSONObject obj(stream);
611 int64_t pid = OS::ProcessId();
612 int64_t tid = OSThread::ThreadIdToIntPtr(thread_);
613 obj.AddProperty("name", label_);
614 obj.AddProperty("cat", stream_ != NULL ? stream_->name() : NULL);
615 obj.AddProperty64("tid", tid);
616 obj.AddProperty64("pid", pid);
617 obj.AddPropertyTimeMicros("ts", TimeOrigin());
618 if (HasThreadCPUTime()) {
619 obj.AddPropertyTimeMicros("tts", ThreadCPUTimeOrigin());
620 }
621 switch (event_type()) {
622 case kBegin: {
623 obj.AddProperty("ph", "B");
624 } break;
625 case kEnd: {
626 obj.AddProperty("ph", "E");
627 } break;
628 case kDuration: {
629 obj.AddProperty("ph", "X");
630 obj.AddPropertyTimeMicros("dur", TimeDuration());
631 if (HasThreadCPUTime()) {
632 obj.AddPropertyTimeMicros("tdur", ThreadCPUTimeDuration());
633 }
634 } break;
635 case kInstant: {
636 obj.AddProperty("ph", "i");
637 obj.AddProperty("s", "p");
638 } break;
639 case kAsyncBegin: {
640 obj.AddProperty("ph", "b");
641 obj.AddPropertyF("id", "%" Px64 "", AsyncId());
642 } break;
643 case kAsyncInstant: {
644 obj.AddProperty("ph", "n");
645 obj.AddPropertyF("id", "%" Px64 "", AsyncId());
646 } break;
647 case kAsyncEnd: {
648 obj.AddProperty("ph", "e");
649 obj.AddPropertyF("id", "%" Px64 "", AsyncId());
650 } break;
651 case kCounter: {
652 obj.AddProperty("ph", "C");
653 } break;
654 case kFlowBegin: {
655 obj.AddProperty("ph", "s");
656 obj.AddPropertyF("id", "%" Px64 "", AsyncId());
657 } break;
658 case kFlowStep: {
659 obj.AddProperty("ph", "t");
660 obj.AddPropertyF("id", "%" Px64 "", AsyncId());
661 } break;
662 case kFlowEnd: {
663 obj.AddProperty("ph", "f");
664 obj.AddProperty("bp", "e");
665 obj.AddPropertyF("id", "%" Px64 "", AsyncId());
666 } break;
667 case kMetadata: {
668 obj.AddProperty("ph", "M");
669 } break;
670 default:
671 UNIMPLEMENTED();
672 }
673
674 if (pre_serialized_args()) {
675 ASSERT(arguments_.length() == 1);
676 stream->AppendSerializedObject("args", arguments_[0].value);
677 if (isolate_id_ != ILLEGAL_PORT) {
678 stream->UncloseObject();
679 stream->PrintfProperty("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
680 static_cast<int64_t>(isolate_id_));
681 stream->CloseObject();
682 }
683 if (isolate_group_id_ != 0) {
684 stream->UncloseObject();
685 stream->PrintfProperty("isolateGroupId",
686 ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING,
687 isolate_group_id_);
688 stream->CloseObject();
689 } else {
690 ASSERT(isolate_group_id_ == ILLEGAL_PORT);
691 }
692 } else {
693 JSONObject args(&obj, "args");
694 for (intptr_t i = 0; i < arguments_.length(); i++) {
695 const TimelineEventArgument& arg = arguments_[i];
696 args.AddProperty(arg.name, arg.value);
697 }
698 if (isolate_id_ != ILLEGAL_PORT) {
699 args.AddPropertyF("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
700 static_cast<int64_t>(isolate_id_));
701 }
702 if (isolate_group_id_ != 0) {
703 args.AddPropertyF("isolateGroupId",
704 ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING,
705 isolate_group_id_);
706 } else {
707 ASSERT(isolate_group_id_ == ILLEGAL_PORT);
708 }
709 }
710}
711#endif
712
713int64_t TimelineEvent::TimeOrigin() const {
714 return timestamp0_;
715}
716
717int64_t TimelineEvent::AsyncId() const {
718 return timestamp1_;
719}
720
721int64_t TimelineEvent::LowTime() const {
722 return timestamp0_;
723}
724
725int64_t TimelineEvent::HighTime() const {
726 if (event_type() == kDuration) {
727 return timestamp1_;
728 } else {
729 return timestamp0_;
730 }
731}
732
733int64_t TimelineEvent::TimeDuration() const {
734 if (timestamp1_ == 0) {
735 // This duration is still open, use current time as end.
736 return OS::GetCurrentMonotonicMicros() - timestamp0_;
737 }
738 return timestamp1_ - timestamp0_;
739}
740
741bool TimelineEvent::HasThreadCPUTime() const {
742 return (thread_timestamp0_ != -1);
743}
744
745int64_t TimelineEvent::ThreadCPUTimeOrigin() const {
746 ASSERT(HasThreadCPUTime());
747 return thread_timestamp0_;
748}
749
750int64_t TimelineEvent::ThreadCPUTimeDuration() const {
751 ASSERT(HasThreadCPUTime());
752 if (thread_timestamp1_ == -1) {
753 // This duration is still open, use current time as end.
754 return OS::GetCurrentThreadCPUMicros() - thread_timestamp0_;
755 }
756 return thread_timestamp1_ - thread_timestamp0_;
757}
758
759TimelineStream::TimelineStream(const char* name,
760 const char* fuchsia_name,
761 bool enabled)
762 : name_(name),
763 fuchsia_name_(fuchsia_name),
764#if defined(HOST_OS_FUCHSIA)
765 enabled_(static_cast<uintptr_t>(true)) // For generated code.
766#else
767 enabled_(static_cast<uintptr_t>(enabled))
768#endif
769{
770#if defined(HOST_OS_MACOS)
771 if (__builtin_available(iOS 12.0, macOS 10.14, *)) {
772 macos_log_ = os_log_create("Dart", name);
773 }
774#endif
775}
776
777TimelineEvent* TimelineStream::StartEvent() {
778 TimelineEventRecorder* recorder = Timeline::recorder();
779 if (!enabled() || (recorder == NULL)) {
780 return NULL;
781 }
782 ASSERT(name_ != NULL);
783 TimelineEvent* event = recorder->StartEvent();
784 if (event != NULL) {
785 event->StreamInit(this);
786 }
787 return event;
788}
789
790TimelineEventScope::TimelineEventScope(TimelineStream* stream,
791 const char* label)
792 : StackResource(reinterpret_cast<Thread*>(NULL)),
793 stream_(stream),
794 label_(label),
795 enabled_(false) {
796 Init();
797}
798
799TimelineEventScope::TimelineEventScope(Thread* thread,
800 TimelineStream* stream,
801 const char* label)
802 : StackResource(thread), stream_(stream), label_(label), enabled_(false) {
803 Init();
804}
805
806TimelineEventScope::~TimelineEventScope() {}
807
808void TimelineEventScope::Init() {
809 ASSERT(enabled_ == false);
810 ASSERT(label_ != NULL);
811 ASSERT(stream_ != NULL);
812 if (!stream_->enabled()) {
813 // Stream is not enabled, do nothing.
814 return;
815 }
816 enabled_ = true;
817}
818
819void TimelineEventScope::SetNumArguments(intptr_t length) {
820 if (!enabled()) {
821 return;
822 }
823 arguments_.SetNumArguments(length);
824}
825
826// |name| must be a compile time constant. Takes ownership of |argumentp|.
827void TimelineEventScope::SetArgument(intptr_t i,
828 const char* name,
829 char* argument) {
830 if (!enabled()) {
831 return;
832 }
833 arguments_.SetArgument(i, name, argument);
834}
835
836// |name| must be a compile time constant. Copies |argument|.
837void TimelineEventScope::CopyArgument(intptr_t i,
838 const char* name,
839 const char* argument) {
840 if (!enabled()) {
841 return;
842 }
843 arguments_.CopyArgument(i, name, argument);
844}
845
846void TimelineEventScope::FormatArgument(intptr_t i,
847 const char* name,
848 const char* fmt,
849 ...) {
850 if (!enabled()) {
851 return;
852 }
853 va_list args;
854 va_start(args, fmt);
855 arguments_.FormatArgument(i, name, fmt, args);
856}
857
858void TimelineEventScope::StealArguments(TimelineEvent* event) {
859 if (event == NULL) {
860 return;
861 }
862 event->StealArguments(&arguments_);
863}
864
865TimelineBeginEndScope::TimelineBeginEndScope(TimelineStream* stream,
866 const char* label)
867 : TimelineEventScope(stream, label) {
868 EmitBegin();
869}
870
871TimelineBeginEndScope::TimelineBeginEndScope(Thread* thread,
872 TimelineStream* stream,
873 const char* label)
874 : TimelineEventScope(thread, stream, label) {
875 EmitBegin();
876}
877
878TimelineBeginEndScope::~TimelineBeginEndScope() {
879 EmitEnd();
880}
881
882void TimelineBeginEndScope::EmitBegin() {
883 if (!ShouldEmitEvent()) {
884 return;
885 }
886 TimelineEvent* event = stream()->StartEvent();
887 if (event == NULL) {
888 // Stream is now disabled.
889 set_enabled(false);
890 return;
891 }
892 ASSERT(event != NULL);
893 // Emit a begin event.
894 event->Begin(label());
895 event->Complete();
896}
897
898void TimelineBeginEndScope::EmitEnd() {
899 if (!ShouldEmitEvent()) {
900 return;
901 }
902 TimelineEvent* event = stream()->StartEvent();
903 if (event == NULL) {
904 // Stream is now disabled.
905 set_enabled(false);
906 return;
907 }
908 ASSERT(event != NULL);
909 // Emit an end event.
910 event->End(label());
911 StealArguments(event);
912 event->Complete();
913}
914
915TimelineEventFilter::TimelineEventFilter(int64_t time_origin_micros,
916 int64_t time_extent_micros)
917 : time_origin_micros_(time_origin_micros),
918 time_extent_micros_(time_extent_micros) {
919 ASSERT(time_origin_micros_ >= -1);
920 ASSERT(time_extent_micros_ >= -1);
921}
922
923TimelineEventFilter::~TimelineEventFilter() {}
924
925IsolateTimelineEventFilter::IsolateTimelineEventFilter(
926 Dart_Port isolate_id,
927 int64_t time_origin_micros,
928 int64_t time_extent_micros)
929 : TimelineEventFilter(time_origin_micros, time_extent_micros),
930 isolate_id_(isolate_id) {}
931
932TimelineEventRecorder::TimelineEventRecorder()
933 : async_id_(0), time_low_micros_(0), time_high_micros_(0) {}
934
935#ifndef PRODUCT
936void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const {
937 OSThreadIterator it;
938 while (it.HasNext()) {
939 OSThread* thread = it.Next();
940 const char* thread_name = thread->name();
941 if (thread_name == NULL) {
942 // Only emit a thread name if one was set.
943 continue;
944 }
945 JSONObject obj(events);
946 int64_t pid = OS::ProcessId();
947 int64_t tid = OSThread::ThreadIdToIntPtr(thread->trace_id());
948 obj.AddProperty("name", "thread_name");
949 obj.AddProperty("ph", "M");
950 obj.AddProperty64("pid", pid);
951 obj.AddProperty64("tid", tid);
952 {
953 JSONObject args(&obj, "args");
954 args.AddPropertyF("name", "%s (%" Pd64 ")", thread_name, tid);
955 args.AddProperty("mode", "basic");
956 }
957 }
958}
959#endif
960
961TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() {
962 // Grab the current thread.
963 OSThread* thread = OSThread::Current();
964 ASSERT(thread != NULL);
965 Mutex* thread_block_lock = thread->timeline_block_lock();
966 ASSERT(thread_block_lock != NULL);
967 // We are accessing the thread's timeline block- so take the lock here.
968 // This lock will be held until the call to |CompleteEvent| is made.
969 thread_block_lock->Lock();
970#if defined(DEBUG)
971 Thread* T = Thread::Current();
972 if (T != NULL) {
973 T->IncrementNoSafepointScopeDepth();
974 }
975#endif // defined(DEBUG)
976
977 TimelineEventBlock* thread_block = thread->timeline_block();
978
979 if ((thread_block != NULL) && thread_block->IsFull()) {
980 MutexLocker ml(&lock_);
981 // Thread has a block and it is full:
982 // 1) Mark it as finished.
983 thread_block->Finish();
984 // 2) Allocate a new block.
985 thread_block = GetNewBlockLocked();
986 thread->set_timeline_block(thread_block);
987 } else if (thread_block == NULL) {
988 MutexLocker ml(&lock_);
989 // Thread has no block. Attempt to allocate one.
990 thread_block = GetNewBlockLocked();
991 thread->set_timeline_block(thread_block);
992 }
993 if (thread_block != NULL) {
994 // NOTE: We are exiting this function with the thread's block lock held.
995 ASSERT(!thread_block->IsFull());
996 TimelineEvent* event = thread_block->StartEvent();
997 return event;
998 }
999// Drop lock here as no event is being handed out.
1000#if defined(DEBUG)
1001 if (T != NULL) {
1002 T->DecrementNoSafepointScopeDepth();
1003 }
1004#endif // defined(DEBUG)
1005 thread_block_lock->Unlock();
1006 return NULL;
1007}
1008
1009void TimelineEventRecorder::ResetTimeTracking() {
1010 time_high_micros_ = 0;
1011 time_low_micros_ = kMaxInt64;
1012}
1013
1014void TimelineEventRecorder::ReportTime(int64_t micros) {
1015 if (time_high_micros_ < micros) {
1016 time_high_micros_ = micros;
1017 }
1018 if (time_low_micros_ > micros) {
1019 time_low_micros_ = micros;
1020 }
1021}
1022
1023int64_t TimelineEventRecorder::TimeOriginMicros() const {
1024 if (time_high_micros_ == 0) {
1025 return 0;
1026 }
1027 return time_low_micros_;
1028}
1029
1030int64_t TimelineEventRecorder::TimeExtentMicros() const {
1031 if (time_high_micros_ == 0) {
1032 return 0;
1033 }
1034 return time_high_micros_ - time_low_micros_;
1035}
1036
1037void TimelineEventRecorder::ThreadBlockCompleteEvent(TimelineEvent* event) {
1038 if (event == NULL) {
1039 return;
1040 }
1041 // Grab the current thread.
1042 OSThread* thread = OSThread::Current();
1043 ASSERT(thread != NULL);
1044 // Unlock the thread's block lock.
1045 Mutex* thread_block_lock = thread->timeline_block_lock();
1046 ASSERT(thread_block_lock != NULL);
1047#if defined(DEBUG)
1048 Thread* T = Thread::Current();
1049 if (T != NULL) {
1050 T->DecrementNoSafepointScopeDepth();
1051 }
1052#endif // defined(DEBUG)
1053 thread_block_lock->Unlock();
1054}
1055
1056#ifndef PRODUCT
1057void TimelineEventRecorder::WriteTo(const char* directory) {
1058 Dart_FileOpenCallback file_open = Dart::file_open_callback();
1059 Dart_FileWriteCallback file_write = Dart::file_write_callback();
1060 Dart_FileCloseCallback file_close = Dart::file_close_callback();
1061 if ((file_open == NULL) || (file_write == NULL) || (file_close == NULL)) {
1062 return;
1063 }
1064
1065 Timeline::ReclaimCachedBlocksFromThreads();
1066
1067 intptr_t pid = OS::ProcessId();
1068 char* filename =
1069 OS::SCreate(NULL, "%s/dart-timeline-%" Pd ".json", directory, pid);
1070 void* file = (*file_open)(filename, true);
1071 if (file == NULL) {
1072 OS::PrintErr("Failed to write timeline file: %s\n", filename);
1073 free(filename);
1074 return;
1075 }
1076 free(filename);
1077
1078 JSONStream js;
1079 TimelineEventFilter filter;
1080 PrintTraceEvent(&js, &filter);
1081 // Steal output from JSONStream.
1082 char* output = NULL;
1083 intptr_t output_length = 0;
1084 js.Steal(&output, &output_length);
1085 (*file_write)(output, output_length, file);
1086 // Free the stolen output.
1087 free(output);
1088 (*file_close)(file);
1089
1090 return;
1091}
1092#endif
1093
1094int64_t TimelineEventRecorder::GetNextAsyncId() {
1095 // TODO(johnmccutchan): Gracefully handle wrap around.
1096#if defined(HOST_OS_FUCHSIA)
1097 return trace_generate_nonce();
1098#else
1099 uint32_t next = static_cast<uint32_t>(async_id_.fetch_add(1u));
1100 return static_cast<int64_t>(next);
1101#endif
1102}
1103
1104void TimelineEventRecorder::FinishBlock(TimelineEventBlock* block) {
1105 if (block == NULL) {
1106 return;
1107 }
1108 MutexLocker ml(&lock_);
1109 block->Finish();
1110}
1111
1112TimelineEventBlock* TimelineEventRecorder::GetNewBlock() {
1113 MutexLocker ml(&lock_);
1114 return GetNewBlockLocked();
1115}
1116
1117TimelineEventFixedBufferRecorder::TimelineEventFixedBufferRecorder(
1118 intptr_t capacity)
1119 : memory_(NULL),
1120 blocks_(NULL),
1121 capacity_(capacity),
1122 num_blocks_(0),
1123 block_cursor_(0) {
1124 // Capacity must be a multiple of TimelineEventBlock::kBlockSize
1125 ASSERT((capacity % TimelineEventBlock::kBlockSize) == 0);
1126 // Allocate blocks array.
1127 num_blocks_ = capacity / TimelineEventBlock::kBlockSize;
1128
1129 intptr_t size = Utils::RoundUp(num_blocks_ * sizeof(TimelineEventBlock),
1130 VirtualMemory::PageSize());
1131 const bool kNotExecutable = false;
1132 memory_ = VirtualMemory::Allocate(size, kNotExecutable, "dart-timeline");
1133 if (memory_ == NULL) {
1134 OUT_OF_MEMORY();
1135 }
1136 blocks_ = reinterpret_cast<TimelineEventBlock*>(memory_->address());
1137}
1138
1139TimelineEventFixedBufferRecorder::~TimelineEventFixedBufferRecorder() {
1140 // Delete all blocks.
1141 for (intptr_t i = 0; i < num_blocks_; i++) {
1142 blocks_[i].Reset();
1143 }
1144 delete memory_;
1145}
1146
1147intptr_t TimelineEventFixedBufferRecorder::Size() {
1148 return memory_->size();
1149}
1150
1151#ifndef PRODUCT
1152void TimelineEventFixedBufferRecorder::PrintJSONEvents(
1153 JSONArray* events,
1154 TimelineEventFilter* filter) {
1155 MutexLocker ml(&lock_);
1156 ResetTimeTracking();
1157 intptr_t block_offset = FindOldestBlockIndex();
1158 if (block_offset == -1) {
1159 // All blocks are empty.
1160 return;
1161 }
1162 for (intptr_t block_idx = 0; block_idx < num_blocks_; block_idx++) {
1163 TimelineEventBlock* block =
1164 &blocks_[(block_idx + block_offset) % num_blocks_];
1165 if (!filter->IncludeBlock(block)) {
1166 continue;
1167 }
1168 for (intptr_t event_idx = 0; event_idx < block->length(); event_idx++) {
1169 TimelineEvent* event = block->At(event_idx);
1170 if (filter->IncludeEvent(event) &&
1171 event->Within(filter->time_origin_micros(),
1172 filter->time_extent_micros())) {
1173 ReportTime(event->LowTime());
1174 ReportTime(event->HighTime());
1175 events->AddValue(event);
1176 }
1177 }
1178 }
1179}
1180
1181void TimelineEventFixedBufferRecorder::PrintJSON(JSONStream* js,
1182 TimelineEventFilter* filter) {
1183 JSONObject topLevel(js);
1184 topLevel.AddProperty("type", "Timeline");
1185 {
1186 JSONArray events(&topLevel, "traceEvents");
1187 PrintJSONMeta(&events);
1188 PrintJSONEvents(&events, filter);
1189 }
1190 topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
1191 topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
1192}
1193
1194void TimelineEventFixedBufferRecorder::PrintTraceEvent(
1195 JSONStream* js,
1196 TimelineEventFilter* filter) {
1197 JSONArray events(js);
1198 PrintJSONMeta(&events);
1199 PrintJSONEvents(&events, filter);
1200}
1201#endif
1202
1203TimelineEventBlock* TimelineEventFixedBufferRecorder::GetHeadBlockLocked() {
1204 return &blocks_[0];
1205}
1206
1207void TimelineEventFixedBufferRecorder::Clear() {
1208 MutexLocker ml(&lock_);
1209 for (intptr_t i = 0; i < num_blocks_; i++) {
1210 TimelineEventBlock* block = &blocks_[i];
1211 block->Reset();
1212 }
1213}
1214
1215intptr_t TimelineEventFixedBufferRecorder::FindOldestBlockIndex() const {
1216 int64_t earliest_time = kMaxInt64;
1217 intptr_t earliest_index = -1;
1218 for (intptr_t block_idx = 0; block_idx < num_blocks_; block_idx++) {
1219 TimelineEventBlock* block = &blocks_[block_idx];
1220 if (block->IsEmpty()) {
1221 // Skip empty blocks.
1222 continue;
1223 }
1224 if (block->LowerTimeBound() < earliest_time) {
1225 earliest_time = block->LowerTimeBound();
1226 earliest_index = block_idx;
1227 }
1228 }
1229 return earliest_index;
1230}
1231
1232TimelineEvent* TimelineEventFixedBufferRecorder::StartEvent() {
1233 return ThreadBlockStartEvent();
1234}
1235
1236void TimelineEventFixedBufferRecorder::CompleteEvent(TimelineEvent* event) {
1237 if (event == NULL) {
1238 return;
1239 }
1240 ThreadBlockCompleteEvent(event);
1241}
1242
1243TimelineEventBlock* TimelineEventRingRecorder::GetNewBlockLocked() {
1244 // TODO(johnmccutchan): This function should only hand out blocks
1245 // which have been marked as finished.
1246 if (block_cursor_ == num_blocks_) {
1247 block_cursor_ = 0;
1248 }
1249 TimelineEventBlock* block = &blocks_[block_cursor_++];
1250 block->Reset();
1251 block->Open();
1252 return block;
1253}
1254
1255TimelineEventBlock* TimelineEventStartupRecorder::GetNewBlockLocked() {
1256 if (block_cursor_ == num_blocks_) {
1257 return NULL;
1258 }
1259 TimelineEventBlock* block = &blocks_[block_cursor_++];
1260 block->Reset();
1261 block->Open();
1262 return block;
1263}
1264
1265TimelineEventCallbackRecorder::TimelineEventCallbackRecorder() {}
1266
1267TimelineEventCallbackRecorder::~TimelineEventCallbackRecorder() {}
1268
1269#ifndef PRODUCT
1270void TimelineEventCallbackRecorder::PrintJSON(JSONStream* js,
1271 TimelineEventFilter* filter) {
1272 JSONObject topLevel(js);
1273 topLevel.AddProperty("type", "Timeline");
1274 {
1275 JSONArray events(&topLevel, "traceEvents");
1276 PrintJSONMeta(&events);
1277 }
1278 topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
1279 topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
1280}
1281
1282void TimelineEventCallbackRecorder::PrintTraceEvent(
1283 JSONStream* js,
1284 TimelineEventFilter* filter) {
1285 JSONArray events(js);
1286}
1287#endif
1288
1289TimelineEvent* TimelineEventCallbackRecorder::StartEvent() {
1290 TimelineEvent* event = new TimelineEvent();
1291 return event;
1292}
1293
1294void TimelineEventCallbackRecorder::CompleteEvent(TimelineEvent* event) {
1295 OnEvent(event);
1296 delete event;
1297}
1298
1299TimelineEventPlatformRecorder::TimelineEventPlatformRecorder() {}
1300
1301TimelineEventPlatformRecorder::~TimelineEventPlatformRecorder() {}
1302
1303#ifndef PRODUCT
1304void TimelineEventPlatformRecorder::PrintJSON(JSONStream* js,
1305 TimelineEventFilter* filter) {
1306 JSONObject topLevel(js);
1307 topLevel.AddProperty("type", "Timeline");
1308 {
1309 JSONArray events(&topLevel, "traceEvents");
1310 PrintJSONMeta(&events);
1311 }
1312 topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
1313 topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
1314}
1315
1316void TimelineEventPlatformRecorder::PrintTraceEvent(
1317 JSONStream* js,
1318 TimelineEventFilter* filter) {
1319 JSONArray events(js);
1320}
1321#endif
1322
1323TimelineEvent* TimelineEventPlatformRecorder::StartEvent() {
1324 TimelineEvent* event = new TimelineEvent();
1325 return event;
1326}
1327
1328void TimelineEventPlatformRecorder::CompleteEvent(TimelineEvent* event) {
1329 OnEvent(event);
1330 delete event;
1331}
1332
1333TimelineEventEndlessRecorder::TimelineEventEndlessRecorder()
1334 : head_(nullptr), tail_(nullptr), block_index_(0) {}
1335
1336TimelineEventEndlessRecorder::~TimelineEventEndlessRecorder() {
1337 TimelineEventBlock* current = head_;
1338 head_ = tail_ = nullptr;
1339
1340 while (current != nullptr) {
1341 TimelineEventBlock* next = current->next();
1342 delete current;
1343 current = next;
1344 }
1345}
1346
1347#ifndef PRODUCT
1348void TimelineEventEndlessRecorder::PrintJSON(JSONStream* js,
1349 TimelineEventFilter* filter) {
1350 JSONObject topLevel(js);
1351 topLevel.AddProperty("type", "Timeline");
1352 {
1353 JSONArray events(&topLevel, "traceEvents");
1354 PrintJSONMeta(&events);
1355 PrintJSONEvents(&events, filter);
1356 }
1357 topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
1358 topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
1359}
1360
1361void TimelineEventEndlessRecorder::PrintTraceEvent(
1362 JSONStream* js,
1363 TimelineEventFilter* filter) {
1364 JSONArray events(js);
1365 PrintJSONMeta(&events);
1366 PrintJSONEvents(&events, filter);
1367}
1368#endif
1369
1370TimelineEventBlock* TimelineEventEndlessRecorder::GetHeadBlockLocked() {
1371 return head_;
1372}
1373
1374TimelineEvent* TimelineEventEndlessRecorder::StartEvent() {
1375 return ThreadBlockStartEvent();
1376}
1377
1378void TimelineEventEndlessRecorder::CompleteEvent(TimelineEvent* event) {
1379 if (event == NULL) {
1380 return;
1381 }
1382 ThreadBlockCompleteEvent(event);
1383}
1384
1385TimelineEventBlock* TimelineEventEndlessRecorder::GetNewBlockLocked() {
1386 TimelineEventBlock* block = new TimelineEventBlock(block_index_++);
1387 block->Open();
1388 if (head_ == nullptr) {
1389 head_ = tail_ = block;
1390 } else {
1391 tail_->set_next(block);
1392 tail_ = block;
1393 }
1394 if (FLAG_trace_timeline) {
1395 OS::PrintErr("Created new block %p\n", block);
1396 }
1397 return block;
1398}
1399
1400#ifndef PRODUCT
1401void TimelineEventEndlessRecorder::PrintJSONEvents(
1402 JSONArray* events,
1403 TimelineEventFilter* filter) {
1404 MutexLocker ml(&lock_);
1405 ResetTimeTracking();
1406 for (TimelineEventBlock* current = head_; current != nullptr;
1407 current = current->next()) {
1408 if (!filter->IncludeBlock(current)) {
1409 continue;
1410 }
1411 intptr_t length = current->length();
1412 for (intptr_t i = 0; i < length; i++) {
1413 TimelineEvent* event = current->At(i);
1414 if (filter->IncludeEvent(event) &&
1415 event->Within(filter->time_origin_micros(),
1416 filter->time_extent_micros())) {
1417 ReportTime(event->LowTime());
1418 ReportTime(event->HighTime());
1419 events->AddValue(event);
1420 }
1421 }
1422 }
1423}
1424#endif
1425
1426void TimelineEventEndlessRecorder::Clear() {
1427 TimelineEventBlock* current = head_;
1428 while (current != NULL) {
1429 TimelineEventBlock* next = current->next();
1430 delete current;
1431 current = next;
1432 }
1433 head_ = NULL;
1434 block_index_ = 0;
1435 OSThread* thread = OSThread::Current();
1436 thread->set_timeline_block(NULL);
1437}
1438
1439TimelineEventBlock::TimelineEventBlock(intptr_t block_index)
1440 : next_(NULL),
1441 length_(0),
1442 block_index_(block_index),
1443 thread_id_(OSThread::kInvalidThreadId),
1444 in_use_(false) {}
1445
1446TimelineEventBlock::~TimelineEventBlock() {
1447 Reset();
1448}
1449
1450#ifndef PRODUCT
1451void TimelineEventBlock::PrintJSON(JSONStream* js) const {
1452 ASSERT(!in_use());
1453 JSONArray events(js);
1454 for (intptr_t i = 0; i < length(); i++) {
1455 const TimelineEvent* event = At(i);
1456 events.AddValue(event);
1457 }
1458}
1459#endif
1460
1461TimelineEvent* TimelineEventBlock::StartEvent() {
1462 ASSERT(!IsFull());
1463 if (FLAG_trace_timeline) {
1464 OSThread* os_thread = OSThread::Current();
1465 ASSERT(os_thread != NULL);
1466 intptr_t tid = OSThread::ThreadIdToIntPtr(os_thread->id());
1467 OS::PrintErr("StartEvent in block %p for thread %" Pd "\n", this, tid);
1468 }
1469 return &events_[length_++];
1470}
1471
1472int64_t TimelineEventBlock::LowerTimeBound() const {
1473 if (length_ == 0) {
1474 return kMaxInt64;
1475 }
1476 ASSERT(length_ > 0);
1477 return events_[0].TimeOrigin();
1478}
1479
1480bool TimelineEventBlock::CheckBlock() {
1481 if (length() == 0) {
1482 return true;
1483 }
1484
1485 for (intptr_t i = 0; i < length(); i++) {
1486 if (At(i)->thread() != thread_id()) {
1487 return false;
1488 }
1489 }
1490
1491 // - events have monotonically increasing timestamps.
1492 int64_t last_time = LowerTimeBound();
1493 for (intptr_t i = 0; i < length(); i++) {
1494 if (last_time > At(i)->TimeOrigin()) {
1495 return false;
1496 }
1497 last_time = At(i)->TimeOrigin();
1498 }
1499
1500 return true;
1501}
1502
1503void TimelineEventBlock::Reset() {
1504 for (intptr_t i = 0; i < kBlockSize; i++) {
1505 // Clear any extra data.
1506 events_[i].Reset();
1507 }
1508 length_ = 0;
1509 thread_id_ = OSThread::kInvalidThreadId;
1510 in_use_ = false;
1511}
1512
1513void TimelineEventBlock::Open() {
1514 OSThread* os_thread = OSThread::Current();
1515 ASSERT(os_thread != NULL);
1516 thread_id_ = os_thread->trace_id();
1517 in_use_ = true;
1518}
1519
1520void TimelineEventBlock::Finish() {
1521 if (FLAG_trace_timeline) {
1522 OS::PrintErr("Finish block %p\n", this);
1523 }
1524 in_use_ = false;
1525#ifndef PRODUCT
1526 if (Service::timeline_stream.enabled()) {
1527 ServiceEvent service_event(NULL, ServiceEvent::kTimelineEvents);
1528 service_event.set_timeline_event_block(this);
1529 Service::HandleEvent(&service_event);
1530 }
1531#endif
1532}
1533
1534TimelineEventBlockIterator::TimelineEventBlockIterator(
1535 TimelineEventRecorder* recorder)
1536 : current_(NULL), recorder_(NULL) {
1537 Reset(recorder);
1538}
1539
1540TimelineEventBlockIterator::~TimelineEventBlockIterator() {
1541 Reset(NULL);
1542}
1543
1544void TimelineEventBlockIterator::Reset(TimelineEventRecorder* recorder) {
1545 // Clear current.
1546 current_ = NULL;
1547 if (recorder_ != NULL) {
1548 // Unlock old recorder.
1549 recorder_->lock_.Unlock();
1550 }
1551 recorder_ = recorder;
1552 if (recorder_ == NULL) {
1553 return;
1554 }
1555 // Lock new recorder.
1556 recorder_->lock_.Lock();
1557 // Queue up first block.
1558 current_ = recorder_->GetHeadBlockLocked();
1559}
1560
1561bool TimelineEventBlockIterator::HasNext() const {
1562 return current_ != NULL;
1563}
1564
1565TimelineEventBlock* TimelineEventBlockIterator::Next() {
1566 ASSERT(current_ != NULL);
1567 TimelineEventBlock* r = current_;
1568 current_ = current_->next();
1569 return r;
1570}
1571
1572void DartTimelineEventHelpers::ReportTaskEvent(Thread* thread,
1573 TimelineEvent* event,
1574 int64_t id,
1575 const char* phase,
1576 const char* category,
1577 char* name,
1578 char* args) {
1579 ASSERT(phase != NULL);
1580 ASSERT((phase[0] == 'n') || (phase[0] == 'b') || (phase[0] == 'e') ||
1581 (phase[0] == 'B') || (phase[0] == 'E'));
1582 ASSERT(phase[1] == '\0');
1583 const int64_t start = OS::GetCurrentMonotonicMicros();
1584 const int64_t start_cpu = OS::GetCurrentThreadCPUMicrosForTimeline();
1585 switch (phase[0]) {
1586 case 'n':
1587 event->AsyncInstant(name, id, start);
1588 break;
1589 case 'b':
1590 event->AsyncBegin(name, id, start);
1591 break;
1592 case 'e':
1593 event->AsyncEnd(name, id, start);
1594 break;
1595 case 'B':
1596 event->Begin(name, start, start_cpu);
1597 break;
1598 case 'E':
1599 event->End(name, start, start_cpu);
1600 break;
1601 default:
1602 UNREACHABLE();
1603 }
1604 event->set_owns_label(true);
1605 event->CompleteWithPreSerializedArgs(args);
1606}
1607
1608void DartTimelineEventHelpers::ReportFlowEvent(Thread* thread,
1609 TimelineEvent* event,
1610 const char* category,
1611 char* name,
1612 int64_t type,
1613 int64_t flow_id,
1614 char* args) {
1615 const int64_t start = OS::GetCurrentMonotonicMicros();
1616 TimelineEvent::EventType event_type =
1617 static_cast<TimelineEvent::EventType>(type);
1618 switch (event_type) {
1619 case TimelineEvent::kFlowBegin:
1620 event->FlowBegin(name, flow_id, start);
1621 break;
1622 case TimelineEvent::kFlowStep:
1623 event->FlowStep(name, flow_id, start);
1624 break;
1625 case TimelineEvent::kFlowEnd:
1626 event->FlowEnd(name, flow_id, start);
1627 break;
1628 default:
1629 UNREACHABLE();
1630 break;
1631 }
1632 event->set_owns_label(true);
1633 event->CompleteWithPreSerializedArgs(args);
1634}
1635
1636void DartTimelineEventHelpers::ReportInstantEvent(Thread* thread,
1637 TimelineEvent* event,
1638 const char* category,
1639 char* name,
1640 char* args) {
1641 const int64_t start = OS::GetCurrentMonotonicMicros();
1642 event->Instant(name, start);
1643 event->set_owns_label(true);
1644 event->CompleteWithPreSerializedArgs(args);
1645}
1646
1647} // namespace dart
1648
1649#endif // defined(SUPPORT_TIMELINE)
1650