| 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 "vm/timeline_analysis.h" | 5 #include "vm/timeline_analysis.h" |
| 6 | 6 |
| 7 #include "vm/flags.h" | 7 #include "vm/flags.h" |
| 8 #include "vm/isolate.h" | 8 #include "vm/isolate.h" |
| 9 #include "vm/log.h" | 9 #include "vm/log.h" |
| 10 #include "vm/os_thread.h" | 10 #include "vm/os_thread.h" |
| 11 | 11 |
| 12 namespace dart { | 12 namespace dart { |
| 13 | 13 |
| 14 #ifndef PRODUCT | 14 #ifndef PRODUCT |
| 15 | 15 |
| 16 DECLARE_FLAG(bool, trace_timeline_analysis); | 16 DECLARE_FLAG(bool, trace_timeline_analysis); |
| 17 DECLARE_FLAG(bool, timing); | 17 DECLARE_FLAG(bool, timing); |
| 18 | 18 |
| 19 TimelineAnalysisThread::TimelineAnalysisThread(ThreadId id) : id_(id) {} | 19 TimelineAnalysisThread::TimelineAnalysisThread(ThreadId id) : id_(id) {} |
| 20 | 20 |
| 21 | |
| 22 TimelineAnalysisThread::~TimelineAnalysisThread() {} | 21 TimelineAnalysisThread::~TimelineAnalysisThread() {} |
| 23 | 22 |
| 24 | |
| 25 void TimelineAnalysisThread::AddBlock(TimelineEventBlock* block) { | 23 void TimelineAnalysisThread::AddBlock(TimelineEventBlock* block) { |
| 26 blocks_.Add(block); | 24 blocks_.Add(block); |
| 27 } | 25 } |
| 28 | 26 |
| 29 | |
| 30 static int CompareBlocksLowerTimeBound(TimelineEventBlock* const* a, | 27 static int CompareBlocksLowerTimeBound(TimelineEventBlock* const* a, |
| 31 TimelineEventBlock* const* b) { | 28 TimelineEventBlock* const* b) { |
| 32 ASSERT(a != NULL); | 29 ASSERT(a != NULL); |
| 33 ASSERT(*a != NULL); | 30 ASSERT(*a != NULL); |
| 34 ASSERT(b != NULL); | 31 ASSERT(b != NULL); |
| 35 ASSERT(*b != NULL); | 32 ASSERT(*b != NULL); |
| 36 return (*a)->LowerTimeBound() - (*b)->LowerTimeBound(); | 33 return (*a)->LowerTimeBound() - (*b)->LowerTimeBound(); |
| 37 } | 34 } |
| 38 | 35 |
| 39 | |
| 40 void TimelineAnalysisThread::Finalize() { | 36 void TimelineAnalysisThread::Finalize() { |
| 41 blocks_.Sort(CompareBlocksLowerTimeBound); | 37 blocks_.Sort(CompareBlocksLowerTimeBound); |
| 42 if (FLAG_trace_timeline_analysis) { | 38 if (FLAG_trace_timeline_analysis) { |
| 43 THR_Print("Thread %" Px " has %" Pd " blocks\n", | 39 THR_Print("Thread %" Px " has %" Pd " blocks\n", |
| 44 OSThread::ThreadIdToIntPtr(id_), blocks_.length()); | 40 OSThread::ThreadIdToIntPtr(id_), blocks_.length()); |
| 45 } | 41 } |
| 46 } | 42 } |
| 47 | 43 |
| 48 | |
| 49 TimelineAnalysisThreadEventIterator::TimelineAnalysisThreadEventIterator( | 44 TimelineAnalysisThreadEventIterator::TimelineAnalysisThreadEventIterator( |
| 50 TimelineAnalysisThread* thread) { | 45 TimelineAnalysisThread* thread) { |
| 51 Reset(thread); | 46 Reset(thread); |
| 52 } | 47 } |
| 53 | 48 |
| 54 | |
| 55 TimelineAnalysisThreadEventIterator::~TimelineAnalysisThreadEventIterator() { | 49 TimelineAnalysisThreadEventIterator::~TimelineAnalysisThreadEventIterator() { |
| 56 Reset(NULL); | 50 Reset(NULL); |
| 57 } | 51 } |
| 58 | 52 |
| 59 | |
| 60 void TimelineAnalysisThreadEventIterator::Reset( | 53 void TimelineAnalysisThreadEventIterator::Reset( |
| 61 TimelineAnalysisThread* thread) { | 54 TimelineAnalysisThread* thread) { |
| 62 current_ = NULL; | 55 current_ = NULL; |
| 63 thread_ = thread; | 56 thread_ = thread; |
| 64 block_cursor_ = 0; | 57 block_cursor_ = 0; |
| 65 event_cursor_ = 0; | 58 event_cursor_ = 0; |
| 66 if (thread_ == NULL) { | 59 if (thread_ == NULL) { |
| 67 return; | 60 return; |
| 68 } | 61 } |
| 69 if (thread_->NumBlocks() == 0) { | 62 if (thread_->NumBlocks() == 0) { |
| 70 return; | 63 return; |
| 71 } | 64 } |
| 72 TimelineEventBlock* block = thread_->At(block_cursor_); | 65 TimelineEventBlock* block = thread_->At(block_cursor_); |
| 73 ASSERT(!block->IsEmpty()); | 66 ASSERT(!block->IsEmpty()); |
| 74 current_ = block->At(event_cursor_++); | 67 current_ = block->At(event_cursor_++); |
| 75 } | 68 } |
| 76 | 69 |
| 77 | |
| 78 bool TimelineAnalysisThreadEventIterator::HasNext() const { | 70 bool TimelineAnalysisThreadEventIterator::HasNext() const { |
| 79 return current_ != NULL; | 71 return current_ != NULL; |
| 80 } | 72 } |
| 81 | 73 |
| 82 | |
| 83 TimelineEvent* TimelineAnalysisThreadEventIterator::Next() { | 74 TimelineEvent* TimelineAnalysisThreadEventIterator::Next() { |
| 84 ASSERT(current_ != NULL); | 75 ASSERT(current_ != NULL); |
| 85 TimelineEvent* r = current_; | 76 TimelineEvent* r = current_; |
| 86 current_ = NULL; | 77 current_ = NULL; |
| 87 | 78 |
| 88 TimelineEventBlock* block = thread_->At(block_cursor_); | 79 TimelineEventBlock* block = thread_->At(block_cursor_); |
| 89 if (event_cursor_ == block->length()) { | 80 if (event_cursor_ == block->length()) { |
| 90 // Reached the end of this block, move to the next. | 81 // Reached the end of this block, move to the next. |
| 91 block_cursor_++; | 82 block_cursor_++; |
| 92 if (block_cursor_ == thread_->NumBlocks()) { | 83 if (block_cursor_ == thread_->NumBlocks()) { |
| 93 // Exhausted our supply of blocks. | 84 // Exhausted our supply of blocks. |
| 94 return r; | 85 return r; |
| 95 } | 86 } |
| 96 // Grab next block. | 87 // Grab next block. |
| 97 block = thread_->At(block_cursor_); | 88 block = thread_->At(block_cursor_); |
| 98 event_cursor_ = 0; | 89 event_cursor_ = 0; |
| 99 ASSERT(!block->IsEmpty()); | 90 ASSERT(!block->IsEmpty()); |
| 100 } | 91 } |
| 101 current_ = block->At(event_cursor_++); | 92 current_ = block->At(event_cursor_++); |
| 102 return r; | 93 return r; |
| 103 } | 94 } |
| 104 | 95 |
| 105 | |
| 106 TimelineAnalysis::TimelineAnalysis(Zone* zone, | 96 TimelineAnalysis::TimelineAnalysis(Zone* zone, |
| 107 Isolate* isolate, | 97 Isolate* isolate, |
| 108 TimelineEventRecorder* recorder) | 98 TimelineEventRecorder* recorder) |
| 109 : zone_(zone), | 99 : zone_(zone), |
| 110 isolate_(isolate), | 100 isolate_(isolate), |
| 111 recorder_(recorder), | 101 recorder_(recorder), |
| 112 has_error_(false), | 102 has_error_(false), |
| 113 error_msg_(NULL) { | 103 error_msg_(NULL) { |
| 114 ASSERT(zone_ != NULL); | 104 ASSERT(zone_ != NULL); |
| 115 ASSERT(isolate_ != NULL); | 105 ASSERT(isolate_ != NULL); |
| 116 ASSERT(recorder_ != NULL); | 106 ASSERT(recorder_ != NULL); |
| 117 } | 107 } |
| 118 | 108 |
| 119 | |
| 120 TimelineAnalysis::~TimelineAnalysis() {} | 109 TimelineAnalysis::~TimelineAnalysis() {} |
| 121 | 110 |
| 122 | |
| 123 void TimelineAnalysis::BuildThreads() { | 111 void TimelineAnalysis::BuildThreads() { |
| 124 DiscoverThreads(); | 112 DiscoverThreads(); |
| 125 FinalizeThreads(); | 113 FinalizeThreads(); |
| 126 } | 114 } |
| 127 | 115 |
| 128 | |
| 129 TimelineAnalysisThread* TimelineAnalysis::GetThread(ThreadId tid) { | 116 TimelineAnalysisThread* TimelineAnalysis::GetThread(ThreadId tid) { |
| 130 // Linear lookup because we expect N (# of threads in an isolate) to be small. | 117 // Linear lookup because we expect N (# of threads in an isolate) to be small. |
| 131 for (intptr_t i = 0; i < threads_.length(); i++) { | 118 for (intptr_t i = 0; i < threads_.length(); i++) { |
| 132 TimelineAnalysisThread* thread = threads_.At(i); | 119 TimelineAnalysisThread* thread = threads_.At(i); |
| 133 ASSERT(thread != NULL); | 120 ASSERT(thread != NULL); |
| 134 if (thread->id() == tid) { | 121 if (thread->id() == tid) { |
| 135 return thread; | 122 return thread; |
| 136 } | 123 } |
| 137 } | 124 } |
| 138 return NULL; | 125 return NULL; |
| 139 } | 126 } |
| 140 | 127 |
| 141 | |
| 142 TimelineAnalysisThread* TimelineAnalysis::GetOrAddThread(ThreadId tid) { | 128 TimelineAnalysisThread* TimelineAnalysis::GetOrAddThread(ThreadId tid) { |
| 143 TimelineAnalysisThread* thread = GetThread(tid); | 129 TimelineAnalysisThread* thread = GetThread(tid); |
| 144 if (thread != NULL) { | 130 if (thread != NULL) { |
| 145 return thread; | 131 return thread; |
| 146 } | 132 } |
| 147 // New thread. | 133 // New thread. |
| 148 thread = new TimelineAnalysisThread(tid); | 134 thread = new TimelineAnalysisThread(tid); |
| 149 threads_.Add(thread); | 135 threads_.Add(thread); |
| 150 return thread; | 136 return thread; |
| 151 } | 137 } |
| 152 | 138 |
| 153 | |
| 154 void TimelineAnalysis::DiscoverThreads() { | 139 void TimelineAnalysis::DiscoverThreads() { |
| 155 TimelineEventBlockIterator it(recorder_); | 140 TimelineEventBlockIterator it(recorder_); |
| 156 while (it.HasNext()) { | 141 while (it.HasNext()) { |
| 157 TimelineEventBlock* block = it.Next(); | 142 TimelineEventBlock* block = it.Next(); |
| 158 ASSERT(block != NULL); | 143 ASSERT(block != NULL); |
| 159 if (block->IsEmpty()) { | 144 if (block->IsEmpty()) { |
| 160 // Skip empty blocks. | 145 // Skip empty blocks. |
| 161 continue; | 146 continue; |
| 162 } | 147 } |
| 163 if (!block->CheckBlock()) { | 148 if (!block->CheckBlock()) { |
| 164 if (FLAG_trace_timeline_analysis) { | 149 if (FLAG_trace_timeline_analysis) { |
| 165 THR_Print("DiscoverThreads block %" Pd | 150 THR_Print("DiscoverThreads block %" Pd |
| 166 " " | 151 " " |
| 167 "violates invariants.\n", | 152 "violates invariants.\n", |
| 168 block->block_index()); | 153 block->block_index()); |
| 169 } | 154 } |
| 170 SetError("Block %" Pd | 155 SetError("Block %" Pd |
| 171 " violates invariants. See " | 156 " violates invariants. See " |
| 172 "TimelineEventBlock::CheckBlock", | 157 "TimelineEventBlock::CheckBlock", |
| 173 block->block_index()); | 158 block->block_index()); |
| 174 return; | 159 return; |
| 175 } | 160 } |
| 176 TimelineAnalysisThread* thread = GetOrAddThread(block->thread_id()); | 161 TimelineAnalysisThread* thread = GetOrAddThread(block->thread_id()); |
| 177 ASSERT(thread != NULL); | 162 ASSERT(thread != NULL); |
| 178 thread->AddBlock(block); | 163 thread->AddBlock(block); |
| 179 } | 164 } |
| 180 } | 165 } |
| 181 | 166 |
| 182 | |
| 183 void TimelineAnalysis::FinalizeThreads() { | 167 void TimelineAnalysis::FinalizeThreads() { |
| 184 for (intptr_t i = 0; i < threads_.length(); i++) { | 168 for (intptr_t i = 0; i < threads_.length(); i++) { |
| 185 TimelineAnalysisThread* thread = threads_.At(i); | 169 TimelineAnalysisThread* thread = threads_.At(i); |
| 186 ASSERT(thread != NULL); | 170 ASSERT(thread != NULL); |
| 187 thread->Finalize(); | 171 thread->Finalize(); |
| 188 } | 172 } |
| 189 } | 173 } |
| 190 | 174 |
| 191 | |
| 192 void TimelineAnalysis::SetError(const char* format, ...) { | 175 void TimelineAnalysis::SetError(const char* format, ...) { |
| 193 ASSERT(!has_error_); | 176 ASSERT(!has_error_); |
| 194 ASSERT(error_msg_ == NULL); | 177 ASSERT(error_msg_ == NULL); |
| 195 has_error_ = true; | 178 has_error_ = true; |
| 196 va_list args; | 179 va_list args; |
| 197 va_start(args, format); | 180 va_start(args, format); |
| 198 error_msg_ = zone_->VPrint(format, args); | 181 error_msg_ = zone_->VPrint(format, args); |
| 199 ASSERT(error_msg_ != NULL); | 182 ASSERT(error_msg_ != NULL); |
| 200 if (FLAG_trace_timeline_analysis) { | 183 if (FLAG_trace_timeline_analysis) { |
| 201 OS::Print("TimelineAnalysis error = %s\n", error_msg_); | 184 OS::Print("TimelineAnalysis error = %s\n", error_msg_); |
| 202 } | 185 } |
| 203 } | 186 } |
| 204 | 187 |
| 205 | |
| 206 TimelineLabelPauseInfo::TimelineLabelPauseInfo(const char* name) | 188 TimelineLabelPauseInfo::TimelineLabelPauseInfo(const char* name) |
| 207 : name_(name), | 189 : name_(name), |
| 208 inclusive_micros_(0), | 190 inclusive_micros_(0), |
| 209 exclusive_micros_(0), | 191 exclusive_micros_(0), |
| 210 max_inclusive_micros_(0), | 192 max_inclusive_micros_(0), |
| 211 max_exclusive_micros_(0) { | 193 max_exclusive_micros_(0) { |
| 212 ASSERT(name_ != NULL); | 194 ASSERT(name_ != NULL); |
| 213 } | 195 } |
| 214 | 196 |
| 215 | |
| 216 void TimelineLabelPauseInfo::OnPush(int64_t micros, bool already_on_stack) { | 197 void TimelineLabelPauseInfo::OnPush(int64_t micros, bool already_on_stack) { |
| 217 UpdateInclusiveMicros(micros, already_on_stack); | 198 UpdateInclusiveMicros(micros, already_on_stack); |
| 218 } | 199 } |
| 219 | 200 |
| 220 | |
| 221 void TimelineLabelPauseInfo::OnPop(int64_t exclusive_micros) { | 201 void TimelineLabelPauseInfo::OnPop(int64_t exclusive_micros) { |
| 222 UpdateExclusiveMicros(exclusive_micros); | 202 UpdateExclusiveMicros(exclusive_micros); |
| 223 } | 203 } |
| 224 | 204 |
| 225 | |
| 226 void TimelineLabelPauseInfo::OnBeginPop(int64_t inclusive_micros, | 205 void TimelineLabelPauseInfo::OnBeginPop(int64_t inclusive_micros, |
| 227 int64_t exclusive_micros, | 206 int64_t exclusive_micros, |
| 228 bool already_on_stack) { | 207 bool already_on_stack) { |
| 229 UpdateInclusiveMicros(inclusive_micros, already_on_stack); | 208 UpdateInclusiveMicros(inclusive_micros, already_on_stack); |
| 230 UpdateExclusiveMicros(exclusive_micros); | 209 UpdateExclusiveMicros(exclusive_micros); |
| 231 } | 210 } |
| 232 | 211 |
| 233 | |
| 234 void TimelineLabelPauseInfo::UpdateInclusiveMicros(int64_t inclusive_micros, | 212 void TimelineLabelPauseInfo::UpdateInclusiveMicros(int64_t inclusive_micros, |
| 235 bool already_on_stack) { | 213 bool already_on_stack) { |
| 236 if (!already_on_stack) { | 214 if (!already_on_stack) { |
| 237 // Only adjust inclusive counts if we aren't already on the stack. | 215 // Only adjust inclusive counts if we aren't already on the stack. |
| 238 add_inclusive_micros(inclusive_micros); | 216 add_inclusive_micros(inclusive_micros); |
| 239 if (inclusive_micros > max_inclusive_micros_) { | 217 if (inclusive_micros > max_inclusive_micros_) { |
| 240 max_inclusive_micros_ = inclusive_micros; | 218 max_inclusive_micros_ = inclusive_micros; |
| 241 } | 219 } |
| 242 } | 220 } |
| 243 } | 221 } |
| 244 | 222 |
| 245 | |
| 246 void TimelineLabelPauseInfo::UpdateExclusiveMicros(int64_t exclusive_micros) { | 223 void TimelineLabelPauseInfo::UpdateExclusiveMicros(int64_t exclusive_micros) { |
| 247 add_exclusive_micros(exclusive_micros); | 224 add_exclusive_micros(exclusive_micros); |
| 248 if (exclusive_micros > max_exclusive_micros_) { | 225 if (exclusive_micros > max_exclusive_micros_) { |
| 249 max_exclusive_micros_ = exclusive_micros; | 226 max_exclusive_micros_ = exclusive_micros; |
| 250 } | 227 } |
| 251 } | 228 } |
| 252 | 229 |
| 253 | |
| 254 void TimelineLabelPauseInfo::Aggregate( | 230 void TimelineLabelPauseInfo::Aggregate( |
| 255 const TimelineLabelPauseInfo* thread_pause_info) { | 231 const TimelineLabelPauseInfo* thread_pause_info) { |
| 256 ASSERT(thread_pause_info != NULL); | 232 ASSERT(thread_pause_info != NULL); |
| 257 inclusive_micros_ += thread_pause_info->inclusive_micros_; | 233 inclusive_micros_ += thread_pause_info->inclusive_micros_; |
| 258 exclusive_micros_ += thread_pause_info->exclusive_micros_; | 234 exclusive_micros_ += thread_pause_info->exclusive_micros_; |
| 259 if (max_inclusive_micros_ < thread_pause_info->max_inclusive_micros_) { | 235 if (max_inclusive_micros_ < thread_pause_info->max_inclusive_micros_) { |
| 260 max_inclusive_micros_ = thread_pause_info->max_inclusive_micros_; | 236 max_inclusive_micros_ = thread_pause_info->max_inclusive_micros_; |
| 261 } | 237 } |
| 262 if (max_exclusive_micros_ < thread_pause_info->max_exclusive_micros_) { | 238 if (max_exclusive_micros_ < thread_pause_info->max_exclusive_micros_) { |
| 263 max_exclusive_micros_ = thread_pause_info->max_exclusive_micros_; | 239 max_exclusive_micros_ = thread_pause_info->max_exclusive_micros_; |
| 264 } | 240 } |
| 265 } | 241 } |
| 266 | 242 |
| 267 | |
| 268 TimelinePauses::TimelinePauses(Zone* zone, | 243 TimelinePauses::TimelinePauses(Zone* zone, |
| 269 Isolate* isolate, | 244 Isolate* isolate, |
| 270 TimelineEventRecorder* recorder) | 245 TimelineEventRecorder* recorder) |
| 271 : TimelineAnalysis(zone, isolate, recorder) {} | 246 : TimelineAnalysis(zone, isolate, recorder) {} |
| 272 | 247 |
| 273 | |
| 274 void TimelinePauses::Setup() { | 248 void TimelinePauses::Setup() { |
| 275 BuildThreads(); | 249 BuildThreads(); |
| 276 } | 250 } |
| 277 | 251 |
| 278 | |
| 279 void TimelinePauses::CalculatePauseTimesForThread(ThreadId tid) { | 252 void TimelinePauses::CalculatePauseTimesForThread(ThreadId tid) { |
| 280 if (has_error()) { | 253 if (has_error()) { |
| 281 return; | 254 return; |
| 282 } | 255 } |
| 283 TimelineAnalysisThread* thread = GetThread(tid); | 256 TimelineAnalysisThread* thread = GetThread(tid); |
| 284 if (thread == NULL) { | 257 if (thread == NULL) { |
| 285 SetError("Thread %" Px " does not exist.", OSThread::ThreadIdToIntPtr(tid)); | 258 SetError("Thread %" Px " does not exist.", OSThread::ThreadIdToIntPtr(tid)); |
| 286 return; | 259 return; |
| 287 } | 260 } |
| 288 ProcessThread(thread); | 261 ProcessThread(thread); |
| 289 } | 262 } |
| 290 | 263 |
| 291 | |
| 292 TimelineLabelPauseInfo* TimelinePauses::GetLabelPauseInfo( | 264 TimelineLabelPauseInfo* TimelinePauses::GetLabelPauseInfo( |
| 293 const char* name) const { | 265 const char* name) const { |
| 294 ASSERT(name != NULL); | 266 ASSERT(name != NULL); |
| 295 // Linear lookup because we expect N (# of labels in an isolate) to be small. | 267 // Linear lookup because we expect N (# of labels in an isolate) to be small. |
| 296 for (intptr_t i = 0; i < labels_.length(); i++) { | 268 for (intptr_t i = 0; i < labels_.length(); i++) { |
| 297 TimelineLabelPauseInfo* label = labels_.At(i); | 269 TimelineLabelPauseInfo* label = labels_.At(i); |
| 298 if (strcmp(label->name(), name) == 0) { | 270 if (strcmp(label->name(), name) == 0) { |
| 299 return label; | 271 return label; |
| 300 } | 272 } |
| 301 } | 273 } |
| 302 return NULL; | 274 return NULL; |
| 303 } | 275 } |
| 304 | 276 |
| 305 | |
| 306 int64_t TimelinePauses::InclusiveTime(const char* name) const { | 277 int64_t TimelinePauses::InclusiveTime(const char* name) const { |
| 307 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); | 278 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); |
| 308 ASSERT(pause_info != NULL); | 279 ASSERT(pause_info != NULL); |
| 309 return pause_info->inclusive_micros(); | 280 return pause_info->inclusive_micros(); |
| 310 } | 281 } |
| 311 | 282 |
| 312 | |
| 313 int64_t TimelinePauses::ExclusiveTime(const char* name) const { | 283 int64_t TimelinePauses::ExclusiveTime(const char* name) const { |
| 314 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); | 284 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); |
| 315 ASSERT(pause_info != NULL); | 285 ASSERT(pause_info != NULL); |
| 316 return pause_info->exclusive_micros(); | 286 return pause_info->exclusive_micros(); |
| 317 } | 287 } |
| 318 | 288 |
| 319 | |
| 320 int64_t TimelinePauses::MaxInclusiveTime(const char* name) const { | 289 int64_t TimelinePauses::MaxInclusiveTime(const char* name) const { |
| 321 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); | 290 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); |
| 322 ASSERT(pause_info != NULL); | 291 ASSERT(pause_info != NULL); |
| 323 return pause_info->max_inclusive_micros(); | 292 return pause_info->max_inclusive_micros(); |
| 324 } | 293 } |
| 325 | 294 |
| 326 | |
| 327 int64_t TimelinePauses::MaxExclusiveTime(const char* name) const { | 295 int64_t TimelinePauses::MaxExclusiveTime(const char* name) const { |
| 328 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); | 296 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); |
| 329 ASSERT(pause_info != NULL); | 297 ASSERT(pause_info != NULL); |
| 330 return pause_info->max_exclusive_micros(); | 298 return pause_info->max_exclusive_micros(); |
| 331 } | 299 } |
| 332 | 300 |
| 333 | |
| 334 void TimelinePauses::ProcessThread(TimelineAnalysisThread* thread) { | 301 void TimelinePauses::ProcessThread(TimelineAnalysisThread* thread) { |
| 335 ASSERT(thread != NULL); | 302 ASSERT(thread != NULL); |
| 336 stack_.Clear(); | 303 stack_.Clear(); |
| 337 labels_.Clear(); | 304 labels_.Clear(); |
| 338 | 305 |
| 339 TimelineAnalysisThreadEventIterator it(thread); | 306 TimelineAnalysisThreadEventIterator it(thread); |
| 340 if (FLAG_trace_timeline_analysis) { | 307 if (FLAG_trace_timeline_analysis) { |
| 341 THR_Print(">>> TimelinePauses::ProcessThread %" Px "\n", | 308 THR_Print(">>> TimelinePauses::ProcessThread %" Px "\n", |
| 342 OSThread::ThreadIdToIntPtr(thread->id())); | 309 OSThread::ThreadIdToIntPtr(thread->id())); |
| 343 } | 310 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 372 } | 339 } |
| 373 } | 340 } |
| 374 // Pop remaining duration stack. | 341 // Pop remaining duration stack. |
| 375 PopFinishedDurations(kMaxInt64); | 342 PopFinishedDurations(kMaxInt64); |
| 376 if (FLAG_trace_timeline_analysis) { | 343 if (FLAG_trace_timeline_analysis) { |
| 377 THR_Print("<<< TimelinePauses::ProcessThread %" Px " had %" Pd " events\n", | 344 THR_Print("<<< TimelinePauses::ProcessThread %" Px " had %" Pd " events\n", |
| 378 OSThread::ThreadIdToIntPtr(thread->id()), event_count); | 345 OSThread::ThreadIdToIntPtr(thread->id()), event_count); |
| 379 } | 346 } |
| 380 } | 347 } |
| 381 | 348 |
| 382 | |
| 383 // Verify that |event| is contained within all parent events on the stack. | 349 // Verify that |event| is contained within all parent events on the stack. |
| 384 bool TimelinePauses::CheckStack(TimelineEvent* event) { | 350 bool TimelinePauses::CheckStack(TimelineEvent* event) { |
| 385 ASSERT(event != NULL); | 351 ASSERT(event != NULL); |
| 386 for (intptr_t i = 0; i < stack_.length(); i++) { | 352 for (intptr_t i = 0; i < stack_.length(); i++) { |
| 387 const StackItem& slot = stack_.At(i); | 353 const StackItem& slot = stack_.At(i); |
| 388 if (slot.event->IsDuration()) { | 354 if (slot.event->IsDuration()) { |
| 389 if (!slot.event->DurationContains(event)) { | 355 if (!slot.event->DurationContains(event)) { |
| 390 return false; | 356 return false; |
| 391 } | 357 } |
| 392 } else { | 358 } else { |
| 393 ASSERT(slot.event->IsBegin()); | 359 ASSERT(slot.event->IsBegin()); |
| 394 if (slot.event->TimeOrigin() > event->TimeOrigin()) { | 360 if (slot.event->TimeOrigin() > event->TimeOrigin()) { |
| 395 return false; | 361 return false; |
| 396 } | 362 } |
| 397 } | 363 } |
| 398 } | 364 } |
| 399 return true; | 365 return true; |
| 400 } | 366 } |
| 401 | 367 |
| 402 | |
| 403 void TimelinePauses::PopFinishedDurations(int64_t start) { | 368 void TimelinePauses::PopFinishedDurations(int64_t start) { |
| 404 while (stack_.length() > 0) { | 369 while (stack_.length() > 0) { |
| 405 const StackItem& top = stack_.Last(); | 370 const StackItem& top = stack_.Last(); |
| 406 if (top.event->IsDuration() && top.event->DurationFinishedBefore(start)) { | 371 if (top.event->IsDuration() && top.event->DurationFinishedBefore(start)) { |
| 407 top.pause_info->OnPop(top.exclusive_micros); | 372 top.pause_info->OnPop(top.exclusive_micros); |
| 408 // Top of stack completes before |start|. | 373 // Top of stack completes before |start|. |
| 409 stack_.RemoveLast(); | 374 stack_.RemoveLast(); |
| 410 if (FLAG_trace_timeline_analysis) { | 375 if (FLAG_trace_timeline_analysis) { |
| 411 THR_Print("Popping %s (%" Pd64 " <= %" Pd64 ")\n", top.event->label(), | 376 THR_Print("Popping %s (%" Pd64 " <= %" Pd64 ")\n", top.event->label(), |
| 412 top.event->TimeEnd(), start); | 377 top.event->TimeEnd(), start); |
| 413 } | 378 } |
| 414 } else { | 379 } else { |
| 415 return; | 380 return; |
| 416 } | 381 } |
| 417 } | 382 } |
| 418 } | 383 } |
| 419 | 384 |
| 420 | |
| 421 void TimelinePauses::PopBegin(const char* label, int64_t end) { | 385 void TimelinePauses::PopBegin(const char* label, int64_t end) { |
| 422 if (stack_.length() == 0) { | 386 if (stack_.length() == 0) { |
| 423 SetError("PopBegin(%s, ...) called with empty stack.", label); | 387 SetError("PopBegin(%s, ...) called with empty stack.", label); |
| 424 return; | 388 return; |
| 425 } | 389 } |
| 426 ASSERT(stack_.length() > 0); | 390 ASSERT(stack_.length() > 0); |
| 427 const StackItem& top = stack_.Last(); | 391 const StackItem& top = stack_.Last(); |
| 428 const char* top_label = top.event->label(); | 392 const char* top_label = top.event->label(); |
| 429 const bool top_is_begin = top.event->IsBegin(); | 393 const bool top_is_begin = top.event->IsBegin(); |
| 430 const int64_t start = top.event->TimeOrigin(); | 394 const int64_t start = top.event->TimeOrigin(); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 453 stack_.RemoveLast(); | 417 stack_.RemoveLast(); |
| 454 top.pause_info->OnBeginPop(duration, exclusive_micros, | 418 top.pause_info->OnBeginPop(duration, exclusive_micros, |
| 455 IsLabelOnStack(top_label)); | 419 IsLabelOnStack(top_label)); |
| 456 if (StackDepth() > 0) { | 420 if (StackDepth() > 0) { |
| 457 StackItem& top = GetStackTop(); | 421 StackItem& top = GetStackTop(); |
| 458 // |top| is under the popped |event|'s shadow, adjust the exclusive micros. | 422 // |top| is under the popped |event|'s shadow, adjust the exclusive micros. |
| 459 top.exclusive_micros -= duration; | 423 top.exclusive_micros -= duration; |
| 460 } | 424 } |
| 461 } | 425 } |
| 462 | 426 |
| 463 | |
| 464 void TimelinePauses::Push(TimelineEvent* event) { | 427 void TimelinePauses::Push(TimelineEvent* event) { |
| 465 TimelineLabelPauseInfo* pause_info = GetOrAddLabelPauseInfo(event->label()); | 428 TimelineLabelPauseInfo* pause_info = GetOrAddLabelPauseInfo(event->label()); |
| 466 ASSERT(pause_info != NULL); | 429 ASSERT(pause_info != NULL); |
| 467 // |pause_info| will be running for |event->TimeDuration()|. | 430 // |pause_info| will be running for |event->TimeDuration()|. |
| 468 if (FLAG_trace_timeline_analysis) { | 431 if (FLAG_trace_timeline_analysis) { |
| 469 THR_Print("Pushing %s %" Pd64 " us\n", pause_info->name(), | 432 THR_Print("Pushing %s %" Pd64 " us\n", pause_info->name(), |
| 470 event->TimeDuration()); | 433 event->TimeDuration()); |
| 471 } | 434 } |
| 472 if (event->IsDuration()) { | 435 if (event->IsDuration()) { |
| 473 pause_info->OnPush(event->TimeDuration(), IsLabelOnStack(event->label())); | 436 pause_info->OnPush(event->TimeDuration(), IsLabelOnStack(event->label())); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 487 pause_info->OnPush(0, IsLabelOnStack(event->label())); | 450 pause_info->OnPush(0, IsLabelOnStack(event->label())); |
| 488 // Push onto the stack. | 451 // Push onto the stack. |
| 489 StackItem item; | 452 StackItem item; |
| 490 item.event = event; | 453 item.event = event; |
| 491 item.pause_info = pause_info; | 454 item.pause_info = pause_info; |
| 492 item.exclusive_micros = 0; | 455 item.exclusive_micros = 0; |
| 493 stack_.Add(item); | 456 stack_.Add(item); |
| 494 } | 457 } |
| 495 } | 458 } |
| 496 | 459 |
| 497 | |
| 498 bool TimelinePauses::IsLabelOnStack(const char* label) const { | 460 bool TimelinePauses::IsLabelOnStack(const char* label) const { |
| 499 ASSERT(label != NULL); | 461 ASSERT(label != NULL); |
| 500 for (intptr_t i = 0; i < stack_.length(); i++) { | 462 for (intptr_t i = 0; i < stack_.length(); i++) { |
| 501 const StackItem& slot = stack_.At(i); | 463 const StackItem& slot = stack_.At(i); |
| 502 if (strcmp(slot.event->label(), label) == 0) { | 464 if (strcmp(slot.event->label(), label) == 0) { |
| 503 return true; | 465 return true; |
| 504 } | 466 } |
| 505 } | 467 } |
| 506 return false; | 468 return false; |
| 507 } | 469 } |
| 508 | 470 |
| 509 | |
| 510 intptr_t TimelinePauses::StackDepth() const { | 471 intptr_t TimelinePauses::StackDepth() const { |
| 511 return stack_.length(); | 472 return stack_.length(); |
| 512 } | 473 } |
| 513 | 474 |
| 514 | |
| 515 TimelinePauses::StackItem& TimelinePauses::GetStackTop() { | 475 TimelinePauses::StackItem& TimelinePauses::GetStackTop() { |
| 516 ASSERT(stack_.length() > 0); | 476 ASSERT(stack_.length() > 0); |
| 517 return stack_.Last(); | 477 return stack_.Last(); |
| 518 } | 478 } |
| 519 | 479 |
| 520 | |
| 521 TimelineLabelPauseInfo* TimelinePauses::GetOrAddLabelPauseInfo( | 480 TimelineLabelPauseInfo* TimelinePauses::GetOrAddLabelPauseInfo( |
| 522 const char* name) { | 481 const char* name) { |
| 523 ASSERT(name != NULL); | 482 ASSERT(name != NULL); |
| 524 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); | 483 TimelineLabelPauseInfo* pause_info = GetLabelPauseInfo(name); |
| 525 if (pause_info != NULL) { | 484 if (pause_info != NULL) { |
| 526 return pause_info; | 485 return pause_info; |
| 527 } | 486 } |
| 528 // New label. | 487 // New label. |
| 529 pause_info = new TimelineLabelPauseInfo(name); | 488 pause_info = new TimelineLabelPauseInfo(name); |
| 530 labels_.Add(pause_info); | 489 labels_.Add(pause_info); |
| 531 return pause_info; | 490 return pause_info; |
| 532 } | 491 } |
| 533 | 492 |
| 534 | |
| 535 TimelinePauseTrace::TimelinePauseTrace() {} | 493 TimelinePauseTrace::TimelinePauseTrace() {} |
| 536 | 494 |
| 537 | |
| 538 TimelinePauseTrace::~TimelinePauseTrace() {} | 495 TimelinePauseTrace::~TimelinePauseTrace() {} |
| 539 | 496 |
| 540 | |
| 541 void TimelinePauseTrace::Print() { | 497 void TimelinePauseTrace::Print() { |
| 542 Thread* thread = Thread::Current(); | 498 Thread* thread = Thread::Current(); |
| 543 ASSERT(thread != NULL); | 499 ASSERT(thread != NULL); |
| 544 Isolate* isolate = thread->isolate(); | 500 Isolate* isolate = thread->isolate(); |
| 545 ASSERT(isolate != NULL); | 501 ASSERT(isolate != NULL); |
| 546 Zone* zone = thread->zone(); | 502 Zone* zone = thread->zone(); |
| 547 ASSERT(zone != NULL); | 503 ASSERT(zone != NULL); |
| 548 TimelineEventRecorder* recorder = Timeline::recorder(); | 504 TimelineEventRecorder* recorder = Timeline::recorder(); |
| 549 ASSERT(recorder != NULL); | 505 ASSERT(recorder != NULL); |
| 550 TimelinePauses pauses(zone, isolate, recorder); | 506 TimelinePauses pauses(zone, isolate, recorder); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 569 } | 525 } |
| 570 THR_Print("Totals:\n"); | 526 THR_Print("Totals:\n"); |
| 571 for (intptr_t i = 0; i < isolate_labels_.length(); i++) { | 527 for (intptr_t i = 0; i < isolate_labels_.length(); i++) { |
| 572 TimelineLabelPauseInfo* pause_info = isolate_labels_.At(i); | 528 TimelineLabelPauseInfo* pause_info = isolate_labels_.At(i); |
| 573 ASSERT(pause_info != NULL); | 529 ASSERT(pause_info != NULL); |
| 574 PrintPauseInfo(pause_info); | 530 PrintPauseInfo(pause_info); |
| 575 } | 531 } |
| 576 THR_Print("\n"); | 532 THR_Print("\n"); |
| 577 } | 533 } |
| 578 | 534 |
| 579 | |
| 580 TimelineLabelPauseInfo* TimelinePauseTrace::GetOrAddLabelPauseInfo( | 535 TimelineLabelPauseInfo* TimelinePauseTrace::GetOrAddLabelPauseInfo( |
| 581 const char* name) { | 536 const char* name) { |
| 582 ASSERT(name != NULL); | 537 ASSERT(name != NULL); |
| 583 // Linear lookup because we expect N (# of labels in an isolate) to be small. | 538 // Linear lookup because we expect N (# of labels in an isolate) to be small. |
| 584 for (intptr_t i = 0; i < isolate_labels_.length(); i++) { | 539 for (intptr_t i = 0; i < isolate_labels_.length(); i++) { |
| 585 TimelineLabelPauseInfo* label = isolate_labels_.At(i); | 540 TimelineLabelPauseInfo* label = isolate_labels_.At(i); |
| 586 if (strcmp(label->name(), name) == 0) { | 541 if (strcmp(label->name(), name) == 0) { |
| 587 return label; | 542 return label; |
| 588 } | 543 } |
| 589 } | 544 } |
| 590 // New label. | 545 // New label. |
| 591 TimelineLabelPauseInfo* pause_info = new TimelineLabelPauseInfo(name); | 546 TimelineLabelPauseInfo* pause_info = new TimelineLabelPauseInfo(name); |
| 592 isolate_labels_.Add(pause_info); | 547 isolate_labels_.Add(pause_info); |
| 593 return pause_info; | 548 return pause_info; |
| 594 } | 549 } |
| 595 | 550 |
| 596 | |
| 597 void TimelinePauseTrace::Aggregate( | 551 void TimelinePauseTrace::Aggregate( |
| 598 const TimelineLabelPauseInfo* thread_pause_info) { | 552 const TimelineLabelPauseInfo* thread_pause_info) { |
| 599 ASSERT(thread_pause_info != NULL); | 553 ASSERT(thread_pause_info != NULL); |
| 600 TimelineLabelPauseInfo* isolate_pause_info = | 554 TimelineLabelPauseInfo* isolate_pause_info = |
| 601 GetOrAddLabelPauseInfo(thread_pause_info->name()); | 555 GetOrAddLabelPauseInfo(thread_pause_info->name()); |
| 602 ASSERT(isolate_pause_info != NULL); | 556 ASSERT(isolate_pause_info != NULL); |
| 603 isolate_pause_info->Aggregate(thread_pause_info); | 557 isolate_pause_info->Aggregate(thread_pause_info); |
| 604 } | 558 } |
| 605 | 559 |
| 606 | |
| 607 void TimelinePauseTrace::PrintPauseInfo( | 560 void TimelinePauseTrace::PrintPauseInfo( |
| 608 const TimelineLabelPauseInfo* pause_info) { | 561 const TimelineLabelPauseInfo* pause_info) { |
| 609 ASSERT(pause_info != NULL); | 562 ASSERT(pause_info != NULL); |
| 610 THR_Print("%s : ", pause_info->name()); | 563 THR_Print("%s : ", pause_info->name()); |
| 611 THR_Print("%.3f ms total on stack; ", | 564 THR_Print("%.3f ms total on stack; ", |
| 612 MicrosecondsToMilliseconds(pause_info->inclusive_micros())); | 565 MicrosecondsToMilliseconds(pause_info->inclusive_micros())); |
| 613 THR_Print("%.3f ms total executing; ", | 566 THR_Print("%.3f ms total executing; ", |
| 614 MicrosecondsToMilliseconds(pause_info->exclusive_micros())); | 567 MicrosecondsToMilliseconds(pause_info->exclusive_micros())); |
| 615 THR_Print("%.3f ms max on stack; ", | 568 THR_Print("%.3f ms max on stack; ", |
| 616 MicrosecondsToMilliseconds(pause_info->max_inclusive_micros())); | 569 MicrosecondsToMilliseconds(pause_info->max_inclusive_micros())); |
| 617 THR_Print("%.3f ms max executing.\n", | 570 THR_Print("%.3f ms max executing.\n", |
| 618 MicrosecondsToMilliseconds(pause_info->max_exclusive_micros())); | 571 MicrosecondsToMilliseconds(pause_info->max_exclusive_micros())); |
| 619 } | 572 } |
| 620 | 573 |
| 621 #endif // !PRODUCT | 574 #endif // !PRODUCT |
| 622 | 575 |
| 623 } // namespace dart | 576 } // namespace dart |
| OLD | NEW |