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/log.h" |
| 9 #include "vm/os_thread.h" |
8 | 10 |
9 namespace dart { | 11 namespace dart { |
10 | 12 |
11 DECLARE_FLAG(bool, trace_timeline); | 13 DEFINE_FLAG(bool, trace_timeline_analysis, false, "Trace timeline analysis"); |
12 | |
13 | 14 |
14 TimelineAnalysisThread::TimelineAnalysisThread(ThreadId id) | 15 TimelineAnalysisThread::TimelineAnalysisThread(ThreadId id) |
15 : id_(id) { | 16 : id_(id) { |
16 } | 17 } |
17 | 18 |
18 | 19 |
19 TimelineAnalysisThread::~TimelineAnalysisThread() { | 20 TimelineAnalysisThread::~TimelineAnalysisThread() { |
20 } | 21 } |
21 | 22 |
22 | 23 |
23 void TimelineAnalysisThread::AddBlock(TimelineEventBlock* block) { | 24 void TimelineAnalysisThread::AddBlock(TimelineEventBlock* block) { |
24 blocks_.Add(block); | 25 blocks_.Add(block); |
25 } | 26 } |
26 | 27 |
27 | 28 |
28 static int CompareBlocksLowerTimeBound(TimelineEventBlock* const* a, | 29 static int CompareBlocksLowerTimeBound(TimelineEventBlock* const* a, |
29 TimelineEventBlock* const* b) { | 30 TimelineEventBlock* const* b) { |
30 ASSERT(a != NULL); | 31 ASSERT(a != NULL); |
31 ASSERT(*a != NULL); | 32 ASSERT(*a != NULL); |
32 ASSERT(b != NULL); | 33 ASSERT(b != NULL); |
33 ASSERT(*b != NULL); | 34 ASSERT(*b != NULL); |
34 return (*a)->LowerTimeBound() - (*b)->LowerTimeBound(); | 35 return (*a)->LowerTimeBound() - (*b)->LowerTimeBound(); |
35 } | 36 } |
36 | 37 |
37 | 38 |
38 void TimelineAnalysisThread::Finalize() { | 39 void TimelineAnalysisThread::Finalize() { |
39 blocks_.Sort(CompareBlocksLowerTimeBound); | 40 blocks_.Sort(CompareBlocksLowerTimeBound); |
| 41 ISL_Print("Thread %" Px " has %" Pd " blocks\n", |
| 42 OSThread::ThreadIdToIntPtr(id_), |
| 43 blocks_.length()); |
40 } | 44 } |
41 | 45 |
42 | 46 |
43 TimelineAnalysisThreadEventIterator::TimelineAnalysisThreadEventIterator( | 47 TimelineAnalysisThreadEventIterator::TimelineAnalysisThreadEventIterator( |
44 TimelineAnalysisThread* thread) { | 48 TimelineAnalysisThread* thread) { |
45 Reset(thread); | 49 Reset(thread); |
46 } | 50 } |
47 | 51 |
48 | 52 |
49 TimelineAnalysisThreadEventIterator::~TimelineAnalysisThreadEventIterator() { | 53 TimelineAnalysisThreadEventIterator::~TimelineAnalysisThreadEventIterator() { |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 void TimelineAnalysis::DiscoverThreads() { | 153 void TimelineAnalysis::DiscoverThreads() { |
150 TimelineEventBlockIterator it(recorder_); | 154 TimelineEventBlockIterator it(recorder_); |
151 while (it.HasNext()) { | 155 while (it.HasNext()) { |
152 TimelineEventBlock* block = it.Next(); | 156 TimelineEventBlock* block = it.Next(); |
153 ASSERT(block != NULL); | 157 ASSERT(block != NULL); |
154 if (block->IsEmpty()) { | 158 if (block->IsEmpty()) { |
155 // Skip empty blocks. | 159 // Skip empty blocks. |
156 continue; | 160 continue; |
157 } | 161 } |
158 if (!block->CheckBlock()) { | 162 if (!block->CheckBlock()) { |
159 // Skip bad blocks. | 163 if (FLAG_trace_timeline_analysis) { |
160 // TODO(johnmccutchan): Make this into an error? | 164 ISL_Print("DiscoverThreads block %" Pd " " |
161 continue; | 165 "violates invariants.\n", block->block_index()); |
| 166 } |
| 167 SetError("Block %" Pd " violates invariants. See " |
| 168 "TimelineEventBlock::CheckBlock", block->block_index()); |
| 169 return; |
162 } | 170 } |
163 TimelineAnalysisThread* thread = GetOrAddThread(block->thread()); | 171 TimelineAnalysisThread* thread = GetOrAddThread(block->thread()); |
164 ASSERT(thread != NULL); | 172 ASSERT(thread != NULL); |
165 thread->AddBlock(block); | 173 thread->AddBlock(block); |
166 } | 174 } |
167 } | 175 } |
168 | 176 |
169 | 177 |
170 void TimelineAnalysis::FinalizeThreads() { | 178 void TimelineAnalysis::FinalizeThreads() { |
171 for (intptr_t i = 0; i < threads_.length(); i++) { | 179 for (intptr_t i = 0; i < threads_.length(); i++) { |
172 TimelineAnalysisThread* thread = threads_.At(i); | 180 TimelineAnalysisThread* thread = threads_.At(i); |
173 ASSERT(thread != NULL); | 181 ASSERT(thread != NULL); |
174 thread->Finalize(); | 182 thread->Finalize(); |
175 } | 183 } |
176 } | 184 } |
177 | 185 |
178 | 186 |
179 void TimelineAnalysis::SetError(const char* format, ...) { | 187 void TimelineAnalysis::SetError(const char* format, ...) { |
180 ASSERT(!has_error_); | 188 ASSERT(!has_error_); |
181 ASSERT(error_msg_ == NULL); | 189 ASSERT(error_msg_ == NULL); |
182 has_error_ = true; | 190 has_error_ = true; |
183 va_list args; | 191 va_list args; |
184 va_start(args, format); | 192 va_start(args, format); |
185 error_msg_ = zone_->VPrint(format, args); | 193 error_msg_ = zone_->VPrint(format, args); |
186 ASSERT(error_msg_ != NULL); | 194 ASSERT(error_msg_ != NULL); |
187 } | 195 } |
188 | 196 |
189 | 197 |
| 198 TimelineLabelPauseInfo::TimelineLabelPauseInfo(const char* name) |
| 199 : name_(name), |
| 200 inclusive_micros_(0), |
| 201 exclusive_micros_(0), |
| 202 max_duration_micros_(0) { |
| 203 ASSERT(name_ != NULL); |
| 204 } |
| 205 |
| 206 |
| 207 void TimelineLabelPauseInfo::OnPush(int64_t micros) { |
| 208 add_inclusive_micros(micros); |
| 209 add_exclusive_micros(micros); |
| 210 if (micros > max_duration_micros_) { |
| 211 max_duration_micros_ = micros; |
| 212 } |
| 213 } |
| 214 |
| 215 |
| 216 void TimelineLabelPauseInfo::OnChildPush(int64_t micros) { |
| 217 ASSERT(micros >= 0); |
| 218 add_exclusive_micros(-micros); |
| 219 } |
| 220 |
| 221 |
190 TimelinePauses::TimelinePauses(Zone* zone, | 222 TimelinePauses::TimelinePauses(Zone* zone, |
191 Isolate* isolate, | 223 Isolate* isolate, |
192 TimelineEventRecorder* recorder) | 224 TimelineEventRecorder* recorder) |
193 : TimelineAnalysis(zone, isolate, recorder) { | 225 : TimelineAnalysis(zone, isolate, recorder) { |
194 } | 226 } |
195 | 227 |
196 | 228 |
197 void TimelinePauses::CalculatePauseTimes() { | 229 void TimelinePauses::Setup() { |
| 230 BuildThreads(); |
| 231 } |
| 232 |
| 233 |
| 234 void TimelinePauses::CalculatePauseTimesForThread(ThreadId tid) { |
| 235 if (has_error()) { |
| 236 return; |
| 237 } |
| 238 TimelineAnalysisThread* thread = GetThread(tid); |
| 239 if (thread == NULL) { |
| 240 SetError("Thread %" Px " does not exist.", OSThread::ThreadIdToIntPtr(tid)); |
| 241 return; |
| 242 } |
| 243 ProcessThread(thread); |
| 244 } |
| 245 |
| 246 |
| 247 TimelineLabelPauseInfo* TimelinePauses::GetLabel(const char* name) const { |
| 248 ASSERT(name != NULL); |
| 249 // Linear lookup because we expect N (# of labels in an isolate) to be small. |
| 250 for (intptr_t i = 0; i < labels_.length(); i++) { |
| 251 TimelineLabelPauseInfo* label = labels_.At(i); |
| 252 if (strcmp(label->name(), name) == 0) { |
| 253 return label; |
| 254 } |
| 255 } |
| 256 return NULL; |
| 257 } |
| 258 |
| 259 |
| 260 int64_t TimelinePauses::InclusiveTime(const char* name) const { |
| 261 TimelineLabelPauseInfo* pause_info = GetLabel(name); |
| 262 ASSERT(pause_info != NULL); |
| 263 return pause_info->inclusive_micros(); |
| 264 } |
| 265 |
| 266 |
| 267 int64_t TimelinePauses::ExclusiveTime(const char* name) const { |
| 268 TimelineLabelPauseInfo* pause_info = GetLabel(name); |
| 269 ASSERT(pause_info != NULL); |
| 270 return pause_info->exclusive_micros(); |
| 271 } |
| 272 |
| 273 |
| 274 int64_t TimelinePauses::MaxDurationTime(const char* name) const { |
| 275 TimelineLabelPauseInfo* pause_info = GetLabel(name); |
| 276 ASSERT(pause_info != NULL); |
| 277 return pause_info->max_duration_micros(); |
| 278 } |
| 279 |
| 280 |
| 281 void TimelinePauses::ProcessThread(TimelineAnalysisThread* thread) { |
| 282 ASSERT(thread != NULL); |
| 283 stack_.Clear(); |
| 284 labels_.Clear(); |
| 285 |
| 286 TimelineAnalysisThreadEventIterator it(thread); |
| 287 if (FLAG_trace_timeline_analysis) { |
| 288 ISL_Print(">>> TimelinePauses::ProcessThread %" Px "\n", |
| 289 OSThread::ThreadIdToIntPtr(thread->id())); |
| 290 } |
| 291 intptr_t event_count = 0; |
| 292 while (it.HasNext()) { |
| 293 TimelineEvent* event = it.Next(); |
| 294 if (!event->IsFinishedDuration()) { |
| 295 // We only care about finished duration events. |
| 296 continue; |
| 297 } |
| 298 int64_t start = event->TimeOrigin(); |
| 299 PopFinished(start); |
| 300 if (!CheckStack(event)) { |
| 301 SetError("Duration check fail."); |
| 302 return; |
| 303 } |
| 304 event_count++; |
| 305 Push(event); |
| 306 } |
| 307 // Pop remaining stack. |
| 308 PopFinished(kMaxInt64); |
| 309 if (FLAG_trace_timeline_analysis) { |
| 310 ISL_Print("<<< TimelinePauses::ProcessThread %" Px " had %" Pd " events\n", |
| 311 OSThread::ThreadIdToIntPtr(thread->id()), |
| 312 event_count); |
| 313 } |
| 314 } |
| 315 |
| 316 |
| 317 // Verify that |event| is contained within all parent events on the stack. |
| 318 bool TimelinePauses::CheckStack(TimelineEvent* event) { |
| 319 ASSERT(event != NULL); |
| 320 for (intptr_t i = 0; i < stack_.length(); i++) { |
| 321 TimelineEvent* slot = stack_.At(i); |
| 322 if (!slot->DurationContains(event)) { |
| 323 return false; |
| 324 } |
| 325 } |
| 326 return true; |
| 327 } |
| 328 |
| 329 |
| 330 void TimelinePauses::PopFinished(int64_t start) { |
| 331 while (stack_.length() > 0) { |
| 332 TimelineEvent* top = stack_.Last(); |
| 333 if (top->DurationFinishedBefore(start)) { |
| 334 // Top of stack completes before |start|. |
| 335 stack_.RemoveLast(); |
| 336 if (FLAG_trace_timeline_analysis) { |
| 337 ISL_Print("Popping %s (%" Pd64 " <= %" Pd64 ")\n", |
| 338 top->label(), |
| 339 top->TimeEnd(), |
| 340 start); |
| 341 } |
| 342 } else { |
| 343 return; |
| 344 } |
| 345 } |
| 346 } |
| 347 |
| 348 |
| 349 void TimelinePauses::Push(TimelineEvent* event) { |
| 350 TimelineLabelPauseInfo* pause_info = GetOrAddLabel(event->label()); |
| 351 ASSERT(pause_info != NULL); |
| 352 // |pause_info| will be running for |event->TimeDuration()|. |
| 353 if (FLAG_trace_timeline_analysis) { |
| 354 ISL_Print("Pushing %s %" Pd64 " us\n", |
| 355 pause_info->name(), |
| 356 event->TimeDuration()); |
| 357 } |
| 358 pause_info->OnPush(event->TimeDuration()); |
| 359 TimelineLabelPauseInfo* top_pause_info = GetTopLabel(); |
| 360 if (top_pause_info != NULL) { |
| 361 // |top_pause_info| is under |event|'s shadow, adjust the exclusive micros. |
| 362 if (FLAG_trace_timeline_analysis) { |
| 363 ISL_Print("Adjusting %s by %" Pd64 " us\n", |
| 364 top_pause_info->name(), |
| 365 event->TimeDuration()); |
| 366 } |
| 367 top_pause_info->OnChildPush(event->TimeDuration()); |
| 368 } |
| 369 // Push onto the stack. |
| 370 stack_.Add(event); |
| 371 } |
| 372 |
| 373 |
| 374 TimelineLabelPauseInfo* TimelinePauses::GetTopLabel() { |
| 375 if (stack_.length() == 0) { |
| 376 return NULL; |
| 377 } |
| 378 TimelineEvent* event = stack_.Last(); |
| 379 return GetLabel(event->label()); |
| 380 } |
| 381 |
| 382 |
| 383 TimelineLabelPauseInfo* TimelinePauses::GetOrAddLabel(const char* name) { |
| 384 ASSERT(name != NULL); |
| 385 TimelineLabelPauseInfo* label = GetLabel(name); |
| 386 if (label != NULL) { |
| 387 return label; |
| 388 } |
| 389 // New label. |
| 390 label = new TimelineLabelPauseInfo(name); |
| 391 labels_.Add(label); |
| 392 return label; |
198 } | 393 } |
199 | 394 |
200 } // namespace dart | 395 } // namespace dart |
OLD | NEW |