Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
285 RecorderSynchronizationLock::Init();
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
472void TimelineEventArguments::Free() {
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
511void TimelineEvent::Reset() {
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(
828 perfetto::protos::pbzero::TrackEvent::Type::TYPE_SLICE_BEGIN);
829}
830
831inline void AddInstantEventFields(
833 const TimelineEvent& event) {
834 AddBeginAndInstantEventCommonFields(track_event, event);
835 track_event->set_type(
836 perfetto::protos::pbzero::TrackEvent::Type::TYPE_INSTANT);
837}
838
839inline void AddEndEventFields(
841 track_event->set_type(
842 perfetto::protos::pbzero::TrackEvent::Type::TYPE_SLICE_END);
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(
1004 intptr_t pid,
1005 intptr_t tid,
1006 Utils::CStringUniquePtr&& track_name)
1007 : pid_(pid), tid_(tid), track_name_(std::move(track_name)) {}
1008
1009void TimelineTrackMetadata::set_track_name(
1010 Utils::CStringUniquePtr&& track_name) {
1011 track_name_ = std::move(track_name);
1012}
1013
1014#if !defined(PRODUCT)
1015void TimelineTrackMetadata::PrintJSON(const JSONArray& jsarr_events) const {
1016 JSONObject jsobj(&jsarr_events);
1017 jsobj.AddProperty("name", "thread_name");
1018 jsobj.AddProperty("ph", "M");
1019 jsobj.AddProperty("pid", pid());
1020 jsobj.AddProperty("tid", tid());
1021 {
1022 JSONObject jsobj_args(&jsobj, "args");
1023 jsobj_args.AddPropertyF("name", "%s (%" Pd ")", track_name(), tid());
1024 jsobj_args.AddProperty("mode", "basic");
1025 }
1026}
1027
1028#if defined(SUPPORT_PERFETTO)
1029void TimelineTrackMetadata::PopulateTracePacket(
1030 perfetto::protos::pbzero::TracePacket* track_descriptor_packet) const {
1031 perfetto_utils::SetTrustedPacketSequenceId(track_descriptor_packet);
1032
1034 *track_descriptor_packet->set_track_descriptor();
1035 track_descriptor.set_parent_uuid(pid());
1036 track_descriptor.set_uuid(tid());
1037
1039 *track_descriptor.set_thread();
1040 thread_descriptor.set_pid(pid());
1041 thread_descriptor.set_tid(tid());
1042 thread_descriptor.set_thread_name(track_name());
1043}
1044#endif // defined(SUPPORT_PERFETTO)
1045#endif // !defined(PRODUCT)
1046
1047AsyncTimelineTrackMetadata::AsyncTimelineTrackMetadata(intptr_t pid,
1048 intptr_t async_id)
1049 : pid_(pid), async_id_(async_id) {}
1050
1051#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
1052void AsyncTimelineTrackMetadata::PopulateTracePacket(
1053 perfetto::protos::pbzero::TracePacket* track_descriptor_packet) const {
1054 perfetto_utils::SetTrustedPacketSequenceId(track_descriptor_packet);
1056 *track_descriptor_packet->set_track_descriptor();
1057 track_descriptor.set_parent_uuid(pid());
1058 track_descriptor.set_uuid(async_id());
1059}
1060#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
1061
1062TimelineStream::TimelineStream(const char* name,
1063 const char* fuchsia_name,
1064 bool has_static_labels,
1065 bool enabled)
1066 : name_(name),
1067 fuchsia_name_(fuchsia_name),
1068#if defined(DART_HOST_OS_FUCHSIA)
1069 enabled_(static_cast<uintptr_t>(true)) // For generated code.
1070#else
1071 enabled_(static_cast<uintptr_t>(enabled))
1072#endif
1073{
1074#if defined(DART_HOST_OS_MACOS)
1075 macos_log_ = os_log_create("Dart", name);
1076 has_static_labels_ = has_static_labels;
1077#endif
1078}
1079
1080TimelineEvent* TimelineStream::StartEvent() {
1081 // Paired with |RecorderSynchronizationLock::ExitLock()| in
1082 // |TimelineEvent::Complete()|.
1083 //
1084 // The lock must be held until the event is completed to avoid having the
1085 // memory backing the event being freed in the middle of processing the
1086 // event.
1087 RecorderSynchronizationLock::EnterLock();
1088 TimelineEventRecorder* recorder = Timeline::recorder();
1089 if (!enabled() || (recorder == nullptr) ||
1090 !RecorderSynchronizationLock::IsActive()) {
1091 RecorderSynchronizationLock::ExitLock();
1092 return nullptr;
1093 }
1094 ASSERT(name_ != nullptr);
1095 TimelineEvent* event = recorder->StartEvent();
1096 if (event == nullptr) {
1097 RecorderSynchronizationLock::ExitLock();
1098 return nullptr;
1099 }
1100 event->StreamInit(this);
1101 return event;
1102}
1103
1104TimelineEventScope::TimelineEventScope(TimelineStream* stream,
1105 const char* label)
1106 : StackResource(static_cast<Thread*>(nullptr)),
1107 stream_(stream),
1108 label_(label),
1109 enabled_(false) {
1110 Init();
1111}
1112
1113TimelineEventScope::TimelineEventScope(Thread* thread,
1114 TimelineStream* stream,
1115 const char* label)
1116 : StackResource(thread), stream_(stream), label_(label), enabled_(false) {
1117 Init();
1118}
1119
1120TimelineEventScope::~TimelineEventScope() {}
1121
1122void TimelineEventScope::Init() {
1123 ASSERT(enabled_ == false);
1124 ASSERT(label_ != nullptr);
1125 ASSERT(stream_ != nullptr);
1126 if (!stream_->enabled()) {
1127 // Stream is not enabled, do nothing.
1128 return;
1129 }
1130 enabled_ = true;
1131 Thread* thread = static_cast<Thread*>(this->thread());
1132 if (thread != nullptr) {
1133 id_ = thread->GetNextTaskId();
1134 } else {
1135 static RelaxedAtomic<int64_t> next_bootstrap_task_id = {0};
1136 id_ = next_bootstrap_task_id.fetch_add(1);
1137 }
1138}
1139
1140void TimelineEventScope::SetNumArguments(intptr_t length) {
1141 if (!enabled()) {
1142 return;
1143 }
1144 arguments_.SetNumArguments(length);
1145}
1146
1147// |name| must be a compile time constant. Takes ownership of |argumentp|.
1148void TimelineEventScope::SetArgument(intptr_t i,
1149 const char* name,
1150 char* argument) {
1151 if (!enabled()) {
1152 return;
1153 }
1154 arguments_.SetArgument(i, name, argument);
1155}
1156
1157// |name| must be a compile time constant. Copies |argument|.
1158void TimelineEventScope::CopyArgument(intptr_t i,
1159 const char* name,
1160 const char* argument) {
1161 if (!enabled()) {
1162 return;
1163 }
1164 arguments_.CopyArgument(i, name, argument);
1165}
1166
1167void TimelineEventScope::FormatArgument(intptr_t i,
1168 const char* name,
1169 const char* fmt,
1170 ...) {
1171 if (!enabled()) {
1172 return;
1173 }
1174 va_list args;
1175 va_start(args, fmt);
1176 arguments_.FormatArgument(i, name, fmt, args);
1177 va_end(args);
1178}
1179
1180void TimelineEventScope::StealArguments(TimelineEvent* event) {
1181 if (event == nullptr) {
1182 return;
1183 }
1184 event->StealArguments(&arguments_);
1185}
1186
1187TimelineBeginEndScope::TimelineBeginEndScope(TimelineStream* stream,
1188 const char* label)
1189 : TimelineEventScope(stream, label) {
1190 EmitBegin();
1191}
1192
1193TimelineBeginEndScope::TimelineBeginEndScope(Thread* thread,
1194 TimelineStream* stream,
1195 const char* label)
1196 : TimelineEventScope(thread, stream, label) {
1197 EmitBegin();
1198}
1199
1200TimelineBeginEndScope::~TimelineBeginEndScope() {
1201 EmitEnd();
1202}
1203
1204void TimelineBeginEndScope::EmitBegin() {
1205 if (!ShouldEmitEvent()) {
1206 return;
1207 }
1208 TimelineEvent* event = stream()->StartEvent();
1209 if (event == nullptr) {
1210 // Stream is now disabled.
1211 set_enabled(false);
1212 return;
1213 }
1214 ASSERT(event != nullptr);
1215 // Emit a begin event.
1216 event->Begin(label(), id());
1217 event->Complete();
1218}
1219
1220void TimelineBeginEndScope::EmitEnd() {
1221 if (!ShouldEmitEvent()) {
1222 return;
1223 }
1224 TimelineEvent* event = stream()->StartEvent();
1225 if (event == nullptr) {
1226 // Stream is now disabled.
1227 set_enabled(false);
1228 return;
1229 }
1230 ASSERT(event != nullptr);
1231 // Emit an end event.
1232 event->End(label(), id());
1233 StealArguments(event);
1234 event->Complete();
1235}
1236
1237bool TimelineEventBlock::InUseLocked() const {
1238 ASSERT(Timeline::recorder()->lock_.IsOwnedByCurrentThread());
1239 return in_use_;
1240}
1241
1242bool TimelineEventBlock::ContainsEventsThatCanBeSerializedLocked() const {
1243 ASSERT(Timeline::recorder()->lock_.IsOwnedByCurrentThread());
1244 // Check that the block is not in use and not empty. |!block->in_use()| must
1245 // be checked first because we are only holding |lock_|. Holding |lock_|
1246 // makes it safe to call |in_use()| on any block, but only makes it safe to
1247 // call |IsEmpty()| on blocks that are not in use.
1248 return !InUseLocked() && !IsEmpty();
1249}
1250
1251TimelineEventFilter::TimelineEventFilter(int64_t time_origin_micros,
1252 int64_t time_extent_micros)
1253 : time_origin_micros_(time_origin_micros),
1254 time_extent_micros_(time_extent_micros) {
1255 ASSERT(time_origin_micros_ >= -1);
1256 ASSERT(time_extent_micros_ >= -1);
1257}
1258
1259TimelineEventFilter::~TimelineEventFilter() {}
1260
1261IsolateTimelineEventFilter::IsolateTimelineEventFilter(
1262 Dart_Port isolate_id,
1263 int64_t time_origin_micros,
1264 int64_t time_extent_micros)
1265 : TimelineEventFilter(time_origin_micros, time_extent_micros),
1266 isolate_id_(isolate_id) {}
1267
1268TimelineEventRecorder::TimelineEventRecorder()
1269 : time_low_micros_(0),
1270 time_high_micros_(0),
1271 track_uuid_to_track_metadata_lock_(),
1272 track_uuid_to_track_metadata_(
1273 &SimpleHashMap::SamePointerValue,
1274 TimelineEventRecorder::kTrackUuidToTrackMetadataInitialCapacity),
1275 async_track_uuid_to_track_metadata_lock_(),
1276 async_track_uuid_to_track_metadata_(
1277 &SimpleHashMap::SamePointerValue,
1278 TimelineEventRecorder::kTrackUuidToTrackMetadataInitialCapacity) {}
1279
1280TimelineEventRecorder::~TimelineEventRecorder() {
1281 // We do not need to lock the following section, because at this point
1282 // |RecorderSynchronizationLock| must have been put in a state that prevents
1283 // the metadata maps from being modified.
1284 for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata_.Start();
1285 entry != nullptr; entry = track_uuid_to_track_metadata_.Next(entry)) {
1286 TimelineTrackMetadata* value =
1287 static_cast<TimelineTrackMetadata*>(entry->value);
1288 delete value;
1289 }
1290 for (SimpleHashMap::Entry* entry =
1291 async_track_uuid_to_track_metadata_.Start();
1292 entry != nullptr;
1293 entry = async_track_uuid_to_track_metadata_.Next(entry)) {
1294 AsyncTimelineTrackMetadata* value =
1295 static_cast<AsyncTimelineTrackMetadata*>(entry->value);
1296 delete value;
1297 }
1298}
1299
1300#ifndef PRODUCT
1301void TimelineEventRecorder::PrintJSONMeta(const JSONArray& jsarr_events) {
1302 MutexLocker ml(&track_uuid_to_track_metadata_lock_);
1303 for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata_.Start();
1304 entry != nullptr; entry = track_uuid_to_track_metadata_.Next(entry)) {
1305 TimelineTrackMetadata* value =
1306 static_cast<TimelineTrackMetadata*>(entry->value);
1307 value->PrintJSON(jsarr_events);
1308 }
1309}
1310
1311#if defined(SUPPORT_PERFETTO)
1312void TimelineEventRecorder::PrintPerfettoMeta(
1313 JSONBase64String* jsonBase64String) {
1314 ASSERT(jsonBase64String != nullptr);
1315
1316 perfetto_utils::PopulateClockSnapshotPacket(packet_.get());
1317 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet_);
1318 packet_.Reset();
1319 perfetto_utils::PopulateProcessDescriptorPacket(packet_.get());
1320 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String, &packet_);
1321 packet_.Reset();
1322
1323 {
1324 MutexLocker ml(&async_track_uuid_to_track_metadata_lock_);
1325 for (SimpleHashMap::Entry* entry =
1326 async_track_uuid_to_track_metadata_.Start();
1327 entry != nullptr;
1328 entry = async_track_uuid_to_track_metadata_.Next(entry)) {
1329 AsyncTimelineTrackMetadata* value =
1330 static_cast<AsyncTimelineTrackMetadata*>(entry->value);
1331 value->PopulateTracePacket(packet_.get());
1332 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String,
1333 &packet_);
1334 packet_.Reset();
1335 }
1336 }
1337
1338 {
1339 MutexLocker ml(&track_uuid_to_track_metadata_lock_);
1340 for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata_.Start();
1341 entry != nullptr; entry = track_uuid_to_track_metadata_.Next(entry)) {
1342 TimelineTrackMetadata* value =
1343 static_cast<TimelineTrackMetadata*>(entry->value);
1344 value->PopulateTracePacket(packet_.get());
1345 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String,
1346 &packet_);
1347 packet_.Reset();
1348 }
1349 }
1350}
1351#endif // defined(SUPPORT_PERFETTO)
1352#endif // !defined(PRODUCT)
1353
1354TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() {
1355 // Grab the current thread.
1356 OSThread* thread = OSThread::Current();
1357 ASSERT(thread != nullptr);
1358 // Acquire the recorder lock in case we need to call |GetNewBlockLocked|. We
1359 // acquire the lock here and not directly before calls to |GetNewBlockLocked|
1360 // due to locking order restrictions.
1361 Mutex& recorder_lock = lock_;
1362 recorder_lock.Lock();
1363 Mutex* thread_block_lock = thread->timeline_block_lock();
1364 ASSERT(thread_block_lock != nullptr);
1365 // We are accessing the thread's timeline block- so take the lock here.
1366 // This lock will be held until the call to |CompleteEvent| is made.
1367 thread_block_lock->Lock();
1368#if defined(DEBUG)
1369 Thread* T = Thread::Current();
1370 if (T != nullptr) {
1371 T->IncrementNoSafepointScopeDepth();
1372 }
1373#endif // defined(DEBUG)
1374
1375 TimelineEventBlock* thread_block = thread->TimelineBlockLocked();
1376
1377 if ((thread_block != nullptr) && thread_block->IsFull()) {
1378 // Thread has a block and it is full:
1379 // 1) Mark it as finished.
1380 thread->SetTimelineBlockLocked(nullptr);
1381 FinishBlock(thread_block);
1382 // 2) Allocate a new block.
1383 // We release |thread_block_lock| before calling |GetNewBlockLocked| to
1384 // avoid TSAN warnings about lock order inversion.
1385 thread_block_lock->Unlock();
1386 thread_block = GetNewBlockLocked();
1387 thread_block_lock->Lock();
1388 thread->SetTimelineBlockLocked(thread_block);
1389 } else if (thread_block == nullptr) {
1390 // Thread has no block. Attempt to allocate one.
1391 // We release |thread_block_lock| before calling |GetNewBlockLocked| to
1392 // avoid TSAN warnings about lock order inversion.
1393 thread_block_lock->Unlock();
1394 thread_block = GetNewBlockLocked();
1395 thread_block_lock->Lock();
1396 thread->SetTimelineBlockLocked(thread_block);
1397 }
1398 recorder_lock.Unlock();
1399 if (thread_block != nullptr) {
1400 // NOTE: We are exiting this function with the thread's block lock held.
1401 ASSERT(!thread_block->IsFull());
1402 TimelineEvent* event = thread_block->StartEventLocked();
1403 return event;
1404 }
1405// Drop lock here as no event is being handed out.
1406#if defined(DEBUG)
1407 if (T != nullptr) {
1408 T->DecrementNoSafepointScopeDepth();
1409 }
1410#endif // defined(DEBUG)
1411 thread_block_lock->Unlock();
1412 return nullptr;
1413}
1414
1415void TimelineEventRecorder::ResetTimeTracking() {
1416 time_high_micros_ = 0;
1417 time_low_micros_ = kMaxInt64;
1418}
1419
1420void TimelineEventRecorder::ReportTime(int64_t micros) {
1421 if (time_high_micros_ < micros) {
1422 time_high_micros_ = micros;
1423 }
1424 if (time_low_micros_ > micros) {
1425 time_low_micros_ = micros;
1426 }
1427}
1428
1429int64_t TimelineEventRecorder::TimeOriginMicros() const {
1430 if (time_high_micros_ == 0) {
1431 return 0;
1432 }
1433 return time_low_micros_;
1434}
1435
1436int64_t TimelineEventRecorder::TimeExtentMicros() const {
1437 if (time_high_micros_ == 0) {
1438 return 0;
1439 }
1440 return time_high_micros_ - time_low_micros_;
1441}
1442
1443void TimelineEventRecorder::ThreadBlockCompleteEvent(TimelineEvent* event) {
1444 if (event == nullptr) {
1445 return;
1446 }
1447#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
1448 // Async track metadata is only written in Perfetto traces, and Perfetto
1449 // traces cannot be written when SUPPORT_PERFETTO is not defined, or when
1450 // PRODUCT is defined.
1451 if (event->event_type() == TimelineEvent::kAsyncBegin ||
1452 event->event_type() == TimelineEvent::kAsyncInstant) {
1453 AddAsyncTrackMetadataBasedOnEvent(*event);
1454 }
1455#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
1456 // Grab the current thread.
1457 OSThread* thread = OSThread::Current();
1458 ASSERT(thread != nullptr);
1459 // Unlock the thread's block lock.
1460 Mutex* thread_block_lock = thread->timeline_block_lock();
1461 ASSERT(thread_block_lock != nullptr);
1462#if defined(DEBUG)
1463 Thread* T = Thread::Current();
1464 if (T != nullptr) {
1465 T->DecrementNoSafepointScopeDepth();
1466 }
1467#endif // defined(DEBUG)
1468 thread_block_lock->Unlock();
1469}
1470
1471#ifndef PRODUCT
1472void TimelineEventRecorder::WriteTo(const char* directory) {
1473 Dart_FileOpenCallback file_open = Dart::file_open_callback();
1474 Dart_FileWriteCallback file_write = Dart::file_write_callback();
1475 Dart_FileCloseCallback file_close = Dart::file_close_callback();
1476 if ((file_open == nullptr) || (file_write == nullptr) ||
1477 (file_close == nullptr)) {
1478 OS::PrintErr("warning: Could not access file callbacks.");
1479 return;
1480 }
1481
1482 // Acquire the recorder's lock to prevent the reclaimed blocks from being
1483 // handed out again until the trace has been serialized.
1484 MutexLocker ml(&lock_);
1485 Timeline::ReclaimCachedBlocksFromThreads();
1486
1487 intptr_t pid = OS::ProcessId();
1488 char* filename =
1489 OS::SCreate(nullptr, "%s/dart-timeline-%" Pd ".json", directory, pid);
1490 void* file = (*file_open)(filename, true);
1491 if (file == nullptr) {
1492 OS::PrintErr("warning: Failed to write timeline file: %s\n", filename);
1493 free(filename);
1494 return;
1495 }
1496 free(filename);
1497
1498 JSONStream js;
1499 TimelineEventFilter filter;
1500 PrintTraceEvent(&js, &filter);
1501 // Steal output from JSONStream.
1502 char* output = nullptr;
1503 intptr_t output_length = 0;
1504 js.Steal(&output, &output_length);
1505 (*file_write)(output, output_length, file);
1506 // Free the stolen output.
1507 free(output);
1508 (*file_close)(file);
1509
1510 return;
1511}
1512#endif
1513
1514void TimelineEventRecorder::FinishBlock(TimelineEventBlock* block) {
1515 if (block != nullptr) {
1516 block->Finish();
1517 }
1518}
1519
1520void TimelineEventRecorder::AddTrackMetadataBasedOnThread(
1521 const intptr_t process_id,
1522 const intptr_t trace_id,
1523 const char* thread_name) {
1524 ASSERT(FLAG_timeline_recorder != nullptr);
1525 if (strcmp("none", FLAG_timeline_recorder) == 0 ||
1526 strcmp("callback", FLAG_timeline_recorder) == 0 ||
1527 strcmp("systrace", FLAG_timeline_recorder) == 0 ||
1528 FLAG_systrace_timeline) {
1529 // There is no way to retrieve track metadata when a no-op, callback, or
1530 // systrace recorder is in use, so we don't need to update the map in these
1531 // cases.
1532 return;
1533 }
1534 MutexLocker ml(&track_uuid_to_track_metadata_lock_);
1535
1536 void* key = reinterpret_cast<void*>(trace_id);
1537 const intptr_t hash = Utils::WordHash(trace_id);
1538 SimpleHashMap::Entry* entry =
1539 track_uuid_to_track_metadata_.Lookup(key, hash, true);
1540 if (entry->value == nullptr) {
1541 entry->value = new TimelineTrackMetadata(
1542 process_id, trace_id,
1543 Utils::CreateCStringUniquePtr(
1544 Utils::StrDup(thread_name == nullptr ? "" : thread_name)));
1545 } else {
1546 TimelineTrackMetadata* value =
1547 static_cast<TimelineTrackMetadata*>(entry->value);
1548 ASSERT(process_id == value->pid());
1549 value->set_track_name(Utils::CreateCStringUniquePtr(
1550 Utils::StrDup(thread_name == nullptr ? "" : thread_name)));
1551 }
1552}
1553
1554#if !defined(PRODUCT)
1555void TimelineEventRecorder::AddAsyncTrackMetadataBasedOnEvent(
1556 const TimelineEvent& event) {
1557 ASSERT(FLAG_timeline_recorder != nullptr);
1558 if (strcmp("none", FLAG_timeline_recorder) == 0 ||
1559 strcmp("callback", FLAG_timeline_recorder) == 0 ||
1560 strcmp("systrace", FLAG_timeline_recorder) == 0 ||
1561 FLAG_systrace_timeline) {
1562 // There is no way to retrieve track metadata when a no-op, callback, or
1563 // systrace recorder is in use, so we don't need to update the map in
1564 // these cases.
1565 return;
1566 }
1567 MutexLocker ml(&async_track_uuid_to_track_metadata_lock_);
1568
1569 void* key = reinterpret_cast<void*>(event.Id());
1570 const intptr_t hash = Utils::WordHash(event.Id());
1571 SimpleHashMap::Entry* entry =
1572 async_track_uuid_to_track_metadata_.Lookup(key, hash, true);
1573 if (entry->value == nullptr) {
1574 entry->value = new AsyncTimelineTrackMetadata(OS::ProcessId(), event.Id());
1575 }
1576}
1577#endif // !defined(PRODUCT)
1578
1579TimelineEventFixedBufferRecorder::TimelineEventFixedBufferRecorder(
1580 intptr_t capacity)
1581 : memory_(nullptr),
1582 blocks_(nullptr),
1583 capacity_(capacity),
1584 num_blocks_(0),
1585 block_cursor_(0) {
1586 // Capacity must be a multiple of TimelineEventBlock::kBlockSize
1587 ASSERT((capacity % TimelineEventBlock::kBlockSize) == 0);
1588 // Allocate blocks array.
1589 num_blocks_ = capacity / TimelineEventBlock::kBlockSize;
1590
1591 intptr_t size = Utils::RoundUp(num_blocks_ * sizeof(TimelineEventBlock),
1592 VirtualMemory::PageSize());
1593 const bool executable = false;
1594 const bool compressed = false;
1595 memory_ =
1596 VirtualMemory::Allocate(size, executable, compressed, "dart-timeline");
1597 if (memory_ == nullptr) {
1598 OUT_OF_MEMORY();
1599 }
1600 blocks_ = reinterpret_cast<TimelineEventBlock*>(memory_->address());
1601}
1602
1603TimelineEventFixedBufferRecorder::~TimelineEventFixedBufferRecorder() {
1604 // We do not need to acquire any locks, because at this point we must have
1605 // reclaimed all the blocks, and |RecorderSynchronizationLock| must have been
1606 // put in a state that prevents blocks from being given out.
1607 delete memory_;
1608}
1609
1610intptr_t TimelineEventFixedBufferRecorder::Size() {
1611 return memory_->size();
1612}
1613
1614#ifndef PRODUCT
1615void TimelineEventFixedBufferRecorder::PrintEventsCommon(
1616 const TimelineEventFilter& filter,
1617 std::function<void(const TimelineEvent&)>&& print_impl) {
1618 // Acquire the recorder's lock to prevent the reclaimed blocks from being
1619 // handed out again until the trace has been serialized.
1620 MutexLocker ml(&lock_);
1621 Timeline::ReclaimCachedBlocksFromThreads();
1622 ResetTimeTracking();
1623 intptr_t block_offset = FindOldestBlockIndexLocked();
1624 if (block_offset == -1) {
1625 // All blocks are in use or empty.
1626 return;
1627 }
1628 for (intptr_t block_idx = 0; block_idx < num_blocks_; block_idx++) {
1629 TimelineEventBlock* block =
1630 &blocks_[(block_idx + block_offset) % num_blocks_];
1631 if (!block->ContainsEventsThatCanBeSerializedLocked()) {
1632 continue;
1633 }
1634 for (intptr_t event_idx = 0; event_idx < block->length(); event_idx++) {
1635 TimelineEvent* event = block->At(event_idx);
1636 if (filter.IncludeEvent(event) &&
1637 event->Within(filter.time_origin_micros(),
1638 filter.time_extent_micros())) {
1639 ReportTime(event->LowTime());
1640 ReportTime(event->HighTime());
1641 print_impl(*event);
1642 }
1643 }
1644 }
1645}
1646
1647void TimelineEventFixedBufferRecorder::PrintJSONEvents(
1648 const JSONArray& events,
1649 const TimelineEventFilter& filter) {
1650 PrintEventsCommon(filter, [&events](const TimelineEvent& event) {
1651 events.AddValue(&event);
1652 });
1653}
1654
1655#if defined(SUPPORT_PERFETTO)
1656// Populates the fields of |heap_buffered_packet| with the data in |event|, and
1657// then calls |print_callback| with the populated |heap_buffered_packet| as the
1658// only argument. This function resets |heap_buffered_packet| right before
1659// returning.
1660inline void PrintPerfettoEventCallbackBody(
1661 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*
1662 heap_buffered_packet,
1663 const TimelineEvent& event,
1664 const std::function<
1665 void(protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*)>&&
1666 print_callback) {
1667 ASSERT(heap_buffered_packet != nullptr);
1668 if (!event.CanBeRepresentedByPerfettoTracePacket()) {
1669 return;
1670 }
1671 if (event.IsDuration()) {
1672 // Duration events must be converted to pairs of begin and end events to
1673 // be serialized in Perfetto's format.
1675 *heap_buffered_packet->get();
1676 {
1677 perfetto_utils::SetTrustedPacketSequenceId(&packet);
1678 perfetto_utils::SetTimestampAndMonotonicClockId(&packet,
1679 event.TimeOrigin());
1680
1682 packet.set_track_event();
1683 track_event->add_categories(event.stream()->name());
1684 AddSyncEventFields(track_event, event);
1685 AddBeginEventFields(track_event, event);
1686 AddDebugAnnotations(track_event, event);
1687 }
1688 print_callback(heap_buffered_packet);
1689 heap_buffered_packet->Reset();
1690
1691 {
1692 perfetto_utils::SetTrustedPacketSequenceId(&packet);
1693 perfetto_utils::SetTimestampAndMonotonicClockId(&packet, event.TimeEnd());
1694
1696 packet.set_track_event();
1697 track_event->add_categories(event.stream()->name());
1698 AddSyncEventFields(track_event, event);
1699 AddEndEventFields(track_event);
1700 AddDebugAnnotations(track_event, event);
1701 }
1702 } else {
1703 event.PopulateTracePacket(heap_buffered_packet->get());
1704 }
1705 print_callback(heap_buffered_packet);
1706 heap_buffered_packet->Reset();
1707}
1708
1709void TimelineEventFixedBufferRecorder::PrintPerfettoEvents(
1710 JSONBase64String* jsonBase64String,
1711 const TimelineEventFilter& filter) {
1712 PrintEventsCommon(
1713 filter, [this, &jsonBase64String](const TimelineEvent& event) {
1714 PrintPerfettoEventCallbackBody(
1715 &packet(), event,
1716 [&jsonBase64String](
1717 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*
1718 packet) {
1719 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String,
1720 packet);
1721 });
1722 });
1723}
1724#endif // defined(SUPPORT_PERFETTO)
1725
1726void TimelineEventFixedBufferRecorder::PrintJSON(JSONStream* js,
1727 TimelineEventFilter* filter) {
1728 JSONObject topLevel(js);
1729 topLevel.AddProperty("type", "Timeline");
1730 {
1731 JSONArray events(&topLevel, "traceEvents");
1732 PrintJSONMeta(events);
1733 PrintJSONEvents(events, *filter);
1734 }
1735 topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
1736 topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
1737}
1738
1739#define PRINT_PERFETTO_TIMELINE_BODY \
1740 JSONObject jsobj_topLevel(js); \
1741 jsobj_topLevel.AddProperty("type", "PerfettoTimeline"); \
1742 \
1743 js->AppendSerializedObject("\"trace\":"); \
1744 { \
1745 JSONBase64String jsonBase64String(js); \
1746 PrintPerfettoMeta(&jsonBase64String); \
1747 PrintPerfettoEvents(&jsonBase64String, filter); \
1748 } \
1749 \
1750 jsobj_topLevel.AddPropertyTimeMicros("timeOriginMicros", \
1751 TimeOriginMicros()); \
1752 jsobj_topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
1753
1754#if defined(SUPPORT_PERFETTO)
1755void TimelineEventFixedBufferRecorder::PrintPerfettoTimeline(
1756 JSONStream* js,
1757 const TimelineEventFilter& filter) {
1758 PRINT_PERFETTO_TIMELINE_BODY
1759}
1760#endif // defined(SUPPORT_PERFETTO)
1761
1762void TimelineEventFixedBufferRecorder::PrintTraceEvent(
1763 JSONStream* js,
1764 TimelineEventFilter* filter) {
1765 JSONArray events(js);
1766 PrintJSONMeta(events);
1767 PrintJSONEvents(events, *filter);
1768}
1769#endif // !defined(PRODUCT)
1770
1771TimelineEventBlock* TimelineEventFixedBufferRecorder::GetHeadBlockLocked() {
1772 ASSERT(lock_.IsOwnedByCurrentThread());
1773 return &blocks_[0];
1774}
1775
1776void TimelineEventFixedBufferRecorder::ClearLocked() {
1777 ASSERT(lock_.IsOwnedByCurrentThread());
1778 for (intptr_t i = 0; i < num_blocks_; i++) {
1779 TimelineEventBlock* block = &blocks_[i];
1780 block->Reset();
1781 }
1782}
1783
1784intptr_t TimelineEventFixedBufferRecorder::FindOldestBlockIndexLocked() const {
1785 ASSERT(lock_.IsOwnedByCurrentThread());
1786 int64_t earliest_time = kMaxInt64;
1787 intptr_t earliest_index = -1;
1788 for (intptr_t block_idx = 0; block_idx < num_blocks_; block_idx++) {
1789 TimelineEventBlock* block = &blocks_[block_idx];
1790 if (!block->ContainsEventsThatCanBeSerializedLocked()) {
1791 // Skip in use and empty blocks.
1792 continue;
1793 }
1794 if (block->LowerTimeBound() < earliest_time) {
1795 earliest_time = block->LowerTimeBound();
1796 earliest_index = block_idx;
1797 }
1798 }
1799 return earliest_index;
1800}
1801
1802TimelineEvent* TimelineEventFixedBufferRecorder::StartEvent() {
1803 return ThreadBlockStartEvent();
1804}
1805
1806void TimelineEventFixedBufferRecorder::CompleteEvent(TimelineEvent* event) {
1807 if (event == nullptr) {
1808 return;
1809 }
1810 ThreadBlockCompleteEvent(event);
1811}
1812
1813TimelineEventBlock* TimelineEventRingRecorder::GetNewBlockLocked() {
1814 ASSERT(lock_.IsOwnedByCurrentThread());
1815 if (block_cursor_ == num_blocks_) {
1816 block_cursor_ = 0;
1817 }
1818 TimelineEventBlock* block = &blocks_[block_cursor_++];
1819 if (block->current_owner_ != nullptr) {
1820 MutexLocker ml(block->current_owner_->timeline_block_lock());
1821 block->current_owner_->SetTimelineBlockLocked(nullptr);
1822 block->Reset();
1823 block->Open();
1824 } else {
1825 block->Reset();
1826 block->Open();
1827 }
1828 return block;
1829}
1830
1831TimelineEventBlock* TimelineEventStartupRecorder::GetNewBlockLocked() {
1832 ASSERT(lock_.IsOwnedByCurrentThread());
1833 if (block_cursor_ == num_blocks_) {
1834 return nullptr;
1835 }
1836 TimelineEventBlock* block = &blocks_[block_cursor_++];
1837 block->Reset();
1838 block->Open();
1839 return block;
1840}
1841
1842TimelineEventCallbackRecorder::TimelineEventCallbackRecorder() {}
1843
1844TimelineEventCallbackRecorder::~TimelineEventCallbackRecorder() {}
1845
1846#ifndef PRODUCT
1847void TimelineEventCallbackRecorder::PrintJSON(JSONStream* js,
1848 TimelineEventFilter* filter) {
1849 UNREACHABLE();
1850}
1851
1852#if defined(SUPPORT_PERFETTO)
1853void TimelineEventCallbackRecorder::PrintPerfettoTimeline(
1854 JSONStream* js,
1855 const TimelineEventFilter& filter) {
1856 UNREACHABLE();
1857}
1858#endif // defined(SUPPORT_PERFETTO)
1859
1860void TimelineEventCallbackRecorder::PrintTraceEvent(
1861 JSONStream* js,
1862 TimelineEventFilter* filter) {
1863 JSONArray events(js);
1864}
1865#endif // !defined(PRODUCT)
1866
1867TimelineEvent* TimelineEventCallbackRecorder::StartEvent() {
1868 TimelineEvent* event = new TimelineEvent();
1869 return event;
1870}
1871
1872void TimelineEventCallbackRecorder::CompleteEvent(TimelineEvent* event) {
1873 OnEvent(event);
1874 delete event;
1875}
1876
1877void TimelineEventEmbedderCallbackRecorder::OnEvent(TimelineEvent* event) {
1878 Dart_TimelineRecorderCallback callback = Timeline::callback();
1879 if (callback == nullptr) {
1880 return;
1881 }
1882
1883 Dart_TimelineRecorderEvent recorder_event;
1885 switch (event->event_type()) {
1886 case TimelineEvent::kBegin:
1887 recorder_event.type = Dart_Timeline_Event_Begin;
1888 break;
1889 case TimelineEvent::kEnd:
1890 recorder_event.type = Dart_Timeline_Event_End;
1891 break;
1892 case TimelineEvent::kInstant:
1893 recorder_event.type = Dart_Timeline_Event_Instant;
1894 break;
1895 case TimelineEvent::kDuration:
1896 recorder_event.type = Dart_Timeline_Event_Duration;
1897 break;
1898 case TimelineEvent::kAsyncBegin:
1899 recorder_event.type = Dart_Timeline_Event_Async_Begin;
1900 break;
1901 case TimelineEvent::kAsyncEnd:
1902 recorder_event.type = Dart_Timeline_Event_Async_End;
1903 break;
1904 case TimelineEvent::kAsyncInstant:
1905 recorder_event.type = Dart_Timeline_Event_Async_Instant;
1906 break;
1907 case TimelineEvent::kCounter:
1908 recorder_event.type = Dart_Timeline_Event_Counter;
1909 break;
1910 case TimelineEvent::kFlowBegin:
1911 recorder_event.type = Dart_Timeline_Event_Flow_Begin;
1912 break;
1913 case TimelineEvent::kFlowStep:
1914 recorder_event.type = Dart_Timeline_Event_Flow_Step;
1915 break;
1916 case TimelineEvent::kFlowEnd:
1917 recorder_event.type = Dart_Timeline_Event_Flow_End;
1918 break;
1919 default:
1920 // Type not expressible as Dart_Timeline_Event_Type: drop event.
1921 return;
1922 }
1923 recorder_event.timestamp0 = event->timestamp0();
1924 recorder_event.timestamp1_or_id = event->timestamp1_or_id();
1925 recorder_event.isolate = event->isolate_id();
1926 recorder_event.isolate_group = event->isolate_group_id();
1927 recorder_event.isolate_data = event->isolate_data();
1928 recorder_event.isolate_group_data = event->isolate_group_data();
1929 recorder_event.label = event->label();
1930 recorder_event.stream = event->stream()->name();
1931 recorder_event.argument_count = event->GetNumArguments();
1932 recorder_event.arguments =
1933 reinterpret_cast<Dart_TimelineRecorderEvent_Argument*>(
1934 event->arguments());
1935
1936 NoActiveIsolateScope no_active_isolate_scope;
1937 callback(&recorder_event);
1938}
1939
1940void TimelineEventNopRecorder::OnEvent(TimelineEvent* event) {
1941 // Do nothing.
1942}
1943
1944TimelineEventPlatformRecorder::TimelineEventPlatformRecorder() {}
1945
1946TimelineEventPlatformRecorder::~TimelineEventPlatformRecorder() {}
1947
1948#ifndef PRODUCT
1949void TimelineEventPlatformRecorder::PrintJSON(JSONStream* js,
1950 TimelineEventFilter* filter) {
1951 UNREACHABLE();
1952}
1953
1954#if defined(SUPPORT_PERFETTO)
1955void TimelineEventPlatformRecorder::PrintPerfettoTimeline(
1956 JSONStream* js,
1957 const TimelineEventFilter& filter) {
1958 UNREACHABLE();
1959}
1960#endif // defined(SUPPORT_PERFETTO)
1961
1962void TimelineEventPlatformRecorder::PrintTraceEvent(
1963 JSONStream* js,
1964 TimelineEventFilter* filter) {
1965 JSONArray events(js);
1966}
1967#endif // !defined(PRODUCT)
1968
1969TimelineEvent* TimelineEventPlatformRecorder::StartEvent() {
1970 TimelineEvent* event = new TimelineEvent();
1971 return event;
1972}
1973
1974void TimelineEventPlatformRecorder::CompleteEvent(TimelineEvent* event) {
1975 OnEvent(event);
1976 delete event;
1977}
1978
1979static void TimelineEventFileRecorderBaseStart(uword parameter) {
1980 reinterpret_cast<TimelineEventFileRecorderBase*>(parameter)->Drain();
1981}
1982
1983TimelineEventFileRecorderBase::TimelineEventFileRecorderBase(const char* path)
1984 : TimelineEventPlatformRecorder(),
1985 monitor_(),
1986 head_(nullptr),
1987 tail_(nullptr),
1988 file_(nullptr),
1989 shutting_down_(false),
1990 drained_(false),
1991 thread_id_(OSThread::kInvalidThreadJoinId) {
1992 Dart_FileOpenCallback file_open = Dart::file_open_callback();
1993 Dart_FileWriteCallback file_write = Dart::file_write_callback();
1994 Dart_FileCloseCallback file_close = Dart::file_close_callback();
1995 if ((file_open == nullptr) || (file_write == nullptr) ||
1996 (file_close == nullptr)) {
1997 OS::PrintErr("warning: Could not access file callbacks.");
1998 return;
1999 }
2000 void* file = (*file_open)(path, true);
2001 if (file == nullptr) {
2002 OS::PrintErr("warning: Failed to open timeline file: %s\n", path);
2003 return;
2004 }
2005
2006 file_ = file;
2007}
2008
2009TimelineEventFileRecorderBase::~TimelineEventFileRecorderBase() {
2010 // WARNING: |ShutDown()| must be called in the derived class destructor. This
2011 // work cannot be performed in this destructor, because then |DrainImpl()|
2012 // might run between when the derived class destructor completes, and when
2013 // |shutting_down_| is set to true, causing possible use-after-free errors.
2014 ASSERT(shutting_down_);
2015
2016 if (file_ == nullptr) return;
2017
2018 ASSERT(thread_id_ != OSThread::kInvalidThreadJoinId);
2019 OSThread::Join(thread_id_);
2020 thread_id_ = OSThread::kInvalidThreadJoinId;
2021
2022 TimelineEvent* event = head_;
2023 while (event != nullptr) {
2024 TimelineEvent* next = event->next();
2025 delete event;
2026 event = next;
2027 }
2028 head_ = tail_ = nullptr;
2029
2030 Dart_FileCloseCallback file_close = Dart::file_close_callback();
2031 (*file_close)(file_);
2032 file_ = nullptr;
2033}
2034
2035void TimelineEventFileRecorderBase::Drain() {
2036 MonitorLocker ml(&monitor_);
2037 thread_id_ = OSThread::GetCurrentThreadJoinId(OSThread::Current());
2038 while (!shutting_down_) {
2039 if (head_ == nullptr) {
2040 ml.Wait();
2041 continue; // Recheck empty and shutting down.
2042 }
2043 TimelineEvent* event = head_;
2044 TimelineEvent* next = event->next();
2045 head_ = next;
2046 if (next == nullptr) {
2047 tail_ = nullptr;
2048 }
2049 ml.Exit();
2050 {
2051 DrainImpl(*event);
2052 delete event;
2053 }
2054 ml.Enter();
2055 }
2056 drained_ = true;
2057 ml.Notify();
2058}
2059
2060void TimelineEventFileRecorderBase::Write(const char* buffer,
2061 intptr_t len) const {
2062 Dart_FileWriteCallback file_write = Dart::file_write_callback();
2063 (*file_write)(buffer, len, file_);
2064}
2065
2066void TimelineEventFileRecorderBase::CompleteEvent(TimelineEvent* event) {
2067 if (event == nullptr) {
2068 return;
2069 }
2070 if (file_ == nullptr) {
2071 delete event;
2072 return;
2073 }
2074
2075 MonitorLocker ml(&monitor_);
2076 ASSERT(!shutting_down_);
2077 event->set_next(nullptr);
2078 if (tail_ == nullptr) {
2079 head_ = tail_ = event;
2080 } else {
2081 tail_->set_next(event);
2082 tail_ = event;
2083 }
2084 ml.Notify();
2085}
2086
2087// Must be called in derived class destructors.
2088// See |~TimelineEventFileRecorderBase()| for an explanation.
2089void TimelineEventFileRecorderBase::ShutDown() {
2090 MonitorLocker ml(&monitor_);
2091 shutting_down_ = true;
2092 ml.NotifyAll();
2093 while (!drained_) {
2094 ml.Wait();
2095 }
2096}
2097
2098TimelineEventFileRecorder::TimelineEventFileRecorder(const char* path)
2099 : TimelineEventFileRecorderBase(path), first_(true) {
2100 // Chrome trace format has two forms:
2101 // Object form: { "traceEvents": [ event, event, event ] }
2102 // Array form: [ event, event, event ]
2103 // For this recorder, we use the array form because Catapult will handle a
2104 // missing ending bracket in this form in case we don't cleanly end the
2105 // trace.
2106 Write("[\n");
2107 OSThread::Start("TimelineEventFileRecorder",
2108 TimelineEventFileRecorderBaseStart,
2109 reinterpret_cast<uword>(this));
2110}
2111
2112TimelineEventFileRecorder::~TimelineEventFileRecorder() {
2113 ShutDown();
2114 Write("]\n");
2115}
2116
2117void TimelineEventFileRecorder::DrainImpl(const TimelineEvent& event) {
2118 JSONWriter writer;
2119 if (first_) {
2120 first_ = false;
2121 } else {
2122 writer.buffer()->AddChar(',');
2123 }
2124 event.PrintJSON(&writer);
2125 char* output = nullptr;
2126 intptr_t output_length = 0;
2127 writer.Steal(&output, &output_length);
2128 Write(output, output_length);
2129 free(output);
2130}
2131
2132#if defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
2133TimelineEventPerfettoFileRecorder::TimelineEventPerfettoFileRecorder(
2134 const char* path)
2135 : TimelineEventFileRecorderBase(path) {
2136 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>& packet =
2137 this->packet();
2138
2139 perfetto_utils::PopulateClockSnapshotPacket(packet.get());
2140 WritePacket(&packet);
2141 packet.Reset();
2142
2143 perfetto_utils::PopulateProcessDescriptorPacket(packet.get());
2144 WritePacket(&packet);
2145 packet.Reset();
2146
2147 OSThread::Start("TimelineEventPerfettoFileRecorder",
2148 TimelineEventFileRecorderBaseStart,
2149 reinterpret_cast<uword>(this));
2150}
2151
2152TimelineEventPerfettoFileRecorder::~TimelineEventPerfettoFileRecorder() {
2153 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>& packet =
2154 this->packet();
2155 ShutDown();
2156 // We do not need to lock the following section, because at this point
2157 // |RecorderSynchronizationLock| must have been put in a state that prevents
2158 // the metadata maps from being modified.
2159 for (SimpleHashMap::Entry* entry = track_uuid_to_track_metadata().Start();
2160 entry != nullptr; entry = track_uuid_to_track_metadata().Next(entry)) {
2161 TimelineTrackMetadata* value =
2162 static_cast<TimelineTrackMetadata*>(entry->value);
2163 value->PopulateTracePacket(packet.get());
2164 WritePacket(&packet);
2165 packet.Reset();
2166 }
2167 for (SimpleHashMap::Entry* entry =
2168 async_track_uuid_to_track_metadata().Start();
2169 entry != nullptr;
2170 entry = async_track_uuid_to_track_metadata().Next(entry)) {
2171 AsyncTimelineTrackMetadata* value =
2172 static_cast<AsyncTimelineTrackMetadata*>(entry->value);
2173 value->PopulateTracePacket(packet.get());
2174 WritePacket(&packet);
2175 packet.Reset();
2176 }
2177}
2178
2179void TimelineEventPerfettoFileRecorder::WritePacket(
2180 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet)
2181 const {
2182 const std::tuple<std::unique_ptr<const uint8_t[]>, intptr_t>& response =
2183 perfetto_utils::GetProtoPreamble(packet);
2184 Write(reinterpret_cast<const char*>(std::get<0>(response).get()),
2185 std::get<1>(response));
2186 for (const protozero::ScatteredHeapBuffer::Slice& slice :
2187 packet->GetSlices()) {
2188 Write(reinterpret_cast<char*>(slice.start()),
2189 slice.size() - slice.unused_bytes());
2190 }
2191}
2192
2193void TimelineEventPerfettoFileRecorder::DrainImpl(const TimelineEvent& event) {
2194 PrintPerfettoEventCallbackBody(
2195 &packet(), event,
2196 [this](protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*
2197 packet) { WritePacket(packet); });
2198
2199 if (event.event_type() == TimelineEvent::kAsyncBegin ||
2200 event.event_type() == TimelineEvent::kAsyncInstant) {
2201 AddAsyncTrackMetadataBasedOnEvent(event);
2202 }
2203}
2204#endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT)
2205
2206TimelineEventEndlessRecorder::TimelineEventEndlessRecorder()
2207 : head_(nullptr), tail_(nullptr), block_index_(0) {}
2208
2209TimelineEventEndlessRecorder::~TimelineEventEndlessRecorder() {
2210 ASSERT(head_ == nullptr);
2211}
2212
2213#ifndef PRODUCT
2214void TimelineEventEndlessRecorder::PrintEventsCommon(
2215 const TimelineEventFilter& filter,
2216 std::function<void(const TimelineEvent&)>&& print_impl) {
2217 // Acquire the recorder's lock to prevent the reclaimed blocks from being
2218 // handed out again until the trace has been serialized.
2219 MutexLocker ml(&lock_);
2220 Timeline::ReclaimCachedBlocksFromThreads();
2221 ResetTimeTracking();
2222 for (TimelineEventBlock* current = head_; current != nullptr;
2223 current = current->next()) {
2224 if (!current->ContainsEventsThatCanBeSerializedLocked()) {
2225 continue;
2226 }
2227 intptr_t length = current->length();
2228 for (intptr_t i = 0; i < length; i++) {
2229 TimelineEvent* event = current->At(i);
2230 if (filter.IncludeEvent(event) &&
2231 event->Within(filter.time_origin_micros(),
2232 filter.time_extent_micros())) {
2233 ReportTime(event->LowTime());
2234 ReportTime(event->HighTime());
2235 print_impl(*event);
2236 }
2237 }
2238 }
2239}
2240
2241void TimelineEventEndlessRecorder::PrintJSONEvents(
2242 const JSONArray& events,
2243 const TimelineEventFilter& filter) {
2244 PrintEventsCommon(filter, [&events](const TimelineEvent& event) {
2245 events.AddValue(&event);
2246 });
2247}
2248
2249#if defined(SUPPORT_PERFETTO)
2250void TimelineEventEndlessRecorder::PrintPerfettoEvents(
2251 JSONBase64String* jsonBase64String,
2252 const TimelineEventFilter& filter) {
2253 PrintEventsCommon(
2254 filter, [this, &jsonBase64String](const TimelineEvent& event) {
2255 PrintPerfettoEventCallbackBody(
2256 &packet(), event,
2257 [&jsonBase64String](
2258 protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>*
2259 packet) {
2260 perfetto_utils::AppendPacketToJSONBase64String(jsonBase64String,
2261 packet);
2262 });
2263 });
2264}
2265#endif // defined(SUPPORT_PERFETTO)
2266
2267void TimelineEventEndlessRecorder::PrintJSON(JSONStream* js,
2268 TimelineEventFilter* filter) {
2269 JSONObject topLevel(js);
2270 topLevel.AddProperty("type", "Timeline");
2271 {
2272 JSONArray events(&topLevel, "traceEvents");
2273 PrintJSONMeta(events);
2274 PrintJSONEvents(events, *filter);
2275 }
2276 topLevel.AddPropertyTimeMicros("timeOriginMicros", TimeOriginMicros());
2277 topLevel.AddPropertyTimeMicros("timeExtentMicros", TimeExtentMicros());
2278}
2279
2280#if defined(SUPPORT_PERFETTO)
2281void TimelineEventEndlessRecorder::PrintPerfettoTimeline(
2282 JSONStream* js,
2283 const TimelineEventFilter& filter) {
2284 PRINT_PERFETTO_TIMELINE_BODY
2285}
2286#endif // defined(SUPPORT_PERFETTO)
2287
2288void TimelineEventEndlessRecorder::PrintTraceEvent(
2289 JSONStream* js,
2290 TimelineEventFilter* filter) {
2291 JSONArray events(js);
2292 PrintJSONMeta(events);
2293 PrintJSONEvents(events, *filter);
2294}
2295#endif // !defined(PRODUCT)
2296
2297TimelineEventBlock* TimelineEventEndlessRecorder::GetHeadBlockLocked() {
2298 ASSERT(lock_.IsOwnedByCurrentThread());
2299 return head_;
2300}
2301
2302TimelineEvent* TimelineEventEndlessRecorder::StartEvent() {
2303 return ThreadBlockStartEvent();
2304}
2305
2306void TimelineEventEndlessRecorder::CompleteEvent(TimelineEvent* event) {
2307 if (event == nullptr) {
2308 return;
2309 }
2310 ThreadBlockCompleteEvent(event);
2311}
2312
2313TimelineEventBlock* TimelineEventEndlessRecorder::GetNewBlockLocked() {
2314 ASSERT(lock_.IsOwnedByCurrentThread());
2315 TimelineEventBlock* block = new TimelineEventBlock(block_index_++);
2316 block->Open();
2317 if (head_ == nullptr) {
2318 head_ = tail_ = block;
2319 } else {
2320 tail_->set_next(block);
2321 tail_ = block;
2322 }
2323 if (FLAG_trace_timeline) {
2324 OS::PrintErr("Created new block %p\n", block);
2325 }
2326 return block;
2327}
2328
2329void TimelineEventEndlessRecorder::ClearLocked() {
2330 ASSERT(lock_.IsOwnedByCurrentThread());
2331 TimelineEventBlock* current = head_;
2332 while (current != nullptr) {
2333 TimelineEventBlock* next = current->next();
2334 delete current;
2335 current = next;
2336 }
2337 head_ = nullptr;
2338 tail_ = nullptr;
2339 block_index_ = 0;
2340}
2341
2342TimelineEventBlock::TimelineEventBlock(intptr_t block_index)
2343 : next_(nullptr),
2344 length_(0),
2345 block_index_(block_index),
2346 current_owner_(nullptr),
2347 in_use_(false) {}
2348
2349TimelineEventBlock::~TimelineEventBlock() {
2350 Reset();
2351}
2352
2353#ifndef PRODUCT
2354void TimelineEventBlock::PrintJSON(JSONStream* js) const {
2355 ASSERT(!InUseLocked());
2356 JSONArray events(js);
2357 for (intptr_t i = 0; i < length(); i++) {
2358 const TimelineEvent* event = At(i);
2359 if (event->IsValid()) {
2360 events.AddValue(event);
2361 }
2362 }
2363}
2364#endif
2365
2366TimelineEvent* TimelineEventBlock::StartEventLocked() {
2367 OSThread* os_thread = OSThread::Current();
2368 ASSERT(os_thread != nullptr);
2369 ASSERT(os_thread == current_owner_);
2370 ASSERT(os_thread->timeline_block_lock()->IsOwnedByCurrentThread());
2371 ASSERT(!IsFull());
2372 if (FLAG_trace_timeline) {
2373 intptr_t tid = OSThread::ThreadIdToIntPtr(os_thread->id());
2374 OS::PrintErr("StartEvent in block %p for thread %" Pd "\n", this, tid);
2375 }
2376 return &events_[length_++];
2377}
2378
2379int64_t TimelineEventBlock::LowerTimeBound() const {
2380 if (length_ == 0) {
2381 return kMaxInt64;
2382 }
2383 ASSERT(length_ > 0);
2384 return events_[0].TimeOrigin();
2385}
2386
2387void TimelineEventBlock::Reset() {
2388 for (intptr_t i = 0; i < kBlockSize; i++) {
2389 // Clear any extra data.
2390 events_[i].Reset();
2391 }
2392 length_ = 0;
2393 current_owner_ = nullptr;
2394 in_use_ = false;
2395}
2396
2397void TimelineEventBlock::Open() {
2398 OSThread* os_thread = OSThread::Current();
2399 ASSERT(os_thread != nullptr);
2400 current_owner_ = os_thread;
2401 in_use_ = true;
2402}
2403
2404void TimelineEventBlock::Finish() {
2405 if (FLAG_trace_timeline) {
2406 OS::PrintErr("Finish block %p\n", this);
2407 }
2408 current_owner_ = nullptr;
2409 in_use_ = false;
2410#ifndef PRODUCT
2411 if (Service::timeline_stream.enabled()) {
2412 ServiceEvent service_event(ServiceEvent::kTimelineEvents);
2413 service_event.set_timeline_event_block(this);
2414 Service::HandleEvent(&service_event, /* enter_safepoint */ false);
2415 }
2416#endif
2417}
2418
2419void DartTimelineEventHelpers::ReportTaskEvent(
2420 TimelineEvent* event,
2421 int64_t id,
2422 intptr_t flow_id_count,
2423 std::unique_ptr<const int64_t[]>& flow_ids,
2424 intptr_t type,
2425 char* name,
2426 char* args) {
2427 const int64_t start = OS::GetCurrentMonotonicMicrosForTimeline();
2428 switch (static_cast<TimelineEvent::EventType>(type)) {
2429 case TimelineEvent::kAsyncInstant:
2430 event->AsyncInstant(name, id, start);
2431 break;
2432 case TimelineEvent::kAsyncBegin:
2433 event->AsyncBegin(name, id, start);
2434 break;
2435 case TimelineEvent::kAsyncEnd:
2436 event->AsyncEnd(name, id, start);
2437 break;
2438 case TimelineEvent::kBegin:
2439 event->Begin(name, id, start);
2440 break;
2441 case TimelineEvent::kEnd:
2442 event->End(name, id, start);
2443 break;
2444 case TimelineEvent::kFlowBegin:
2445 event->FlowBegin(name, id, start);
2446 break;
2447 case TimelineEvent::kFlowStep:
2448 event->FlowStep(name, id, start);
2449 break;
2450 case TimelineEvent::kFlowEnd:
2451 event->FlowEnd(name, id, start);
2452 break;
2453 case TimelineEvent::kInstant:
2454 event->Instant(name, start);
2455 break;
2456 default:
2457 UNREACHABLE();
2458 }
2459 if (flow_id_count > 0) {
2460 ASSERT(type == TimelineEvent::kBegin || type == TimelineEvent::kInstant ||
2461 type == TimelineEvent::kAsyncBegin ||
2462 type == TimelineEvent::kAsyncInstant);
2463
2464 event->SetFlowIds(flow_id_count, flow_ids);
2465 }
2466 event->set_owns_label(true);
2467 event->CompleteWithPreSerializedArgs(args);
2468}
2469
2470} // namespace dart
2471
2472#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
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:612
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:806
#define ILLEGAL_PORT
Definition dart_api.h:1530
void(* Dart_FileCloseCallback)(void *stream)
Definition dart_api.h:819
int64_t Dart_Port
Definition dart_api.h:1524
void *(* Dart_FileOpenCallback)(const char *name, bool write)
Definition dart_api.h:775
#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
static const uint8_t buffer[]
uint8_t value
GAsyncResult * result
const char * charp
Definition flags.h:12
#define DEFINE_FLAG(type, name, default_value, comment)
Definition flags.h:16
const char * name
Definition fuchsia.cc:50
void Init()
size_t length
va_start(args, format)
va_end(args)
constexpr int64_t kMaxInt64
Definition globals.h:486
const char *const name
void * malloc(size_t size)
Definition allocation.cc:19
uintptr_t uword
Definition globals.h:501
static void Free(FreeList *free_list, uword address, intptr_t size, bool is_protected)
void * calloc(size_t n, size_t size)
Definition allocation.cc:11
void * realloc(void *ptr, size_t size)
Definition allocation.cc:27
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
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
const myers::Point & get(const myers::Segment &)
Definition ref_ptr.h:256
static SkString fmt(SkColor4f c)
Definition p3.cpp:43
#define Px64
Definition globals.h:418
#define Pd
Definition globals.h:408
#define T
#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