| Index: runtime/vm/timeline.cc
|
| diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
|
| index d9b09573e3e4a26ada188fbc38da314d3b989593..a603b037de1e8e1743e84b59464b73db09090337 100644
|
| --- a/runtime/vm/timeline.cc
|
| +++ b/runtime/vm/timeline.cc
|
| @@ -30,7 +30,7 @@ DEFINE_FLAG(charp, timeline_dir, NULL,
|
| //
|
| // Writing events:
|
| // |TimelineEvent|s are written into |TimelineEventBlock|s. Each |Thread| caches
|
| -// a |TimelineEventBlock| in TLS so that it can write events without
|
| +// a |TimelineEventBlock| object so that it can write events without
|
| // synchronizing with other threads in the system. Even though the |Thread| owns
|
| // the |TimelineEventBlock| the block may need to be reclaimed by the reporting
|
| // system. To support that, a |Thread| must hold its |timeline_block_lock_|
|
| @@ -41,30 +41,17 @@ DEFINE_FLAG(charp, timeline_dir, NULL,
|
| // When requested, the timeline is serialized in the trace-event format
|
| // (https://goo.gl/hDZw5M). The request can be for a VM-wide timeline or an
|
| // isolate specific timeline. In both cases it may be that a thread has
|
| -// a |TimelineEventBlock| cached in TLS. In order to report a complete timeline
|
| -// the cached |TimelineEventBlock|s need to be reclaimed.
|
| +// a |TimelineEventBlock| cached in TLS partially filled with events. In order
|
| +// to report a complete timeline the cached |TimelineEventBlock|s need to be
|
| +// reclaimed.
|
| //
|
| -// Reclaiming open |TimelineEventBlock|s for an isolate:
|
| +// Reclaiming open |TimelineEventBlock|s from threads:
|
| //
|
| -// Cached |TimelineEventBlock|s can be in two places:
|
| -// 1) In a |Thread| (Thread currently in an |Isolate|)
|
| -// 2) In a |Thread::State| (Thread not currently in an |Isolate|).
|
| +// Each |Thread| can have one |TimelineEventBlock| cached in it.
|
| //
|
| -// As a |Thread| enters and exits an |Isolate|, a |TimelineEventBlock|
|
| -// will move between (1) and (2).
|
| -//
|
| -// The first case occurs for |Thread|s that are currently running inside an
|
| -// isolate. The second case occurs for |Thread|s that are not currently
|
| -// running inside an isolate.
|
| -//
|
| -// To reclaim the first case, we take the |Thread|'s |timeline_block_lock_|
|
| -// and reclaim the cached block.
|
| -//
|
| -// To reclaim the second case, we can take the |ThreadRegistry| lock and
|
| -// reclaim these blocks.
|
| -//
|
| -// |Timeline::ReclaimIsolateBlocks| and |Timeline::ReclaimAllBlocks| are
|
| -// the two utility methods used to reclaim blocks before reporting.
|
| +// To reclaim blocks, we iterate over all threads and remove the cached
|
| +// |TimelineEventBlock| from each thread. This is safe because we hold the
|
| +// |Thread|'s |timeline_block_lock_| meaning the block can't be being modified.
|
| //
|
| // Locking notes:
|
| // The following locks are used by the timeline system:
|
| @@ -72,14 +59,11 @@ DEFINE_FLAG(charp, timeline_dir, NULL,
|
| // |TimelineEventBlock| is being requested or reclaimed.
|
| // - |Thread::timeline_block_lock_| This lock is held whenever a |Thread|'s
|
| // cached block is being operated on.
|
| -// - |ThreadRegistry::monitor_| This lock protects the cached block for
|
| -// unscheduled threads of an isolate.
|
| -// - |Isolate::isolates_list_monitor_| This lock protects the list of
|
| -// isolates in the system.
|
| +// - |Thread::thread_list_lock_| This lock is held when iterating over
|
| +// |Thread|s.
|
| //
|
| // Locks must always be taken in the following order:
|
| -// |Isolate::isolates_list_monitor_|
|
| -// |ThreadRegistry::monitor_|
|
| +// |Thread::thread_list_lock_|
|
| // |Thread::timeline_block_lock_|
|
| // |TimelineEventRecorder::lock_|
|
| //
|
| @@ -135,41 +119,25 @@ TimelineStream* Timeline::GetVMStream() {
|
| }
|
|
|
|
|
| -void Timeline::ReclaimIsolateBlocks() {
|
| - ReclaimBlocksForIsolate(Isolate::Current());
|
| -}
|
| -
|
| -
|
| -class ReclaimBlocksIsolateVisitor : public IsolateVisitor {
|
| - public:
|
| - ReclaimBlocksIsolateVisitor() {}
|
| -
|
| - virtual void VisitIsolate(Isolate* isolate) {
|
| - Timeline::ReclaimBlocksForIsolate(isolate);
|
| - }
|
| -
|
| - private:
|
| -};
|
| -
|
| -
|
| -void Timeline::ReclaimAllBlocks() {
|
| - if (recorder() == NULL) {
|
| +void Timeline::ReclaimCachedBlocksFromThreads() {
|
| + TimelineEventRecorder* recorder = Timeline::recorder();
|
| + if (recorder == NULL) {
|
| return;
|
| }
|
| - // Reclaim all blocks cached for all isolates.
|
| - ReclaimBlocksIsolateVisitor visitor;
|
| - Isolate::VisitIsolates(&visitor);
|
| - // Reclaim the global VM block.
|
| - recorder()->ReclaimGlobalBlock();
|
| -}
|
|
|
| -
|
| -void Timeline::ReclaimBlocksForIsolate(Isolate* isolate) {
|
| - if (recorder() == NULL) {
|
| - return;
|
| + // Iterate over threads.
|
| + ThreadIterator it;
|
| + while (it.HasNext()) {
|
| + Thread* thread = it.Next();
|
| + MutexLocker ml(thread->timeline_block_lock());
|
| + // Grab block and clear it.
|
| + TimelineEventBlock* block = thread->timeline_block();
|
| + thread->set_timeline_block(NULL);
|
| + // TODO(johnmccutchan): Consider dropping the timeline_block_lock here
|
| + // if we can do it everywhere. This would simplify the lock ordering
|
| + // requirements.
|
| + recorder->FinishBlock(block);
|
| }
|
| - ASSERT(isolate != NULL);
|
| - isolate->ReclaimTimelineBlocks();
|
| }
|
|
|
|
|
| @@ -656,8 +624,7 @@ void DartTimelineEvent::Init(Isolate* isolate, const char* event) {
|
|
|
|
|
| TimelineEventRecorder::TimelineEventRecorder()
|
| - : global_block_(NULL),
|
| - async_id_(0) {
|
| + : async_id_(0) {
|
| }
|
|
|
|
|
| @@ -696,9 +663,6 @@ TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() {
|
| // NOTE: We are exiting this function with the thread's block lock held.
|
| ASSERT(!thread_block->IsFull());
|
| TimelineEvent* event = thread_block->StartEvent();
|
| - if (event != NULL) {
|
| - event->set_global_block(false);
|
| - }
|
| return event;
|
| }
|
| // Drop lock here as no event is being handed out.
|
| @@ -707,67 +671,20 @@ TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() {
|
| }
|
|
|
|
|
| -TimelineEvent* TimelineEventRecorder::GlobalBlockStartEvent() {
|
| - // Take recorder lock. This lock will be held until the call to
|
| - // |CompleteEvent| is made.
|
| - lock_.Lock();
|
| - if (FLAG_trace_timeline) {
|
| - OS::Print("GlobalBlockStartEvent in block %p for thread %" Px "\n",
|
| - global_block_, OSThread::CurrentCurrentThreadIdAsIntPtr());
|
| - }
|
| - if ((global_block_ != NULL) && global_block_->IsFull()) {
|
| - // Global block is full.
|
| - global_block_->Finish();
|
| - global_block_ = NULL;
|
| - }
|
| - if (global_block_ == NULL) {
|
| - // Allocate a new block.
|
| - global_block_ = GetNewBlockLocked(NULL);
|
| - ASSERT(global_block_ != NULL);
|
| - }
|
| - if (global_block_ != NULL) {
|
| - // NOTE: We are exiting this function with the recorder's lock held.
|
| - ASSERT(!global_block_->IsFull());
|
| - TimelineEvent* event = global_block_->StartEvent();
|
| - if (event != NULL) {
|
| - event->set_global_block(true);
|
| - }
|
| - return event;
|
| - }
|
| - // Drop lock here as no event is being handed out.
|
| - lock_.Unlock();
|
| - return NULL;
|
| -}
|
| -
|
| -
|
| void TimelineEventRecorder::ThreadBlockCompleteEvent(TimelineEvent* event) {
|
| if (event == NULL) {
|
| return;
|
| }
|
| - ASSERT(!event->global_block());
|
| // Grab the current thread.
|
| Thread* thread = Thread::Current();
|
| ASSERT(thread != NULL);
|
| - ASSERT(thread->isolate() != NULL);
|
| - // This event came from the isolate's thread local block. Unlock the
|
| - // thread's block lock.
|
| + // Unlock the thread's block lock.
|
| Mutex* thread_block_lock = thread->timeline_block_lock();
|
| ASSERT(thread_block_lock != NULL);
|
| thread_block_lock->Unlock();
|
| }
|
|
|
|
|
| -void TimelineEventRecorder::GlobalBlockCompleteEvent(TimelineEvent* event) {
|
| - if (event == NULL) {
|
| - return;
|
| - }
|
| - ASSERT(event->global_block());
|
| - // This event came from the global block, unlock the recorder's lock now
|
| - // that the event is filled.
|
| - lock_.Unlock();
|
| -}
|
| -
|
| -
|
| // Trims the ']' character.
|
| static void TrimOutput(char* output,
|
| intptr_t* output_length) {
|
| @@ -793,7 +710,7 @@ void TimelineEventRecorder::WriteTo(const char* directory) {
|
| Thread* T = Thread::Current();
|
| StackZone zone(T);
|
|
|
| - Timeline::ReclaimAllBlocks();
|
| + Timeline::ReclaimCachedBlocksFromThreads();
|
|
|
| intptr_t pid = OS::ProcessId();
|
| char* filename = OS::SCreate(NULL,
|
| @@ -845,15 +762,6 @@ void TimelineEventRecorder::WriteTo(const char* directory) {
|
| }
|
|
|
|
|
| -void TimelineEventRecorder::ReclaimGlobalBlock() {
|
| - MutexLocker ml(&lock_);
|
| - if (global_block_ != NULL) {
|
| - global_block_->Finish();
|
| - global_block_ = NULL;
|
| - }
|
| -}
|
| -
|
| -
|
| int64_t TimelineEventRecorder::GetNextAsyncId() {
|
| // TODO(johnmccutchan): Gracefully handle wrap around.
|
| uint32_t next = static_cast<uint32_t>(
|
| @@ -1038,10 +946,6 @@ TimelineEvent* TimelineEventRingRecorder::StartEvent() {
|
| // Grab the current thread.
|
| Thread* thread = Thread::Current();
|
| ASSERT(thread != NULL);
|
| - if (thread->isolate() == NULL) {
|
| - // Non-isolate thread case. This should be infrequent.
|
| - return GlobalBlockStartEvent();
|
| - }
|
| return ThreadBlockStartEvent();
|
| }
|
|
|
| @@ -1050,11 +954,7 @@ void TimelineEventRingRecorder::CompleteEvent(TimelineEvent* event) {
|
| if (event == NULL) {
|
| return;
|
| }
|
| - if (event->global_block()) {
|
| - GlobalBlockCompleteEvent(event);
|
| - } else {
|
| - ThreadBlockCompleteEvent(event);
|
| - }
|
| + ThreadBlockCompleteEvent(event);
|
| }
|
|
|
|
|
| @@ -1179,10 +1079,6 @@ TimelineEvent* TimelineEventEndlessRecorder::StartEvent() {
|
| // Grab the current thread.
|
| Thread* thread = Thread::Current();
|
| ASSERT(thread != NULL);
|
| - if (thread->isolate() == NULL) {
|
| - // Non-isolate thread case. This should be infrequent.
|
| - return GlobalBlockStartEvent();
|
| - }
|
| return ThreadBlockStartEvent();
|
| }
|
|
|
| @@ -1191,11 +1087,7 @@ void TimelineEventEndlessRecorder::CompleteEvent(TimelineEvent* event) {
|
| if (event == NULL) {
|
| return;
|
| }
|
| - if (event->global_block()) {
|
| - GlobalBlockCompleteEvent(event);
|
| - } else {
|
| - ThreadBlockCompleteEvent(event);
|
| - }
|
| + ThreadBlockCompleteEvent(event);
|
| }
|
|
|
|
|
|
|