1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "main_window/SchedulingPage.h" 8 9 #include <stdio.h> 10 11 #include <algorithm> 12 #include <new> 13 14 #include <LayoutBuilder.h> 15 #include <LayoutUtils.h> 16 #include <ScrollView.h> 17 #include <SplitView.h> 18 19 #include <DebugEventStream.h> 20 21 #include <thread_defs.h> 22 23 #include "Array.h" 24 25 #include "chart/BigtimeChartAxisLegendSource.h" 26 #include "chart/LegendChartAxis.h" 27 #include "chart/StringChartLegend.h" 28 #include "HeaderView.h" 29 #include "Model.h" 30 31 32 static const float kThreadNameMargin = 3.0f; 33 static const float kViewSeparationMargin = 5.0f; 34 35 36 struct MainWindow::SchedulingPage::SchedulingEvent { 37 bigtime_t time; 38 Model::ThreadWaitObjectGroup* waitObject; 39 ThreadState state; 40 41 SchedulingEvent(bigtime_t time, ThreadState state, 42 Model::ThreadWaitObjectGroup* waitObject) 43 : 44 time(time), 45 waitObject(waitObject), 46 state(state) 47 { 48 } 49 }; 50 51 52 class MainWindow::SchedulingPage::SchedulingData { 53 public: 54 SchedulingData() 55 : 56 fModel(NULL), 57 fDataArrays(NULL), 58 fRecordingEnabled(false) 59 { 60 } 61 62 status_t InitCheck() const 63 { 64 return fDataArrays != NULL ? B_OK : B_NO_MEMORY; 65 } 66 67 void SetModel(Model* model) 68 { 69 delete[] fDataArrays; 70 71 fModel = model; 72 fDataArrays = NULL; 73 74 if (fModel != NULL) 75 fDataArrays = new(std::nothrow) DataArray[fModel->CountThreads()]; 76 } 77 78 void SetRecordingEnabled(bool enabled) 79 { 80 fRecordingEnabled = enabled; 81 } 82 83 void Clear() 84 { 85 if (fDataArrays == NULL) 86 return; 87 88 int32 count = fModel->CountThreads(); 89 for (int32 i = 0; i < count; i++) 90 fDataArrays[i].Clear(); 91 } 92 93 const Array<SchedulingEvent>& EventsForThread(int32 index) 94 { 95 return fDataArrays[index]; 96 } 97 98 void AddState(Model::Thread* thread, bigtime_t time, ThreadState state, 99 Model::ThreadWaitObjectGroup* waitObject) 100 { 101 DataArray& array = fDataArrays[thread->Index()]; 102 if (!array.IsEmpty()) { 103 SchedulingEvent& lastEvent = array[array.Size() - 1]; 104 if (fRecordingEnabled) { 105 if (lastEvent.state == state 106 && lastEvent.waitObject == waitObject) { 107 return; 108 } 109 } else { 110 // recording not enabled yet -- overwrite the last event 111 lastEvent = SchedulingEvent(time, state, waitObject); 112 return; 113 } 114 } 115 116 SchedulingEvent event(time, state, waitObject); 117 array.Add(event); 118 } 119 120 void AddRun(Model::Thread* thread, bigtime_t time) 121 { 122 AddState(thread, time, RUNNING, NULL); 123 } 124 125 void AddLatency(Model::Thread* thread, bigtime_t time) 126 { 127 AddState(thread, time, READY, NULL); 128 } 129 130 void AddPreemption(Model::Thread* thread, bigtime_t time) 131 { 132 AddState(thread, time, PREEMPTED, NULL); 133 } 134 135 void AddWait(Model::Thread* thread, bigtime_t time, 136 Model::ThreadWaitObjectGroup* waitObject) 137 { 138 AddState(thread, time, WAITING, waitObject); 139 } 140 141 void AddUnspecifiedWait(Model::Thread* thread, bigtime_t time) 142 { 143 AddState(thread, time, WAITING, NULL); 144 } 145 146 private: 147 typedef Array<SchedulingEvent> DataArray; 148 149 private: 150 Model* fModel; 151 DataArray* fDataArrays; 152 bool fRecordingEnabled; 153 }; 154 155 156 struct MainWindow::SchedulingPage::TimeRange : BReferenceable { 157 bigtime_t startTime; 158 bigtime_t endTime; 159 160 TimeRange(bigtime_t startTime, bigtime_t endTime) 161 : 162 startTime(startTime), 163 endTime(endTime) 164 { 165 } 166 }; 167 168 169 class MainWindow::SchedulingPage::TimelineHeaderRenderer 170 : public HeaderRenderer { 171 public: 172 TimelineHeaderRenderer() 173 : 174 fAxis(new BigtimeChartAxisLegendSource, new StringChartLegendRenderer) 175 { 176 fAxis.SetLocation(CHART_AXIS_TOP); 177 } 178 179 virtual float HeaderHeight(BView* view, const Header* header) 180 { 181 _SetupAxis(view, header); 182 return fAxis.PreferredSize(view, view->Frame().Size()).height; 183 } 184 185 virtual float PreferredHeaderWidth(BView* view, const Header* header) 186 { 187 _SetupAxis(view, header); 188 return fAxis.PreferredSize(view, view->Frame().Size()).width; 189 } 190 191 virtual void DrawHeader(BView* view, BRect frame, BRect updateRect, 192 const Header* header, uint32 flags) 193 { 194 _SetupAxis(view, header); 195 fAxis.SetFrame(frame); 196 DrawHeaderBackground(view, frame, updateRect, flags); 197 fAxis.Render(view, updateRect); 198 } 199 200 private: 201 void _SetupAxis(BView* view, const Header* header) 202 { 203 BVariant value; 204 if (header->GetValue(value)) { 205 TimeRange* timeRange = dynamic_cast<TimeRange*>( 206 value.ToReferenceable()); 207 if (timeRange != NULL) { 208 ChartDataRange range = ChartDataRange(timeRange->startTime, 209 timeRange->endTime); 210 if (range != fRange) { 211 fAxis.SetRange(range); 212 fRange = range; 213 } 214 } 215 } 216 } 217 218 private: 219 LegendChartAxis fAxis; 220 ChartDataRange fRange; 221 }; 222 223 224 class MainWindow::SchedulingPage::BaseView : public BView { 225 public: 226 BaseView(const char* name, FontInfo& fontInfo) 227 : 228 BView(name, B_WILL_DRAW), 229 fModel(NULL), 230 fFontInfo(fontInfo) 231 { 232 } 233 234 virtual void SetModel(Model* model) 235 { 236 fModel = model; 237 238 InvalidateLayout(); 239 } 240 241 protected: 242 int32 _CountLines() const 243 { 244 return fModel != NULL ? fModel->CountThreads() : 0; 245 } 246 247 float TotalHeight() const 248 { 249 return fFontInfo.lineHeight * _CountLines(); 250 } 251 252 void GetLineRange(BRect rect, int32& minLine, int32& maxLine) const 253 { 254 int32 lineHeight = (int32)fFontInfo.lineHeight; 255 minLine = (int32)rect.top / lineHeight; 256 maxLine = ((int32)ceilf(rect.bottom) + lineHeight - 1) / lineHeight; 257 minLine = std::max(minLine, 0L); 258 maxLine = std::min(maxLine, _CountLines() - 1); 259 } 260 261 BRect LineRect(uint32 line) const 262 { 263 float y = (float)line * fFontInfo.lineHeight; 264 return BRect(0, y, Bounds().right, y + fFontInfo.lineHeight - 1); 265 } 266 267 protected: 268 Model* fModel; 269 FontInfo& fFontInfo; 270 }; 271 272 273 class MainWindow::SchedulingPage::ThreadsView : public BaseView { 274 public: 275 ThreadsView(FontInfo& fontInfo) 276 : 277 BaseView("threads", fontInfo) 278 { 279 } 280 281 virtual void Draw(BRect updateRect) 282 { 283 if (fModel == NULL) 284 return; 285 286 // get the lines intersecting with the update rect 287 int32 minLine, maxLine; 288 GetLineRange(updateRect, minLine, maxLine); 289 290 for (int32 i = minLine; i <= maxLine; i++) { 291 float y = (float)(i + 1) * fFontInfo.lineHeight 292 - fFontInfo.fontHeight.descent; 293 DrawString(fModel->ThreadAt(i)->Name(), 294 BPoint(kThreadNameMargin, y)); 295 } 296 } 297 298 virtual BSize MinSize() 299 { 300 return BSize(100, TotalHeight()); 301 } 302 303 virtual BSize MaxSize() 304 { 305 return BSize(MinSize().width, B_SIZE_UNLIMITED); 306 } 307 }; 308 309 310 class MainWindow::SchedulingPage::SchedulingView : public BaseView { 311 public: 312 struct Listener { 313 virtual ~Listener() 314 { 315 } 316 317 virtual void DataWidthChanged() = 0; 318 virtual void DataRangeChanged() = 0; 319 }; 320 321 public: 322 SchedulingView(FontInfo& fontInfo) 323 : 324 BaseView("scheduling", fontInfo), 325 fStartTime(0), 326 fEndTime(0), 327 fUSecsPerPixel(1000), 328 fLastMousePos(-1, -1), 329 fListener(NULL) 330 { 331 } 332 333 virtual void SetModel(Model* model) 334 { 335 BaseView::SetModel(model); 336 fSchedulingData.SetModel(model); 337 fStartTime = 0; 338 fEndTime = 0; 339 340 if (fListener != NULL) { 341 fListener->DataWidthChanged(); 342 fListener->DataRangeChanged(); 343 } 344 345 } 346 347 void SetListener(Listener* listener) 348 { 349 fListener = listener; 350 } 351 352 void UpdateScrollBar() 353 { 354 float width = Frame().Width(); 355 float dataWidth = std::max(width, MinSize().width); 356 357 if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) { 358 float range = dataWidth - width; 359 if (range > 0) { 360 scrollBar->SetRange(0, range); 361 scrollBar->SetProportion((width + 1) / (dataWidth + 1)); 362 scrollBar->SetSteps(fFontInfo.lineHeight, width + 1); 363 } else { 364 scrollBar->SetRange(0, 0); 365 scrollBar->SetProportion(1); 366 } 367 } 368 } 369 370 void GetDataRange(bigtime_t& _startTime, bigtime_t& _endTime) 371 { 372 _GetEventTimeRange(_startTime, _endTime); 373 } 374 375 virtual BSize MinSize() 376 { 377 bigtime_t timeSpan = fModel != NULL ? fModel->LastEventTime() : 0; 378 float width = std::max(float(timeSpan / fUSecsPerPixel), 100.0f); 379 return BSize(width, TotalHeight()); 380 } 381 382 virtual BSize MaxSize() 383 { 384 return BSize(MinSize().width, B_SIZE_UNLIMITED); 385 } 386 387 virtual void ScrollTo(BPoint where) 388 { 389 BaseView::ScrollTo(where); 390 fStartTime = 0; 391 fEndTime = 0; 392 393 if (fListener != NULL) 394 fListener->DataRangeChanged(); 395 } 396 397 void MessageReceived(BMessage* message) 398 { 399 switch (message->what) { 400 case B_MOUSE_WHEEL_CHANGED: 401 { 402 // We're only interested in Shift + vertical wheel. 403 float deltaY; 404 if ((modifiers() & B_SHIFT_KEY) == 0 405 || message->FindFloat("be:wheel_delta_y", &deltaY) 406 != B_OK) { 407 break; 408 } 409 410 _Zoom(fLastMousePos.x, deltaY); 411 412 return; 413 } 414 } 415 416 BView::MessageReceived(message); 417 } 418 419 void MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage) 420 { 421 fLastMousePos = where - LeftTop(); 422 423 // if (fDraggingStartPos.x < 0) 424 // return; 425 // 426 // ScrollBar(B_HORIZONTAL)->SetValue(fDraggingStartScrollValue 427 // + fDraggingStartPos.x - where.x); 428 } 429 430 virtual void Draw(BRect updateRect) 431 { 432 if (fModel == NULL || fSchedulingData.InitCheck() != B_OK) 433 return; 434 435 _UpdateData(); 436 437 // draw the events 438 // TODO: Draw only the threads currently visible. 439 int32 threadCount = fModel->CountThreads(); 440 for (int32 i = 0; i < threadCount; i++) { 441 Model::Thread* thread = fModel->ThreadAt(i); 442 const Array<SchedulingEvent>& events 443 = fSchedulingData.EventsForThread(thread->Index()); 444 445 int32 eventCount = events.Size(); 446 //printf("drawing events for thread %ld: %ld events\n", thread->Index(), eventCount); 447 for (int32 k = 0; k < eventCount; k++) { 448 const SchedulingEvent& event = events[k]; 449 bigtime_t startTime = std::max(event.time, fStartTime); 450 bigtime_t endTime = k + 1 < eventCount 451 ? std::min(events[k + 1].time, fEndTime) : fEndTime; 452 453 rgb_color color; 454 switch (event.state) { 455 case RUNNING: 456 case STILL_RUNNING: 457 color.set_to(0, 255, 0); 458 break; 459 case PREEMPTED: 460 color.set_to(255, 127, 0); 461 break; 462 case READY: 463 color.set_to(255, 0, 0); 464 break; 465 case WAITING: 466 case UNKNOWN: 467 default: 468 continue; 469 } 470 471 SetHighColor(color); 472 BRect rect = LineRect(i); 473 rect.left = startTime / fUSecsPerPixel; 474 rect.right = endTime / fUSecsPerPixel - 1; 475 FillRect(rect); 476 } 477 } 478 } 479 480 private: 481 // shorthands for the longish structure names 482 typedef system_profiler_thread_enqueued_in_run_queue 483 thread_enqueued_in_run_queue; 484 typedef system_profiler_thread_removed_from_run_queue 485 thread_removed_from_run_queue; 486 487 private: 488 void _UpdateData() 489 { 490 // get the interesting event time range 491 bigtime_t startTime; 492 bigtime_t endTime; 493 _GetEventTimeRange(startTime, endTime); 494 495 if (startTime == fStartTime && endTime == fEndTime) 496 return; 497 fStartTime = startTime; 498 fEndTime = endTime; 499 500 fSchedulingData.Clear(); 501 502 //printf("MainWindow::SchedulingPage::SchedulingView::_UpdateData()\n"); 503 //printf(" time range: %lld - %lld\n", startTime, endTime); 504 505 // get a scheduling state close to our start time 506 const Model::CompactSchedulingState* compactState 507 = fModel->ClosestSchedulingState(startTime); 508 //printf(" compactState: %p\n", compactState); 509 fState.Clear(); 510 status_t error = fState.Init(compactState); 511 if (error != B_OK) 512 return; 513 514 // init the event stream 515 BDebugEventInputStream input; 516 error = input.SetTo((uint8*)fModel->EventData(), 517 fModel->EventDataSize(), false); 518 if (error == B_OK && compactState != NULL) 519 error = input.Seek(compactState->EventOffset()); 520 //printf(" event offset: %lld, input init error: %s\n", compactState != NULL ? compactState->EventOffset() : 0, strerror(error)); 521 if (error != B_OK) 522 return; 523 524 fSchedulingData.SetRecordingEnabled( 525 fState.LastEventTime() >= startTime); 526 527 // add the initial thread states to the scheduling data 528 if (compactState != NULL) { 529 int32 threadStateCount = compactState->CountThreadsStates(); 530 for (int32 i = 0; i < threadStateCount; i++) { 531 const Model::CompactThreadSchedulingState* threadState 532 = compactState->ThreadStateAt(i); 533 switch (threadState->state) { 534 case RUNNING: 535 case STILL_RUNNING: 536 fSchedulingData.AddRun(threadState->thread, 537 threadState->lastTime); 538 break; 539 case PREEMPTED: 540 fSchedulingData.AddPreemption(threadState->thread, 541 threadState->lastTime); 542 break; 543 case READY: 544 fSchedulingData.AddLatency(threadState->thread, 545 threadState->lastTime); 546 break; 547 case WAITING: 548 { 549 Model::ThreadWaitObjectGroup* group = NULL; 550 if (threadState->waitObject != NULL) { 551 group = fModel->ThreadWaitObjectGroupFor( 552 threadState->ID(), 553 threadState->waitObject->Type(), 554 threadState->waitObject->Object()); 555 } 556 fSchedulingData.AddWait(threadState->thread, 557 threadState->lastTime, group); 558 break; 559 } 560 case UNKNOWN: 561 default: 562 break; 563 } 564 } 565 } 566 567 // process the events 568 while (true) { 569 // get next event 570 uint32 event; 571 uint32 cpu; 572 const void* buffer; 573 off_t offset; 574 ssize_t bufferSize = input.ReadNextEvent(&event, &cpu, &buffer, 575 &offset); 576 if (bufferSize < 0) 577 { 578 printf("failed to read event!\n"); 579 return; 580 } 581 if (buffer == NULL) 582 break; 583 584 // process the event 585 error = _ProcessEvent(event, cpu, buffer, bufferSize); 586 if (error != B_OK) 587 return; 588 589 if (fState.LastEventTime() >= startTime) 590 fSchedulingData.SetRecordingEnabled(true); 591 if (fState.LastEventTime() >= endTime) 592 break; 593 } 594 } 595 596 void _GetEventTimeRange(bigtime_t& _startTime, bigtime_t& _endTime) 597 { 598 if (fModel != NULL) { 599 float scrollOffset = _ScrollOffset(); 600 _startTime = (bigtime_t)scrollOffset * fUSecsPerPixel; 601 _endTime = (bigtime_t)(scrollOffset + Bounds().Width() + 1) 602 * fUSecsPerPixel; 603 } else { 604 _startTime = 0; 605 _endTime = 1; 606 } 607 } 608 609 float _ScrollOffset() const 610 { 611 if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) 612 return scrollBar->Value(); 613 return 0; 614 } 615 616 void _Zoom(float x, float steps) 617 { 618 if (steps == 0 || fModel == NULL) 619 return; 620 621 // compute the domain point where to zoom in 622 float scrollOffset = _ScrollOffset(); 623 double timeForX = (scrollOffset + x) * fUSecsPerPixel; 624 625 uint32 factor = 4; 626 if (steps < 0) { 627 steps = -steps; 628 factor = 1; 629 } 630 631 uint32 oldUsecPerPixel = fUSecsPerPixel; 632 for (; steps > 0; steps--) 633 fUSecsPerPixel = fUSecsPerPixel * factor / 2; 634 635 if (fUSecsPerPixel < 1) 636 fUSecsPerPixel = 1; 637 else if (fUSecsPerPixel > 1000) 638 fUSecsPerPixel = 1000; 639 640 if (fUSecsPerPixel == oldUsecPerPixel) 641 return; 642 643 Invalidate(); 644 645 UpdateScrollBar(); 646 if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) 647 scrollBar->SetValue(timeForX / fUSecsPerPixel - x); 648 649 if (fListener != NULL) { 650 fListener->DataWidthChanged(); 651 fListener->DataRangeChanged(); 652 } 653 } 654 655 inline void _UpdateLastEventTime(bigtime_t time) 656 { 657 fState.SetLastEventTime(time - fModel->BaseTime()); 658 } 659 660 status_t _ProcessEvent(uint32 event, uint32 cpu, const void* buffer, 661 size_t size) 662 { 663 switch (event) { 664 case B_SYSTEM_PROFILER_TEAM_ADDED: 665 case B_SYSTEM_PROFILER_TEAM_REMOVED: 666 case B_SYSTEM_PROFILER_TEAM_EXEC: 667 break; 668 669 case B_SYSTEM_PROFILER_THREAD_ADDED: 670 _HandleThreadAdded((system_profiler_thread_added*)buffer); 671 break; 672 673 case B_SYSTEM_PROFILER_THREAD_REMOVED: 674 _HandleThreadRemoved((system_profiler_thread_removed*)buffer); 675 break; 676 677 case B_SYSTEM_PROFILER_THREAD_SCHEDULED: 678 _HandleThreadScheduled( 679 (system_profiler_thread_scheduled*)buffer); 680 break; 681 682 case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE: 683 _HandleThreadEnqueuedInRunQueue( 684 (thread_enqueued_in_run_queue*)buffer); 685 break; 686 687 case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE: 688 _HandleThreadRemovedFromRunQueue( 689 (thread_removed_from_run_queue*)buffer); 690 break; 691 692 case B_SYSTEM_PROFILER_WAIT_OBJECT_INFO: 693 break; 694 695 default: 696 printf("unsupported event type %lu, size: %lu\n", event, size); 697 return B_BAD_DATA; 698 break; 699 } 700 701 return B_OK; 702 } 703 704 void _HandleThreadAdded(system_profiler_thread_added* event) 705 { 706 //printf(" thread added: %ld\n", event->thread); 707 // do we know the thread already? 708 Model::ThreadSchedulingState* info = fState.LookupThread(event->thread); 709 if (info != NULL) { 710 // TODO: ? 711 return; 712 } 713 714 Model::Thread* thread = fModel->ThreadByID(event->thread); 715 if (thread == NULL) 716 return; 717 718 // create and add a ThreadSchedulingState 719 info = new(std::nothrow) Model::ThreadSchedulingState(thread); 720 if (info == NULL) 721 return; 722 723 fState.InsertThread(info); 724 } 725 726 void _HandleThreadRemoved(system_profiler_thread_removed* event) 727 { 728 //printf(" thread removed: %ld\n", event->thread); 729 // Model::ThreadSchedulingState* thread = fState.LookupThread( 730 // event->thread); 731 // if (thread != NULL) { 732 // fState.RemoveThread(thread); 733 // delete thread; 734 //// TODO: The thread will be unscheduled in a moment and cause a warning! So 735 //// maybe keep it around in a separate hash table a bit longer? 736 // } 737 } 738 739 void _HandleThreadScheduled(system_profiler_thread_scheduled* event) 740 { 741 _UpdateLastEventTime(event->time); 742 743 Model::ThreadSchedulingState* thread = fState.LookupThread( 744 event->thread); 745 if (thread == NULL) { 746 printf("Schedule event for unknown thread: %ld\n", event->thread); 747 return; 748 } 749 750 thread->lastTime = fState.LastEventTime(); 751 thread->state = RUNNING; 752 fSchedulingData.AddRun(thread->thread, fState.LastEventTime()); 753 754 // unscheduled thread 755 756 if (event->thread == event->previous_thread) 757 return; 758 759 thread = fState.LookupThread(event->previous_thread); 760 if (thread == NULL) { 761 printf("Schedule event for unknown previous thread: %ld\n", 762 event->previous_thread); 763 return; 764 } 765 766 if (thread->state == STILL_RUNNING) { 767 // thread preempted 768 fSchedulingData.AddPreemption(thread->thread, 769 fState.LastEventTime()); 770 771 thread->lastTime = fState.LastEventTime(); 772 thread->state = PREEMPTED; 773 } else if (thread->state == RUNNING) { 774 // thread starts waiting (it hadn't been added to the run 775 // queue before being unscheduled) 776 if (event->previous_thread_state == B_THREAD_WAITING) { 777 addr_t waitObject = event->previous_thread_wait_object; 778 switch (event->previous_thread_wait_object_type) { 779 case THREAD_BLOCK_TYPE_SNOOZE: 780 case THREAD_BLOCK_TYPE_SIGNAL: 781 waitObject = 0; 782 break; 783 case THREAD_BLOCK_TYPE_SEMAPHORE: 784 case THREAD_BLOCK_TYPE_CONDITION_VARIABLE: 785 case THREAD_BLOCK_TYPE_MUTEX: 786 case THREAD_BLOCK_TYPE_RW_LOCK: 787 case THREAD_BLOCK_TYPE_OTHER: 788 default: 789 break; 790 } 791 792 fSchedulingData.AddWait(thread->thread, fState.LastEventTime(), 793 fModel->ThreadWaitObjectGroupFor(thread->ID(), 794 event->previous_thread_wait_object_type, waitObject)); 795 } else { 796 fSchedulingData.AddUnspecifiedWait(thread->thread, 797 fState.LastEventTime()); 798 } 799 800 thread->lastTime = fState.LastEventTime(); 801 thread->state = WAITING; 802 } else if (thread->state == UNKNOWN) { 803 uint32 threadState = event->previous_thread_state; 804 if (threadState == B_THREAD_WAITING 805 || threadState == B_THREAD_SUSPENDED) { 806 fSchedulingData.AddWait(thread->thread, fState.LastEventTime(), 807 NULL); 808 thread->lastTime = fState.LastEventTime(); 809 thread->state = WAITING; 810 } else if (threadState == B_THREAD_READY) { 811 thread->lastTime = fState.LastEventTime(); 812 thread->state = PREEMPTED; 813 fSchedulingData.AddPreemption(thread->thread, 814 fState.LastEventTime()); 815 } 816 } 817 } 818 819 void _HandleThreadEnqueuedInRunQueue(thread_enqueued_in_run_queue* event) 820 { 821 _UpdateLastEventTime(event->time); 822 823 Model::ThreadSchedulingState* thread = fState.LookupThread( 824 event->thread); 825 if (thread == NULL) { 826 printf("Enqueued in run queue event for unknown thread: %ld\n", 827 event->thread); 828 return; 829 } 830 831 if (thread->state == RUNNING || thread->state == STILL_RUNNING) { 832 // Thread was running and is reentered into the run queue. This 833 // is done by the scheduler, if the thread remains ready. 834 thread->state = STILL_RUNNING; 835 } else { 836 // Thread was waiting and is ready now. 837 bigtime_t diffTime = fState.LastEventTime() - thread->lastTime; 838 if (thread->waitObject != NULL) { 839 thread->waitObject->AddWait(diffTime); 840 thread->waitObject = NULL; 841 } 842 843 fSchedulingData.AddLatency(thread->thread, fState.LastEventTime()); 844 thread->lastTime = fState.LastEventTime(); 845 thread->state = READY; 846 } 847 } 848 849 void _HandleThreadRemovedFromRunQueue(thread_removed_from_run_queue* event) 850 { 851 _UpdateLastEventTime(event->time); 852 853 Model::ThreadSchedulingState* thread = fState.LookupThread( 854 event->thread); 855 if (thread == NULL) { 856 printf("Removed from run queue event for unknown thread: %ld\n", 857 event->thread); 858 return; 859 } 860 861 // This really only happens when the thread priority is changed 862 // while the thread is ready. 863 fSchedulingData.AddUnspecifiedWait(thread->thread, 864 fState.LastEventTime()); 865 866 thread->lastTime = fState.LastEventTime(); 867 thread->state = WAITING; 868 } 869 870 private: 871 Model::SchedulingState fState; 872 SchedulingData fSchedulingData; 873 bigtime_t fStartTime; 874 bigtime_t fEndTime; 875 uint32 fUSecsPerPixel; 876 BPoint fLastMousePos; 877 Listener* fListener; 878 }; 879 880 881 class MainWindow::SchedulingPage::ViewPort : public BaseView, 882 private HeaderListener, private SchedulingView::Listener { 883 public: 884 ViewPort(ThreadsView* threadsView, SchedulingView* schedulingView, 885 FontInfo& fontInfo) 886 : 887 BaseView("viewport", fontInfo), 888 fHeaderView(NULL), 889 fThreadsView(threadsView), 890 fSchedulingView(schedulingView) 891 { 892 AddChild(fHeaderView = new HeaderView); 893 AddChild(threadsView); 894 AddChild(schedulingView); 895 896 fSchedulingView->SetListener(this); 897 898 HeaderModel* headerModel = fHeaderView->Model(); 899 900 Header* header = new Header(100, 100, 10000, 200, 0); 901 header->SetValue("Thread"); 902 headerModel->AddHeader(header); 903 header->AddListener(this); 904 905 header = new Header(100, 100, 10000, 200, 1); 906 // TODO: Set header width correctly! 907 header->SetValue("Activity"); 908 header->SetHeaderRenderer(new TimelineHeaderRenderer); 909 headerModel->AddHeader(header); 910 // header->AddListener(this); 911 } 912 913 ~ViewPort() 914 { 915 fHeaderView->Model()->HeaderAt(0)->RemoveListener(this); 916 // fHeaderView->Model()->HeaderAt(1)->RemoveListener(this); 917 } 918 919 void TargetedByScrollView(BScrollView* scrollView) 920 { 921 _UpdateScrollBars(); 922 } 923 924 virtual BSize MinSize() 925 { 926 return BSize(10, fHeaderView->MinSize().Height()); 927 } 928 929 virtual BSize MaxSize() 930 { 931 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 932 } 933 934 BSize PreferredSize() 935 { 936 BSize headerViewSize(fHeaderView->PreferredSize()); 937 BSize threadsViewSize(fThreadsView->PreferredSize()); 938 BSize schedulingViewSize(fSchedulingView->PreferredSize()); 939 BSize size(BLayoutUtils::AddDistances( 940 threadsViewSize.width + kViewSeparationMargin, 941 schedulingViewSize.width), 942 std::max(threadsViewSize.height, schedulingViewSize.height)); 943 944 return BSize(std::max(size.width, headerViewSize.width), 945 BLayoutUtils::AddDistances(size.height, headerViewSize.height)); 946 } 947 948 void DoLayout() 949 { 950 float headerHeight = fHeaderView->MinSize().Height(); 951 float width = Bounds().Width(); 952 float height = fThreadsView->MinSize().Height(); 953 float threadsViewWidth = fThreadsView->MinSize().width; 954 float schedulingViewLeft = threadsViewWidth + 1 + kViewSeparationMargin; 955 float schedulingViewWidth = width - schedulingViewLeft; 956 957 fHeaderView->MoveTo(0, 0); 958 fHeaderView->ResizeTo(width, headerHeight); 959 960 fThreadsView->MoveTo(0, headerHeight + 1); 961 fThreadsView->ResizeTo(threadsViewWidth, height); 962 963 fSchedulingView->MoveTo(schedulingViewLeft, headerHeight + 1); 964 fSchedulingView->ResizeTo(schedulingViewWidth, height); 965 966 // if (Header* header = fHeaderView->Model()->HeaderAt(0)) { 967 // } 968 969 if (Header* header = fHeaderView->Model()->HeaderAt(1)) { 970 float headerWidth = schedulingViewWidth + 1 + kViewSeparationMargin; 971 header->SetMinWidth(headerWidth); 972 header->SetMaxWidth(headerWidth); 973 header->SetPreferredWidth(headerWidth); 974 header->SetWidth(headerWidth); 975 } 976 977 _UpdateScrollBars(); 978 } 979 980 private: 981 virtual void HeaderWidthChanged(Header* header) 982 { 983 if (header->ModelIndex() != 0) 984 return; 985 986 // first column changed 987 } 988 989 virtual void DataWidthChanged() 990 { 991 } 992 993 virtual void DataRangeChanged() 994 { 995 Header* header = fHeaderView->Model()->HeaderAt(1); 996 if (header == NULL) 997 return; 998 999 bigtime_t startTime; 1000 bigtime_t endTime; 1001 fSchedulingView->GetDataRange(startTime, endTime); 1002 TimeRange* range = new(std::nothrow) TimeRange(startTime, endTime); 1003 if (range != NULL) { 1004 header->SetValue(BVariant(range, 'time')); 1005 range->ReleaseReference(); 1006 } 1007 } 1008 1009 void _UpdateScrollBars() 1010 { 1011 float height = Frame().Height(); 1012 float dataHeight = std::max(height, fSchedulingView->MinSize().height); 1013 1014 fSchedulingView->UpdateScrollBar(); 1015 1016 if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) { 1017 float range = dataHeight - height; 1018 if (range > 0) { 1019 scrollBar->SetRange(0, range); 1020 scrollBar->SetProportion( 1021 (height + 1) / (dataHeight + 1)); 1022 scrollBar->SetSteps(fFontInfo.lineHeight, height + 1); 1023 } else { 1024 scrollBar->SetRange(0, 0); 1025 scrollBar->SetProportion(1); 1026 } 1027 } 1028 } 1029 1030 private: 1031 HeaderView* fHeaderView; 1032 ThreadsView* fThreadsView; 1033 SchedulingView* fSchedulingView; 1034 }; 1035 1036 1037 MainWindow::SchedulingPage::SchedulingPage(MainWindow* parent) 1038 : 1039 BGroupView(B_VERTICAL), 1040 fParent(parent), 1041 fModel(NULL), 1042 fScrollView(NULL), 1043 fViewPort(NULL), 1044 fThreadsView(NULL), 1045 fSchedulingView(NULL) 1046 { 1047 SetName("Scheduling"); 1048 1049 be_plain_font->GetHeight(&fFontInfo.fontHeight); 1050 fFontInfo.lineHeight = ceilf(fFontInfo.fontHeight.ascent) 1051 + ceilf(fFontInfo.fontHeight.descent); 1052 1053 fViewPort = new ViewPort(fThreadsView = new ThreadsView(fFontInfo), 1054 fSchedulingView = new SchedulingView(fFontInfo), fFontInfo); 1055 1056 AddChild(fScrollView = new BScrollView("scroll", fViewPort, 0, true, true)); 1057 1058 fScrollView->ScrollBar(B_HORIZONTAL)->SetTarget(fSchedulingView); 1059 } 1060 1061 1062 MainWindow::SchedulingPage::~SchedulingPage() 1063 { 1064 } 1065 1066 1067 void 1068 MainWindow::SchedulingPage::SetModel(Model* model) 1069 { 1070 if (model == fModel) 1071 return; 1072 1073 if (fModel != NULL) { 1074 } 1075 1076 fModel = model; 1077 1078 if (fModel != NULL) { 1079 } 1080 1081 fViewPort->SetModel(fModel); 1082 fThreadsView->SetModel(fModel); 1083 fSchedulingView->SetModel(fModel); 1084 } 1085