| // 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) && !defined(PRODUCT) |
| #include "perfetto/protozero/scattered_heap_buffer.h" |
| #include "vm/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> |
| #elif defined(DART_HOST_OS_MACOS) |
| #include <os/signpost.h> |
| #endif // defined(FUCHSIA_SDK) || defined(DART_HOST_OS_FUCHSIA) |
| |
| 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 Thread; |
| class TimelineEvent; |
| class TimelineEventBlock; |
| class TimelineEventRecorder; |
| class TimelineStream; |
| class VirtualMemory; |
| class Zone; |
| |
| #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" |
| |
| // (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(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. |
| 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(); |
| |
| // 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(); |
| |
| 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 |
| |
| private: |
| 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; |
| std::unique_ptr<const char[]> GetFormattedIsolateId() const; |
| std::unique_ptr<const char[]> GetFormattedIsolateGroupId() const; |
| |
| // 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) && !defined(PRODUCT) |
| 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) && !defined(PRODUCT) |
| |
| 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); |
| |
| 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_); |
| } |
| |
| TimelineEvent* next() const { return next_; } |
| void set_next(TimelineEvent* next) { next_ = next; } |
| |
| 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_; |
| TimelineEvent* next_; |
| |
| 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) && !defined(PRODUCT) |
| friend class TimelineEventPerfettoFileRecorder; |
| #endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT) |
| 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; |
| #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) |
| #endif // !defined(PRODUCT) |
| |
| 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_; |
| }; |
| |
| 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_; } |
| #if defined(SUPPORT_PERFETTO) && !defined(PRODUCT) |
| /* |
| * 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) && !defined(PRODUCT) |
| |
| 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_; |
| }; |
| |
| #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 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(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(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; |
| 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); |
| void AddAsyncTrackMetadataBasedOnEvent(const TimelineEvent& event); |
| |
| protected: |
| SimpleHashMap& track_uuid_to_track_metadata() { |
| return track_uuid_to_track_metadata_; |
| } |
| SimpleHashMap& async_track_uuid_to_track_metadata() { |
| return async_track_uuid_to_track_metadata_; |
| } |
| #if defined(SUPPORT_PERFETTO) && !defined(PRODUCT) |
| protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>& packet() { |
| return packet_; |
| } |
| #endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT) |
| |
| #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 TimelineEventBlock* GetHeadBlockLocked() = 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: |
| static constexpr intptr_t kTrackUuidToTrackMetadataInitialCapacity = 1 << 4; |
| Mutex track_uuid_to_track_metadata_lock_; |
| SimpleHashMap track_uuid_to_track_metadata_; |
| Mutex async_track_uuid_to_track_metadata_lock_; |
| SimpleHashMap async_track_uuid_to_track_metadata_; |
| #if defined(SUPPORT_PERFETTO) && !defined(PRODUCT) |
| // 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_; |
| #endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT) |
| |
| DISALLOW_COPY_AND_ASSIGN(TimelineEventRecorder); |
| }; |
| |
| // An abstract recorder that stores events in a buffer of fixed capacity. |
| class TimelineEventFixedBufferRecorder : public TimelineEventRecorder { |
| public: |
| static constexpr intptr_t kDefaultCapacity = 32 * KB; // Number of events. |
| |
| explicit TimelineEventFixedBufferRecorder(intptr_t capacity); |
| virtual ~TimelineEventFixedBufferRecorder(); |
| |
| #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) |
| |
| intptr_t Size(); |
| |
| protected: |
| TimelineEvent* StartEvent(); |
| void CompleteEvent(TimelineEvent* event); |
| TimelineEventBlock* GetHeadBlockLocked(); |
| // Only safe to call when holding |lock_|. |
| intptr_t FindOldestBlockIndexLocked() const; |
| void ClearLocked(); |
| |
| #ifndef PRODUCT |
| void PrintJSONEvents(const JSONArray& array, |
| const TimelineEventFilter& filter); |
| #if defined(SUPPORT_PERFETTO) |
| void PrintPerfettoEvents(JSONBase64String* jsonBase64String, |
| const TimelineEventFilter& filter); |
| #endif // defined(SUPPORT_PERFETTO) |
| #endif // !defined(PRODUCT) |
| |
| VirtualMemory* memory_; |
| TimelineEventBlock* blocks_; |
| intptr_t capacity_; |
| intptr_t num_blocks_; |
| intptr_t block_cursor_; |
| |
| private: |
| #if !defined(PRODUCT) |
| inline void PrintEventsCommon( |
| const TimelineEventFilter& filter, |
| std::function<void(const TimelineEvent&)>&& print_impl); |
| #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(); } |
| TimelineEventBlock* GetHeadBlockLocked() { 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 OnEvent(TimelineEvent* event); |
| }; |
| |
| // A recorder that does nothing. |
| class TimelineEventNopRecorder : public TimelineEventCallbackRecorder { |
| public: |
| TimelineEventNopRecorder() {} |
| virtual ~TimelineEventNopRecorder() {} |
| |
| 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 TimelineEventRecorder { |
| public: |
| TimelineEventEndlessRecorder(); |
| virtual ~TimelineEventEndlessRecorder(); |
| |
| #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) |
| |
| 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(); |
| TimelineEventBlock* GetHeadBlockLocked(); |
| void ClearLocked(); |
| |
| #ifndef PRODUCT |
| void PrintJSONEvents(const JSONArray& array, |
| const TimelineEventFilter& filter); |
| #if defined(SUPPORT_PERFETTO) |
| void PrintPerfettoEvents(JSONBase64String* jsonBase64String, |
| const TimelineEventFilter& filter); |
| #endif // defined(SUPPORT_PERFETTO) |
| #endif // !defined(PRODUCT) |
| |
| TimelineEventBlock* head_; |
| TimelineEventBlock* tail_; |
| intptr_t block_index_; |
| |
| private: |
| #if !defined(PRODUCT) |
| inline void PrintEventsCommon( |
| const TimelineEventFilter& filter, |
| std::function<void(const TimelineEvent&)>&& print_impl); |
| #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 const char* name() const = 0; |
| |
| protected: |
| TimelineEventBlock* GetNewBlockLocked() { UNREACHABLE(); } |
| TimelineEventBlock* GetHeadBlockLocked() { 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 TimelineEventPlatformRecorder { |
| public: |
| explicit TimelineEventFileRecorderBase(const char* path); |
| virtual ~TimelineEventFileRecorderBase(); |
| |
| intptr_t Size() final { return 0; } |
| void OnEvent(TimelineEvent* event) final { UNREACHABLE(); } |
| void Drain(); |
| |
| protected: |
| void Write(const char* buffer, intptr_t len) const; |
| void Write(const char* buffer) const { Write(buffer, strlen(buffer)); } |
| void CompleteEvent(TimelineEvent* event) final; |
| void StartUp(const char* name); |
| void ShutDown(); |
| |
| private: |
| virtual void DrainImpl(const TimelineEvent& event) = 0; |
| |
| Monitor monitor_; |
| TimelineEvent* head_; |
| TimelineEvent* tail_; |
| void* file_; |
| bool shutting_down_; |
| bool drained_; |
| ThreadJoinId thread_id_; |
| }; |
| |
| class TimelineEventFileRecorder : public TimelineEventFileRecorderBase { |
| public: |
| explicit TimelineEventFileRecorder(const char* path); |
| 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_; |
| }; |
| |
| #if defined(SUPPORT_PERFETTO) && !defined(PRODUCT) |
| class TimelineEventPerfettoFileRecorder : public TimelineEventFileRecorderBase { |
| public: |
| explicit TimelineEventPerfettoFileRecorder(const char* path); |
| virtual ~TimelineEventPerfettoFileRecorder(); |
| |
| const char* name() const final { return PERFETTO_FILE_RECORDER_NAME; } |
| |
| private: |
| void WritePacket( |
| protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet) |
| const; |
| void DrainImpl(const TimelineEvent& event) final; |
| }; |
| #endif // defined(SUPPORT_PERFETTO) && !defined(PRODUCT) |
| |
| 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_ |