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(); |