Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include <cstdlib> | 5 #include <cstdlib> |
| 6 | 6 |
| 7 #include "vm/atomic.h" | 7 #include "vm/atomic.h" |
| 8 #include "vm/isolate.h" | 8 #include "vm/isolate.h" |
| 9 #include "vm/json_stream.h" | 9 #include "vm/json_stream.h" |
| 10 #include "vm/lockers.h" | 10 #include "vm/lockers.h" |
| 11 #include "vm/log.h" | 11 #include "vm/log.h" |
| 12 #include "vm/object.h" | 12 #include "vm/object.h" |
| 13 #include "vm/thread.h" | 13 #include "vm/thread.h" |
| 14 #include "vm/timeline.h" | 14 #include "vm/timeline.h" |
| 15 | 15 |
| 16 namespace dart { | 16 namespace dart { |
| 17 | 17 |
| 18 DEFINE_FLAG(bool, complete_timeline, false, "Record the complete timeline"); | 18 DEFINE_FLAG(bool, complete_timeline, false, "Record the complete timeline"); |
| 19 DEFINE_FLAG(bool, trace_timeline, false, | 19 DEFINE_FLAG(bool, trace_timeline, false, |
| 20 "Trace timeline backend"); | 20 "Trace timeline backend"); |
| 21 DEFINE_FLAG(bool, trace_timeline_analysis, false, | 21 DEFINE_FLAG(bool, trace_timeline_analysis, false, |
| 22 "Trace timeline analysis backend"); | 22 "Trace timeline analysis backend"); |
| 23 DEFINE_FLAG(bool, timing, false, | 23 DEFINE_FLAG(bool, timing, false, |
| 24 "Dump isolate timing information from timeline."); | 24 "Dump isolate timing information from timeline."); |
| 25 DEFINE_FLAG(charp, timeline_dir, NULL, | 25 DEFINE_FLAG(charp, timeline_dir, NULL, |
| 26 "Enable all timeline trace streams and output VM global trace " | 26 "Enable all timeline trace streams and output VM global trace " |
| 27 "into specified directory."); | 27 "into specified directory."); |
| 28 | 28 |
| 29 // Implementation notes: | |
| 30 // | |
| 31 // Writing events: | |
| 32 // |TimelineEvent|s are written into |TimelineEventBlock|s. Each |Thread| caches | |
| 33 // a |TimelineEventBlock| in TLS so that it can write events without | |
| 34 // synchronizing with other threads in the system. Even though the |Thread| owns | |
| 35 // the |TimelineEventBlock| the block may need to be reclaimed by the reporting | |
| 36 // system. To support that, a |Thread| must hold its |timeline_block_lock_| | |
| 37 // when operating on the |TimelineEventBlock|. This lock will only ever be | |
| 38 // busy if blocks are being reclaimed by the reporting system. | |
| 39 // | |
| 40 // Reporting: | |
| 41 // When requested, the timeline is serialized in the trace-event format | |
| 42 // (https://goo.gl/hDZw5M). The request can be for a VM-wide timeline or an | |
| 43 // isolate specific timeline. In both cases it may be that a thread has | |
| 44 // a |TimelineEventBlock| cached in TLS. In order to report a complete timeline | |
| 45 // the cached |TimelineEventBlock|s need to be reclaimed. | |
| 46 // | |
| 47 // Reclaiming open |TimelineEventBlock|s for an isolate: | |
| 48 // | |
| 49 // Cached |TimelineEventBlock|s can be in two places: | |
| 50 // 1) In a |Thread| (Thread currently in an |Isolate|) | |
| 51 // 2) In a |Thread::State| (Thread not currently in an |Isolate|). | |
| 52 // | |
| 53 // As a |Thread| enters and exits an |Isolate|, a |TimelineEventBlock| | |
| 54 // will move between (1) and (2). | |
| 55 // | |
| 56 // The first case occurs for |Thread|s that are currently running inside an | |
| 57 // isolate. The second case occurs for |Thread|s that are not currently | |
| 58 // running inside an isolate. | |
| 59 // | |
| 60 // To reclaim the first case, we take the |Thread|'s |timeline_block_lock_| | |
| 61 // and reclaim the cached block. | |
| 62 // | |
| 63 // To reclaim the second case, we can take the |ThreadRegistry| lock and | |
| 64 // reclaim these blocks. | |
| 65 // | |
| 66 // |Timeline::ReclaimIsolateBlocks| and |Timeline::ReclaimAllBlocks| are | |
| 67 // the two utility methods used to reclaim blocks before reporting. | |
| 68 // | |
| 69 // Locking notes: | |
| 70 // The following locks are used by the timeline system: | |
| 71 // - |TimelineEventRecorder::lock_| This lock is held whenever a | |
| 72 // |TimelineEventBlock| is being requested or reclaimed. | |
| 73 // - |Thread::timeline_block_lock_| This lock is held whenever a |Thread|'s | |
| 74 // cached block is being operated on. | |
| 75 // - |ThreadRegistry::monitor_| This lock protects the cached block for | |
| 76 // unscheduled threads of an isolate. | |
| 77 // - |Isolate::isolates_list_monitor_| This lock protects the list of | |
| 78 // isolates in the system. | |
| 79 // | |
| 80 // Locks must always be taken in the following order: | |
| 81 // |Isolate::isolates_list_monitor_| | |
| 82 // |ThreadRegistry::monitor_| | |
| 83 // |Thread::timeline_block_lock_| | |
| 84 // |TimelineEventRecorder::lock_| | |
| 85 // | |
| 86 | |
| 29 void Timeline::InitOnce() { | 87 void Timeline::InitOnce() { |
| 30 ASSERT(recorder_ == NULL); | 88 ASSERT(recorder_ == NULL); |
| 31 // Default to ring recorder being enabled. | 89 // Default to ring recorder being enabled. |
| 32 const bool use_ring_recorder = true; | 90 const bool use_ring_recorder = true; |
| 33 // Some flags require that we use the endless recorder. | 91 // Some flags require that we use the endless recorder. |
| 34 const bool use_endless_recorder = | 92 const bool use_endless_recorder = |
| 35 (FLAG_timeline_dir != NULL) || FLAG_timing; | 93 (FLAG_timeline_dir != NULL) || FLAG_timing; |
| 36 if (use_endless_recorder) { | 94 if (use_endless_recorder) { |
| 37 recorder_ = new TimelineEventEndlessRecorder(); | 95 recorder_ = new TimelineEventEndlessRecorder(); |
| 38 } else if (use_ring_recorder) { | 96 } else if (use_ring_recorder) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 70 return (FLAG_timeline_dir != NULL) || FLAG_timing; | 128 return (FLAG_timeline_dir != NULL) || FLAG_timing; |
| 71 } | 129 } |
| 72 | 130 |
| 73 | 131 |
| 74 TimelineStream* Timeline::GetVMStream() { | 132 TimelineStream* Timeline::GetVMStream() { |
| 75 ASSERT(vm_stream_ != NULL); | 133 ASSERT(vm_stream_ != NULL); |
| 76 return vm_stream_; | 134 return vm_stream_; |
| 77 } | 135 } |
| 78 | 136 |
| 79 | 137 |
| 138 void Timeline::ReclaimIsolateBlocks() { | |
| 139 if (recorder() == NULL) { | |
| 140 return; | |
| 141 } | |
|
turnidge
2015/09/24 17:05:52
This check is redundant (also in callee).
Cutch
2015/09/24 18:14:16
Done.
| |
| 142 ReclaimBlocksForIsolate(Isolate::Current()); | |
| 143 } | |
| 144 | |
| 145 | |
| 146 class ReclaimBlocksIsolateVisitor : public IsolateVisitor { | |
| 147 public: | |
| 148 ReclaimBlocksIsolateVisitor() {} | |
| 149 | |
| 150 virtual void VisitIsolate(Isolate* isolate) { | |
| 151 Timeline::ReclaimBlocksForIsolate(isolate); | |
| 152 } | |
| 153 | |
| 154 private: | |
| 155 }; | |
| 156 | |
| 157 | |
| 158 void Timeline::ReclaimAllBlocks() { | |
| 159 if (recorder() == NULL) { | |
| 160 return; | |
| 161 } | |
| 162 // Reclaim all blocks cached for all isolates. | |
| 163 ReclaimBlocksIsolateVisitor visitor; | |
| 164 Isolate::VisitIsolates(&visitor); | |
| 165 // Reclaim the global VM block. | |
| 166 recorder()->ReclaimGlobalBlock(); | |
| 167 } | |
| 168 | |
| 169 | |
| 170 void Timeline::ReclaimBlocksForIsolate(Isolate* isolate) { | |
| 171 if (recorder() == NULL) { | |
| 172 return; | |
| 173 } | |
| 174 if (isolate == NULL) { | |
| 175 return; | |
| 176 } | |
|
turnidge
2015/09/24 17:05:52
Consider an ASSERT for isolate != NULL?
Cutch
2015/09/24 18:14:16
Done.
| |
| 177 isolate->ReclaimTimelineBlocks(); | |
| 178 } | |
| 179 | |
| 180 | |
| 80 TimelineEventRecorder* Timeline::recorder_ = NULL; | 181 TimelineEventRecorder* Timeline::recorder_ = NULL; |
| 81 TimelineStream* Timeline::vm_stream_ = NULL; | 182 TimelineStream* Timeline::vm_stream_ = NULL; |
| 82 | 183 |
| 83 #define ISOLATE_TIMELINE_STREAM_DEFINE_FLAG(name, enabled_by_default) \ | 184 #define ISOLATE_TIMELINE_STREAM_DEFINE_FLAG(name, enabled_by_default) \ |
| 84 bool Timeline::stream_##name##_enabled_ = false; | 185 bool Timeline::stream_##name##_enabled_ = false; |
| 85 ISOLATE_TIMELINE_STREAM_LIST(ISOLATE_TIMELINE_STREAM_DEFINE_FLAG) | 186 ISOLATE_TIMELINE_STREAM_LIST(ISOLATE_TIMELINE_STREAM_DEFINE_FLAG) |
| 86 #undef ISOLATE_TIMELINE_STREAM_DEFINE_FLAG | 187 #undef ISOLATE_TIMELINE_STREAM_DEFINE_FLAG |
| 87 | 188 |
| 88 TimelineEvent::TimelineEvent() | 189 TimelineEvent::TimelineEvent() |
| 89 : timestamp0_(0), | 190 : timestamp0_(0), |
| (...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 387 : global_block_(NULL), | 488 : global_block_(NULL), |
| 388 async_id_(0) { | 489 async_id_(0) { |
| 389 } | 490 } |
| 390 | 491 |
| 391 | 492 |
| 392 void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const { | 493 void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const { |
| 393 } | 494 } |
| 394 | 495 |
| 395 | 496 |
| 396 TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() { | 497 TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() { |
| 397 // Grab the thread's timeline event block. | 498 // Grab the current thread. |
| 398 Thread* thread = Thread::Current(); | 499 Thread* thread = Thread::Current(); |
| 399 ASSERT(thread != NULL); | 500 ASSERT(thread != NULL); |
| 501 // We are accessing the thread's timeline block- so take the lock. | |
| 502 MutexLocker ml(thread->timeline_block_lock()); | |
| 400 | 503 |
| 401 if (thread->isolate() == NULL) { | 504 if (thread->isolate() == NULL) { |
| 402 // Non-isolate thread case. This should be infrequent. | 505 // Non-isolate thread case. This should be infrequent. |
| 403 return GlobalBlockStartEvent(); | 506 return GlobalBlockStartEvent(); |
| 404 } | 507 } |
| 405 | 508 |
| 406 TimelineEventBlock* thread_block = thread->timeline_block(); | 509 TimelineEventBlock* thread_block = thread->timeline_block(); |
| 407 | 510 |
| 408 if ((thread_block != NULL) && thread_block->IsFull()) { | 511 if ((thread_block != NULL) && thread_block->IsFull()) { |
| 409 MutexLocker ml(&lock_); | 512 MutexLocker ml(&lock_); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 453 | 556 |
| 454 | 557 |
| 455 void TimelineEventRecorder::WriteTo(const char* directory) { | 558 void TimelineEventRecorder::WriteTo(const char* directory) { |
| 456 Dart_FileOpenCallback file_open = Isolate::file_open_callback(); | 559 Dart_FileOpenCallback file_open = Isolate::file_open_callback(); |
| 457 Dart_FileWriteCallback file_write = Isolate::file_write_callback(); | 560 Dart_FileWriteCallback file_write = Isolate::file_write_callback(); |
| 458 Dart_FileCloseCallback file_close = Isolate::file_close_callback(); | 561 Dart_FileCloseCallback file_close = Isolate::file_close_callback(); |
| 459 if ((file_open == NULL) || (file_write == NULL) || (file_close == NULL)) { | 562 if ((file_open == NULL) || (file_write == NULL) || (file_close == NULL)) { |
| 460 return; | 563 return; |
| 461 } | 564 } |
| 462 | 565 |
| 463 FinishGlobalBlock(); | 566 Timeline::ReclaimAllBlocks(); |
| 464 | 567 |
| 465 JSONStream js; | 568 JSONStream js; |
| 466 TimelineEventFilter filter; | 569 TimelineEventFilter filter; |
| 467 PrintJSON(&js, &filter); | 570 PrintJSON(&js, &filter); |
| 468 | 571 |
| 469 intptr_t pid = OS::ProcessId(); | 572 intptr_t pid = OS::ProcessId(); |
| 470 char* filename = OS::SCreate(NULL, | 573 char* filename = OS::SCreate(NULL, |
| 471 "%s/dart-timeline-%" Pd ".json", directory, pid); | 574 "%s/dart-timeline-%" Pd ".json", directory, pid); |
| 472 void* file = (*file_open)(filename, true); | 575 void* file = (*file_open)(filename, true); |
| 473 if (file == NULL) { | 576 if (file == NULL) { |
| 474 OS::Print("Failed to write timeline file: %s\n", filename); | 577 OS::Print("Failed to write timeline file: %s\n", filename); |
| 475 free(filename); | 578 free(filename); |
| 476 return; | 579 return; |
| 477 } | 580 } |
| 478 free(filename); | 581 free(filename); |
| 479 (*file_write)(js.buffer()->buf(), js.buffer()->length(), file); | 582 (*file_write)(js.buffer()->buf(), js.buffer()->length(), file); |
| 480 (*file_close)(file); | 583 (*file_close)(file); |
| 481 } | 584 } |
| 482 | 585 |
| 483 | 586 |
| 484 void TimelineEventRecorder::FinishGlobalBlock() { | 587 void TimelineEventRecorder::ReclaimGlobalBlock() { |
| 485 MutexLocker ml(&lock_); | 588 MutexLocker ml(&lock_); |
| 486 if (global_block_ != NULL) { | 589 if (global_block_ != NULL) { |
| 487 global_block_->Finish(); | 590 global_block_->Finish(); |
| 488 global_block_ = NULL; | 591 global_block_ = NULL; |
| 489 } | 592 } |
| 490 } | 593 } |
| 491 | 594 |
| 492 | 595 |
| 493 int64_t TimelineEventRecorder::GetNextAsyncId() { | 596 int64_t TimelineEventRecorder::GetNextAsyncId() { |
| 494 // TODO(johnmccutchan): Gracefully handle wrap around. | 597 // TODO(johnmccutchan): Gracefully handle wrap around. |
| (...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 863 | 966 |
| 864 | 967 |
| 865 TimelineEventBlock* TimelineEventBlockIterator::Next() { | 968 TimelineEventBlock* TimelineEventBlockIterator::Next() { |
| 866 ASSERT(current_ != NULL); | 969 ASSERT(current_ != NULL); |
| 867 TimelineEventBlock* r = current_; | 970 TimelineEventBlock* r = current_; |
| 868 current_ = current_->next(); | 971 current_ = current_->next(); |
| 869 return r; | 972 return r; |
| 870 } | 973 } |
| 871 | 974 |
| 872 } // namespace dart | 975 } // namespace dart |
| OLD | NEW |