| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #ifndef RUNTIME_VM_TIMELINE_H_ | |
| #define RUNTIME_VM_TIMELINE_H_ | |
| #include <functional> | |
| #include <memory> | |
| #include "include/dart_tools_api.h" | |
| #include "platform/assert.h" | |
| #include "platform/atomic.h" | |
| #include "platform/hashmap.h" | |
| #include "vm/allocation.h" | |
| #include "vm/bitfield.h" | |
| #include "vm/globals.h" | |
| #include "vm/growable_array.h" | |
| #include "vm/os.h" | |
| #include "vm/os_thread.h" | |
| #if defined(SUPPORT_TIMELINE) && defined(SUPPORT_PERFETTO) | |
| #include "perfetto/protozero/scattered_heap_buffer.h" | |
| #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h" | |
| #endif // defined(SUPPORT_TIMELINE) && defined(SUPPORT_PERFETTO) && \ | |
| // !defined(PRODUCT) | |
| #if defined(FUCHSIA_SDK) || defined(DART_HOST_OS_FUCHSIA) | |
| #include <lib/trace-engine/context.h> | |
| #include <lib/trace-engine/instrumentation.h> | |
| #endif // defined(FUCHSIA_SDK) || defined(DART_HOST_OS_FUCHSIA) | |
| #if defined(DART_HOST_OS_MACOS) | |
| // Including <os/signpost.h> in this header leads to an include of | |
| // <mach-o/loader.h>, which causes files that use this header and the | |
| // definitions in "platform/mach_o.h" to fail to build. To avoid this, | |
| // duplicate the typedef that <os/log.h> creates here and only include | |
| // <os/signpost.h> in timeline.cc and timeline_macos.cc. | |
| typedef struct os_log_s* os_log_t; | |
| #endif | |
| namespace dart { | |
| #if !defined(SUPPORT_TIMELINE) | |
| #define TIMELINE_DURATION(thread, stream, name) | |
| #define TIMELINE_FUNCTION_COMPILATION_DURATION(thread, name, function) | |
| #define TIMELINE_FUNCTION_GC_DURATION(thread, name) | |
| #endif // !defined(SUPPORT_TIMELINE) | |
| class JSONArray; | |
| class JSONBase64String; | |
| class JSONObject; | |
| class JSONStream; | |
| class JSONWriter; | |
| class Object; | |
| class ObjectPointerVisitor; | |
| class Isolate; | |
| class IsolateGroup; | |
| class Thread; | |
| class TimelineEvent; | |
| class TimelineEventBlock; | |
| class TimelineEventRecorder; | |
| class TimelineStream; | |
| class VirtualMemory; | |
| class Zone; | |
| #if defined(DART_INCLUDE_PROFILER) | |
| // In AOT mode we don't preprocess collected samples into a Profile before | |
| // writing them out. See also Timeline::DrainCompletedSampleBlocksIntoRecorder | |
| // below. | |
| #if defined(DART_PRECOMPILED_RUNTIME) | |
| class SampleBlockBuffer; | |
| using TimelineProfileType = SampleBlockBuffer; | |
| #else | |
| class Profile; | |
| using TimelineProfileType = Profile; | |
| #endif | |
| #endif | |
| #if defined(SUPPORT_TIMELINE) | |
| #define CALLBACK_RECORDER_NAME "Callback" | |
| #define ENDLESS_RECORDER_NAME "Endless" | |
| #define FILE_RECORDER_NAME "File" | |
| #define FUCHSIA_RECORDER_NAME "Fuchsia" | |
| #define MACOS_RECORDER_NAME "Macos" | |
| #define PERFETTO_FILE_RECORDER_NAME "Perfettofile" | |
| #define RING_RECORDER_NAME "Ring" | |
| #define STARTUP_RECORDER_NAME "Startup" | |
| #define SYSTRACE_RECORDER_NAME "Systrace" | |
| // Note: when updating this list consider updating TimelineStream enum in | |
| // sdk/lib/developer.dart. | |
| // (name, fuchsia_name, has_static_labels). | |
| #define TIMELINE_STREAM_LIST(V) \ | |
| V(API, "dart:api", true) \ | |
| V(Compiler, "dart:compiler", true) \ | |
| V(CompilerVerbose, "dart:compiler.verbose", true) \ | |
| V(Dart, "dart:dart", false) \ | |
| V(Debugger, "dart:debugger", true) \ | |
| V(Embedder, "dart:embedder", true) \ | |
| V(GC, "dart:gc", true) \ | |
| V(Isolate, "dart:isolate", true) \ | |
| V(Microtask, "dart:microtask", true) \ | |
| V(VM, "dart:vm", true) | |
| #endif // defined(SUPPORT_TIMELINE) | |
| // A stream of timeline events. A stream has a name and can be enabled or | |
| // disabled (globally and per isolate). | |
| class TimelineStream { | |
| public: | |
| TimelineStream(const char* name, | |
| const char* fuchsia_name, | |
| bool static_labels, | |
| bool enabled); | |
| const char* name() const { return name_; } | |
| const char* fuchsia_name() const { return fuchsia_name_; } | |
| bool enabled() { | |
| #if defined(DART_HOST_OS_FUCHSIA) | |
| #ifdef PRODUCT | |
| return trace_is_category_enabled(fuchsia_name_); | |
| #else | |
| return trace_is_category_enabled(fuchsia_name_) || enabled_ != 0; | |
| #endif // PRODUCT | |
| #else | |
| return enabled_ != 0; | |
| #endif // defined(DART_HOST_OS_FUCHSIA) | |
| } | |
| void set_enabled(bool enabled) { enabled_ = enabled ? 1 : 0; } | |
| // Records an event. Will return |nullptr| if not enabled. The returned | |
| // |TimelineEvent| is in an undefined state and must be initialized. | |
| TimelineEvent* StartEvent(); | |
| static intptr_t enabled_offset() { | |
| return OFFSET_OF(TimelineStream, enabled_); | |
| } | |
| #if defined(DART_HOST_OS_FUCHSIA) | |
| trace_site_t* trace_site() { return &trace_site_; } | |
| #elif defined(DART_HOST_OS_MACOS) | |
| os_log_t macos_log() const { return macos_log_; } | |
| bool has_static_labels() const { return has_static_labels_; } | |
| #endif | |
| private: | |
| const char* const name_; | |
| const char* const fuchsia_name_; | |
| // This field is accessed by generated code (intrinsic) and expects to see | |
| // 0 or 1. If this becomes a BitField, the generated code must be updated. | |
| RelaxedAtomic<uintptr_t> enabled_; | |
| #if defined(DART_HOST_OS_FUCHSIA) | |
| trace_site_t trace_site_ = {}; | |
| #elif defined(DART_HOST_OS_MACOS) | |
| os_log_t macos_log_ = {}; | |
| bool has_static_labels_ = false; | |
| #endif | |
| }; | |
| #if defined(SUPPORT_TIMELINE) | |
| class RecorderSynchronizationLock : public AllStatic { | |
| public: | |
| static void Init() { | |
| recorder_state_.store(kActive, std::memory_order_release); | |
| outstanding_event_writes_.store(0); | |
| } | |
| static void EnterLock() { | |
| outstanding_event_writes_.fetch_add(1, std::memory_order_acquire); | |
| } | |
| static void ExitLock() { | |
| intptr_t count = | |
| outstanding_event_writes_.fetch_sub(1, std::memory_order_release); | |
| ASSERT(count >= 0); | |
| } | |
| static bool IsUninitialized() { | |
| return (recorder_state_.load(std::memory_order_acquire) == kUninitialized); | |
| } | |
| static bool IsActive() { | |
| return (recorder_state_.load(std::memory_order_acquire) == kActive); | |
| } | |
| static bool IsShuttingDown() { | |
| return (recorder_state_.load(std::memory_order_acquire) == kShuttingDown); | |
| } | |
| static void WaitForShutdown() { | |
| recorder_state_.store(kShuttingDown, std::memory_order_release); | |
| // Spin waiting for outstanding events to be completed. | |
| while (outstanding_event_writes_.load(std::memory_order_relaxed) > 0) { | |
| } | |
| } | |
| private: | |
| typedef enum { kUninitialized = 0, kActive, kShuttingDown } RecorderState; | |
| static std::atomic<RecorderState> recorder_state_; | |
| static std::atomic<intptr_t> outstanding_event_writes_; | |
| DISALLOW_COPY_AND_ASSIGN(RecorderSynchronizationLock); | |
| }; | |
| // Any modifications to the timeline must be guarded by a | |
| // |RecorderSynchronizationLockScope| to prevent the timeline from being | |
| // cleaned up in the middle of the modifications. | |
| class RecorderSynchronizationLockScope { | |
| public: | |
| RecorderSynchronizationLockScope() { | |
| RecorderSynchronizationLock::EnterLock(); | |
| } | |
| ~RecorderSynchronizationLockScope() { | |
| RecorderSynchronizationLock::ExitLock(); | |
| } | |
| bool IsUninitialized() const { | |
| return RecorderSynchronizationLock::IsUninitialized(); | |
| } | |
| bool IsActive() const { return RecorderSynchronizationLock::IsActive(); } | |
| bool IsShuttingDown() const { | |
| return RecorderSynchronizationLock::IsShuttingDown(); | |
| } | |
| private: | |
| DISALLOW_COPY_AND_ASSIGN(RecorderSynchronizationLockScope); | |
| }; | |
| class Timeline : public AllStatic { | |
| public: | |
| // Initialize timeline system. Not thread safe. | |
| static void Init(); | |
| // Cleanup timeline system. Not thread safe. | |
| static void Cleanup(); | |
| // Start streaming timeline using given |recorder| (perfettofile, file or | |
| // systrace). | |
| // | |
| // Returns true on success, otherwise returns false and stores error message | |
| // into |error|. | |
| static bool StreamTo(const char* recorder, | |
| const char* file, | |
| const char* streams, | |
| const char** error); | |
| // Stop streaming started by |StreamTo|. | |
| // | |
| // If |reinitialize| is true, restores original recorder configured via | |
| // flags otherwise leaves timeline recording stopped. | |
| static void StopStreaming(bool reinitialize = true); | |
| // Notify current recorder about isolate group shutdown. | |
| // | |
| // Most recorders don't care about this but when streaming Perfetto timeline | |
| // in AOT mode with profiler enabled we might need to emit symbols for | |
| // collected frames into the timeline stream. | |
| static void NotifyAboutIsolateGroupShutdown(IsolateGroup* isolate_group); | |
| // Access the global recorder. Not thread safe. | |
| static TimelineEventRecorder* recorder() { return recorder_; } | |
| static bool recorder_discards_clock_values() { | |
| return recorder_discards_clock_values_; | |
| } | |
| static void set_recorder_discards_clock_values(bool value) { | |
| recorder_discards_clock_values_ = value; | |
| } | |
| static Dart_TimelineRecorderCallback callback() { return callback_; } | |
| static void set_callback(Dart_TimelineRecorderCallback callback) { | |
| callback_ = callback; | |
| } | |
| // Reclaim all |TimelineEventBlocks|s that are cached by threads. | |
| static void ReclaimCachedBlocksFromThreads(OSThreadIterator* it); | |
| static void Clear(); | |
| #ifndef PRODUCT | |
| // Print information about streams to JSON. | |
| static void PrintFlagsToJSON(JSONStream* json); | |
| // Output the recorded streams to a JSONS array. | |
| static void PrintFlagsToJSONArray(JSONArray* arr); | |
| #endif | |
| #define TIMELINE_STREAM_ACCESSOR(name, ...) \ | |
| static TimelineStream* Get##name##Stream() { \ | |
| return &stream_##name##_; \ | |
| } | |
| TIMELINE_STREAM_LIST(TIMELINE_STREAM_ACCESSOR) | |
| #undef TIMELINE_STREAM_ACCESSOR | |
| #define TIMELINE_STREAM_FLAGS(name, ...) \ | |
| static void SetStream##name##Enabled(bool enabled) { \ | |
| stream_##name##_.set_enabled(enabled); \ | |
| } | |
| TIMELINE_STREAM_LIST(TIMELINE_STREAM_FLAGS) | |
| #undef TIMELINE_STREAM_FLAGS | |
| #if defined(DART_INCLUDE_PROFILER) | |
| // Drains completed sample blocks from Profiler's |SampleBlockBuffer| into | |
| // the timeline recorder. This should only be called when using Perfetto | |
| // recorder (because it is the only one that supports profiling data as | |
| // part of the timeline). | |
| // | |
| // In JIT mode we drain blocks for each isolate independently by processing | |
| // them into |Profile| object. This is an extremely expensive operation | |
| // because it requires stopping the whole isolate group to construct | |
| // code map used for symbolization. | |
| // | |
| // In AOT mode we drain the whole |SampleBlockBuffer| at once and we do not | |
| // symbolize Dart frames as we do it. Instead we expect to emit additional | |
| // symbolization data when recording is complete. | |
| static void DrainCompletedSampleBlocksIntoRecorder( | |
| NOT_IN_PRECOMPILED(Isolate* isolate)); | |
| #endif | |
| private: | |
| // Initialize timeline system. Not thread safe. | |
| static void InitWithRecorder(TimelineEventRecorder* recorder, | |
| const char* streams); | |
| static TimelineEventRecorder* recorder_; | |
| static Dart_TimelineRecorderCallback callback_; | |
| static MallocGrowableArray<char*>* enabled_streams_; | |
| static bool recorder_discards_clock_values_; | |
| #define TIMELINE_STREAM_DECLARE(name, ...) \ | |
| static TimelineStream stream_##name##_; | |
| TIMELINE_STREAM_LIST(TIMELINE_STREAM_DECLARE) | |
| #undef TIMELINE_STREAM_DECLARE | |
| template <class> | |
| friend class TimelineRecorderOverride; | |
| friend class ReclaimBlocksIsolateVisitor; | |
| }; | |
| struct TimelineEventArgument { | |
| const char* name; | |
| char* value; | |
| }; | |
| class TimelineEventArguments { | |
| public: | |
| TimelineEventArguments() : buffer_(nullptr), length_(0) {} | |
| ~TimelineEventArguments() { Free(); } | |
| // Get/Set the number of arguments in the event. | |
| void SetNumArguments(intptr_t length); | |
| // |name| must be a compile time constant. Takes ownership of |argument|. | |
| void SetArgument(intptr_t i, const char* name, char* argument); | |
| // |name| must be a compile time constant. Copies |argument|. | |
| void CopyArgument(intptr_t i, const char* name, const char* argument); | |
| // |name| must be a compile time constant. Takes ownership of |args| | |
| void FormatArgument(intptr_t i, | |
| const char* name, | |
| const char* fmt, | |
| va_list args); | |
| void StealArguments(TimelineEventArguments* arguments); | |
| TimelineEventArgument* buffer() const { return buffer_; } | |
| intptr_t length() const { return length_; } | |
| void Free(); | |
| TimelineEventArgument& operator[](intptr_t index) const { | |
| return buffer_[index]; | |
| } | |
| bool IsEmpty() { return length_ == 0; } | |
| bool IsNotEmpty() { return length_ != 0; } | |
| private: | |
| TimelineEventArgument* buffer_; | |
| intptr_t length_; | |
| DISALLOW_COPY_AND_ASSIGN(TimelineEventArguments); | |
| }; | |
| // You should get a |TimelineEvent| from a |TimelineStream|. | |
| class TimelineEvent { | |
| public: | |
| // Keep in sync with StateBits below. | |
| // Keep in sync with constants in sdk/lib/developer/timeline.dart. | |
| enum EventType { | |
| kNone = 0, | |
| kBegin = 1, | |
| kEnd = 2, | |
| kDuration = 3, | |
| kInstant = 4, | |
| kAsyncBegin = 5, | |
| kAsyncInstant = 6, | |
| kAsyncEnd = 7, | |
| kCounter = 8, | |
| kFlowBegin = 9, | |
| kFlowStep = 10, | |
| kFlowEnd = 11, | |
| kMetadata = 12, | |
| kNumEventTypes, | |
| }; | |
| // This value must be kept in sync with the value of _noFlowId in | |
| // sdk/lib/developer/timeline.dart. | |
| static const int64_t kNoFlowId = -1; | |
| TimelineEvent(); | |
| ~TimelineEvent(); | |
| void Reset(); | |
| bool IsValid() const { | |
| return (event_type() > kNone) && (event_type() < kNumEventTypes); | |
| } | |
| // Marks the beginning of an asynchronous operation with |async_id|. | |
| void AsyncBegin(const char* label, | |
| int64_t async_id, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| // Marks an instantaneous event associated with |async_id|. | |
| void AsyncInstant( | |
| const char* label, | |
| int64_t async_id, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| // Marks the end of an asynchronous operation associated with |async_id|. | |
| void AsyncEnd(const char* label, | |
| int64_t async_id, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void DurationBegin( | |
| const char* label, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void Instant(const char* label, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void Duration(const char* label, int64_t start_micros, int64_t end_micros); | |
| void Begin(const char* label, | |
| int64_t id, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void End(const char* label, | |
| int64_t id, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void Counter(const char* label, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void FlowBegin(const char* label, | |
| int64_t id, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void FlowStep(const char* label, | |
| int64_t id, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void FlowEnd(const char* label, | |
| int64_t id, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void Metadata(const char* label, | |
| int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()); | |
| void CompleteWithPreSerializedArgs(char* args_json); | |
| // Get/Set the number of arguments in the event. | |
| intptr_t GetNumArguments() const { return arguments_.length(); } | |
| void SetNumArguments(intptr_t length) { arguments_.SetNumArguments(length); } | |
| // |name| must be a compile time constant. Takes ownership of |argument|. | |
| void SetArgument(intptr_t i, const char* name, char* argument) { | |
| arguments_.SetArgument(i, name, argument); | |
| } | |
| // |name| must be a compile time constant. Copies |argument|. | |
| void CopyArgument(intptr_t i, const char* name, const char* argument) { | |
| arguments_.CopyArgument(i, name, argument); | |
| } | |
| // |name| must be a compile time constant. | |
| void FormatArgument(intptr_t i, const char* name, const char* fmt, ...) | |
| PRINTF_ATTRIBUTE(4, 5); | |
| void StealArguments(TimelineEventArguments* arguments) { | |
| arguments_.StealArguments(arguments); | |
| } | |
| // Mandatory to call when this event is completely filled out. | |
| void Complete(); | |
| EventType event_type() const { return EventTypeField::decode(state_); } | |
| TimelineStream* stream() const { return stream_; } | |
| int64_t TimeOrigin() const { return timestamp0_; } | |
| int64_t Id() const { | |
| ASSERT(event_type() != kDuration && event_type() != kInstant && | |
| event_type() != kCounter); | |
| return timestamp1_or_id_; | |
| } | |
| int64_t TimeDuration() const; | |
| void SetTimeEnd(int64_t micros = OS::GetCurrentMonotonicMicrosForTimeline()) { | |
| ASSERT(event_type() == kDuration); | |
| ASSERT(timestamp1_or_id_ == 0); | |
| set_timestamp1_or_id(micros); | |
| } | |
| int64_t TimeEnd() const { | |
| ASSERT(IsFinishedDuration()); | |
| return timestamp1_or_id_; | |
| } | |
| int64_t timestamp0() const { return timestamp0_; } | |
| int64_t timestamp1_or_id() const { return timestamp1_or_id_; } | |
| void SetFlowIds(intptr_t flow_id_count, | |
| std::unique_ptr<const int64_t[]>& flow_ids) { | |
| flow_id_count_ = flow_id_count; | |
| flow_ids_.swap(flow_ids); | |
| } | |
| intptr_t flow_id_count() const { return flow_id_count_; } | |
| const int64_t* FlowIds() const { return flow_ids_.get(); } | |
| bool HasIsolateId() const; | |
| bool HasIsolateGroupId() const; | |
| void ClearIsolateGroupId(); | |
| // The lowest time value stored in this event. | |
| int64_t LowTime() const; | |
| // The highest time value stored in this event. | |
| int64_t HighTime() const; | |
| #ifndef PRODUCT | |
| void PrintJSON(JSONStream* stream) const; | |
| #endif | |
| void PrintJSON(JSONWriter* writer) const; | |
| #if defined(SUPPORT_PERFETTO) | |
| bool CanBeRepresentedByPerfettoTracePacket() const; | |
| /* | |
| * Populates the fields of |packet| with this event's data. | |
| */ | |
| void PopulateTracePacket(perfetto::protos::pbzero::TracePacket* packet) const; | |
| #endif // defined(SUPPORT_PERFETTO) | |
| ThreadId thread() const { return thread_; } | |
| void set_thread(ThreadId tid) { thread_ = tid; } | |
| Dart_Port isolate_id() const { return isolate_id_; } | |
| uint64_t isolate_group_id() const { return isolate_group_id_; } | |
| void* isolate_data() const { return isolate_data_; } | |
| void* isolate_group_data() const { return isolate_group_data_; } | |
| const char* label() const { return label_; } | |
| // Does this duration end before |micros| ? | |
| bool DurationFinishedBefore(int64_t micros) const { | |
| return TimeEnd() <= micros; | |
| } | |
| bool IsDuration() const { return (event_type() == kDuration); } | |
| bool IsBegin() const { return (event_type() == kBegin); } | |
| bool IsEnd() const { return (event_type() == kEnd); } | |
| // Is this event a synchronous begin or end event? | |
| bool IsBeginOrEnd() const { return IsBegin() || IsEnd(); } | |
| // Does this duration fully contain |other| ? | |
| bool DurationContains(TimelineEvent* other) const { | |
| ASSERT(IsFinishedDuration()); | |
| if (other->IsBegin()) { | |
| if (other->TimeOrigin() < TimeOrigin()) { | |
| return false; | |
| } | |
| if (other->TimeOrigin() > TimeEnd()) { | |
| return false; | |
| } | |
| return true; | |
| } else { | |
| ASSERT(other->IsFinishedDuration()); | |
| if (other->TimeOrigin() < TimeOrigin()) { | |
| return false; | |
| } | |
| if (other->TimeEnd() < TimeOrigin()) { | |
| return false; | |
| } | |
| if (other->TimeOrigin() > TimeEnd()) { | |
| return false; | |
| } | |
| if (other->TimeEnd() > TimeEnd()) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| bool Within(int64_t time_origin_micros, int64_t time_extent_micros) const; | |
| void set_owns_label(bool owns_label) { | |
| state_ = OwnsLabelBit::update(owns_label, state_); | |
| } | |
| TimelineEventArgument* arguments() const { return arguments_.buffer(); } | |
| intptr_t arguments_length() const { return arguments_.length(); } | |
| bool ArgsArePreSerialized() const { | |
| return PreSerializedArgsBit::decode(state_); | |
| } | |
| private: | |
| void StreamInit(TimelineStream* stream) { stream_ = stream; } | |
| void Init(EventType event_type, const char* label); | |
| void set_event_type(EventType event_type) { | |
| // We only reserve 4 bits to hold the event type. | |
| COMPILE_ASSERT(kNumEventTypes < 16); | |
| state_ = EventTypeField::update(event_type, state_); | |
| } | |
| void set_timestamp0(int64_t value) { | |
| ASSERT(timestamp0_ == 0); | |
| timestamp0_ = value; | |
| } | |
| void set_timestamp1_or_id(int64_t value) { | |
| ASSERT(timestamp1_or_id_ == 0); | |
| timestamp1_or_id_ = value; | |
| } | |
| bool IsFinishedDuration() const { | |
| return (event_type() == kDuration) && (timestamp1_or_id_ > timestamp0_); | |
| } | |
| void set_pre_serialized_args(bool pre_serialized_args) { | |
| state_ = PreSerializedArgsBit::update(pre_serialized_args, state_); | |
| } | |
| bool owns_label() const { return OwnsLabelBit::decode(state_); } | |
| using EventTypeField = | |
| BitField<uword, EventType, 0, Utils::BitLength(kNumEventTypes - 1)>; | |
| using PreSerializedArgsBit = BitField<uword, bool, EventTypeField::kNextBit>; | |
| using OwnsLabelBit = BitField<uword, bool, PreSerializedArgsBit::kNextBit>; | |
| int64_t timestamp0_; | |
| // For an event of type |kDuration|, this is the end time. For an event of | |
| // type |kFlowBegin|, |kFlowStep|, or |kFlowEnd| this is the flow ID. For an | |
| // event of type |kBegin| or |kEnd|, this is the event ID (which is only | |
| // referenced by the MacOS recorder). For an async event, this is the async | |
| // ID. | |
| int64_t timestamp1_or_id_; | |
| intptr_t flow_id_count_; | |
| // This field is needed to support trace serialization in Perfetto's proto | |
| // format. Flow IDs must be associated with |TimelineEvent::kBegin|, | |
| // |TimelineEvent::kDuration|, |TimelineEvent::kInstant|, | |
| // |TimelineEvent::kAsyncBegin|, and |TimelineEvent::kAsyncInstant| events to | |
| // serialize traces in Perfetto's format. Flow event information is serialized | |
| // in Chrome's JSON trace format through events of type | |
| // |TimelineEvent::kFlowBegin|, |TimelineEvent::kFlowStep|, and | |
| // |TimelineEvent::kFlowEnd|. | |
| std::unique_ptr<const int64_t[]> flow_ids_; | |
| TimelineEventArguments arguments_; | |
| uword state_; | |
| const char* label_; | |
| TimelineStream* stream_; | |
| ThreadId thread_; | |
| Dart_Port isolate_id_; | |
| uint64_t isolate_group_id_; | |
| void* isolate_data_; | |
| void* isolate_group_data_; | |
| friend class TimelineEventRecorder; | |
| friend class TimelineEventEndlessRecorder; | |
| friend class TimelineEventRingRecorder; | |
| friend class TimelineEventStartupRecorder; | |
| friend class TimelineEventPlatformRecorder; | |
| friend class TimelineEventFuchsiaRecorder; | |
| friend class TimelineEventMacosRecorder; | |
| #if defined(SUPPORT_PERFETTO) | |
| friend class TimelineEventPerfettoFileRecorder; | |
| #endif // defined(SUPPORT_PERFETTO) | |
| friend class TimelineStream; | |
| friend class TimelineTestHelper; | |
| DISALLOW_COPY_AND_ASSIGN(TimelineEvent); | |
| }; | |
| class TimelineTrackMetadata { | |
| public: | |
| TimelineTrackMetadata(intptr_t pid, | |
| intptr_t tid, | |
| CStringUniquePtr&& track_name); | |
| intptr_t pid() const { return pid_; } | |
| intptr_t tid() const { return tid_; } | |
| const char* track_name() const { return track_name_.get(); } | |
| inline void set_track_name(CStringUniquePtr&& track_name); | |
| #if !defined(PRODUCT) | |
| /* | |
| * Prints a Chrome-format event representing the metadata stored by this | |
| * object into |jsarr_events|. | |
| */ | |
| void PrintJSON(const JSONArray& jsarr_events) const; | |
| #endif // !defined(PRODUCT) | |
| #if defined(SUPPORT_PERFETTO) | |
| /* | |
| * Populates the fields of |track_descriptor_packet| with the metadata stored | |
| * by this object. | |
| */ | |
| void PopulateTracePacket( | |
| perfetto::protos::pbzero::TracePacket* track_descriptor_packet) const; | |
| #endif // defined(SUPPORT_PERFETTO) | |
| private: | |
| // The ID of the process that this track is associated with. | |
| intptr_t pid_; | |
| // The trace ID of the thread that this track is associated with. | |
| intptr_t tid_; | |
| // The name of this track. | |
| CStringUniquePtr track_name_; | |
| }; | |
| #if defined(SUPPORT_PERFETTO) | |
| class AsyncTimelineTrackMetadata { | |
| public: | |
| AsyncTimelineTrackMetadata(intptr_t pid, intptr_t async_id); | |
| intptr_t pid() const { return pid_; } | |
| intptr_t async_id() const { return async_id_; } | |
| /* | |
| * Populates the fields of |track_descriptor_packet| with the metadata stored | |
| * by this object. | |
| */ | |
| void PopulateTracePacket( | |
| perfetto::protos::pbzero::TracePacket* track_descriptor_packet) const; | |
| private: | |
| // The ID of the process that this track is associated with. | |
| intptr_t pid_; | |
| // The async ID that this track is associated with. | |
| intptr_t async_id_; | |
| }; | |
| #endif // defined(SUPPORT_PERFETTO) | |
| #define TIMELINE_DURATION(thread, stream, name) \ | |
| TimelineBeginEndScope tbes(thread, Timeline::Get##stream##Stream(), name); | |
| #define TIMELINE_FUNCTION_COMPILATION_DURATION(thread, name, function) \ | |
| TimelineBeginEndScope tbes(thread, Timeline::GetCompilerStream(), name); \ | |
| if (tbes.enabled()) { \ | |
| tbes.SetNumArguments(1); \ | |
| tbes.CopyArgument(0, "function", function.ToQualifiedCString()); \ | |
| } | |
| #define TIMELINE_FUNCTION_GC_DURATION(thread, name) \ | |
| TimelineBeginEndScope tbes(thread, Timeline::GetGCStream(), name); | |
| // See |TimelineBeginEndScope|. | |
| class TimelineEventScope : public StackResource { | |
| public: | |
| bool enabled() const { return enabled_; } | |
| intptr_t GetNumArguments() { return arguments_.length(); } | |
| void SetNumArguments(intptr_t length); | |
| void SetArgument(intptr_t i, const char* name, char* argument); | |
| void CopyArgument(intptr_t i, const char* name, const char* argument); | |
| void FormatArgument(intptr_t i, const char* name, const char* fmt, ...) | |
| PRINTF_ATTRIBUTE(4, 5); | |
| protected: | |
| TimelineEventScope(TimelineStream* stream, const char* label); | |
| TimelineEventScope(Thread* thread, TimelineStream* stream, const char* label); | |
| bool ShouldEmitEvent() const { return enabled_; } | |
| void set_enabled(bool enabled) { enabled_ = enabled; } | |
| const char* label() const { return label_; } | |
| int64_t id() const { return id_; } | |
| TimelineEventArgument* arguments() const { return arguments_.buffer(); } | |
| intptr_t arguments_length() const { return arguments_.length(); } | |
| TimelineStream* stream() const { return stream_; } | |
| virtual ~TimelineEventScope(); | |
| void StealArguments(TimelineEvent* event); | |
| private: | |
| void Init(); | |
| TimelineStream* stream_; | |
| const char* label_; | |
| int64_t id_; | |
| TimelineEventArguments arguments_; | |
| bool enabled_; | |
| DISALLOW_COPY_AND_ASSIGN(TimelineEventScope); | |
| }; | |
| class TimelineBeginEndScope : public TimelineEventScope { | |
| public: | |
| TimelineBeginEndScope(TimelineStream* stream, const char* label); | |
| TimelineBeginEndScope(Thread* thread, | |
| TimelineStream* stream, | |
| const char* label); | |
| virtual ~TimelineBeginEndScope(); | |
| private: | |
| void EmitBegin(); | |
| void EmitEnd(); | |
| DISALLOW_COPY_AND_ASSIGN(TimelineBeginEndScope); | |
| }; | |
| // A block of |TimelineEvent|s. Not thread safe. | |
| class TimelineEventBlock : public MallocAllocated { | |
| public: | |
| static constexpr intptr_t kBlockSize = 64; | |
| explicit TimelineEventBlock(intptr_t index); | |
| ~TimelineEventBlock(); | |
| TimelineEventBlock* next() const { return next_; } | |
| void set_next(TimelineEventBlock* next) { next_ = next; } | |
| intptr_t length() const { return length_; } | |
| intptr_t block_index() const { return block_index_; } | |
| bool IsEmpty() const { return length_ == 0; } | |
| bool IsFull() const { return length_ == kBlockSize; } | |
| TimelineEvent* At(intptr_t index) { | |
| ASSERT(index >= 0); | |
| ASSERT(index < kBlockSize); | |
| return &events_[index]; | |
| } | |
| const TimelineEvent* At(intptr_t index) const { | |
| ASSERT(index >= 0); | |
| ASSERT(index < kBlockSize); | |
| return &events_[index]; | |
| } | |
| // Attempt to sniff the timestamp from the first event. | |
| int64_t LowerTimeBound() const; | |
| // Call Reset on all events and set length to 0. | |
| void Reset(); | |
| // Only safe to access under the recorder's lock. | |
| inline bool InUseLocked() const; | |
| // Only safe to access under the recorder's lock. | |
| inline bool ContainsEventsThatCanBeSerializedLocked() const; | |
| protected: | |
| #ifndef PRODUCT | |
| void PrintJSON(JSONStream* stream) const; | |
| #endif | |
| // Only safe to call from the thread that owns this block, while the thread is | |
| // holding its block lock. | |
| TimelineEvent* StartEventLocked(); | |
| TimelineEvent events_[kBlockSize]; | |
| TimelineEventBlock* next_; | |
| intptr_t length_; | |
| intptr_t block_index_; | |
| // Only safe to access under the recorder's lock. | |
| OSThread* current_owner_; | |
| bool in_use_; | |
| void Open(); | |
| friend class Thread; | |
| friend class TimelineEventRecorder; | |
| friend class TimelineEventEndlessRecorder; | |
| friend class TimelineEventRingRecorder; | |
| friend class TimelineEventStartupRecorder; | |
| friend class TimelineEventPlatformRecorder; | |
| friend class TimelineEventFileRecorderBase; | |
| friend class TimelineTestHelper; | |
| friend class JSONStream; | |
| private: | |
| void Finish(); | |
| DISALLOW_COPY_AND_ASSIGN(TimelineEventBlock); | |
| }; | |
| class TimelineEventFilter : public ValueObject { | |
| public: | |
| TimelineEventFilter(int64_t time_origin_micros = -1, | |
| int64_t time_extent_micros = -1); | |
| virtual ~TimelineEventFilter(); | |
| virtual bool IncludeEvent(const TimelineEvent* event) const { | |
| if (event == nullptr) { | |
| return false; | |
| } | |
| return event->IsValid(); | |
| } | |
| int64_t time_origin_micros() const { return time_origin_micros_; } | |
| int64_t time_extent_micros() const { return time_extent_micros_; } | |
| private: | |
| int64_t time_origin_micros_; | |
| int64_t time_extent_micros_; | |
| }; | |
| class IsolateTimelineEventFilter : public TimelineEventFilter { | |
| public: | |
| explicit IsolateTimelineEventFilter(Dart_Port isolate_id, | |
| int64_t time_origin_micros = -1, | |
| int64_t time_extent_micros = -1); | |
| bool IncludeEvent(const TimelineEvent* event) const final { | |
| return event->IsValid() && (event->isolate_id() == isolate_id_); | |
| } | |
| private: | |
| Dart_Port isolate_id_; | |
| }; | |
| // Recorder of |TimelineEvent|s. | |
| class TimelineEventRecorder : public MallocAllocated { | |
| public: | |
| TimelineEventRecorder(); | |
| virtual ~TimelineEventRecorder(); | |
| // Interface method(s) which must be implemented. | |
| #ifndef PRODUCT | |
| virtual void PrintJSON(JSONStream* js, TimelineEventFilter* filter) = 0; | |
| #if defined(SUPPORT_PERFETTO) | |
| /* | |
| * Prints a PerfettoTimeline service response into |js|. | |
| */ | |
| virtual void PrintPerfettoTimeline(JSONStream* js, | |
| const TimelineEventFilter& filter) = 0; | |
| #endif // defined(SUPPORT_PERFETTO) | |
| virtual void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) = 0; | |
| #endif // !defined(PRODUCT) | |
| virtual const char* name() const = 0; | |
| virtual intptr_t Size() = 0; | |
| // Only safe to call when holding |lock_|. | |
| virtual TimelineEventBlock* GetNewBlockLocked() = 0; | |
| virtual void FinishBlock(TimelineEventBlock* block); | |
| // This function must be called at least once for each thread that corresponds | |
| // to a track in the trace. | |
| virtual void AddTrackMetadataBasedOnThread(const intptr_t process_id, | |
| const intptr_t trace_id, | |
| const char* thread_name); | |
| virtual void AddAsyncTrackMetadataBasedOnEvent(const TimelineEvent& event); | |
| virtual void NotifyAboutIsolateGroupShutdown(IsolateGroup* isolate_group) { | |
| // Most recorders don't care about it. | |
| } | |
| #if defined(DART_INCLUDE_PROFILER) | |
| virtual void WriteProfile(TimelineProfileType& profile) {} | |
| #endif | |
| protected: | |
| static constexpr intptr_t kTrackUuidToTrackMetadataInitialCapacity = 1 << 4; | |
| SimpleHashMap& track_uuid_to_track_metadata() { | |
| return track_uuid_to_track_metadata_; | |
| } | |
| Mutex& track_uuid_to_track_metadata_lock() { | |
| return track_uuid_to_track_metadata_lock_; | |
| } | |
| #ifndef PRODUCT | |
| void WriteTo(const char* directory); | |
| #endif | |
| // Interface method(s) which must be implemented. | |
| virtual TimelineEvent* StartEvent() = 0; | |
| virtual void CompleteEvent(TimelineEvent* event) = 0; | |
| // Only safe to call when holding |lock_|. | |
| virtual void ClearLocked() = 0; | |
| // Utility method(s). | |
| #ifndef PRODUCT | |
| void PrintJSONMeta(const JSONArray& jsarr_events); | |
| #if defined(SUPPORT_PERFETTO) | |
| /* | |
| * Appends metadata about the timeline in Perfetto's proto format to | |
| * |jsonBase64String|. | |
| */ | |
| void PrintPerfettoMeta(JSONBase64String* jsonBase64String); | |
| #endif // defined(SUPPORT_PERFETTO) | |
| #endif // !defined(PRODUCT) | |
| TimelineEvent* ThreadBlockStartEvent(); | |
| void ThreadBlockCompleteEvent(TimelineEvent* event); | |
| void ResetTimeTracking(); | |
| void ReportTime(int64_t micros); | |
| int64_t TimeOriginMicros() const; | |
| int64_t TimeExtentMicros() const; | |
| Mutex lock_; | |
| int64_t time_low_micros_; | |
| int64_t time_high_micros_; | |
| friend class TimelineEvent; | |
| friend class TimelineEventBlock; | |
| friend class TimelineStream; | |
| friend class TimelineTestHelper; | |
| friend class Timeline; | |
| friend class OSThread; | |
| private: | |
| Mutex track_uuid_to_track_metadata_lock_; | |
| SimpleHashMap track_uuid_to_track_metadata_; | |
| DISALLOW_COPY_AND_ASSIGN(TimelineEventRecorder); | |
| }; | |
| #if defined(SUPPORT_PERFETTO) | |
| template <typename Base> | |
| class TimelineEventRecorderWithPerfettoSupport : public Base { | |
| public: | |
| template <typename... Args> | |
| explicit TimelineEventRecorderWithPerfettoSupport(Args&&... args); | |
| ~TimelineEventRecorderWithPerfettoSupport(); | |
| void PrintPerfettoMeta(JSONBase64String* jsonBase64String); | |
| void AddAsyncTrackMetadataBasedOnEvent(const TimelineEvent& event); | |
| protected: | |
| protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>& packet() { | |
| return packet_; | |
| } | |
| SimpleHashMap& async_track_uuid_to_track_metadata() { | |
| return async_track_uuid_to_track_metadata_; | |
| } | |
| private: | |
| // We allocate one heap-buffered packet as a class member, because it lets us | |
| // continuously follow a cycle of resetting the buffer and writing its | |
| // contents. | |
| protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket> packet_; | |
| static constexpr intptr_t kTrackUuidToTrackMetadataInitialCapacity = 1 << 4; | |
| Mutex async_track_uuid_to_track_metadata_lock_; | |
| SimpleHashMap async_track_uuid_to_track_metadata_; | |
| }; | |
| #else | |
| template <typename Base> | |
| using TimelineEventRecorderWithPerfettoSupport = Base; | |
| #endif | |
| // An abstract recorder that buffers recorded events. | |
| class TimelineEventBufferedRecorder | |
| : public TimelineEventRecorderWithPerfettoSupport<TimelineEventRecorder> { | |
| public: | |
| #ifndef PRODUCT | |
| void PrintJSON(JSONStream* js, TimelineEventFilter* filter) final; | |
| #if defined(SUPPORT_PERFETTO) | |
| void PrintPerfettoTimeline(JSONStream* js, | |
| const TimelineEventFilter& filter) final; | |
| #endif // defined(SUPPORT_PERFETTO) | |
| void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) final; | |
| private: | |
| virtual void ForEachNonEmptyBlock( | |
| std::function<void(const TimelineEventBlock&)>&& handle_block) = 0; | |
| void PrintJSONEvents(const JSONArray& array, | |
| const TimelineEventFilter& filter); | |
| #if defined(SUPPORT_PERFETTO) | |
| void PrintPerfettoEvents(JSONBase64String* jsonBase64String, | |
| const TimelineEventFilter& filter); | |
| #endif // defined(SUPPORT_PERFETTO) | |
| void PrintEventsCommon( | |
| const TimelineEventFilter& filter, | |
| std::function<void(const TimelineEvent&)>&& print_impl); | |
| #endif // !defined(PRODUCT) | |
| }; | |
| // An abstract recorder that stores events in a buffer of fixed capacity. | |
| class TimelineEventFixedBufferRecorder : public TimelineEventBufferedRecorder { | |
| public: | |
| static constexpr intptr_t kDefaultCapacity = 32 * KB; // Number of events. | |
| explicit TimelineEventFixedBufferRecorder(intptr_t capacity); | |
| virtual ~TimelineEventFixedBufferRecorder(); | |
| intptr_t Size(); | |
| protected: | |
| TimelineEvent* StartEvent(); | |
| void CompleteEvent(TimelineEvent* event); | |
| // Only safe to call when holding |lock_|. | |
| intptr_t FindOldestBlockIndexLocked() const; | |
| void ClearLocked(); | |
| VirtualMemory* memory_; | |
| TimelineEventBlock* blocks_; | |
| intptr_t capacity_; | |
| intptr_t num_blocks_; | |
| intptr_t block_cursor_; | |
| private: | |
| #ifndef PRODUCT | |
| void ForEachNonEmptyBlock( | |
| std::function<void(const TimelineEventBlock&)>&& handle_block) final; | |
| #endif // !defined(PRODUCT) | |
| }; | |
| // A recorder that stores events in a buffer of fixed capacity. When the buffer | |
| // is full, new events overwrite old events. | |
| class TimelineEventRingRecorder : public TimelineEventFixedBufferRecorder { | |
| public: | |
| explicit TimelineEventRingRecorder(intptr_t capacity = kDefaultCapacity) | |
| : TimelineEventFixedBufferRecorder(capacity) {} | |
| virtual ~TimelineEventRingRecorder() {} | |
| const char* name() const { return RING_RECORDER_NAME; } | |
| protected: | |
| TimelineEventBlock* GetNewBlockLocked(); | |
| }; | |
| // A recorder that stores events in a buffer of fixed capacity. When the buffer | |
| // is full, new events are dropped. | |
| class TimelineEventStartupRecorder : public TimelineEventFixedBufferRecorder { | |
| public: | |
| explicit TimelineEventStartupRecorder(intptr_t capacity = kDefaultCapacity) | |
| : TimelineEventFixedBufferRecorder(capacity) {} | |
| virtual ~TimelineEventStartupRecorder() {} | |
| const char* name() const { return STARTUP_RECORDER_NAME; } | |
| protected: | |
| TimelineEventBlock* GetNewBlockLocked(); | |
| }; | |
| // An abstract recorder that calls |OnEvent| whenever an event is complete. | |
| // This should only be used for testing. | |
| class TimelineEventCallbackRecorder : public TimelineEventRecorder { | |
| public: | |
| TimelineEventCallbackRecorder(); | |
| virtual ~TimelineEventCallbackRecorder(); | |
| #ifndef PRODUCT | |
| void PrintJSON(JSONStream* js, TimelineEventFilter* filter) final; | |
| #if defined(SUPPORT_PERFETTO) | |
| void PrintPerfettoTimeline(JSONStream* js, | |
| const TimelineEventFilter& filter) final; | |
| #endif // defined(SUPPORT_PERFETTO) | |
| void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) final; | |
| #endif // !defined(PRODUCT) | |
| // Called when |event| is completed. It is unsafe to keep a reference to | |
| // |event| as it may be freed as soon as this function returns. | |
| virtual void OnEvent(TimelineEvent* event) = 0; | |
| const char* name() const { return CALLBACK_RECORDER_NAME; } | |
| intptr_t Size() { return 0; } | |
| protected: | |
| TimelineEventBlock* GetNewBlockLocked() { UNREACHABLE(); } | |
| void ClearLocked() { ASSERT(lock_.IsOwnedByCurrentThread()); } | |
| TimelineEvent* StartEvent(); | |
| void CompleteEvent(TimelineEvent* event); | |
| }; | |
| // A recorder that forwards completed events to the callback provided by | |
| // Dart_SetTimelineRecorderCallback. | |
| class TimelineEventEmbedderCallbackRecorder | |
| : public TimelineEventCallbackRecorder { | |
| public: | |
| TimelineEventEmbedderCallbackRecorder() {} | |
| virtual ~TimelineEventEmbedderCallbackRecorder() {} | |
| virtual void AddTrackMetadataBasedOnThread(const intptr_t process_id, | |
| const intptr_t trace_id, | |
| const char* thread_name); | |
| virtual void OnEvent(TimelineEvent* event); | |
| }; | |
| // A recorder that does nothing. | |
| class TimelineEventNopRecorder : public TimelineEventCallbackRecorder { | |
| public: | |
| TimelineEventNopRecorder() {} | |
| virtual ~TimelineEventNopRecorder() {} | |
| virtual void AddTrackMetadataBasedOnThread(const intptr_t process_id, | |
| const intptr_t trace_id, | |
| const char* thread_name); | |
| virtual void OnEvent(TimelineEvent* event); | |
| }; | |
| // A recorder that stores events in chains of blocks of events. | |
| // NOTE: This recorder will continue to allocate blocks until it exhausts | |
| // memory. | |
| class TimelineEventEndlessRecorder : public TimelineEventBufferedRecorder { | |
| public: | |
| TimelineEventEndlessRecorder(); | |
| virtual ~TimelineEventEndlessRecorder(); | |
| const char* name() const { return ENDLESS_RECORDER_NAME; } | |
| intptr_t Size() { return block_index_ * sizeof(TimelineEventBlock); } | |
| protected: | |
| TimelineEvent* StartEvent(); | |
| void CompleteEvent(TimelineEvent* event); | |
| TimelineEventBlock* GetNewBlockLocked(); | |
| void ClearLocked(); | |
| TimelineEventBlock* head_; | |
| TimelineEventBlock* tail_; | |
| intptr_t block_index_; | |
| private: | |
| #ifndef PRODUCT | |
| void ForEachNonEmptyBlock( | |
| std::function<void(const TimelineEventBlock&)>&& handle_block) final; | |
| #endif // !defined(PRODUCT) | |
| friend class TimelineTestHelper; | |
| }; | |
| // The TimelineEventPlatformRecorder records timeline events to a platform | |
| // specific destination. It's implementation is in the timeline_{linux,...}.cc | |
| // files. | |
| class TimelineEventPlatformRecorder : public TimelineEventRecorder { | |
| public: | |
| TimelineEventPlatformRecorder(); | |
| virtual ~TimelineEventPlatformRecorder(); | |
| #ifndef PRODUCT | |
| void PrintJSON(JSONStream* js, TimelineEventFilter* filter) final; | |
| #if defined(SUPPORT_PERFETTO) | |
| void PrintPerfettoTimeline(JSONStream* js, | |
| const TimelineEventFilter& filter) final; | |
| #endif // defined(SUPPORT_PERFETTO) | |
| void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) final; | |
| #endif // !defined(PRODUCT) | |
| // Called when |event| is completed. It is unsafe to keep a reference to | |
| // |event| as it may be freed as soon as this function returns. | |
| virtual void OnEvent(TimelineEvent* event) = 0; | |
| virtual void AddTrackMetadataBasedOnThread(const intptr_t process_id, | |
| const intptr_t trace_id, | |
| const char* thread_name); | |
| virtual const char* name() const = 0; | |
| protected: | |
| TimelineEventBlock* GetNewBlockLocked() { UNREACHABLE(); } | |
| void ClearLocked() { ASSERT(lock_.IsOwnedByCurrentThread()); } | |
| TimelineEvent* StartEvent(); | |
| void CompleteEvent(TimelineEvent* event); | |
| }; | |
| #if defined(DART_HOST_OS_FUCHSIA) | |
| // A recorder that sends events to Fuchsia's tracing app. | |
| class TimelineEventFuchsiaRecorder : public TimelineEventPlatformRecorder { | |
| public: | |
| TimelineEventFuchsiaRecorder() {} | |
| virtual ~TimelineEventFuchsiaRecorder() {} | |
| const char* name() const { return FUCHSIA_RECORDER_NAME; } | |
| intptr_t Size() { return 0; } | |
| private: | |
| void OnEvent(TimelineEvent* event); | |
| }; | |
| #endif // defined(DART_HOST_OS_FUCHSIA) | |
| #if defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) | |
| // A recorder that writes events to Android Systrace. This class is exposed in | |
| // this header file only so that PrintSystrace can be visible to | |
| // timeline_test.cc. | |
| class TimelineEventSystraceRecorder : public TimelineEventPlatformRecorder { | |
| public: | |
| TimelineEventSystraceRecorder(); | |
| virtual ~TimelineEventSystraceRecorder(); | |
| static intptr_t PrintSystrace(TimelineEvent* event, | |
| char* buffer, | |
| intptr_t buffer_size); | |
| const char* name() const { return SYSTRACE_RECORDER_NAME; } | |
| intptr_t Size() { return 0; } | |
| private: | |
| void OnEvent(TimelineEvent* event); | |
| int systrace_fd_; | |
| }; | |
| #endif // defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_LINUX) | |
| #if defined(DART_HOST_OS_MACOS) | |
| // A recorder that sends events to macOS's tracing app. See: | |
| // https://developer.apple.com/documentation/os/logging?language=objc | |
| class TimelineEventMacosRecorder : public TimelineEventPlatformRecorder { | |
| public: | |
| TimelineEventMacosRecorder(); | |
| virtual ~TimelineEventMacosRecorder(); | |
| const char* name() const { return MACOS_RECORDER_NAME; } | |
| intptr_t Size() { return 0; } | |
| private: | |
| void OnEvent(TimelineEvent* event); | |
| }; | |
| #endif // defined(DART_HOST_OS_MACOS) | |
| class TimelineEventFileRecorderBase : public TimelineEventRecorder { | |
| public: | |
| explicit TimelineEventFileRecorderBase(void* file); | |
| virtual ~TimelineEventFileRecorderBase(); | |
| static void* OpenTimelineFileForWriting(const char* path, const char** error); | |
| intptr_t Size() { return block_count_ * sizeof(TimelineEventBlock); } | |
| void Drain(); | |
| #ifndef PRODUCT | |
| void PrintJSON(JSONStream* js, TimelineEventFilter* filter) final { | |
| UNREACHABLE(); | |
| } | |
| #if defined(SUPPORT_PERFETTO) | |
| void PrintPerfettoTimeline(JSONStream* js, | |
| const TimelineEventFilter& filter) final { | |
| UNREACHABLE(); | |
| } | |
| #endif // defined(SUPPORT_PERFETTO) | |
| void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) final { | |
| UNREACHABLE(); | |
| } | |
| #endif | |
| protected: | |
| void Write(const char* buffer, intptr_t len); | |
| void Write(const char* buffer) { Write(buffer, strlen(buffer)); } | |
| void CompleteEvent(TimelineEvent* event) final; | |
| void StartUp(const char* name); | |
| void ShutDown(); | |
| TimelineEvent* StartEvent(); | |
| TimelineEventBlock* GetNewBlockLocked(); | |
| void ClearLocked(); | |
| void FinishBlock(TimelineEventBlock* block) final; | |
| private: | |
| // Size of internal buffer which is used to buffer writes before passing | |
| // them to |Dart::file_write_callback()|. | |
| static constexpr intptr_t kBufferSize = 8 * KB; | |
| void FlushBuffer(); | |
| void WriteToFile(const char* buffer, intptr_t len) const; | |
| void DrainBlockChain(TimelineEventBlock* block); | |
| virtual void DrainImpl(const TimelineEvent& event) = 0; | |
| Monitor monitor_; | |
| // Completed blocks which are waiting to be drained by the writer thread. | |
| TimelineEventBlock* completed_blocks_head_ = nullptr; | |
| TimelineEventBlock* completed_blocks_tail_ = nullptr; | |
| // Empty blocks which can be reused. | |
| TimelineEventBlock* empty_blocks_ = nullptr; | |
| std::unique_ptr<char[]> buffer_{new char[kBufferSize]}; | |
| intptr_t buffer_pos_ = 0; | |
| void* file_ = nullptr; | |
| intptr_t block_count_ = 0; | |
| bool shutting_down_ = false; | |
| bool drained_ = false; | |
| ThreadJoinId thread_id_ = OSThread::kInvalidThreadJoinId; | |
| }; | |
| class TimelineEventFileRecorder : public TimelineEventFileRecorderBase { | |
| public: | |
| explicit TimelineEventFileRecorder(void* file); | |
| virtual ~TimelineEventFileRecorder(); | |
| const char* name() const final { return FILE_RECORDER_NAME; } | |
| virtual void AddTrackMetadataBasedOnThread(const intptr_t process_id, | |
| const intptr_t trace_id, | |
| const char* thread_name); | |
| private: | |
| void DrainImpl(const TimelineEvent& event) final; | |
| bool first_; | |
| }; | |
| class DartTimelineEventHelpers : public AllStatic { | |
| public: | |
| // When reporting an event of type |kAsyncBegin|, |kAsyncEnd|, or | |
| // |kAsyncInstant|, the async ID associated with the event should be passed | |
| // through |id|. When reporting an event of type |kFlowBegin|, |kFlowStep|, | |
| // or |kFlowEnd|, the flow ID associated with the event should be passed | |
| // through |id|. When reporting an event of type |kBegin| or |kEnd|, the event | |
| // ID associated with the event should be passed through |id|. Note that this | |
| // event ID will only be used by the MacOS recorder. | |
| static void ReportTaskEvent(TimelineEvent* event, | |
| int64_t id, | |
| intptr_t flow_id_count, | |
| std::unique_ptr<const int64_t[]>& flow_ids, | |
| intptr_t type, | |
| char* name, | |
| char* args); | |
| }; | |
| #endif // defined(SUPPORT_TIMELINE) | |
| } // namespace dart | |
| #endif // RUNTIME_VM_TIMELINE_H_ |