| 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 ReclaimBlocksForIsolate(Isolate::Current()); |
| 140 } |
| 141 |
| 142 |
| 143 class ReclaimBlocksIsolateVisitor : public IsolateVisitor { |
| 144 public: |
| 145 ReclaimBlocksIsolateVisitor() {} |
| 146 |
| 147 virtual void VisitIsolate(Isolate* isolate) { |
| 148 Timeline::ReclaimBlocksForIsolate(isolate); |
| 149 } |
| 150 |
| 151 private: |
| 152 }; |
| 153 |
| 154 |
| 155 void Timeline::ReclaimAllBlocks() { |
| 156 if (recorder() == NULL) { |
| 157 return; |
| 158 } |
| 159 // Reclaim all blocks cached for all isolates. |
| 160 ReclaimBlocksIsolateVisitor visitor; |
| 161 Isolate::VisitIsolates(&visitor); |
| 162 // Reclaim the global VM block. |
| 163 recorder()->ReclaimGlobalBlock(); |
| 164 } |
| 165 |
| 166 |
| 167 void Timeline::ReclaimBlocksForIsolate(Isolate* isolate) { |
| 168 if (recorder() == NULL) { |
| 169 return; |
| 170 } |
| 171 ASSERT(isolate != NULL); |
| 172 isolate->ReclaimTimelineBlocks(); |
| 173 } |
| 174 |
| 175 |
| 80 TimelineEventRecorder* Timeline::recorder_ = NULL; | 176 TimelineEventRecorder* Timeline::recorder_ = NULL; |
| 81 TimelineStream* Timeline::vm_stream_ = NULL; | 177 TimelineStream* Timeline::vm_stream_ = NULL; |
| 82 | 178 |
| 83 #define ISOLATE_TIMELINE_STREAM_DEFINE_FLAG(name, enabled_by_default) \ | 179 #define ISOLATE_TIMELINE_STREAM_DEFINE_FLAG(name, enabled_by_default) \ |
| 84 bool Timeline::stream_##name##_enabled_ = false; | 180 bool Timeline::stream_##name##_enabled_ = false; |
| 85 ISOLATE_TIMELINE_STREAM_LIST(ISOLATE_TIMELINE_STREAM_DEFINE_FLAG) | 181 ISOLATE_TIMELINE_STREAM_LIST(ISOLATE_TIMELINE_STREAM_DEFINE_FLAG) |
| 86 #undef ISOLATE_TIMELINE_STREAM_DEFINE_FLAG | 182 #undef ISOLATE_TIMELINE_STREAM_DEFINE_FLAG |
| 87 | 183 |
| 88 TimelineEvent::TimelineEvent() | 184 TimelineEvent::TimelineEvent() |
| 89 : timestamp0_(0), | 185 : timestamp0_(0), |
| (...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 387 : global_block_(NULL), | 483 : global_block_(NULL), |
| 388 async_id_(0) { | 484 async_id_(0) { |
| 389 } | 485 } |
| 390 | 486 |
| 391 | 487 |
| 392 void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const { | 488 void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const { |
| 393 } | 489 } |
| 394 | 490 |
| 395 | 491 |
| 396 TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() { | 492 TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() { |
| 397 // Grab the thread's timeline event block. | 493 // Grab the current thread. |
| 398 Thread* thread = Thread::Current(); | 494 Thread* thread = Thread::Current(); |
| 399 ASSERT(thread != NULL); | 495 ASSERT(thread != NULL); |
| 496 // We are accessing the thread's timeline block- so take the lock. |
| 497 MutexLocker ml(thread->timeline_block_lock()); |
| 400 | 498 |
| 401 if (thread->isolate() == NULL) { | 499 if (thread->isolate() == NULL) { |
| 402 // Non-isolate thread case. This should be infrequent. | 500 // Non-isolate thread case. This should be infrequent. |
| 403 return GlobalBlockStartEvent(); | 501 return GlobalBlockStartEvent(); |
| 404 } | 502 } |
| 405 | 503 |
| 406 TimelineEventBlock* thread_block = thread->timeline_block(); | 504 TimelineEventBlock* thread_block = thread->timeline_block(); |
| 407 | 505 |
| 408 if ((thread_block != NULL) && thread_block->IsFull()) { | 506 if ((thread_block != NULL) && thread_block->IsFull()) { |
| 409 MutexLocker ml(&lock_); | 507 MutexLocker ml(&lock_); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 453 | 551 |
| 454 | 552 |
| 455 void TimelineEventRecorder::WriteTo(const char* directory) { | 553 void TimelineEventRecorder::WriteTo(const char* directory) { |
| 456 Dart_FileOpenCallback file_open = Isolate::file_open_callback(); | 554 Dart_FileOpenCallback file_open = Isolate::file_open_callback(); |
| 457 Dart_FileWriteCallback file_write = Isolate::file_write_callback(); | 555 Dart_FileWriteCallback file_write = Isolate::file_write_callback(); |
| 458 Dart_FileCloseCallback file_close = Isolate::file_close_callback(); | 556 Dart_FileCloseCallback file_close = Isolate::file_close_callback(); |
| 459 if ((file_open == NULL) || (file_write == NULL) || (file_close == NULL)) { | 557 if ((file_open == NULL) || (file_write == NULL) || (file_close == NULL)) { |
| 460 return; | 558 return; |
| 461 } | 559 } |
| 462 | 560 |
| 463 FinishGlobalBlock(); | 561 Timeline::ReclaimAllBlocks(); |
| 464 | 562 |
| 465 JSONStream js; | 563 JSONStream js; |
| 466 TimelineEventFilter filter; | 564 TimelineEventFilter filter; |
| 467 PrintJSON(&js, &filter); | 565 PrintJSON(&js, &filter); |
| 468 | 566 |
| 469 intptr_t pid = OS::ProcessId(); | 567 intptr_t pid = OS::ProcessId(); |
| 470 char* filename = OS::SCreate(NULL, | 568 char* filename = OS::SCreate(NULL, |
| 471 "%s/dart-timeline-%" Pd ".json", directory, pid); | 569 "%s/dart-timeline-%" Pd ".json", directory, pid); |
| 472 void* file = (*file_open)(filename, true); | 570 void* file = (*file_open)(filename, true); |
| 473 if (file == NULL) { | 571 if (file == NULL) { |
| 474 OS::Print("Failed to write timeline file: %s\n", filename); | 572 OS::Print("Failed to write timeline file: %s\n", filename); |
| 475 free(filename); | 573 free(filename); |
| 476 return; | 574 return; |
| 477 } | 575 } |
| 478 free(filename); | 576 free(filename); |
| 479 (*file_write)(js.buffer()->buf(), js.buffer()->length(), file); | 577 (*file_write)(js.buffer()->buf(), js.buffer()->length(), file); |
| 480 (*file_close)(file); | 578 (*file_close)(file); |
| 481 } | 579 } |
| 482 | 580 |
| 483 | 581 |
| 484 void TimelineEventRecorder::FinishGlobalBlock() { | 582 void TimelineEventRecorder::ReclaimGlobalBlock() { |
| 485 MutexLocker ml(&lock_); | 583 MutexLocker ml(&lock_); |
| 486 if (global_block_ != NULL) { | 584 if (global_block_ != NULL) { |
| 487 global_block_->Finish(); | 585 global_block_->Finish(); |
| 488 global_block_ = NULL; | 586 global_block_ = NULL; |
| 489 } | 587 } |
| 490 } | 588 } |
| 491 | 589 |
| 492 | 590 |
| 493 int64_t TimelineEventRecorder::GetNextAsyncId() { | 591 int64_t TimelineEventRecorder::GetNextAsyncId() { |
| 494 // TODO(johnmccutchan): Gracefully handle wrap around. | 592 // TODO(johnmccutchan): Gracefully handle wrap around. |
| 495 uint32_t next = static_cast<uint32_t>( | 593 uint32_t next = static_cast<uint32_t>( |
| 496 AtomicOperations::FetchAndIncrement(&async_id_)); | 594 AtomicOperations::FetchAndIncrement(&async_id_)); |
| 497 return static_cast<int64_t>(next); | 595 return static_cast<int64_t>(next); |
| 498 } | 596 } |
| 499 | 597 |
| 500 | 598 |
| 599 void TimelineEventRecorder::FinishBlock(TimelineEventBlock* block) { |
| 600 if (block == NULL) { |
| 601 return; |
| 602 } |
| 603 MutexLocker ml(&lock_); |
| 604 block->Finish(); |
| 605 } |
| 606 |
| 607 |
| 501 TimelineEventBlock* TimelineEventRecorder::GetNewBlock() { | 608 TimelineEventBlock* TimelineEventRecorder::GetNewBlock() { |
| 502 MutexLocker ml(&lock_); | 609 MutexLocker ml(&lock_); |
| 503 return GetNewBlockLocked(Isolate::Current()); | 610 return GetNewBlockLocked(Isolate::Current()); |
| 504 } | 611 } |
| 505 | 612 |
| 506 | 613 |
| 507 TimelineEventRingRecorder::TimelineEventRingRecorder(intptr_t capacity) | 614 TimelineEventRingRecorder::TimelineEventRingRecorder(intptr_t capacity) |
| 508 : blocks_(NULL), | 615 : blocks_(NULL), |
| 509 capacity_(capacity), | 616 capacity_(capacity), |
| 510 num_blocks_(0), | 617 num_blocks_(0), |
| (...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 737 head_ = NULL; | 844 head_ = NULL; |
| 738 block_index_ = 0; | 845 block_index_ = 0; |
| 739 Thread* thread = Thread::Current(); | 846 Thread* thread = Thread::Current(); |
| 740 thread->set_timeline_block(NULL); | 847 thread->set_timeline_block(NULL); |
| 741 } | 848 } |
| 742 | 849 |
| 743 | 850 |
| 744 TimelineEventBlock::TimelineEventBlock(intptr_t block_index) | 851 TimelineEventBlock::TimelineEventBlock(intptr_t block_index) |
| 745 : next_(NULL), | 852 : next_(NULL), |
| 746 length_(0), | 853 length_(0), |
| 747 block_index_(block_index) { | 854 block_index_(block_index), |
| 855 isolate_(NULL), |
| 856 in_use_(false) { |
| 748 } | 857 } |
| 749 | 858 |
| 750 | 859 |
| 751 TimelineEventBlock::~TimelineEventBlock() { | 860 TimelineEventBlock::~TimelineEventBlock() { |
| 752 Reset(); | 861 Reset(); |
| 753 } | 862 } |
| 754 | 863 |
| 755 | 864 |
| 756 TimelineEvent* TimelineEventBlock::StartEvent() { | 865 TimelineEvent* TimelineEventBlock::StartEvent() { |
| 757 ASSERT(!IsFull()); | 866 ASSERT(!IsFull()); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 801 } | 910 } |
| 802 | 911 |
| 803 | 912 |
| 804 void TimelineEventBlock::Reset() { | 913 void TimelineEventBlock::Reset() { |
| 805 for (intptr_t i = 0; i < kBlockSize; i++) { | 914 for (intptr_t i = 0; i < kBlockSize; i++) { |
| 806 // Clear any extra data. | 915 // Clear any extra data. |
| 807 events_[i].Reset(); | 916 events_[i].Reset(); |
| 808 } | 917 } |
| 809 length_ = 0; | 918 length_ = 0; |
| 810 isolate_ = NULL; | 919 isolate_ = NULL; |
| 811 open_ = false; | 920 in_use_ = false; |
| 812 } | 921 } |
| 813 | 922 |
| 814 | 923 |
| 815 void TimelineEventBlock::Open(Isolate* isolate) { | 924 void TimelineEventBlock::Open(Isolate* isolate) { |
| 816 isolate_ = isolate; | 925 isolate_ = isolate; |
| 817 open_ = true; | 926 in_use_ = true; |
| 818 } | 927 } |
| 819 | 928 |
| 820 | 929 |
| 821 void TimelineEventBlock::Finish() { | 930 void TimelineEventBlock::Finish() { |
| 822 if (FLAG_trace_timeline) { | 931 if (FLAG_trace_timeline) { |
| 823 OS::Print("Finish block %p\n", this); | 932 OS::Print("Finish block %p\n", this); |
| 824 } | 933 } |
| 825 open_ = false; | 934 in_use_ = false; |
| 826 } | 935 } |
| 827 | 936 |
| 828 | 937 |
| 829 TimelineEventBlockIterator::TimelineEventBlockIterator( | 938 TimelineEventBlockIterator::TimelineEventBlockIterator( |
| 830 TimelineEventRecorder* recorder) | 939 TimelineEventRecorder* recorder) |
| 831 : current_(NULL), | 940 : current_(NULL), |
| 832 recorder_(NULL) { | 941 recorder_(NULL) { |
| 833 Reset(recorder); | 942 Reset(recorder); |
| 834 } | 943 } |
| 835 | 944 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 863 | 972 |
| 864 | 973 |
| 865 TimelineEventBlock* TimelineEventBlockIterator::Next() { | 974 TimelineEventBlock* TimelineEventBlockIterator::Next() { |
| 866 ASSERT(current_ != NULL); | 975 ASSERT(current_ != NULL); |
| 867 TimelineEventBlock* r = current_; | 976 TimelineEventBlock* r = current_; |
| 868 current_ = current_->next(); | 977 current_ = current_->next(); |
| 869 return r; | 978 return r; |
| 870 } | 979 } |
| 871 | 980 |
| 872 } // namespace dart | 981 } // namespace dart |
| OLD | NEW |