Flutter Engine
The Flutter Engine
timeline.cc
Go to the documentation of this file.
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#include <functional>
15#include <memory>
16#include <tuple>
17#include <utility>
18
19#include "platform/atomic.h"
20#include "platform/hashmap.h"
21#include "vm/isolate.h"
22#include "vm/json_stream.h"
23#include "vm/lockers.h"
24#include "vm/log.h"
25#include "vm/object.h"
26#include "vm/service.h"
27#include "vm/service_event.h"
28#include "vm/thread.h"
29
30#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
31#include "perfetto/ext/tracing/core/trace_packet.h"
32#include "vm/perfetto_utils.h"
41#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
42
43namespace dart {
44
45#if defined(PRODUCT)
46#define DEFAULT_TIMELINE_RECORDER "none"
47#define SUPPORTED_TIMELINE_RECORDERS "systrace, file, callback"
48#else
49#define DEFAULT_TIMELINE_RECORDER "ring"
50#if defined(SUPPORT_PERFETTO)
51#define SUPPORTED_TIMELINE_RECORDERS \
52 "ring, endless, startup, systrace, file, callback, perfettofile"
53#else
54#define SUPPORTED_TIMELINE_RECORDERS \
55 "ring, endless, startup, systrace, file, callback"
56#endif
57#endif
58
59DEFINE_FLAG(bool, complete_timeline, false, "Record the complete timeline");
60DEFINE_FLAG(bool, startup_timeline, false, "Record the startup timeline");
61// TODO(derekx): Remove this flag in Dart 3.4.
63 bool,
64 systrace_timeline,
65 false,
66 "Record the timeline to the platform's tracing service if there is one");
67DEFINE_FLAG(bool, trace_timeline, false, "Trace timeline backend");
69 timeline_dir,
70 nullptr,
71 "Enable all timeline trace streams and output VM global trace "
72 "into specified directory. This flag is ignored by the file and "
73 "perfetto recorders.");
75 timeline_streams,
76 nullptr,
77 "Comma separated list of timeline streams to record. "
78 "Valid values: all, API, Compiler, CompilerVerbose, Dart, "
79 "Debugger, Embedder, GC, Isolate, and VM.");
81 timeline_recorder,
82 DEFAULT_TIMELINE_RECORDER,
83 "Select the timeline recorder used. "
84 "Valid values: none, " SUPPORTED_TIMELINE_RECORDERS)
85
86// Implementation notes:
87//
88// Writing events:
89// |TimelineEvent|s are written into |TimelineEventBlock|s. Each |Thread| caches
90// a |TimelineEventBlock| object so that it can write events without
91// synchronizing with other threads in the system. Even though the |Thread| owns
92// the |TimelineEventBlock| the block may need to be reclaimed by the reporting
93// system. To support that, a |Thread| must hold its |timeline_block_lock_|
94// when operating on the |TimelineEventBlock|. This lock will only ever be
95// busy if blocks are being reclaimed by the reporting system.
96//
97// Reporting:
98// When requested, the timeline is serialized in either Chrome's JSON trace
99// format (https://goo.gl/hDZw5M) or Perfetto's proto trace format. In both
100// cases, it may be that a thread has a |TimelineEventBlock| cached in TLS
101// partially filled with events. In order to report a complete timeline, the
102// cached |TimelineEventBlock|s need to be reclaimed.
103//
104// Reclaiming open |TimelineEventBlock|s from threads:
105//
106// Each |Thread| can have one |TimelineEventBlock| cached in it.
107//
108// To reclaim blocks, we iterate over all threads and remove the cached
109// |TimelineEventBlock| from each thread. This is safe because we hold the
110// |Thread|'s |timeline_block_lock_| meaning the block can't be being modified.
111// When clearing the reclaimed blocks, or serializing the events in them, we
112// hold |TimelineEventRecorder::lock_| before reclaiming the blocks, to prevent
113// reclaimed blocks from being handed out again until we release it.
114//
115// Locking notes:
116// The following locks are used by the timeline system:
117// - |TimelineEventRecorder::lock_| This lock is held whenever a
118// |TimelineEventBlock| is being requested or reclaimed.
119// - |Thread::timeline_block_lock_| This lock is held whenever a |Thread|'s
120// cached block is being operated on.
121// - |Thread::thread_list_lock_| This lock is held when iterating over
122// |Thread|s.
123//
124// Locks must always be taken in the following order:
125// |Thread::thread_list_lock_|
126// |TimelineEventRecorder::lock_|
127// |Thread::timeline_block_lock_|
128//
129
130std::atomic<RecorderSynchronizationLock::RecorderState>
131 RecorderSynchronizationLock::recorder_state_ = {
132 RecorderSynchronizationLock::kUninitialized};
133std::atomic<intptr_t> RecorderSynchronizationLock::outstanding_event_writes_ = {
134 0};
135
136static TimelineEventRecorder* CreateDefaultTimelineRecorder() {
137#if defined(PRODUCT)
138 return new TimelineEventNopRecorder();
139#else
140 return new TimelineEventRingRecorder();
141#endif
142}
143
144static TimelineEventRecorder* CreateTimelineRecorder() {
145 ASSERT(FLAG_timeline_recorder != nullptr);
146 const char* flag = FLAG_timeline_recorder;
147
148 if (FLAG_systrace_timeline) {
150 "Warning: the --systrace-timeline flag is deprecated and will "
151 "be removed in Dart SDK v3.4. Please use --timeline-recorder=systrace "
152 "instead.\n");
153 flag = "systrace";
154 } else if (FLAG_timeline_dir != nullptr || FLAG_complete_timeline) {
155 // Some flags require that we use the endless recorder.
156 flag = "endless";
157 } else if (FLAG_startup_timeline) {
158 flag = "startup";
159 }
160
161 if (strcmp("none", flag) == 0) {
162 return new TimelineEventNopRecorder();
163 }
164
165 // Systrace recorder.
166 if (strcmp("systrace", flag) == 0) {
167#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
168 return new TimelineEventSystraceRecorder();
169#elif defined(DART_HOST_OS_MACOS)
170 return new TimelineEventMacosRecorder();
171#elif defined(DART_HOST_OS_FUCHSIA)
172 return new TimelineEventFuchsiaRecorder();
173#else
174 // Not supported. A warning will be emitted below.
175#endif
176 }
177
178 if (Utils::StrStartsWith(flag, "file") &&
179 (flag[4] == '\0' || flag[4] == ':' || flag[4] == '=')) {
180 const char* filename = flag[4] == '\0' ? "dart-timeline.json" : &flag[5];
181 free(const_cast<char*>(FLAG_timeline_dir));
182 FLAG_timeline_dir = nullptr;
183 return new TimelineEventFileRecorder(filename);
184 }
185
186 if (strcmp("callback", flag) == 0) {
187 return new TimelineEventEmbedderCallbackRecorder();
188 }
189
190#if !defined(PRODUCT)
191#if defined(SUPPORT_PERFETTO)
192 // The Perfetto file recorder is disabled in PRODUCT mode to avoid the large
193 // binary size increase that it brings.
194 {
195 const intptr_t kPrefixLength = 12;
196 if (Utils::StrStartsWith(flag, "perfettofile") &&
197 (flag[kPrefixLength] == '\0' || flag[kPrefixLength] == ':' ||
198 flag[kPrefixLength] == '=')) {
199 const char* filename = flag[kPrefixLength] == '\0'
200 ? "dart.perfetto-trace"
201 : &flag[kPrefixLength + 1];
202 free(const_cast<char*>(FLAG_timeline_dir));
203 FLAG_timeline_dir = nullptr;
204 return new TimelineEventPerfettoFileRecorder(filename);
205 }
206 }
207#endif // defined(SUPPORT_PERFETTO)
208
209 // Recorders below do nothing useful in PRODUCT mode. You can't extract
210 // information available in them without vm-service.
211 if (strcmp("endless", flag) == 0) {
212 return new TimelineEventEndlessRecorder();
213 }
214
215 if (strcmp("startup", flag) == 0) {
216 return new TimelineEventStartupRecorder();
217 }
218
219 if (strcmp("ring", flag) == 0) {
220 return new TimelineEventRingRecorder();
221 }
222#endif // !defined(PRODUCT)
223
224 if (strlen(flag) > 0 && strcmp(flag, DEFAULT_TIMELINE_RECORDER) != 0) {
226 "Warning: requested %s timeline recorder which is not supported, "
227 "defaulting to the " DEFAULT_TIMELINE_RECORDER " recorder\n",
228 flag);
229 }
230
231 return CreateDefaultTimelineRecorder();
232}
233
234// Returns a caller freed array of stream names in FLAG_timeline_streams.
235static MallocGrowableArray<char*>* GetEnabledByDefaultTimelineStreams() {
236 MallocGrowableArray<char*>* result = new MallocGrowableArray<char*>();
237 if (FLAG_timeline_streams == nullptr) {
238 // Nothing set.
239 return result;
240 }
241 char* save_ptr; // Needed for strtok_r.
242 // strtok modifies arg 1 so we make a copy of it.
243 char* streams = Utils::StrDup(FLAG_timeline_streams);
244 char* token = strtok_r(streams, ",", &save_ptr);
245 while (token != nullptr) {
246 result->Add(Utils::StrDup(token));
247 token = strtok_r(nullptr, ",", &save_ptr);
248 }
249 free(streams);
250 return result;
251}
252
253// Frees the result of |GetEnabledByDefaultTimelineStreams|.
254static void FreeEnabledByDefaultTimelineStreams(
255 MallocGrowableArray<char*>* streams) {
256 if (streams == nullptr) {
257 return;
258 }
259 for (intptr_t i = 0; i < streams->length(); i++) {
260 free((*streams)[i]);
261 }
262 delete streams;
263}
264
265// Returns true if |streams| contains |stream| or "all". Not case sensitive.
266static bool HasStream(MallocGrowableArray<char*>* streams, const char* stream) {
267 if ((FLAG_timeline_dir != nullptr) || FLAG_complete_timeline ||
268 FLAG_startup_timeline) {
269 return true;
270 }
271 for (intptr_t i = 0; i < streams->length(); i++) {
272 const char* checked_stream = (*streams)[i];
273 if ((strstr(checked_stream, "all") != nullptr) ||
274 (strstr(checked_stream, stream) != nullptr)) {
275 return true;
276 }
277 }
278 return false;
279}
280
281void Timeline::Init() {
282 ASSERT(recorder_ == nullptr);
283 recorder_ = CreateTimelineRecorder();
284
286
287 // The following is needed to backfill information about any |OSThread|s that
288 // were initialized before this point.
289 OSThreadIterator it;
290 while (it.HasNext()) {
291 OSThread& thread = *it.Next();
292 recorder_->AddTrackMetadataBasedOnThread(
293 OS::ProcessId(), OSThread::ThreadIdToIntPtr(thread.trace_id()),
294 thread.name());
295 }
296 if (FLAG_trace_timeline) {
297 OS::PrintErr("Using the %s timeline recorder.\n", recorder_->name());
298 }
299 ASSERT(recorder_ != nullptr);
300 enabled_streams_ = GetEnabledByDefaultTimelineStreams();
301// Global overrides.
302#define TIMELINE_STREAM_FLAG_DEFAULT(name, ...) \
303 stream_##name##_.set_enabled(HasStream(enabled_streams_, #name));
304 TIMELINE_STREAM_LIST(TIMELINE_STREAM_FLAG_DEFAULT)
305#undef TIMELINE_STREAM_FLAG_DEFAULT
306}
307
308void Timeline::Cleanup() {
309 ASSERT(recorder_ != nullptr);
310
311#ifndef PRODUCT
312 if (FLAG_timeline_dir != nullptr) {
313 recorder_->WriteTo(FLAG_timeline_dir);
314 }
315#endif
316
317// Disable global streams.
318#define TIMELINE_STREAM_DISABLE(name, ...) \
319 Timeline::stream_##name##_.set_enabled(false);
320 TIMELINE_STREAM_LIST(TIMELINE_STREAM_DISABLE)
321#undef TIMELINE_STREAM_DISABLE
322 RecorderSynchronizationLock::WaitForShutdown();
323 Timeline::Clear();
324 delete recorder_;
325 recorder_ = nullptr;
326 if (enabled_streams_ != nullptr) {
327 FreeEnabledByDefaultTimelineStreams(enabled_streams_);
328 enabled_streams_ = nullptr;
329 }
330}
331
332void Timeline::ReclaimCachedBlocksFromThreads() {
333 RecorderSynchronizationLockScope ls;
334 TimelineEventRecorder* recorder = Timeline::recorder();
335 if (recorder == nullptr || ls.IsUninitialized()) {
336 return;
337 }
338 ASSERT(recorder != nullptr);
339 // Iterate over threads.
340 OSThreadIterator it;
341 while (it.HasNext()) {
342 OSThread* thread = it.Next();
343 MutexLocker ml(thread->timeline_block_lock());
344 // Grab block and clear it.
345 TimelineEventBlock* block = thread->TimelineBlockLocked();
346 thread->SetTimelineBlockLocked(nullptr);
347 recorder->FinishBlock(block);
348 }
349}
350
351#ifndef PRODUCT
352void Timeline::PrintFlagsToJSONArray(JSONArray* arr) {
353#define ADD_RECORDED_STREAM_NAME(name, ...) \
354 if (stream_##name##_.enabled()) { \
355 arr->AddValue(#name); \
356 }
357 TIMELINE_STREAM_LIST(ADD_RECORDED_STREAM_NAME);
358#undef ADD_RECORDED_STREAM_NAME
359}
360
361void Timeline::PrintFlagsToJSON(JSONStream* js) {
362 JSONObject obj(js);
363 obj.AddProperty("type", "TimelineFlags");
364 RecorderSynchronizationLockScope ls;
365 TimelineEventRecorder* recorder = Timeline::recorder();
366 if (recorder == nullptr || !ls.IsActive()) {
367 obj.AddProperty("recorderName", "null");
368 } else {
369 obj.AddProperty("recorderName", recorder->name());
370 }
371 {
372 JSONArray availableStreams(&obj, "availableStreams");
373#define ADD_STREAM_NAME(name, ...) availableStreams.AddValue(#name);
374 TIMELINE_STREAM_LIST(ADD_STREAM_NAME);
375#undef ADD_STREAM_NAME
376 }
377 {
378 JSONArray recordedStreams(&obj, "recordedStreams");
379#define ADD_RECORDED_STREAM_NAME(name, ...) \
380 if (stream_##name##_.enabled()) { \
381 recordedStreams.AddValue(#name); \
382 }
383 TIMELINE_STREAM_LIST(ADD_RECORDED_STREAM_NAME);
384#undef ADD_RECORDED_STREAM_NAME
385 }
386}
387#endif
388
389void Timeline::Clear() {
390 RecorderSynchronizationLockScope ls;
391 TimelineEventRecorder* recorder = Timeline::recorder();
392 if (recorder == nullptr || ls.IsUninitialized()) {
393 return;
394 }
395 ASSERT(recorder != nullptr);
396 // Acquire the recorder's lock to prevent the reclaimed blocks from being
397 // handed out again until they have been cleared.
398 MutexLocker ml(&recorder->lock_);
399 ReclaimCachedBlocksFromThreads();
400 recorder->ClearLocked();
401}
402
403void TimelineEventArguments::SetNumArguments(intptr_t length) {
404 if (length == length_) {
405 return;
406 }
407 if (length == 0) {
408 Free();
409 return;
410 }
411 if (buffer_ == nullptr) {
412 // calloc already nullifies
413 buffer_ = reinterpret_cast<TimelineEventArgument*>(
414 calloc(sizeof(TimelineEventArgument), length));
415 } else {
416 for (intptr_t i = length; i < length_; ++i) {
417 free(buffer_[i].value);
418 }
419 buffer_ = reinterpret_cast<TimelineEventArgument*>(
420 realloc(buffer_, sizeof(TimelineEventArgument) * length));
421 if (length > length_) {
422 memset(buffer_ + length_, 0,
423 sizeof(TimelineEventArgument) * (length - length_));
424 }
425 }
426 length_ = length;
427}
428
429void TimelineEventArguments::SetArgument(intptr_t i,
430 const char* name,
431 char* argument) {
432 ASSERT(i >= 0);
433 ASSERT(i < length_);
434 buffer_[i].name = name;
435 buffer_[i].value = argument;
436}
437
438void TimelineEventArguments::CopyArgument(intptr_t i,
439 const char* name,
440 const char* argument) {
441 SetArgument(i, name, Utils::StrDup(argument));
442}
443
444void TimelineEventArguments::FormatArgument(intptr_t i,
445 const char* name,
446 const char* fmt,
447 va_list args) {
448 ASSERT(i >= 0);
449 ASSERT(i < length_);
450 va_list measure_args;
451 va_copy(measure_args, args);
452 intptr_t len = Utils::VSNPrint(nullptr, 0, fmt, measure_args);
453 va_end(measure_args);
454
455 char* buffer = reinterpret_cast<char*>(malloc(len + 1));
456 va_list print_args;
457 va_copy(print_args, args);
458 Utils::VSNPrint(buffer, (len + 1), fmt, print_args);
459 va_end(print_args);
460
461 SetArgument(i, name, buffer);
462}
463
464void TimelineEventArguments::StealArguments(TimelineEventArguments* arguments) {
465 Free();
466 length_ = arguments->length_;
467 buffer_ = arguments->buffer_;
468 arguments->length_ = 0;
469 arguments->buffer_ = nullptr;
470}
471
473 if (buffer_ == nullptr) {
474 return;
475 }
476 for (intptr_t i = 0; i < length_; i++) {
477 free(buffer_[i].value);
478 }
479 free(buffer_);
480 buffer_ = nullptr;
481 length_ = 0;
482}
483
484TimelineEventRecorder* Timeline::recorder_ = nullptr;
485Dart_TimelineRecorderCallback Timeline::callback_ = nullptr;
486MallocGrowableArray<char*>* Timeline::enabled_streams_ = nullptr;
487bool Timeline::recorder_discards_clock_values_ = false;
488
489#define TIMELINE_STREAM_DEFINE(name, fuchsia_name, static_labels) \
490 TimelineStream Timeline::stream_##name##_(#name, fuchsia_name, \
491 static_labels, false);
492TIMELINE_STREAM_LIST(TIMELINE_STREAM_DEFINE)
493#undef TIMELINE_STREAM_DEFINE
494
495TimelineEvent::TimelineEvent()
496 : timestamp0_(0),
497 timestamp1_or_id_(0),
498 flow_id_count_(0),
499 flow_ids_(),
500 state_(0),
501 label_(nullptr),
502 stream_(nullptr),
503 thread_(OSThread::kInvalidThreadId),
504 isolate_id_(ILLEGAL_ISOLATE_ID),
505 isolate_group_id_(ILLEGAL_ISOLATE_GROUP_ID) {}
506
507TimelineEvent::~TimelineEvent() {
508 Reset();
509}
510
512 timestamp0_ = 0;
513 timestamp1_or_id_ = 0;
514 flow_id_count_ = 0;
515 flow_ids_.reset();
516 if (owns_label() && label_ != nullptr) {
517 free(const_cast<char*>(label_));
518 }
519 label_ = nullptr;
520 stream_ = nullptr;
521 thread_ = OSThread::kInvalidThreadId;
522 isolate_id_ = ILLEGAL_ISOLATE_ID;
523 isolate_group_id_ = ILLEGAL_ISOLATE_GROUP_ID;
524 arguments_.Free();
525 state_ = 0;
526}
527
528void TimelineEvent::AsyncBegin(const char* label,
529 int64_t async_id,
530 int64_t micros) {
531 Init(kAsyncBegin, label);
532 set_timestamp0(micros);
533 // Overload timestamp1_ with the async_id.
534 set_timestamp1_or_id(async_id);
535}
536
537void TimelineEvent::AsyncInstant(const char* label,
538 int64_t async_id,
539 int64_t micros) {
540 Init(kAsyncInstant, label);
541 set_timestamp0(micros);
542 // Overload timestamp1_ with the async_id.
543 set_timestamp1_or_id(async_id);
544}
545
546void TimelineEvent::AsyncEnd(const char* label,
547 int64_t async_id,
548 int64_t micros) {
549 Init(kAsyncEnd, label);
550 set_timestamp0(micros);
551 // Overload timestamp1_ with the async_id.
552 set_timestamp1_or_id(async_id);
553}
554
555void TimelineEvent::DurationBegin(const char* label, int64_t micros) {
556 Init(kDuration, label);
557 set_timestamp0(micros);
558}
559
560void TimelineEvent::Instant(const char* label, int64_t micros) {
561 Init(kInstant, label);
562 set_timestamp0(micros);
563}
564
565void TimelineEvent::Duration(const char* label,
566 int64_t start_micros,
567 int64_t end_micros) {
568 Init(kDuration, label);
569 set_timestamp0(start_micros);
570 set_timestamp1_or_id(end_micros);
571}
572
573void TimelineEvent::Begin(const char* label, int64_t id, int64_t micros) {
574 Init(kBegin, label);
575 set_timestamp0(micros);
576 // Overload timestamp1_ with the event ID. This is required for the MacOS
577 // recorder to work.
578 set_timestamp1_or_id(id);
579}
580
581void TimelineEvent::End(const char* label, int64_t id, int64_t micros) {
582 Init(kEnd, label);
583 set_timestamp0(micros);
584 // Overload timestamp1_ with the event ID. This is required for the MacOS
585 // recorder to work.
586 set_timestamp1_or_id(id);
587}
588
589void TimelineEvent::Counter(const char* label, int64_t micros) {
590 Init(kCounter, label);
591 set_timestamp0(micros);
592}
593
594void TimelineEvent::FlowBegin(const char* label, int64_t id, int64_t micros) {
595 Init(kFlowBegin, label);
596 set_timestamp0(micros);
597 // Overload timestamp1_ with the flow ID.
598 set_timestamp1_or_id(id);
599}
600
601void TimelineEvent::FlowStep(const char* label, int64_t id, int64_t micros) {
602 Init(kFlowStep, label);
603 set_timestamp0(micros);
604 // Overload timestamp1_ with the flow ID.
605 set_timestamp1_or_id(id);
606}
607
608void TimelineEvent::FlowEnd(const char* label, int64_t id, int64_t micros) {
609 Init(kFlowEnd, label);
610 set_timestamp0(micros);
611 // Overload timestamp1_ with the flow ID.
612 set_timestamp1_or_id(id);
613}
614
615void TimelineEvent::Metadata(const char* label, int64_t micros) {
616 Init(kMetadata, label);
617 set_timestamp0(micros);
618}
619
620void TimelineEvent::CompleteWithPreSerializedArgs(char* args_json) {
621 set_pre_serialized_args(true);
622 SetNumArguments(1);
623 SetArgument(0, "Dart Arguments", args_json);
624 Complete();
625}
626
627void TimelineEvent::FormatArgument(intptr_t i,
628 const char* name,
629 const char* fmt,
630 ...) {
631 va_list args;
632 va_start(args, fmt);
633 arguments_.FormatArgument(i, name, fmt, args);
634 va_end(args);
635}
636
637void TimelineEvent::Complete() {
638 TimelineEventRecorder* recorder = Timeline::recorder();
639 recorder->CompleteEvent(this);
640 // Paired with |RecorderSynchronizationLock::EnterLock()| in
641 // |TimelineStream::StartEvent()|.
642 RecorderSynchronizationLock::ExitLock();
643}
644
645void TimelineEvent::Init(EventType event_type, const char* label) {
646 ASSERT(label != nullptr);
647 state_ = 0;
648 timestamp0_ = 0;
649 timestamp1_or_id_ = 0;
650 flow_id_count_ = 0;
651 flow_ids_.reset();
652 OSThread* os_thread = OSThread::Current();
653 ASSERT(os_thread != nullptr);
654 thread_ = os_thread->trace_id();
655 auto thread = Thread::Current();
656 auto isolate = thread != nullptr ? thread->isolate() : nullptr;
657 auto isolate_group = thread != nullptr ? thread->isolate_group() : nullptr;
658 isolate_id_ = (isolate != nullptr) ? isolate->main_port() : ILLEGAL_PORT;
659 isolate_group_id_ = (isolate_group != nullptr) ? isolate_group->id() : 0;
660 isolate_data_ =
661 (isolate != nullptr) ? isolate->init_callback_data() : nullptr;
662 isolate_group_data_ =
663 (isolate_group != nullptr) ? isolate_group->embedder_data() : nullptr;
664 label_ = label;
665 arguments_.Free();
666 set_event_type(event_type);
667 set_pre_serialized_args(false);
668 set_owns_label(false);
669}
670
671bool TimelineEvent::Within(int64_t time_origin_micros,
672 int64_t time_extent_micros) {
673 if ((time_origin_micros == -1) || (time_extent_micros == -1)) {
674 // No time range specified.
675 return true;
676 }
677 if (IsFinishedDuration()) {
678 // Event is from e_t0 to e_t1.
679 int64_t e_t0 = TimeOrigin();
680 int64_t e_t1 = TimeEnd();
681 ASSERT(e_t0 <= e_t1);
682 // Range is from r_t0 to r_t1.
683 int64_t r_t0 = time_origin_micros;
684 int64_t r_t1 = time_origin_micros + time_extent_micros;
685 ASSERT(r_t0 <= r_t1);
686 return !((r_t1 < e_t0) || (e_t1 < r_t0));
687 }
688 int64_t delta = TimeOrigin() - time_origin_micros;
689 return (delta >= 0) && (delta <= time_extent_micros);
690}
691
692#ifndef PRODUCT
693void TimelineEvent::PrintJSON(JSONStream* stream) const {
694 PrintJSON(stream->writer());
695}
696#endif
697
698void TimelineEvent::PrintJSON(JSONWriter* writer) const {
699 writer->OpenObject();
700 int64_t pid = OS::ProcessId();
701 int64_t tid = OSThread::ThreadIdToIntPtr(thread_);
702 writer->PrintProperty("name", label_);
703 writer->PrintProperty("cat", stream_ != nullptr ? stream_->name() : nullptr);
704 writer->PrintProperty64("tid", tid);
705 writer->PrintProperty64("pid", pid);
706 writer->PrintProperty64("ts", TimeOrigin());
707 switch (event_type()) {
708 case kBegin: {
709 writer->PrintProperty("ph", "B");
710 } break;
711 case kEnd: {
712 writer->PrintProperty("ph", "E");
713 } break;
714 case kDuration: {
715 writer->PrintProperty("ph", "X");
716 writer->PrintProperty64("dur", TimeDuration());
717 } break;
718 case kInstant: {
719 writer->PrintProperty("ph", "i");
720 writer->PrintProperty("s", "p");
721 } break;
722 case kAsyncBegin: {
723 writer->PrintProperty("ph", "b");
724 writer->PrintfProperty("id", "%" Px64 "", Id());
725 } break;
726 case kAsyncInstant: {
727 writer->PrintProperty("ph", "n");
728 writer->PrintfProperty("id", "%" Px64 "", Id());
729 } break;
730 case kAsyncEnd: {
731 writer->PrintProperty("ph", "e");
732 writer->PrintfProperty("id", "%" Px64 "", Id());
733 } break;
734 case kCounter: {
735 writer->PrintProperty("ph", "C");
736 } break;
737 case kFlowBegin: {
738 writer->PrintProperty("ph", "s");
739 writer->PrintfProperty("id", "%" Px64 "", Id());
740 } break;
741 case kFlowStep: {
742 writer->PrintProperty("ph", "t");
743 writer->PrintfProperty("id", "%" Px64 "", Id());
744 } break;
745 case kFlowEnd: {
746 writer->PrintProperty("ph", "f");
747 writer->PrintProperty("bp", "e");
748 writer->PrintfProperty("id", "%" Px64 "", Id());
749 } break;
750 case kMetadata: {
751 writer->PrintProperty("ph", "M");
752 } break;
753 default:
755 }
756
757 if (ArgsArePreSerialized()) {
758 ASSERT(arguments_.length() == 1);
759 writer->AppendSerializedObject("args", arguments_[0].value);
760 if (HasIsolateId()) {
761 writer->UncloseObject();
762 writer->PrintfProperty("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
763 static_cast<int64_t>(isolate_id_));
764 writer->CloseObject();
765 }
766 if (HasIsolateGroupId()) {
767 writer->UncloseObject();
768 writer->PrintfProperty("isolateGroupId",
770 isolate_group_id_);
771 writer->CloseObject();
772 } else {
773 ASSERT(isolate_group_id_ == ILLEGAL_PORT);
774 }
775 } else {
776 writer->OpenObject("args");
777 for (intptr_t i = 0; i < arguments_.length(); i++) {
778 const TimelineEventArgument& arg = arguments_[i];
779 writer->PrintProperty(arg.name, arg.value);
780 }
781 if (HasIsolateId()) {
782 writer->PrintfProperty("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
783 static_cast<int64_t>(isolate_id_));
784 }
785 if (HasIsolateGroupId()) {
786 writer->PrintfProperty("isolateGroupId",
788 isolate_group_id_);
789 } else {
790 ASSERT(isolate_group_id_ == ILLEGAL_PORT);
791 }
792 writer->CloseObject();
793 }
794 writer->CloseObject();
795}
796
797#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
798inline void AddSyncEventFields(
800 const TimelineEvent& event) {
801 track_event->set_track_uuid(OSThread::ThreadIdToIntPtr(event.thread()));
802}
803
804inline void AddAsyncEventFields(
806 const TimelineEvent& event) {
807 track_event->set_track_uuid(event.Id());
808}
809
810inline void AddBeginAndInstantEventCommonFields(
812 const TimelineEvent& event) {
813 track_event->set_name(event.label());
814 for (intptr_t i = 0; i < event.flow_id_count(); ++i) {
815 // TODO(derekx): |TrackEvent|s have a |terminating_flow_ids| field that we
816 // aren't able to populate right now because we aren't keeping track of
817 // terminating flow IDs in |TimelineEvent|. I'm not even sure if using that
818 // field will provide any benefit though.
819 track_event->add_flow_ids(event.FlowIds()[i]);
820 }
821}
822
823inline void AddBeginEventFields(
825 const TimelineEvent& event) {
826 AddBeginAndInstantEventCommonFields(track_event, event);
827 track_event->set_type(
829}
830
831inline void AddInstantEventFields(
833 const TimelineEvent& event) {
834 AddBeginAndInstantEventCommonFields(track_event, event);
835 track_event->set_type(
837}
838
839inline void AddEndEventFields(
841 track_event->set_type(
843}
844
845inline void AddDebugAnnotations(
847 const TimelineEvent& event) {
848 if (event.GetNumArguments() > 0) {
849 if (event.ArgsArePreSerialized()) {
850 ASSERT(event.GetNumArguments() == 1);
852 *track_event->add_debug_annotations();
853 debug_annotation.set_name(event.arguments()[0].name);
854 debug_annotation.set_legacy_json_value(event.arguments()[0].value);
855 } else {
856 for (intptr_t i = 0; i < event.GetNumArguments(); ++i) {
858 *track_event->add_debug_annotations();
859 debug_annotation.set_name(event.arguments()[i].name);
860 debug_annotation.set_string_value(event.arguments()[i].value);
861 }
862 }
863 }
864 if (event.HasIsolateId()) {
866 *track_event->add_debug_annotations();
867 debug_annotation.set_name("isolateId");
868 std::unique_ptr<const char[]> formatted_isolate_id =
869 event.GetFormattedIsolateId();
870 debug_annotation.set_string_value(formatted_isolate_id.get());
871 }
872 if (event.HasIsolateGroupId()) {
874 *track_event->add_debug_annotations();
875 debug_annotation.set_name("isolateGroupId");
876 std::unique_ptr<const char[]> formatted_isolate_group =
877 event.GetFormattedIsolateGroupId();
878 debug_annotation.set_string_value(formatted_isolate_group.get());
879 }
880}
881
882bool TimelineEvent::CanBeRepresentedByPerfettoTracePacket() const {
883 switch (event_type()) {
884 case TimelineEvent::kBegin:
885 case TimelineEvent::kEnd:
886 case TimelineEvent::kDuration:
887 case TimelineEvent::kInstant:
888 case TimelineEvent::kAsyncBegin:
889 case TimelineEvent::kAsyncEnd:
890 case TimelineEvent::kAsyncInstant:
891 return true;
892 default:
893 return false;
894 }
895}
896
897void TimelineEvent::PopulateTracePacket(
899 ASSERT(packet != nullptr);
900 ASSERT(CanBeRepresentedByPerfettoTracePacket());
901
902 perfetto_utils::SetTrustedPacketSequenceId(packet);
903 perfetto_utils::SetTimestampAndMonotonicClockId(packet, TimeOrigin());
905 track_event->add_categories(stream()->name());
906
907 const TimelineEvent& event = *this;
908 switch (event_type()) {
909 case TimelineEvent::kBegin: {
910 AddSyncEventFields(track_event, event);
911 AddBeginEventFields(track_event, event);
912 break;
913 }
914 case TimelineEvent::kEnd: {
915 AddSyncEventFields(track_event, event);
916 AddEndEventFields(track_event);
917 break;
918 }
919 case TimelineEvent::kInstant: {
920 AddSyncEventFields(track_event, event);
921 AddInstantEventFields(track_event, event);
922 break;
923 }
924 case TimelineEvent::kAsyncBegin: {
925 AddAsyncEventFields(track_event, event);
926 AddBeginEventFields(track_event, event);
927 break;
928 }
929 case TimelineEvent::kAsyncEnd: {
930 AddAsyncEventFields(track_event, event);
931 AddEndEventFields(track_event);
932 break;
933 }
934 case TimelineEvent::kAsyncInstant: {
935 AddAsyncEventFields(track_event, event);
936 AddInstantEventFields(track_event, event);
937 break;
938 }
939 default:
940 break;
941 }
942 AddDebugAnnotations(track_event, event);
943}
944#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
945
946int64_t TimelineEvent::LowTime() const {
947 return timestamp0_;
948}
949
950int64_t TimelineEvent::HighTime() const {
951 if (event_type() == kDuration) {
952 return timestamp1_or_id_;
953 } else {
954 return timestamp0_;
955 }
956}
957
958int64_t TimelineEvent::TimeDuration() const {
959 ASSERT(event_type() == kDuration);
960 if (timestamp1_or_id_ == 0) {
961 // This duration is still open, use current time as end.
962 return OS::GetCurrentMonotonicMicrosForTimeline() - timestamp0_;
963 }
964 return timestamp1_or_id_ - timestamp0_;
965}
966
967bool TimelineEvent::HasIsolateId() const {
968 return isolate_id_ != ILLEGAL_ISOLATE_ID;
969}
970
971bool TimelineEvent::HasIsolateGroupId() const {
972 return isolate_group_id_ != ILLEGAL_ISOLATE_GROUP_ID;
973}
974
975std::unique_ptr<const char[]> TimelineEvent::GetFormattedIsolateId() const {
976 ASSERT(HasIsolateId());
977 intptr_t formatted_isolate_id_buffer_size =
978 Utils::SNPrint(nullptr, 0, ISOLATE_SERVICE_ID_FORMAT_STRING,
979 isolate_id_) +
980 1;
981 auto formatted_isolate_id =
982 std::make_unique<char[]>(formatted_isolate_id_buffer_size);
983 Utils::SNPrint(formatted_isolate_id.get(), formatted_isolate_id_buffer_size,
985 return formatted_isolate_id;
986}
987
988std::unique_ptr<const char[]> TimelineEvent::GetFormattedIsolateGroupId()
989 const {
990 ASSERT(HasIsolateGroupId());
991 intptr_t formatted_isolate_group_id_buffer_size =
992 Utils::SNPrint(nullptr, 0, ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING,
993 isolate_group_id_) +
994 1;
995 auto formatted_isolate_group_id =
996 std::make_unique<char[]>(formatted_isolate_group_id_buffer_size);
997 Utils::SNPrint(formatted_isolate_group_id.get(),
998 formatted_isolate_group_id_buffer_size,
999 ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, isolate_group_id_);
1000 return formatted_isolate_group_id;
1001}
1002
1003TimelineTrackMetadata::TimelineTrackMetadata(intptr_t pid,
1004 intptr_t tid,
1005 CStringUniquePtr&& track_name)
1006 : pid_(pid), tid_(tid), track_name_(std::move(track_name)) {}
1007
1008void TimelineTrackMetadata::set_track_name(CStringUniquePtr&& track_name) {
1009 track_name_ = std::move(track_name);
1010}
1011
1012#if !defined(PRODUCT)
1013void TimelineTrackMetadata::PrintJSON(const JSONArray& jsarr_events) const {
1014 JSONObject jsobj(&jsarr_events);
1015 jsobj.AddProperty("name", "thread_name");
1016 jsobj.AddProperty("ph", "M");
1017 jsobj.AddProperty("pid", pid());
1018 jsobj.AddProperty("tid", tid());
1019 {
1020 JSONObject jsobj_args(&jsobj, "args");
1021 jsobj_args.AddPropertyF("name", "%s (%" Pd ")", track_name(), tid());
1022 jsobj_args.AddProperty("mode", "basic");
1023 }
1024}
1025
1026#if defined(SUPPORT_PERFETTO)
1027void TimelineTrackMetadata::PopulateTracePacket(
1028 perfetto::protos::pbzero::TracePacket* track_descriptor_packet) const {
1029 perfetto_utils::SetTrustedPacketSequenceId(track_descriptor_packet);
1030
1032 *track_descriptor_packet->set_track_descriptor();
1033 track_descriptor.set_parent_uuid(pid());
1034 track_descriptor.set_uuid(tid());
1035
1037 *track_descriptor.set_thread();
1038 thread_descriptor.set_pid(pid());
1039 thread_descriptor.set_tid(tid());
1040 thread_descriptor.set_thread_name(track_name());
1041}
1042#endif // defined(SUPPORT_PERFETTO)
1043#endif // !defined(PRODUCT)
1044
1045AsyncTimelineTrackMetadata::AsyncTimelineTrackMetadata(intptr_t pid,
1046 intptr_t async_id)
1047 : pid_(pid), async_id_(async_id) {}
1048
1049#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
1050void AsyncTimelineTrackMetadata::PopulateTracePacket(
1051 perfetto::protos::pbzero::TracePacket* track_descriptor_packet) const {
1052 perfetto_utils::SetTrustedPacketSequenceId(track_descriptor_packet);
1054 *track_descriptor_packet->set_track_descriptor();
1055 track_descriptor.set_parent_uuid(pid());
1056 track_descriptor.set_uuid(async_id());
1057}
1058#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
1059
1060TimelineStream::TimelineStream(const char* name,
1061 const char* fuchsia_name,
1062 bool has_static_labels,
1063 bool enabled)
1064 : name_(name),
1065 fuchsia_name_(fuchsia_name),
1066#if defined(DART_HOST_OS_FUCHSIA)
1067 enabled_(static_cast<uintptr_t>(true)) // For generated code.
1068#else
1069 enabled_(static_cast<uintptr_t>(enabled))
1070#endif
1071{
1072#if defined(DART_HOST_OS_MACOS)
1073 macos_log_ = os_log_create("Dart", name);
1074 has_static_labels_ = has_static_labels;
1075#endif
1076}
1077
1078TimelineEvent* TimelineStream::StartEvent() {
1079 // Paired with |RecorderSynchronizationLock::ExitLock()| in
1080 // |TimelineEvent::Complete()|.
1081 //
1082 // The lock must be held until the event is completed to avoid having the
1083 // memory backing the event being freed in the middle of processing the
1084 // event.
1085 RecorderSynchronizationLock::EnterLock();
1086 TimelineEventRecorder* recorder = Timeline::recorder();
1087 if (!enabled() || (recorder == nullptr) ||
1088 !RecorderSynchronizationLock::IsActive()) {
1089 RecorderSynchronizationLock::ExitLock();
1090 return nullptr;
1091 }
1092 ASSERT(name_ != nullptr);
1093 TimelineEvent* event = recorder->StartEvent();
1094 if (event == nullptr) {
1095 RecorderSynchronizationLock::ExitLock();
1096 return nullptr;
1097 }
1098 event->StreamInit(this);
1099 return event;
1100}
1101
1102TimelineEventScope::TimelineEventScope(TimelineStream* stream,
1103 const char* label)
1104 : StackResource(static_cast<Thread*>(nullptr)),
1105 stream_(stream),
1106 label_(label),
1107 enabled_(false) {
1108 Init();
1109}
1110
1111TimelineEventScope::TimelineEventScope(Thread* thread,
1112 TimelineStream* stream,
1113 const char* label)
1114 : StackResource(thread), stream_(stream), label_(label), enabled_(false) {
1115 Init();
1116}
1117
1118TimelineEventScope::~TimelineEventScope() {}
1119
1121 ASSERT(enabled_ == false);
1122 ASSERT(label_ != nullptr);
1123 ASSERT(stream_ != nullptr);
1124 if (!stream_->enabled()) {
1125 // Stream is not enabled, do nothing.
1126 return;
1127 }
1128 enabled_ = true;
1129 Thread* thread = static_cast<Thread*>(this->thread());
1130 if (thread != nullptr) {
1131 id_ = thread->GetNextTaskId();
1132 } else {
1133 static RelaxedAtomic<int64_t> next_bootstrap_task_id = {0};
1134 id_ = next_bootstrap_task_id.fetch_add(1);
1135 }
1136}
1137
1138void TimelineEventScope::SetNumArguments(intptr_t length) {
1139 if (!enabled()) {
1140 return;
1141 }
1142 arguments_.SetNumArguments(length);
1143}
1144
1145// |name| must be a compile time constant. Takes ownership of |argumentp|.
1146void TimelineEventScope::SetArgument(intptr_t i,
1147 const char* name,
1148 char* argument) {
1149 if (!enabled()) {
1150 return;
1151 }
1152 arguments_.SetArgument(i, name, argument);
1153}
1154
1155// |name| must be a compile time constant. Copies |argument|.
1156void TimelineEventScope::CopyArgument(intptr_t i,
1157 const char* name,
1158 const char* argument) {
1159 if (!enabled()) {
1160 return;
1161 }
1162 arguments_.CopyArgument(i, name, argument);
1163}
1164
1165void TimelineEventScope::FormatArgument(intptr_t i,
1166 const char* name,
1167 const char* fmt,
1168 ...) {
1169 if (!enabled()) {
1170 return;
1171 }
1172 va_list args;
1173 va_start(args, fmt);
1174 arguments_.FormatArgument(i, name, fmt, args);
1175 va_end(args);
1176}
1177
1178void TimelineEventScope::StealArguments(TimelineEvent* event) {
1179 if (event == nullptr) {
1180 return;
1181 }
1182 event->StealArguments(&arguments_);
1183}
1184
1185TimelineBeginEndScope::TimelineBeginEndScope(TimelineStream* stream,
1186 const char* label)
1187 : TimelineEventScope(stream, label) {
1188 EmitBegin();
1189}
1190
1191TimelineBeginEndScope::TimelineBeginEndScope(Thread* thread,
1192 TimelineStream* stream,
1193 const char* label)
1194 : TimelineEventScope(thread, stream, label) {
1195 EmitBegin();
1196}
1197
1198TimelineBeginEndScope::~TimelineBeginEndScope() {
1199 EmitEnd();
1200}
1201
1202void TimelineBeginEndScope::EmitBegin() {
1203 if (!ShouldEmitEvent()) {
1204 return;
1205 }
1206 TimelineEvent* event = stream()->StartEvent();
1207 if (event == nullptr) {
1208 // Stream is now disabled.
1209 set_enabled(false);
1210 return;
1211 }
1212 ASSERT(event != nullptr);
1213 // Emit a begin event.
1214 event->Begin(label(), id());
1215 event->Complete();
1216}
1217
1218void TimelineBeginEndScope::EmitEnd() {
1219 if (!ShouldEmitEvent()) {
1220 return;
1221 }
1222 TimelineEvent* event = stream()->StartEvent();
1223 if (event == nullptr) {
1224 // Stream is now disabled.
1225 set_enabled(false);
1226 return;
1227 }
1228 ASSERT(event != nullptr);
1229 // Emit an end event.
1230 event->End(label(), id());
1231 StealArguments(event);
1232 event->Complete();
1233}
1234
1235bool TimelineEventBlock::InUseLocked() const {
1236 ASSERT(Timeline::recorder()->lock_.IsOwnedByCurrentThread());
1237 return in_use_;
1238}
1239
1240bool TimelineEventBlock::ContainsEventsThatCanBeSerializedLocked() const {
1241 ASSERT(Timeline::recorder()->lock_.IsOwnedByCurrentThread());
1242 // Check that the block is not in use and not empty. |!block->in_use()| must
1243 // be checked first because we are only holding |lock_|. Holding |lock_|
1244 // makes it safe to call |in_use()| on any block, but only makes it safe to
1245 // call |IsEmpty()| on blocks that are not in use.
1246 return !InUseLocked() && !IsEmpty();
1247}
1248
1249TimelineEventFilter::TimelineEventFilter(int64_t time_origin_micros,
1250 int64_t time_extent_micros)
1251 : time_origin_micros_(time_origin_micros),
1252 time_extent_micros_(time_extent_micros) {
1253 ASSERT(time_origin_micros_ >= -1);
1254 ASSERT(time_extent_micros_ >= -1);
1255}
1256
1257TimelineEventFilter::~TimelineEventFilter() {}
1258
1259IsolateTimelineEventFilter::IsolateTimelineEventFilter(
1260 Dart_Port isolate_id,
1261 int64_t time_origin_micros,
1262 int64_t time_extent_micros)
1263 : TimelineEventFilter(time_origin_micros, time_extent_micros),
1264 isolate_id_(isolate_id) {}
1265
1266TimelineEventRecorder::TimelineEventRecorder()
1267 : time_low_micros_(0),
1268 time_high_micros_(0),
1269 track_uuid_to_track_metadata_lock_(),
1270 track_uuid_to_track_metadata_(
1271 &SimpleHashMap::SamePointerValue,
1272 TimelineEventRecorder::kTrackUuidToTrackMetadataInitialCapacity),
1273 async_track_uuid_to_track_metadata_lock_(),
1274 async_track_uuid_to_track_metadata_(
1275 &SimpleHashMap::SamePointerValue,
1276 TimelineEventRecorder::kTrackUuidToTrackMetadataInitialCapacity) {}
1277
1278TimelineEventRecorder::~TimelineEventRecorder() {
1279 // We do not need to lock the following section, because at this point
1280 // |RecorderSynchronizationLock| must have been put in a state that prevents
1281 // the metadata maps from being modified.
1282 for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata_.Start();
1283 entry != nullptr; entry = track_uuid_to_track_metadata_.Next(entry)) {
1284 TimelineTrackMetadata* value =
1285 static_cast<TimelineTrackMetadata*>(entry->value);
1286 delete value;
1287 }
1288 for (SimpleHashMap::Entry* entry =
1289 async_track_uuid_to_track_metadata_.Start();
1290 entry != nullptr;
1291 entry = async_track_uuid_to_track_metadata_.Next(entry)) {
1292 AsyncTimelineTrackMetadata* value =
1293 static_cast<AsyncTimelineTrackMetadata*>(entry->value);
1294 delete value;
1295 }
1296}
1297
1298#ifndef PRODUCT
1299void TimelineEventRecorder::PrintJSONMeta(const JSONArray& jsarr_events) {
1300 MutexLocker ml(&track_uuid_to_track_metadata_lock_);
1301 for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata_.Start();
1302 entry != nullptr; entry = track_uuid_to_track_metadata_.Next(entry)) {
1303 TimelineTrackMetadata* value =
1304 static_cast<TimelineTrackMetadata*>(entry->value);
1305 value->PrintJSON(jsarr_events);
1306 }
1307}
1308
1309#if defined(SUPPORT_PERFETTO)
1310void TimelineEventRecorder::PrintPerfettoMeta(
1311 JSONBase64String* jsonBase64String) {
1312 ASSERT(jsonBase64String != nullptr);
1313
1314 perfetto_utils::PopulateClockSnapshotPacket(packet_.get());
1315 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet_);
1316 packet_.Reset();
1317 perfetto_utils::PopulateProcessDescriptorPacket(packet_.get());
1318 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet_);
1319 packet_.Reset();
1320
1321 {
1322 MutexLocker ml(&async_track_uuid_to_track_metadata_lock_);
1323 for (SimpleHashMap::Entry* entry =
1324 async_track_uuid_to_track_metadata_.Start();
1325 entry != nullptr;
1326 entry = async_track_uuid_to_track_metadata_.Next(entry)) {
1327 AsyncTimelineTrackMetadata* value =
1328 static_cast<AsyncTimelineTrackMetadata*>(entry->value);
1329 value->PopulateTracePacket(packet_.get());
1330 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String,
1331 &packet_);
1332 packet_.Reset();
1333 }
1334 }
1335
1336 {
1337 MutexLocker ml(&track_uuid_to_track_metadata_lock_);
1338 for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata_.Start();
1339 entry != nullptr; entry = track_uuid_to_track_metadata_.Next(entry)) {
1340 TimelineTrackMetadata* value =
1341 static_cast<TimelineTrackMetadata*>(entry->value);
1342 value->PopulateTracePacket(packet_.get());
1343 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String,
1344 &packet_);
1345 packet_.Reset();
1346 }
1347 }
1348}
1349#endif // defined(SUPPORT_PERFETTO)
1350#endif // !defined(PRODUCT)
1351
1352TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() {
1353 // Grab the current thread.
1354 OSThread* thread = OSThread::Current();
1355 ASSERT(thread != nullptr);
1356 // Acquire the recorder lock in case we need to call |GetNewBlockLocked|. We
1357 // acquire the lock here and not directly before calls to |GetNewBlockLocked|
1358 // due to locking order restrictions.
1359 Mutex& recorder_lock = lock_;
1360 recorder_lock.Lock();
1361 Mutex* thread_block_lock = thread->timeline_block_lock();
1362 ASSERT(thread_block_lock != nullptr);
1363 // We are accessing the thread's timeline block- so take the lock here.
1364 // This lock will be held until the call to |CompleteEvent| is made.
1365 thread_block_lock->Lock();
1366#if defined(DEBUG)
1367 Thread* T = Thread::Current();
1368 if (T != nullptr) {
1369 T->IncrementNoSafepointScopeDepth();
1370 }
1371#endif // defined(DEBUG)
1372
1373 TimelineEventBlock* thread_block = thread->TimelineBlockLocked();
1374
1375 if ((thread_block != nullptr) && thread_block->IsFull()) {
1376 // Thread has a block and it is full:
1377 // 1) Mark it as finished.
1378 thread->SetTimelineBlockLocked(nullptr);
1379 FinishBlock(thread_block);
1380 // 2) Allocate a new block.
1381 // We release |thread_block_lock| before calling |GetNewBlockLocked| to
1382 // avoid TSAN warnings about lock order inversion.
1383 thread_block_lock->Unlock();
1384 thread_block = GetNewBlockLocked();
1385 thread_block_lock->Lock();
1386 thread->SetTimelineBlockLocked(thread_block);
1387 } else if (thread_block == nullptr) {
1388 // Thread has no block. Attempt to allocate one.
1389 // We release |thread_block_lock| before calling |GetNewBlockLocked| to
1390 // avoid TSAN warnings about lock order inversion.
1391 thread_block_lock->Unlock();
1392 thread_block = GetNewBlockLocked();
1393 thread_block_lock->Lock();
1394 thread->SetTimelineBlockLocked(thread_block);
1395 }
1396 recorder_lock.Unlock();
1397 if (thread_block != nullptr) {
1398 // NOTE: We are exiting this function with the thread's block lock held.
1399 ASSERT(!thread_block->IsFull());
1400 TimelineEvent* event = thread_block->StartEventLocked();
1401 return event;
1402 }
1403// Drop lock here as no event is being handed out.
1404#if defined(DEBUG)
1405 if (T != nullptr) {
1406 T->DecrementNoSafepointScopeDepth();
1407 }
1408#endif // defined(DEBUG)
1409 thread_block_lock->Unlock();
1410 return nullptr;
1411}
1412
1413void TimelineEventRecorder::ResetTimeTracking() {
1414 time_high_micros_ = 0;
1415 time_low_micros_ = kMaxInt64;
1416}
1417
1418void TimelineEventRecorder::ReportTime(int64_t micros) {
1419 if (time_high_micros_ < micros) {
1420 time_high_micros_ = micros;
1421 }
1422 if (time_low_micros_ > micros) {
1423 time_low_micros_ = micros;
1424 }
1425}
1426
1427int64_t TimelineEventRecorder::TimeOriginMicros() const {
1428 if (time_high_micros_ == 0) {
1429 return 0;
1430 }
1431 return time_low_micros_;
1432}
1433
1434int64_t TimelineEventRecorder::TimeExtentMicros() const {
1435 if (time_high_micros_ == 0) {
1436 return 0;
1437 }
1438 return time_high_micros_ - time_low_micros_;
1439}
1440
1441void TimelineEventRecorder::ThreadBlockCompleteEvent(TimelineEvent* event) {
1442 if (event == nullptr) {
1443 return;
1444 }
1445#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
1446 // Async track metadata is only written in Perfetto traces, and Perfetto
1447 // traces cannot be written when SUPPORT_PERFETTO is not defined, or when
1448 // PRODUCT is defined.
1449 if (event->event_type() == TimelineEvent::kAsyncBegin ||
1450 event->event_type() == TimelineEvent::kAsyncInstant) {
1451 AddAsyncTrackMetadataBasedOnEvent(*event);
1452 }
1453#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
1454 // Grab the current thread.
1455 OSThread* thread = OSThread::Current();
1456 ASSERT(thread != nullptr);
1457 // Unlock the thread's block lock.
1458 Mutex* thread_block_lock = thread->timeline_block_lock();
1459 ASSERT(thread_block_lock != nullptr);
1460#if defined(DEBUG)
1461 Thread* T = Thread::Current();
1462 if (T != nullptr) {
1463 T->DecrementNoSafepointScopeDepth();
1464 }
1465#endif // defined(DEBUG)
1466 thread_block_lock->Unlock();
1467}
1468
1469#ifndef PRODUCT
1470void TimelineEventRecorder::WriteTo(const char* directory) {
1471 Dart_FileOpenCallback file_open = Dart::file_open_callback();
1472 Dart_FileWriteCallback file_write = Dart::file_write_callback();
1473 Dart_FileCloseCallback file_close = Dart::file_close_callback();
1474 if ((file_open == nullptr) || (file_write == nullptr) ||
1475 (file_close == nullptr)) {
1476 OS::PrintErr("warning: Could not access file callbacks.");
1477 return;
1478 }
1479
1480 // Acquire the recorder's lock to prevent the reclaimed blocks from being
1481 // handed out again until the trace has been serialized.
1482 MutexLocker ml(&lock_);
1483 Timeline::ReclaimCachedBlocksFromThreads();
1484
1485 intptr_t pid = OS::ProcessId();
1486 char* filename =
1487 OS::SCreate(nullptr, "%s/dart-timeline-%" Pd ".json", directory, pid);
1488 void* file = (*file_open)(filename, true);
1489 if (file == nullptr) {
1490 OS::PrintErr("warning: Failed to write timeline file: %s\n", filename);
1491 free(filename);
1492 return;
1493 }
1494 free(filename);
1495
1496 JSONStream js;
1497 TimelineEventFilter filter;
1498 PrintTraceEvent(&js, &filter);
1499 // Steal output from JSONStream.
1500 char* output = nullptr;
1501 intptr_t output_length = 0;
1502 js.Steal(&output, &output_length);
1503 (*file_write)(output, output_length, file);
1504 // Free the stolen output.
1505 free(output);
1506 (*file_close)(file);
1507
1508 return;
1509}
1510#endif
1511
1512void TimelineEventRecorder::FinishBlock(TimelineEventBlock* block) {
1513 if (block != nullptr) {
1514 block->Finish();
1515 }
1516}
1517
1518void TimelineEventRecorder::AddTrackMetadataBasedOnThread(
1519 const intptr_t process_id,
1520 const intptr_t trace_id,
1521 const char* thread_name) {
1522 ASSERT(FLAG_timeline_recorder != nullptr);
1523 if (strcmp("none", FLAG_timeline_recorder) == 0 ||
1524 strcmp("callback", FLAG_timeline_recorder) == 0 ||
1525 strcmp("systrace", FLAG_timeline_recorder) == 0 ||
1526 FLAG_systrace_timeline) {
1527 // There is no way to retrieve track metadata when a no-op, callback, or
1528 // systrace recorder is in use, so we don't need to update the map in these
1529 // cases.
1530 return;
1531 }
1532 MutexLocker ml(&track_uuid_to_track_metadata_lock_);
1533
1534 void* key = reinterpret_cast<void*>(trace_id);
1535 const intptr_t hash = Utils::WordHash(trace_id);
1536 SimpleHashMap::Entry* entry =
1537 track_uuid_to_track_metadata_.Lookup(key, hash, true);
1538 if (entry->value == nullptr) {
1539 entry->value = new TimelineTrackMetadata(
1540 process_id, trace_id,
1542 Utils::StrDup(thread_name == nullptr ? "" : thread_name)));
1543 } else {
1544 TimelineTrackMetadata* value =
1545 static_cast<TimelineTrackMetadata*>(entry->value);
1546 ASSERT(process_id == value->pid());
1547 value->set_track_name(CStringUniquePtr(
1548 Utils::StrDup(thread_name == nullptr ? "" : thread_name)));
1549 }
1550}
1551
1552#if !defined(PRODUCT)
1553void TimelineEventRecorder::AddAsyncTrackMetadataBasedOnEvent(
1554 const TimelineEvent& event) {
1555 ASSERT(FLAG_timeline_recorder != nullptr);
1556 if (strcmp("none", FLAG_timeline_recorder) == 0 ||
1557 strcmp("callback", FLAG_timeline_recorder) == 0 ||
1558 strcmp("systrace", FLAG_timeline_recorder) == 0 ||
1559 FLAG_systrace_timeline) {
1560 // There is no way to retrieve track metadata when a no-op, callback, or
1561 // systrace recorder is in use, so we don't need to update the map in
1562 // these cases.
1563 return;
1564 }
1565 MutexLocker ml(&async_track_uuid_to_track_metadata_lock_);
1566
1567 void* key = reinterpret_cast<void*>(event.Id());
1568 const intptr_t hash = Utils::WordHash(event.Id());
1569 SimpleHashMap::Entry* entry =
1570 async_track_uuid_to_track_metadata_.Lookup(key, hash, true);
1571 if (entry->value == nullptr) {
1572 entry->value = new AsyncTimelineTrackMetadata(OS::ProcessId(), event.Id());
1573 }
1574}
1575#endif // !defined(PRODUCT)
1576
1577TimelineEventFixedBufferRecorder::TimelineEventFixedBufferRecorder(
1578 intptr_t capacity)
1579 : memory_(nullptr),
1580 blocks_(nullptr),
1581 capacity_(capacity),
1582 num_blocks_(0),
1583 block_cursor_(0) {
1584 // Capacity must be a multiple of TimelineEventBlock::kBlockSize
1585 ASSERT((capacity % TimelineEventBlock::kBlockSize) == 0);
1586 // Allocate blocks array.
1587 num_blocks_ = capacity / TimelineEventBlock::kBlockSize;
1588
1589 intptr_t size = Utils::RoundUp(num_blocks_ * sizeof(TimelineEventBlock),
1590 VirtualMemory::PageSize());
1591 const bool executable = false;
1592 const bool compressed = false;
1593 memory_ =
1594 VirtualMemory::Allocate(size, executable, compressed, "dart-timeline");
1595 if (memory_ == nullptr) {
1596 OUT_OF_MEMORY();
1597 }
1598 blocks_ = reinterpret_cast<TimelineEventBlock*>(memory_->address());
1599}
1600
1601TimelineEventFixedBufferRecorder::~TimelineEventFixedBufferRecorder() {
1602 // We do not need to acquire any locks, because at this point we must have
1603 // reclaimed all the blocks, and |RecorderSynchronizationLock| must have been
1604 // put in a state that prevents blocks from being given out.
1605 delete memory_;
1606}
1607
1609 return memory_->size();
1610}
1611
1612#ifndef PRODUCT
1613void TimelineEventFixedBufferRecorder::PrintEventsCommon(
1614 const TimelineEventFilter& filter,
1615 std::function<void(const TimelineEvent&)>&& print_impl) {
1616 // Acquire the recorder's lock to prevent the reclaimed blocks from being
1617 // handed out again until the trace has been serialized.
1618 MutexLocker ml(&lock_);
1619 Timeline::ReclaimCachedBlocksFromThreads();
1620 ResetTimeTracking();
1621 intptr_t block_offset = FindOldestBlockIndexLocked();
1622 if (block_offset == -1) {
1623 // All blocks are in use or empty.
1624 return;
1625 }
1626 for (intptr_t block_idx = 0; block_idx < num_blocks_; block_idx++) {
1627 TimelineEventBlock* block =
1628 &blocks_[(block_idx + block_offset) % num_blocks_];
1629 if (!block->ContainsEventsThatCanBeSerializedLocked()) {
1630 continue;
1631 }
1632 for (intptr_t event_idx = 0; event_idx < block->length(); event_idx++) {
1633 TimelineEvent* event = block->At(event_idx);
1634 if (filter.IncludeEvent(event) &&
1635 event->Within(filter.time_origin_micros(),
1636 filter.time_extent_micros())) {
1637 ReportTime(event->LowTime());
1638 ReportTime(event->HighTime());
1639 print_impl(*event);
1640 }
1641 }
1642 }
1643}
1644
1645void TimelineEventFixedBufferRecorder::PrintJSONEvents(
1646 const JSONArray& events,
1647 const TimelineEventFilter& filter) {
1648 PrintEventsCommon(filter, [&events](const TimelineEvent& event) {
1649 events.AddValue(&event);
1650 });
1651}
1652
1653#if defined(SUPPORT_PERFETTO)
1654// Populates the fields of |heap_buffered_packet| with the data in |event|, and
1655// then calls |print_callback| with the populated |heap_buffered_packet| as the
1656// only argument. This function resets |heap_buffered_packet| right before
1657// returning.
1658inline void PrintPerfettoEventCallbackBody(
1659 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*
1660 heap_buffered_packet,
1661 const TimelineEvent& event,
1662 const std::function<
1663 void(protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*)>&&
1664 print_callback) {
1665 ASSERT(heap_buffered_packet != nullptr);
1666 if (!event.CanBeRepresentedByPerfettoTracePacket()) {
1667 return;
1668 }
1669 if (event.IsDuration()) {
1670 // Duration events must be converted to pairs of begin and end events to
1671 // be serialized in Perfetto's format.
1673 *heap_buffered_packet->get();
1674 {
1675 perfetto_utils::SetTrustedPacketSequenceId(&packet);
1676 perfetto_utils::SetTimestampAndMonotonicClockId(&packet,
1677 event.TimeOrigin());
1678
1680 packet.set_track_event();
1681 track_event->add_categories(event.stream()->name());
1682 AddSyncEventFields(track_event, event);
1683 AddBeginEventFields(track_event, event);
1684 AddDebugAnnotations(track_event, event);
1685 }
1686 print_callback(heap_buffered_packet);
1687 heap_buffered_packet->Reset();
1688
1689 {
1690 perfetto_utils::SetTrustedPacketSequenceId(&packet);
1691 perfetto_utils::SetTimestampAndMonotonicClockId(&packet, event.TimeEnd());
1692
1694 packet.set_track_event();
1695 track_event->add_categories(event.stream()->name());
1696 AddSyncEventFields(track_event, event);
1697 AddEndEventFields(track_event);
1698 AddDebugAnnotations(track_event, event);
1699 }
1700 } else {
1701 event.PopulateTracePacket(heap_buffered_packet->get());
1702 }
1703 print_callback(heap_buffered_packet);
1704 heap_buffered_packet->Reset();
1705}
1706
1707void TimelineEventFixedBufferRecorder::PrintPerfettoEvents(
1708 JSONBase64String* jsonBase64String,
1709 const TimelineEventFilter& filter) {
1710 PrintEventsCommon(
1711 filter, [this, &jsonBase64String](const TimelineEvent& event) {
1712 PrintPerfettoEventCallbackBody(
1713 &packet(), event,
1714 [&jsonBase64String](
1715 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*
1716 packet) {
1717 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String,
1718 packet);
1719 });
1720 });
1721}
1722#endif // defined(SUPPORT_PERFETTO)
1723
1724void TimelineEventFixedBufferRecorder::PrintJSON(JSONStream* js,
1725 TimelineEventFilter* filter) {
1726 JSONObject topLevel(js);
1727 topLevel.AddProperty("type", "Timeline");
1728 {
1729 JSONArray events(&topLevel, "traceEvents");
1730 PrintJSONMeta(events);
1731 PrintJSONEvents(events, *filter);
1732 }
1733 topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
1734 topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
1735}
1736
1737#define PRINT_PERFETTO_TIMELINE_BODY \
1738 JSONObject jsobj_topLevel(js); \
1739 jsobj_topLevel.AddProperty("type", "PerfettoTimeline"); \
1740 \
1741 js->AppendSerializedObject("\"trace\":"); \
1742 { \
1743 JSONBase64String jsonBase64String(js); \
1744 PrintPerfettoMeta(&jsonBase64String); \
1745 PrintPerfettoEvents(&jsonBase64String, filter); \
1746 } \
1747 \
1748 jsobj_topLevel.AddPropertyTimeMicros("timeOriginMicros", \
1749 TimeOriginMicros()); \
1750 jsobj_topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
1751
1752#if defined(SUPPORT_PERFETTO)
1753void TimelineEventFixedBufferRecorder::PrintPerfettoTimeline(
1754 JSONStream* js,
1755 const TimelineEventFilter& filter) {
1756 PRINT_PERFETTO_TIMELINE_BODY
1757}
1758#endif // defined(SUPPORT_PERFETTO)
1759
1760void TimelineEventFixedBufferRecorder::PrintTraceEvent(
1761 JSONStream* js,
1762 TimelineEventFilter* filter) {
1763 JSONArray events(js);
1764 PrintJSONMeta(events);
1765 PrintJSONEvents(events, *filter);
1766}
1767#endif // !defined(PRODUCT)
1768
1769TimelineEventBlock* TimelineEventFixedBufferRecorder::GetHeadBlockLocked() {
1770 ASSERT(lock_.IsOwnedByCurrentThread());
1771 return &blocks_[0];
1772}
1773
1774void TimelineEventFixedBufferRecorder::ClearLocked() {
1775 ASSERT(lock_.IsOwnedByCurrentThread());
1776 for (intptr_t i = 0; i < num_blocks_; i++) {
1777 TimelineEventBlock* block = &blocks_[i];
1778 block->Reset();
1779 }
1780}
1781
1782intptr_t TimelineEventFixedBufferRecorder::FindOldestBlockIndexLocked() const {
1783 ASSERT(lock_.IsOwnedByCurrentThread());
1784 int64_t earliest_time = kMaxInt64;
1785 intptr_t earliest_index = -1;
1786 for (intptr_t block_idx = 0; block_idx < num_blocks_; block_idx++) {
1787 TimelineEventBlock* block = &blocks_[block_idx];
1788 if (!block->ContainsEventsThatCanBeSerializedLocked()) {
1789 // Skip in use and empty blocks.
1790 continue;
1791 }
1792 if (block->LowerTimeBound() < earliest_time) {
1793 earliest_time = block->LowerTimeBound();
1794 earliest_index = block_idx;
1795 }
1796 }
1797 return earliest_index;
1798}
1799
1800TimelineEvent* TimelineEventFixedBufferRecorder::StartEvent() {
1801 return ThreadBlockStartEvent();
1802}
1803
1804void TimelineEventFixedBufferRecorder::CompleteEvent(TimelineEvent* event) {
1805 if (event == nullptr) {
1806 return;
1807 }
1808 ThreadBlockCompleteEvent(event);
1809}
1810
1811TimelineEventBlock* TimelineEventRingRecorder::GetNewBlockLocked() {
1812 ASSERT(lock_.IsOwnedByCurrentThread());
1813 if (block_cursor_ == num_blocks_) {
1814 block_cursor_ = 0;
1815 }
1816 TimelineEventBlock* block = &blocks_[block_cursor_++];
1817 if (block->current_owner_ != nullptr) {
1818 MutexLocker ml(block->current_owner_->timeline_block_lock());
1819 block->current_owner_->SetTimelineBlockLocked(nullptr);
1820 block->Reset();
1821 block->Open();
1822 } else {
1823 block->Reset();
1824 block->Open();
1825 }
1826 return block;
1827}
1828
1829TimelineEventBlock* TimelineEventStartupRecorder::GetNewBlockLocked() {
1830 ASSERT(lock_.IsOwnedByCurrentThread());
1831 if (block_cursor_ == num_blocks_) {
1832 return nullptr;
1833 }
1834 TimelineEventBlock* block = &blocks_[block_cursor_++];
1835 block->Reset();
1836 block->Open();
1837 return block;
1838}
1839
1840TimelineEventCallbackRecorder::TimelineEventCallbackRecorder() {}
1841
1842TimelineEventCallbackRecorder::~TimelineEventCallbackRecorder() {}
1843
1844#ifndef PRODUCT
1845void TimelineEventCallbackRecorder::PrintJSON(JSONStream* js,
1846 TimelineEventFilter* filter) {
1847 UNREACHABLE();
1848}
1849
1850#if defined(SUPPORT_PERFETTO)
1851void TimelineEventCallbackRecorder::PrintPerfettoTimeline(
1852 JSONStream* js,
1853 const TimelineEventFilter& filter) {
1854 UNREACHABLE();
1855}
1856#endif // defined(SUPPORT_PERFETTO)
1857
1858void TimelineEventCallbackRecorder::PrintTraceEvent(
1859 JSONStream* js,
1860 TimelineEventFilter* filter) {
1861 JSONArray events(js);
1862}
1863#endif // !defined(PRODUCT)
1864
1865TimelineEvent* TimelineEventCallbackRecorder::StartEvent() {
1866 TimelineEvent* event = new TimelineEvent();
1867 return event;
1868}
1869
1870void TimelineEventCallbackRecorder::CompleteEvent(TimelineEvent* event) {
1871 OnEvent(event);
1872 delete event;
1873}
1874
1875void TimelineEventEmbedderCallbackRecorder::OnEvent(TimelineEvent* event) {
1877 if (callback == nullptr) {
1878 return;
1879 }
1880
1881 Dart_TimelineRecorderEvent recorder_event;
1883 switch (event->event_type()) {
1884 case TimelineEvent::kBegin:
1885 recorder_event.type = Dart_Timeline_Event_Begin;
1886 break;
1887 case TimelineEvent::kEnd:
1888 recorder_event.type = Dart_Timeline_Event_End;
1889 break;
1890 case TimelineEvent::kInstant:
1891 recorder_event.type = Dart_Timeline_Event_Instant;
1892 break;
1893 case TimelineEvent::kDuration:
1894 recorder_event.type = Dart_Timeline_Event_Duration;
1895 break;
1896 case TimelineEvent::kAsyncBegin:
1897 recorder_event.type = Dart_Timeline_Event_Async_Begin;
1898 break;
1899 case TimelineEvent::kAsyncEnd:
1900 recorder_event.type = Dart_Timeline_Event_Async_End;
1901 break;
1902 case TimelineEvent::kAsyncInstant:
1903 recorder_event.type = Dart_Timeline_Event_Async_Instant;
1904 break;
1905 case TimelineEvent::kCounter:
1906 recorder_event.type = Dart_Timeline_Event_Counter;
1907 break;
1908 case TimelineEvent::kFlowBegin:
1909 recorder_event.type = Dart_Timeline_Event_Flow_Begin;
1910 break;
1911 case TimelineEvent::kFlowStep:
1912 recorder_event.type = Dart_Timeline_Event_Flow_Step;
1913 break;
1914 case TimelineEvent::kFlowEnd:
1915 recorder_event.type = Dart_Timeline_Event_Flow_End;
1916 break;
1917 default:
1918 // Type not expressible as Dart_Timeline_Event_Type: drop event.
1919 return;
1920 }
1921 recorder_event.timestamp0 = event->timestamp0();
1922 recorder_event.timestamp1_or_id = event->timestamp1_or_id();
1923 recorder_event.isolate = event->isolate_id();
1924 recorder_event.isolate_group = event->isolate_group_id();
1925 recorder_event.isolate_data = event->isolate_data();
1926 recorder_event.isolate_group_data = event->isolate_group_data();
1927 recorder_event.label = event->label();
1928 recorder_event.stream = event->stream()->name();
1929 recorder_event.argument_count = event->GetNumArguments();
1930 recorder_event.arguments =
1931 reinterpret_cast<Dart_TimelineRecorderEvent_Argument*>(
1932 event->arguments());
1933
1934 NoActiveIsolateScope no_active_isolate_scope;
1935 callback(&recorder_event);
1936}
1937
1938void TimelineEventNopRecorder::OnEvent(TimelineEvent* event) {
1939 // Do nothing.
1940}
1941
1942TimelineEventPlatformRecorder::TimelineEventPlatformRecorder() {}
1943
1944TimelineEventPlatformRecorder::~TimelineEventPlatformRecorder() {}
1945
1946#ifndef PRODUCT
1947void TimelineEventPlatformRecorder::PrintJSON(JSONStream* js,
1948 TimelineEventFilter* filter) {
1949 UNREACHABLE();
1950}
1951
1952#if defined(SUPPORT_PERFETTO)
1953void TimelineEventPlatformRecorder::PrintPerfettoTimeline(
1954 JSONStream* js,
1955 const TimelineEventFilter& filter) {
1956 UNREACHABLE();
1957}
1958#endif // defined(SUPPORT_PERFETTO)
1959
1960void TimelineEventPlatformRecorder::PrintTraceEvent(
1961 JSONStream* js,
1962 TimelineEventFilter* filter) {
1963 JSONArray events(js);
1964}
1965#endif // !defined(PRODUCT)
1966
1967TimelineEvent* TimelineEventPlatformRecorder::StartEvent() {
1968 TimelineEvent* event = new TimelineEvent();
1969 return event;
1970}
1971
1972void TimelineEventPlatformRecorder::CompleteEvent(TimelineEvent* event) {
1973 OnEvent(event);
1974 delete event;
1975}
1976
1977static void TimelineEventFileRecorderBaseStart(uword parameter) {
1978 reinterpret_cast<TimelineEventFileRecorderBase*>(parameter)->Drain();
1979}
1980
1981TimelineEventFileRecorderBase::TimelineEventFileRecorderBase(const char* path)
1982 : TimelineEventPlatformRecorder(),
1983 monitor_(),
1984 head_(nullptr),
1985 tail_(nullptr),
1986 file_(nullptr),
1987 shutting_down_(false),
1988 drained_(false),
1989 thread_id_(OSThread::kInvalidThreadJoinId) {
1990 Dart_FileOpenCallback file_open = Dart::file_open_callback();
1991 Dart_FileWriteCallback file_write = Dart::file_write_callback();
1992 Dart_FileCloseCallback file_close = Dart::file_close_callback();
1993 if ((file_open == nullptr) || (file_write == nullptr) ||
1994 (file_close == nullptr)) {
1995 OS::PrintErr("warning: Could not access file callbacks.");
1996 return;
1997 }
1998 void* file = (*file_open)(path, true);
1999 if (file == nullptr) {
2000 OS::PrintErr("warning: Failed to open timeline file: %s\n", path);
2001 return;
2002 }
2003
2004 file_ = file;
2005}
2006
2007TimelineEventFileRecorderBase::~TimelineEventFileRecorderBase() {
2008 // WARNING: |ShutDown()| must be called in the derived class destructor. This
2009 // work cannot be performed in this destructor, because then |DrainImpl()|
2010 // might run between when the derived class destructor completes, and when
2011 // |shutting_down_| is set to true, causing possible use-after-free errors.
2012 ASSERT(shutting_down_);
2013
2014 if (file_ == nullptr) return;
2015
2016 ASSERT(thread_id_ != OSThread::kInvalidThreadJoinId);
2017 OSThread::Join(thread_id_);
2018 thread_id_ = OSThread::kInvalidThreadJoinId;
2019
2020 TimelineEvent* event = head_;
2021 while (event != nullptr) {
2022 TimelineEvent* next = event->next();
2023 delete event;
2024 event = next;
2025 }
2026 head_ = tail_ = nullptr;
2027
2028 Dart_FileCloseCallback file_close = Dart::file_close_callback();
2029 (*file_close)(file_);
2030 file_ = nullptr;
2031}
2032
2033void TimelineEventFileRecorderBase::Drain() {
2034 MonitorLocker ml(&monitor_);
2035 thread_id_ = OSThread::GetCurrentThreadJoinId(OSThread::Current());
2036 while (!shutting_down_) {
2037 if (head_ == nullptr) {
2038 ml.Wait();
2039 continue; // Recheck empty and shutting down.
2040 }
2041 TimelineEvent* event = head_;
2042 TimelineEvent* next = event->next();
2043 head_ = next;
2044 if (next == nullptr) {
2045 tail_ = nullptr;
2046 }
2047 ml.Exit();
2048 {
2049 DrainImpl(*event);
2050 delete event;
2051 }
2052 ml.Enter();
2053 }
2054 drained_ = true;
2055 ml.Notify();
2056}
2057
2058void TimelineEventFileRecorderBase::Write(const char* buffer,
2059 intptr_t len) const {
2060 Dart_FileWriteCallback file_write = Dart::file_write_callback();
2061 (*file_write)(buffer, len, file_);
2062}
2063
2064void TimelineEventFileRecorderBase::CompleteEvent(TimelineEvent* event) {
2065 if (event == nullptr) {
2066 return;
2067 }
2068 if (file_ == nullptr) {
2069 delete event;
2070 return;
2071 }
2072
2073 MonitorLocker ml(&monitor_);
2074 ASSERT(!shutting_down_);
2075 event->set_next(nullptr);
2076 if (tail_ == nullptr) {
2077 head_ = tail_ = event;
2078 } else {
2079 tail_->set_next(event);
2080 tail_ = event;
2081 }
2082 ml.Notify();
2083}
2084
2085// Must be called in derived class destructors.
2086// See |~TimelineEventFileRecorderBase()| for an explanation.
2087void TimelineEventFileRecorderBase::ShutDown() {
2088 MonitorLocker ml(&monitor_);
2089 shutting_down_ = true;
2090 ml.NotifyAll();
2091 while (!drained_) {
2092 ml.Wait();
2093 }
2094}
2095
2096TimelineEventFileRecorder::TimelineEventFileRecorder(const char* path)
2097 : TimelineEventFileRecorderBase(path), first_(true) {
2098 // Chrome trace format has two forms:
2099 // Object form: { "traceEvents": [ event, event, event ] }
2100 // Array form: [ event, event, event ]
2101 // For this recorder, we use the array form because Catapult will handle a
2102 // missing ending bracket in this form in case we don't cleanly end the
2103 // trace.
2104 Write("[\n");
2105 OSThread::Start("TimelineEventFileRecorder",
2106 TimelineEventFileRecorderBaseStart,
2107 reinterpret_cast<uword>(this));
2108}
2109
2110TimelineEventFileRecorder::~TimelineEventFileRecorder() {
2111 ShutDown();
2112 Write("]\n");
2113}
2114
2115void TimelineEventFileRecorder::DrainImpl(const TimelineEvent& event) {
2116 JSONWriter writer;
2117 if (first_) {
2118 first_ = false;
2119 } else {
2120 writer.buffer()->AddChar(',');
2121 }
2122 event.PrintJSON(&writer);
2123 char* output = nullptr;
2124 intptr_t output_length = 0;
2125 writer.Steal(&output, &output_length);
2126 Write(output, output_length);
2127 free(output);
2128}
2129
2130#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
2131TimelineEventPerfettoFileRecorder::TimelineEventPerfettoFileRecorder(
2132 const char* path)
2133 : TimelineEventFileRecorderBase(path) {
2134 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>& packet =
2135 this->packet();
2136
2137 perfetto_utils::PopulateClockSnapshotPacket(packet.get());
2138 WritePacket(&packet);
2139 packet.Reset();
2140
2141 perfetto_utils::PopulateProcessDescriptorPacket(packet.get());
2142 WritePacket(&packet);
2143 packet.Reset();
2144
2145 OSThread::Start("TimelineEventPerfettoFileRecorder",
2146 TimelineEventFileRecorderBaseStart,
2147 reinterpret_cast<uword>(this));
2148}
2149
2150TimelineEventPerfettoFileRecorder::~TimelineEventPerfettoFileRecorder() {
2151 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>& packet =
2152 this->packet();
2153 ShutDown();
2154 // We do not need to lock the following section, because at this point
2155 // |RecorderSynchronizationLock| must have been put in a state that prevents
2156 // the metadata maps from being modified.
2157 for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata().Start();
2158 entry != nullptr; entry = track_uuid_to_track_metadata().Next(entry)) {
2159 TimelineTrackMetadata* value =
2160 static_cast<TimelineTrackMetadata*>(entry->value);
2161 value->PopulateTracePacket(packet.get());
2162 WritePacket(&packet);
2163 packet.Reset();
2164 }
2165 for (SimpleHashMap::Entry* entry =
2166 async_track_uuid_to_track_metadata().Start();
2167 entry != nullptr;
2168 entry = async_track_uuid_to_track_metadata().Next(entry)) {
2169 AsyncTimelineTrackMetadata* value =
2170 static_cast<AsyncTimelineTrackMetadata*>(entry->value);
2171 value->PopulateTracePacket(packet.get());
2172 WritePacket(&packet);
2173 packet.Reset();
2174 }
2175}
2176
2177void TimelineEventPerfettoFileRecorder::WritePacket(
2178 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet)
2179 const {
2180 const std::tuple<std::unique_ptr<const uint8_t[]>, intptr_t>& response =
2181 perfetto_utils::GetProtoPreamble(packet);
2182 Write(reinterpret_cast<const char*>(std::get<0>(response).get()),
2183 std::get<1>(response));
2184 for (const protozero::ScatteredHeapBuffer::Slice& slice :
2185 packet->GetSlices()) {
2186 Write(reinterpret_cast<char*>(slice.start()),
2187 slice.size() - slice.unused_bytes());
2188 }
2189}
2190
2191void TimelineEventPerfettoFileRecorder::DrainImpl(const TimelineEvent& event) {
2192 PrintPerfettoEventCallbackBody(
2193 &packet(), event,
2194 [this](protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*
2195 packet) { WritePacket(packet); });
2196
2197 if (event.event_type() == TimelineEvent::kAsyncBegin ||
2198 event.event_type() == TimelineEvent::kAsyncInstant) {
2199 AddAsyncTrackMetadataBasedOnEvent(event);
2200 }
2201}
2202#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
2203
2204TimelineEventEndlessRecorder::TimelineEventEndlessRecorder()
2205 : head_(nullptr), tail_(nullptr), block_index_(0) {}
2206
2207TimelineEventEndlessRecorder::~TimelineEventEndlessRecorder() {
2208 ASSERT(head_ == nullptr);
2209}
2210
2211#ifndef PRODUCT
2212void TimelineEventEndlessRecorder::PrintEventsCommon(
2213 const TimelineEventFilter& filter,
2214 std::function<void(const TimelineEvent&)>&& print_impl) {
2215 // Acquire the recorder's lock to prevent the reclaimed blocks from being
2216 // handed out again until the trace has been serialized.
2217 MutexLocker ml(&lock_);
2218 Timeline::ReclaimCachedBlocksFromThreads();
2219 ResetTimeTracking();
2220 for (TimelineEventBlock* current = head_; current != nullptr;
2221 current = current->next()) {
2222 if (!current->ContainsEventsThatCanBeSerializedLocked()) {
2223 continue;
2224 }
2225 intptr_t length = current->length();
2226 for (intptr_t i = 0; i < length; i++) {
2227 TimelineEvent* event = current->At(i);
2228 if (filter.IncludeEvent(event) &&
2229 event->Within(filter.time_origin_micros(),
2230 filter.time_extent_micros())) {
2231 ReportTime(event->LowTime());
2232 ReportTime(event->HighTime());
2233 print_impl(*event);
2234 }
2235 }
2236 }
2237}
2238
2239void TimelineEventEndlessRecorder::PrintJSONEvents(
2240 const JSONArray& events,
2241 const TimelineEventFilter& filter) {
2242 PrintEventsCommon(filter, [&events](const TimelineEvent& event) {
2243 events.AddValue(&event);
2244 });
2245}
2246
2247#if defined(SUPPORT_PERFETTO)
2248void TimelineEventEndlessRecorder::PrintPerfettoEvents(
2249 JSONBase64String* jsonBase64String,
2250 const TimelineEventFilter& filter) {
2251 PrintEventsCommon(
2252 filter, [this, &jsonBase64String](const TimelineEvent& event) {
2253 PrintPerfettoEventCallbackBody(
2254 &packet(), event,
2255 [&jsonBase64String](
2256 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*
2257 packet) {
2258 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String,
2259 packet);
2260 });
2261 });
2262}
2263#endif // defined(SUPPORT_PERFETTO)
2264
2265void TimelineEventEndlessRecorder::PrintJSON(JSONStream* js,
2266 TimelineEventFilter* filter) {
2267 JSONObject topLevel(js);
2268 topLevel.AddProperty("type", "Timeline");
2269 {
2270 JSONArray events(&topLevel, "traceEvents");
2271 PrintJSONMeta(events);
2272 PrintJSONEvents(events, *filter);
2273 }
2274 topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
2275 topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
2276}
2277
2278#if defined(SUPPORT_PERFETTO)
2279void TimelineEventEndlessRecorder::PrintPerfettoTimeline(
2280 JSONStream* js,
2281 const TimelineEventFilter& filter) {
2282 PRINT_PERFETTO_TIMELINE_BODY
2283}
2284#endif // defined(SUPPORT_PERFETTO)
2285
2286void TimelineEventEndlessRecorder::PrintTraceEvent(
2287 JSONStream* js,
2288 TimelineEventFilter* filter) {
2289 JSONArray events(js);
2290 PrintJSONMeta(events);
2291 PrintJSONEvents(events, *filter);
2292}
2293#endif // !defined(PRODUCT)
2294
2295TimelineEventBlock* TimelineEventEndlessRecorder::GetHeadBlockLocked() {
2296 ASSERT(lock_.IsOwnedByCurrentThread());
2297 return head_;
2298}
2299
2300TimelineEvent* TimelineEventEndlessRecorder::StartEvent() {
2301 return ThreadBlockStartEvent();
2302}
2303
2304void TimelineEventEndlessRecorder::CompleteEvent(TimelineEvent* event) {
2305 if (event == nullptr) {
2306 return;
2307 }
2308 ThreadBlockCompleteEvent(event);
2309}
2310
2311TimelineEventBlock* TimelineEventEndlessRecorder::GetNewBlockLocked() {
2312 ASSERT(lock_.IsOwnedByCurrentThread());
2313 TimelineEventBlock* block = new TimelineEventBlock(block_index_++);
2314 block->Open();
2315 if (head_ == nullptr) {
2316 head_ = tail_ = block;
2317 } else {
2318 tail_->set_next(block);
2319 tail_ = block;
2320 }
2321 if (FLAG_trace_timeline) {
2322 OS::PrintErr("Created new block %p\n", block);
2323 }
2324 return block;
2325}
2326
2327void TimelineEventEndlessRecorder::ClearLocked() {
2328 ASSERT(lock_.IsOwnedByCurrentThread());
2329 TimelineEventBlock* current = head_;
2330 while (current != nullptr) {
2331 TimelineEventBlock* next = current->next();
2332 delete current;
2333 current = next;
2334 }
2335 head_ = nullptr;
2336 tail_ = nullptr;
2337 block_index_ = 0;
2338}
2339
2340TimelineEventBlock::TimelineEventBlock(intptr_t block_index)
2341 : next_(nullptr),
2342 length_(0),
2343 block_index_(block_index),
2344 current_owner_(nullptr),
2345 in_use_(false) {}
2346
2347TimelineEventBlock::~TimelineEventBlock() {
2348 Reset();
2349}
2350
2351#ifndef PRODUCT
2352void TimelineEventBlock::PrintJSON(JSONStream* js) const {
2353 ASSERT(!InUseLocked());
2354 JSONArray events(js);
2355 for (intptr_t i = 0; i < length(); i++) {
2356 const TimelineEvent* event = At(i);
2357 if (event->IsValid()) {
2358 events.AddValue(event);
2359 }
2360 }
2361}
2362#endif
2363
2364TimelineEvent* TimelineEventBlock::StartEventLocked() {
2365 OSThread* os_thread = OSThread::Current();
2366 ASSERT(os_thread != nullptr);
2367 ASSERT(os_thread == current_owner_);
2368 ASSERT(os_thread->timeline_block_lock()->IsOwnedByCurrentThread());
2369 ASSERT(!IsFull());
2370 if (FLAG_trace_timeline) {
2371 intptr_t tid = OSThread::ThreadIdToIntPtr(os_thread->id());
2372 OS::PrintErr("StartEvent in block %p for thread %" Pd "\n", this, tid);
2373 }
2374 return &events_[length_++];
2375}
2376
2377int64_t TimelineEventBlock::LowerTimeBound() const {
2378 if (length_ == 0) {
2379 return kMaxInt64;
2380 }
2381 ASSERT(length_ > 0);
2382 return events_[0].TimeOrigin();
2383}
2384
2386 for (intptr_t i = 0; i < kBlockSize; i++) {
2387 // Clear any extra data.
2388 events_[i].Reset();
2389 }
2390 length_ = 0;
2391 current_owner_ = nullptr;
2392 in_use_ = false;
2393}
2394
2395void TimelineEventBlock::Open() {
2396 OSThread* os_thread = OSThread::Current();
2397 ASSERT(os_thread != nullptr);
2398 current_owner_ = os_thread;
2399 in_use_ = true;
2400}
2401
2403 if (FLAG_trace_timeline) {
2404 OS::PrintErr("Finish block %p\n", this);
2405 }
2406 current_owner_ = nullptr;
2407 in_use_ = false;
2408#ifndef PRODUCT
2409 if (Service::timeline_stream.enabled()) {
2410 ServiceEvent service_event(ServiceEvent::kTimelineEvents);
2411 service_event.set_timeline_event_block(this);
2412 Service::HandleEvent(&service_event, /* enter_safepoint */ false);
2413 }
2414#endif
2415}
2416
2417void DartTimelineEventHelpers::ReportTaskEvent(
2418 TimelineEvent* event,
2419 int64_t id,
2420 intptr_t flow_id_count,
2421 std::unique_ptr<const int64_t[]>& flow_ids,
2422 intptr_t type,
2423 char* name,
2424 char* args) {
2425 const int64_t start = OS::GetCurrentMonotonicMicrosForTimeline();
2426 switch (static_cast<TimelineEvent::EventType>(type)) {
2427 case TimelineEvent::kAsyncInstant:
2428 event->AsyncInstant(name, id, start);
2429 break;
2430 case TimelineEvent::kAsyncBegin:
2431 event->AsyncBegin(name, id, start);
2432 break;
2433 case TimelineEvent::kAsyncEnd:
2434 event->AsyncEnd(name, id, start);
2435 break;
2436 case TimelineEvent::kBegin:
2437 event->Begin(name, id, start);
2438 break;
2439 case TimelineEvent::kEnd:
2440 event->End(name, id, start);
2441 break;
2442 case TimelineEvent::kFlowBegin:
2443 event->FlowBegin(name, id, start);
2444 break;
2445 case TimelineEvent::kFlowStep:
2446 event->FlowStep(name, id, start);
2447 break;
2448 case TimelineEvent::kFlowEnd:
2449 event->FlowEnd(name, id, start);
2450 break;
2451 case TimelineEvent::kInstant:
2452 event->Instant(name, start);
2453 break;
2454 default:
2455 UNREACHABLE();
2456 }
2457 if (flow_id_count > 0) {
2458 ASSERT(type == TimelineEvent::kBegin || type == TimelineEvent::kInstant ||
2459 type == TimelineEvent::kAsyncBegin ||
2460 type == TimelineEvent::kAsyncInstant);
2461
2462 event->SetFlowIds(flow_id_count, flow_ids);
2463 }
2464 event->set_owns_label(true);
2465 event->CompleteWithPreSerializedArgs(args);
2466}
2467
2468} // namespace dart
2469
2470#endif // defined(SUPPORT_TIMELINE)
static float next(float f)
static uint32_t hash(const SkShaderBase::GradientInfo &v)
ax::mojom::Event event_type
#define UNREACHABLE()
Definition: assert.h:248
#define OUT_OF_MEMORY()
Definition: assert.h:250
GLenum type
static intptr_t ThreadIdToIntPtr(ThreadId id)
static void static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
static intptr_t ProcessId()
static char * StrDup(const char *s)
static bool StrStartsWith(const char *s, const char *prefix)
Definition: utils.h:627
static int static int VSNPrint(char *str, size_t size, const char *format, va_list args)
void set_name(const char *data, size_t size)
void set_legacy_json_value(const char *data, size_t size)
void set_string_value(const char *data, size_t size)
void set_thread_name(const char *data, size_t size)
void add_categories(const char *data, size_t size)
void set_type(::perfetto::protos::pbzero::TrackEvent_Type value)
void set_name(const char *data, size_t size)
void(* Dart_FileWriteCallback)(const void *data, intptr_t length, void *stream)
Definition: dart_api.h:807
#define ILLEGAL_PORT
Definition: dart_api.h:1535
void(* Dart_FileCloseCallback)(void *stream)
Definition: dart_api.h:820
int64_t Dart_Port
Definition: dart_api.h:1525
void *(* Dart_FileOpenCallback)(const char *name, bool write)
Definition: dart_api.h:776
#define DART_TIMELINE_RECORDER_CURRENT_VERSION
void(* Dart_TimelineRecorderCallback)(Dart_TimelineRecorderEvent *event)
@ Dart_Timeline_Event_Async_Begin
@ Dart_Timeline_Event_Async_End
@ Dart_Timeline_Event_Begin
@ Dart_Timeline_Event_Counter
@ Dart_Timeline_Event_Flow_End
@ Dart_Timeline_Event_Duration
@ Dart_Timeline_Event_Flow_Begin
@ Dart_Timeline_Event_End
@ Dart_Timeline_Event_Instant
@ Dart_Timeline_Event_Async_Instant
@ Dart_Timeline_Event_Flow_Step
#define ILLEGAL_ISOLATE_ID
#define ILLEGAL_ISOLATE_GROUP_ID
#define UNIMPLEMENTED
#define ASSERT(E)
FlutterSemanticsFlag flag
if(end==-1)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
uint8_t value
GAsyncResult * result
const char * charp
Definition: flags.h:12
Dart_NativeFunction function
Definition: fuchsia.cc:51
void Init()
size_t length
std::variant< Lower, Cross, Upper > EventType
Definition: EventQueue.h:47
va_start(args, format)
va_end(args)
Definition: dart_vm.cc:33
constexpr int64_t kMaxInt64
Definition: globals.h:486
static void Finish(Thread *thread)
Definition: bootstrap.cc:44
const char *const name
CAllocUniquePtr< char > CStringUniquePtr
Definition: utils.h:31
void * malloc(size_t size)
Definition: allocation.cc:19
static constexpr intptr_t kBlockSize
Definition: page.h:33
static void * Allocate(uword size, Zone *zone)
Definition: allocation.cc:14
static uint32_t WordHash(uint32_t key)
Definition: hashmap_test.cc:62
uintptr_t uword
Definition: globals.h:501
static void Free(FreeList *free_list, uword address, intptr_t size, bool is_protected)
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
void * calloc(size_t n, size_t size)
Definition: allocation.cc:11
void * realloc(void *ptr, size_t size)
Definition: allocation.cc:27
static void RoundUp(Vector< char > buffer, int *length, int *decimal_point)
Definition: fixed-dtoa.cc:189
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
void Reset(SkPath *path)
Definition: path_ops.cc:40
Join
Definition: path.h:24
TSize< Scalar > Size
Definition: size.h:137
const myers::Point & get< 1 >(const myers::Segment &s)
Definition: Myers.h:81
const myers::Point & get< 0 >(const myers::Segment &s)
Definition: Myers.h:80
const myers::Point & get(const myers::Segment &)
Definition: ref_ptr.h:256
static SkString fmt(SkColor4f c)
Definition: p3.cpp:43
bool EMSCRIPTEN_KEEPALIVE IsEmpty(const SkPath &path)
#define Px64
Definition: globals.h:418
#define Pd
Definition: globals.h:408
#define T
Definition: precompiler.cc:65
#define ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING
Definition: service.h:50
#define ISOLATE_SERVICE_ID_FORMAT_STRING
Definition: service.h:48
Dart_IsolateGroupId isolate_group
Dart_Timeline_Event_Type type
Dart_TimelineRecorderEvent_Argument * arguments