| Index: runtime/vm/timeline.h
|
| diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
|
| index a57b651e5d6eaf17266c9cdbbafefa0b32986a24..960eefa2dd5f28766f2b8c2cdd86141cc97456ab 100644
|
| --- a/runtime/vm/timeline.h
|
| +++ b/runtime/vm/timeline.h
|
| @@ -22,6 +22,30 @@ class TimelineEventBlock;
|
| class TimelineEventRecorder;
|
| class TimelineStream;
|
|
|
| +
|
| +class Timeline : public AllStatic {
|
| + public:
|
| + // Initialize timeline system. Not thread safe.
|
| + static void InitOnce();
|
| +
|
| + // Shutdown timeline system. Not thread safe.
|
| + static void Shutdown();
|
| +
|
| + // Access the global recorder. Not thread safe.
|
| + static TimelineEventRecorder* recorder();
|
| +
|
| + static bool EnableStreamByDefault(const char* stream_name);
|
| +
|
| + static TimelineStream* GetVMStream();
|
| +
|
| + private:
|
| + static TimelineEventRecorder* recorder_;
|
| + static TimelineStream* vm_stream_;
|
| +
|
| + friend class TimelineRecorderOverride;
|
| +};
|
| +
|
| +
|
| // You should get a |TimelineEvent| from a |TimelineStream|.
|
| class TimelineEvent {
|
| public:
|
| @@ -45,9 +69,8 @@ class TimelineEvent {
|
| return (event_type() > kNone) && (event_type() < kNumEventTypes);
|
| }
|
|
|
| - // Marks the beginning of an asynchronous operation.
|
| - // Returns |async_id| which must be passed to |AsyncInstant| and |AsyncEnd|.
|
| - int64_t AsyncBegin(const char* label);
|
| + // Marks the beginning of an asynchronous operation with |async_id|.
|
| + void AsyncBegin(const char* label, int64_t async_id);
|
| // Marks an instantaneous event associated with |async_id|.
|
| void AsyncInstant(const char* label,
|
| int64_t async_id);
|
| @@ -139,8 +162,9 @@ class TimelineEvent {
|
| intptr_t arguments_length_;
|
| uword state_;
|
| const char* label_;
|
| - TimelineStream* stream_;
|
| + const char* category_;
|
| ThreadId thread_;
|
| + Isolate* isolate_;
|
|
|
| void FreeArguments();
|
|
|
| @@ -185,28 +209,13 @@ class TimelineStream {
|
| enabled_ = enabled;
|
| }
|
|
|
| - TimelineEventRecorder* recorder() const {
|
| - return recorder_;
|
| - }
|
| -
|
| - // TODO(johnmccutchan): Disallow setting recorder after Init?
|
| - void set_recorder(TimelineEventRecorder* recorder) {
|
| - recorder_ = recorder;
|
| - }
|
| -
|
| // Records an event. Will return |NULL| if not enabled. The returned
|
| // |TimelineEvent| is in an undefined state and must be initialized.
|
| TimelineEvent* StartEvent();
|
|
|
| - void CompleteEvent(TimelineEvent* event);
|
| -
|
| - int64_t GetNextSeq();
|
| -
|
| private:
|
| - TimelineEventRecorder* recorder_;
|
| const char* name_;
|
| bool enabled_;
|
| - int64_t seq_;
|
| };
|
|
|
|
|
| @@ -228,9 +237,12 @@ class TimelineStream {
|
| tds.CopyArgument( \
|
| 0, \
|
| "function", \
|
| - const_cast<char*>(function.QualifiedUserVisibleNameCString())); \
|
| + const_cast<char*>(function.ToLibNamePrefixedQualifiedCString())); \
|
| }
|
|
|
| +
|
| +// TODO(johnmccutchan): TimelineDurationScope should only allocate the
|
| +// event when complete.
|
| class TimelineDurationScope : public StackResource {
|
| public:
|
| TimelineDurationScope(Isolate* isolate,
|
| @@ -247,6 +259,12 @@ class TimelineDurationScope : public StackResource {
|
| Init(stream, label);
|
| }
|
|
|
| + TimelineDurationScope(TimelineStream* stream,
|
| + const char* label)
|
| + : StackResource(reinterpret_cast<Thread*>(NULL)) {
|
| + Init(stream, label);
|
| + }
|
| +
|
| void Init(TimelineStream* stream, const char* label) {
|
| event_ = stream->StartEvent();
|
| if (event_ == NULL) {
|
| @@ -353,6 +371,16 @@ class TimelineEventBlock {
|
| // Call Reset on all events and set length to 0.
|
| void Reset();
|
|
|
| + // Only safe to access under the recorder's lock.
|
| + bool open() const {
|
| + return open_;
|
| + }
|
| +
|
| + // Only safe to access under the recorder's lock.
|
| + Isolate* isolate() const {
|
| + return isolate_;
|
| + }
|
| +
|
| protected:
|
| TimelineEvent* StartEvent();
|
|
|
| @@ -361,8 +389,17 @@ class TimelineEventBlock {
|
| intptr_t length_;
|
| intptr_t block_index_;
|
|
|
| - friend class TimelineEventEndlessRecorder;
|
| + // Only accessed under the recorder's lock.
|
| + Isolate* isolate_;
|
| + bool open_;
|
| +
|
| + void Open(Isolate* isolate);
|
| + void Finish();
|
| +
|
| + friend class ThreadRegistry;
|
| friend class TimelineEventRecorder;
|
| + friend class TimelineEventRingRecorder;
|
| + friend class TimelineEventEndlessRecorder;
|
| friend class TimelineTestHelper;
|
|
|
| private:
|
| @@ -370,33 +407,88 @@ class TimelineEventBlock {
|
| };
|
|
|
|
|
| +class TimelineEventFilter : public ValueObject {
|
| + public:
|
| + TimelineEventFilter();
|
| + virtual ~TimelineEventFilter();
|
| +
|
| + virtual bool IncludeBlock(TimelineEventBlock* block) {
|
| + if (block == NULL) {
|
| + return false;
|
| + }
|
| + // Not empty and not open.
|
| + return !block->IsEmpty() && !block->open();
|
| + }
|
| +
|
| + virtual bool IncludeEvent(TimelineEvent* event) {
|
| + if (event == NULL) {
|
| + return false;
|
| + }
|
| + return event->IsValid();
|
| + }
|
| +
|
| + private:
|
| +};
|
| +
|
| +
|
| +class IsolateTimelineEventFilter : public TimelineEventFilter {
|
| + public:
|
| + explicit IsolateTimelineEventFilter(Isolate* isolate);
|
| +
|
| + bool IncludeBlock(TimelineEventBlock* block) {
|
| + if (block == NULL) {
|
| + return false;
|
| + }
|
| + // Not empty, not open, and isolate match.
|
| + return !block->IsEmpty() &&
|
| + (block->isolate() == isolate_);
|
| + }
|
| +
|
| + private:
|
| + Isolate* isolate_;
|
| +};
|
| +
|
| +
|
| // Recorder of |TimelineEvent|s.
|
| class TimelineEventRecorder {
|
| public:
|
| TimelineEventRecorder();
|
| virtual ~TimelineEventRecorder() {}
|
|
|
| + TimelineEventBlock* GetNewBlock();
|
| +
|
| // Interface method(s) which must be implemented.
|
| - virtual void PrintJSON(JSONStream* js) = 0;
|
| - virtual TimelineEventBlock* GetNewBlock() = 0;
|
| - virtual TimelineEventBlock* GetHeadBlock() = 0;
|
| + virtual void PrintJSON(JSONStream* js, TimelineEventFilter* filter) = 0;
|
|
|
| - void WriteTo(const char* directory);
|
| + int64_t GetNextAsyncId();
|
|
|
| protected:
|
| + void WriteTo(const char* directory);
|
| +
|
| // Interface method(s) which must be implemented.
|
| virtual TimelineEvent* StartEvent() = 0;
|
| virtual void CompleteEvent(TimelineEvent* event) = 0;
|
| + virtual TimelineEventBlock* GetHeadBlockLocked() = 0;
|
| + virtual TimelineEventBlock* GetNewBlockLocked(Isolate* isolate) = 0;
|
|
|
| // Utility method(s).
|
| void PrintJSONMeta(JSONArray* array) const;
|
| TimelineEvent* ThreadBlockStartEvent();
|
| + TimelineEvent* GlobalBlockStartEvent();
|
|
|
| Mutex lock_;
|
| + // Only accessed under |lock_|.
|
| + TimelineEventBlock* global_block_;
|
| + void FinishGlobalBlock();
|
|
|
| + uintptr_t async_id_;
|
| +
|
| + friend class ThreadRegistry;
|
| + friend class TimelineEvent;
|
| friend class TimelineEventBlockIterator;
|
| friend class TimelineStream;
|
| friend class TimelineTestHelper;
|
| + friend class Timeline;
|
| friend class Isolate;
|
|
|
| private:
|
| @@ -412,21 +504,18 @@ class TimelineEventRingRecorder : public TimelineEventRecorder {
|
| explicit TimelineEventRingRecorder(intptr_t capacity = kDefaultCapacity);
|
| ~TimelineEventRingRecorder();
|
|
|
| - void PrintJSON(JSONStream* js);
|
| - TimelineEventBlock* GetNewBlock();
|
| - TimelineEventBlock* GetHeadBlock();
|
| + void PrintJSON(JSONStream* js, TimelineEventFilter* filter);
|
|
|
| protected:
|
| TimelineEvent* StartEvent();
|
| void CompleteEvent(TimelineEvent* event);
|
| -
|
| + TimelineEventBlock* GetHeadBlockLocked();
|
| intptr_t FindOldestBlockIndex() const;
|
| - TimelineEventBlock* GetNewBlockLocked();
|
| + TimelineEventBlock* GetNewBlockLocked(Isolate* isolate);
|
|
|
| - void PrintJSONEvents(JSONArray* array) const;
|
| + void PrintJSONEvents(JSONArray* array, TimelineEventFilter* filter) const;
|
|
|
| TimelineEventBlock** blocks_;
|
| - RawArray* event_objects_;
|
| intptr_t capacity_;
|
| intptr_t num_blocks_;
|
| intptr_t block_cursor_;
|
| @@ -439,19 +528,19 @@ class TimelineEventStreamingRecorder : public TimelineEventRecorder {
|
| TimelineEventStreamingRecorder();
|
| ~TimelineEventStreamingRecorder();
|
|
|
| - void PrintJSON(JSONStream* js);
|
| - TimelineEventBlock* GetNewBlock() {
|
| - return NULL;
|
| - }
|
| - TimelineEventBlock* GetHeadBlock() {
|
| - return NULL;
|
| - }
|
| + void PrintJSON(JSONStream* js, TimelineEventFilter* filter);
|
|
|
| // Called when |event| is ready to be streamed. It is unsafe to keep a
|
| // reference to |event| as it may be freed as soon as this function returns.
|
| virtual void StreamEvent(TimelineEvent* event) = 0;
|
|
|
| protected:
|
| + TimelineEventBlock* GetNewBlockLocked(Isolate* isolate) {
|
| + return NULL;
|
| + }
|
| + TimelineEventBlock* GetHeadBlockLocked() {
|
| + return NULL;
|
| + }
|
| TimelineEvent* StartEvent();
|
| void CompleteEvent(TimelineEvent* event);
|
| };
|
| @@ -464,26 +553,18 @@ class TimelineEventEndlessRecorder : public TimelineEventRecorder {
|
| public:
|
| TimelineEventEndlessRecorder();
|
|
|
| - // Acquire a new block of events.
|
| - // Takes a lock.
|
| - // Recorder owns the block and it should be filled by only one thread.
|
| - TimelineEventBlock* GetNewBlock();
|
| -
|
| - TimelineEventBlock* GetHeadBlock();
|
| -
|
| - // It is expected that this function is only called when an isolate is
|
| - // shutting itself down.
|
| // NOTE: Calling this while threads are filling in their blocks is not safe
|
| // and there are no checks in place to ensure that doesn't happen.
|
| // TODO(koda): Add isolate count to |ThreadRegistry| and verify that it is 1.
|
| - void PrintJSON(JSONStream* js);
|
| + void PrintJSON(JSONStream* js, TimelineEventFilter* filter);
|
|
|
| protected:
|
| TimelineEvent* StartEvent();
|
| void CompleteEvent(TimelineEvent* event);
|
| + TimelineEventBlock* GetNewBlockLocked(Isolate* isolate);
|
| + TimelineEventBlock* GetHeadBlockLocked();
|
|
|
| - TimelineEventBlock* GetNewBlockLocked();
|
| - void PrintJSONEvents(JSONArray* array) const;
|
| + void PrintJSONEvents(JSONArray* array, TimelineEventFilter* filter) const;
|
|
|
| // Useful only for testing. Only works for one thread.
|
| void Clear();
|
|
|