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 <MenuItem.h> 17 #include <PopUpMenu.h> 18 #include <ScrollView.h> 19 #include <SplitView.h> 20 #include <ToolTip.h> 21 22 #include <DebugEventStream.h> 23 24 #include <thread_defs.h> 25 26 #include "Array.h" 27 28 #include "chart/NanotimeChartAxisLegendSource.h" 29 #include "chart/LegendChartAxis.h" 30 #include "chart/StringChartLegend.h" 31 #include "HeaderView.h" 32 #include "Model.h" 33 #include "util/TimeUtils.h" 34 35 36 static const float kThreadNameMargin = 3.0f; 37 static const float kViewSeparationMargin = 1.0f; 38 39 40 enum { 41 MSG_SCHEDULING_FILTER_HIDE_SELECTED = 'schs', 42 MSG_SCHEDULING_FILTER_HIDE_UNSELECTED = 'schu', 43 MSG_SCHEDULING_FILTER_SHOW_ALL = 'scsa' 44 }; 45 46 47 enum { 48 IO_SCHEDULING_STATE_IDLE, 49 IO_SCHEDULING_STATE_PENDING_REQUEST, 50 IO_SCHEDULING_STATE_PENDING_OPERATION 51 }; 52 53 54 struct MainWindow::SchedulingPage::SchedulingEvent { 55 nanotime_t time; 56 Model::ThreadWaitObjectGroup* waitObject; 57 ThreadState state; 58 59 SchedulingEvent(nanotime_t time, ThreadState state, 60 Model::ThreadWaitObjectGroup* waitObject) 61 : 62 time(time), 63 waitObject(waitObject), 64 state(state) 65 { 66 } 67 }; 68 69 70 struct MainWindow::SchedulingPage::IOSchedulingEvent { 71 nanotime_t time; 72 uint32 state; 73 74 IOSchedulingEvent(nanotime_t time, uint32 state) 75 : 76 time(time), 77 state(state) 78 { 79 } 80 }; 81 82 83 class MainWindow::SchedulingPage::SchedulingData { 84 public: 85 SchedulingData() 86 : 87 fModel(NULL), 88 fDataArrays(NULL), 89 fIODataArrays(NULL), 90 fRecordingEnabled(false) 91 { 92 } 93 94 status_t InitCheck() const 95 { 96 return fDataArrays != NULL && fIODataArrays != NULL 97 ? B_OK : B_NO_MEMORY; 98 } 99 100 void SetModel(Model* model) 101 { 102 delete[] fDataArrays; 103 delete[] fIODataArrays; 104 105 fModel = model; 106 fDataArrays = NULL; 107 fIODataArrays = NULL; 108 109 if (fModel != NULL) { 110 int32 threadCount = fModel->CountThreads(); 111 fDataArrays = new(std::nothrow) DataArray[threadCount]; 112 fIODataArrays = new(std::nothrow) IODataArray[threadCount]; 113 } 114 } 115 116 void SetRecordingEnabled(bool enabled) 117 { 118 fRecordingEnabled = enabled; 119 } 120 121 void Clear() 122 { 123 int32 count = fModel->CountThreads(); 124 125 if (fDataArrays != NULL) { 126 for (int32 i = 0; i < count; i++) 127 fDataArrays[i].Clear(); 128 } 129 130 if (fIODataArrays != NULL) { 131 for (int32 i = 0; i < count; i++) 132 fIODataArrays[i].Clear(); 133 } 134 } 135 136 const Array<SchedulingEvent>& EventsForThread(int32 index) 137 { 138 return fDataArrays[index]; 139 } 140 141 const Array<IOSchedulingEvent>& IOEventsForThread(int32 index) 142 { 143 return fIODataArrays[index]; 144 } 145 146 void AddState(Model::Thread* thread, nanotime_t time, ThreadState state, 147 Model::ThreadWaitObjectGroup* waitObject) 148 { 149 DataArray& array = fDataArrays[thread->Index()]; 150 if (!array.IsEmpty()) { 151 SchedulingEvent& lastEvent = array[array.Size() - 1]; 152 if (fRecordingEnabled) { 153 if (lastEvent.state == state 154 && lastEvent.waitObject == waitObject) { 155 return; 156 } 157 } else { 158 // recording not enabled yet -- overwrite the last event 159 lastEvent = SchedulingEvent(time, state, waitObject); 160 return; 161 } 162 } 163 164 SchedulingEvent event(time, state, waitObject); 165 array.Add(event); 166 } 167 168 void AddIOState(Model::Thread* thread, nanotime_t time, uint32 state) 169 { 170 IODataArray& array = fIODataArrays[thread->Index()]; 171 array.Add(IOSchedulingEvent(time, state)); 172 } 173 174 void AddRun(Model::Thread* thread, nanotime_t time) 175 { 176 AddState(thread, time, RUNNING, NULL); 177 } 178 179 void AddLatency(Model::Thread* thread, nanotime_t time) 180 { 181 AddState(thread, time, READY, NULL); 182 } 183 184 void AddPreemption(Model::Thread* thread, nanotime_t time) 185 { 186 AddState(thread, time, PREEMPTED, NULL); 187 } 188 189 void AddWait(Model::Thread* thread, nanotime_t time, 190 Model::ThreadWaitObjectGroup* waitObject) 191 { 192 AddState(thread, time, WAITING, waitObject); 193 } 194 195 void AddUnspecifiedWait(Model::Thread* thread, nanotime_t time) 196 { 197 AddState(thread, time, WAITING, NULL); 198 } 199 200 private: 201 typedef Array<SchedulingEvent> DataArray; 202 typedef Array<IOSchedulingEvent> IODataArray; 203 204 private: 205 Model* fModel; 206 DataArray* fDataArrays; 207 IODataArray* fIODataArrays; 208 bool fRecordingEnabled; 209 }; 210 211 212 struct MainWindow::SchedulingPage::TimeRange : BReferenceable { 213 nanotime_t startTime; 214 nanotime_t endTime; 215 216 TimeRange(nanotime_t startTime, nanotime_t endTime) 217 : 218 startTime(startTime), 219 endTime(endTime) 220 { 221 } 222 }; 223 224 225 class MainWindow::SchedulingPage::TimelineHeaderRenderer 226 : public HeaderRenderer { 227 public: 228 TimelineHeaderRenderer() 229 : 230 fAxis(new NanotimeChartAxisLegendSource, new StringChartLegendRenderer) 231 { 232 fAxis.SetLocation(CHART_AXIS_TOP); 233 } 234 235 virtual float HeaderHeight(BView* view, const Header* header) 236 { 237 _SetupAxis(view, header); 238 return fAxis.PreferredSize(view, view->Frame().Size()).height; 239 } 240 241 virtual float PreferredHeaderWidth(BView* view, const Header* header) 242 { 243 _SetupAxis(view, header); 244 return fAxis.PreferredSize(view, view->Frame().Size()).width; 245 } 246 247 virtual void DrawHeader(BView* view, BRect frame, BRect updateRect, 248 const Header* header, uint32 flags) 249 { 250 _SetupAxis(view, header); 251 fAxis.SetFrame(frame); 252 DrawHeaderBackground(view, frame, updateRect, flags); 253 fAxis.Render(view, updateRect); 254 } 255 256 private: 257 void _SetupAxis(BView* view, const Header* header) 258 { 259 BVariant value; 260 if (header->GetValue(value)) { 261 TimeRange* timeRange = dynamic_cast<TimeRange*>( 262 value.ToReferenceable()); 263 if (timeRange != NULL) { 264 ChartDataRange range = ChartDataRange(timeRange->startTime, 265 timeRange->endTime); 266 if (range != fRange) { 267 fAxis.SetRange(range); 268 fRange = range; 269 } 270 } 271 } 272 } 273 274 private: 275 LegendChartAxis fAxis; 276 ChartDataRange fRange; 277 }; 278 279 280 class MainWindow::SchedulingPage::BaseView : public BView { 281 public: 282 BaseView(const char* name, FontInfo& fontInfo, 283 ListSelectionModel* filterModel, uint32 flags = 0) 284 : 285 BView(name, flags), 286 fModel(NULL), 287 fFilterModel(filterModel), 288 fFontInfo(fontInfo) 289 { 290 } 291 292 virtual void SetModel(Model* model) 293 { 294 fModel = model; 295 296 InvalidateLayout(); 297 } 298 299 protected: 300 int32 CountLines() const 301 { 302 return fFilterModel->CountSelectedItems(); 303 } 304 305 float TotalHeight() const 306 { 307 return fFontInfo.lineHeight * CountLines(); 308 } 309 310 int32 LineAt(BPoint point) const 311 { 312 int32 line = (int32)point.y / (int32)fFontInfo.lineHeight; 313 return line < CountLines() ? line : -1; 314 } 315 316 void GetLineRange(BRect rect, int32& minLine, int32& maxLine) const 317 { 318 int32 lineHeight = (int32)fFontInfo.lineHeight; 319 minLine = (int32)rect.top / lineHeight; 320 maxLine = ((int32)ceilf(rect.bottom) + lineHeight - 1) / lineHeight; 321 minLine = std::max(minLine, (int32)0); 322 maxLine = std::min(maxLine, CountLines() - 1); 323 } 324 325 BRect LineRect(uint32 line) const 326 { 327 float y = (float)line * fFontInfo.lineHeight; 328 return BRect(0, y, Bounds().right, y + fFontInfo.lineHeight - 1); 329 } 330 331 protected: 332 Model* fModel; 333 ListSelectionModel* fFilterModel; 334 FontInfo& fFontInfo; 335 }; 336 337 338 class MainWindow::SchedulingPage::LineBaseView : public BaseView, 339 protected ListSelectionModel::Listener { 340 public: 341 LineBaseView(const char* name, FontInfo& fontInfo, 342 ListSelectionModel* filterModel, ListSelectionModel* selectionModel) 343 : 344 BaseView(name, fontInfo, filterModel, B_WILL_DRAW), 345 fSelectionModel(selectionModel), 346 fTextColor(ui_color(B_DOCUMENT_TEXT_COLOR)), 347 fSelectedTextColor(fTextColor), 348 fBackgroundColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR)), 349 fSelectedBackgroundColor(tint_color(fBackgroundColor, B_DARKEN_2_TINT)) 350 { 351 fFilterModel->AddListener(this); 352 fSelectionModel->AddListener(this); 353 } 354 355 void RemoveListeners() 356 { 357 fSelectionModel->RemoveListener(this); 358 fFilterModel->RemoveListener(this); 359 } 360 361 virtual void MessageReceived(BMessage* message) 362 { 363 switch (message->what) { 364 case MSG_SCHEDULING_FILTER_HIDE_SELECTED: 365 { 366 int32 filteredCount = fFilterModel->CountSelectedItems(); 367 int32 selectedCount = fSelectionModel->CountSelectedItems(); 368 if (selectedCount == 0 || selectedCount == filteredCount) 369 break; 370 371 // Set the filter model to the unselected items. 372 ListSelectionModel tempModel; 373 for (int32 i = 0; i < filteredCount; i++) { 374 if (!fSelectionModel->IsItemSelected(i)) { 375 tempModel.SelectItem(fFilterModel->SelectedItemAt(i), 376 true); 377 } 378 } 379 380 fSelectionModel->Clear(); 381 *fFilterModel = tempModel; 382 Invalidate(); 383 break; 384 } 385 386 case MSG_SCHEDULING_FILTER_HIDE_UNSELECTED: 387 { 388 int32 filteredCount = fFilterModel->CountSelectedItems(); 389 int32 selectedCount = fSelectionModel->CountSelectedItems(); 390 if (selectedCount == 0 || selectedCount == filteredCount) 391 break; 392 393 // Set the filter model to the selected items. 394 // There might already be a filter applied, so we have to 395 // build the new filter model manually. 396 ListSelectionModel tempModel; 397 for (int32 i = 0; i < selectedCount; i++) { 398 int32 index = fFilterModel->SelectedItemAt( 399 fSelectionModel->SelectedItemAt(i)); 400 if (index >= 0) 401 tempModel.SelectItem(index, true); 402 } 403 404 fSelectionModel->Clear(); 405 *fFilterModel = tempModel; 406 Invalidate(); 407 break; 408 } 409 410 case MSG_SCHEDULING_FILTER_SHOW_ALL: 411 { 412 int32 threadCount = fModel->CountThreads(); 413 if (fFilterModel->CountSelectedItems() == threadCount) 414 break; 415 416 // unset the filter 417 ListSelectionModel tempModel = *fFilterModel; 418 fSelectionModel->Clear(); 419 fFilterModel->SelectItems(0, threadCount, false); 420 *fSelectionModel = tempModel; 421 Invalidate(); 422 break; 423 } 424 425 default: 426 BaseView::MessageReceived(message); 427 break; 428 } 429 } 430 431 virtual void MouseDown(BPoint where) 432 { 433 // get buttons and modifiers 434 BMessage* message = Looper()->CurrentMessage(); 435 if (message == NULL) 436 return; 437 438 int32 buttons; 439 if (message->FindInt32("buttons", &buttons) != B_OK) 440 return; 441 442 int32 modifiers; 443 if (message->FindInt32("modifiers", &modifiers) != B_OK) 444 modifiers = 0; 445 446 // update selection 447 int32 line = LineAt(where); 448 if (line >= 0) { 449 if ((modifiers & B_SHIFT_KEY) != 0) { 450 int32 selectedLines = fSelectionModel->CountSelectedItems(); 451 if (selectedLines > 0) { 452 int32 firstLine = fSelectionModel->SelectedItemAt(0); 453 int32 lastLine = fSelectionModel->SelectedItemAt( 454 selectedLines - 1); 455 firstLine = std::min(firstLine, line); 456 lastLine = std::max(lastLine, line); 457 fSelectionModel->SelectItems(firstLine, 458 lastLine - firstLine + 1, false); 459 } else 460 fSelectionModel->SelectItem(line, true); 461 } else { 462 if (fSelectionModel->IsItemSelected(line)) { 463 if ((modifiers & B_COMMAND_KEY) != 0) 464 fSelectionModel->DeselectItem(line); 465 } else { 466 fSelectionModel->SelectItem(line, 467 (modifiers & B_COMMAND_KEY) != 0); 468 } 469 } 470 } else 471 fSelectionModel->Clear(); 472 473 // on right mouse button open context menu 474 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0 && fModel != NULL) { 475 BPopUpMenu* contextMenu = new BPopUpMenu("scheduling context menu", 476 false, false); 477 478 int32 filteredCount = fFilterModel->CountSelectedItems(); 479 int32 selectedCount = fSelectionModel->CountSelectedItems(); 480 481 BMenuItem* item = new BMenuItem("Hide selected threads", 482 new BMessage(MSG_SCHEDULING_FILTER_HIDE_SELECTED)); 483 contextMenu->AddItem(item); 484 item->SetTarget(this); 485 if (selectedCount == 0 || selectedCount == filteredCount) 486 item->SetEnabled(false); 487 488 item = new BMenuItem("Hide unselected threads", 489 new BMessage(MSG_SCHEDULING_FILTER_HIDE_UNSELECTED)); 490 contextMenu->AddItem(item); 491 item->SetTarget(this); 492 if (selectedCount == 0 || selectedCount == filteredCount) 493 item->SetEnabled(false); 494 495 item = new BMenuItem("Show all threads", 496 new BMessage(MSG_SCHEDULING_FILTER_SHOW_ALL)); 497 contextMenu->AddItem(item); 498 item->SetTarget(this); 499 if (filteredCount == fModel->CountThreads()) 500 item->SetEnabled(false); 501 502 BPoint screenWhere = ConvertToScreen(where); 503 BRect mouseRect(screenWhere, screenWhere); 504 mouseRect.InsetBy(-4.0, -4.0); 505 contextMenu->Go(screenWhere, true, false, mouseRect, true); 506 } 507 } 508 509 virtual void ItemsSelected(ListSelectionModel* model, int32 index, 510 int32 count) 511 { 512 if (model == fFilterModel) 513 Invalidate(); 514 else 515 InvalidateLines(index, count); 516 } 517 518 virtual void ItemsDeselected(ListSelectionModel* model, int32 index, 519 int32 count) 520 { 521 if (model == fFilterModel) 522 Invalidate(); 523 else 524 InvalidateLines(index, count); 525 } 526 527 void InvalidateLines(int32 index, int32 count) 528 { 529 float top = (float)index * fFontInfo.lineHeight; 530 float bottom = (float)(index + count) * fFontInfo.lineHeight - 1; 531 BRect bounds(Bounds()); 532 Invalidate(BRect(bounds.left, top, bounds.right, bottom)); 533 } 534 535 protected: 536 ListSelectionModel* fSelectionModel; 537 rgb_color fTextColor; 538 rgb_color fSelectedTextColor; 539 rgb_color fBackgroundColor; 540 rgb_color fSelectedBackgroundColor; 541 }; 542 543 544 class MainWindow::SchedulingPage::ThreadsView : public LineBaseView { 545 public: 546 ThreadsView(FontInfo& fontInfo, ListSelectionModel* filterModel, 547 ListSelectionModel* selectionModel) 548 : 549 LineBaseView("threads", fontInfo, filterModel, selectionModel) 550 { 551 SetViewColor(B_TRANSPARENT_32_BIT); 552 } 553 554 ~ThreadsView() 555 { 556 } 557 558 virtual void Draw(BRect updateRect) 559 { 560 if (fModel == NULL) 561 return; 562 563 // get the lines intersecting with the update rect 564 int32 minLine, maxLine; 565 GetLineRange(updateRect, minLine, maxLine); 566 567 for (int32 i = minLine; i <= maxLine; i++) { 568 // draw the line background 569 BRect lineRect = LineRect(i); 570 if (fSelectionModel->IsItemSelected(i)) { 571 SetLowColor(fSelectedBackgroundColor); 572 SetHighColor(fTextColor); 573 } else { 574 SetLowColor(fBackgroundColor); 575 SetHighColor(fSelectedTextColor); 576 } 577 FillRect(lineRect, B_SOLID_LOW); 578 579 Model::Thread* thread = fModel->ThreadAt( 580 fFilterModel->SelectedItemAt(i)); 581 if (thread == NULL) 582 continue; 583 584 // compose name string 585 BString name = thread->Name(); 586 name << " (" << thread->ID() << ")"; 587 588 // draw the string 589 float y = lineRect.bottom - fFontInfo.fontHeight.descent + 1; 590 DrawString(name, BPoint(kThreadNameMargin, y)); 591 } 592 593 BRect bounds(Bounds()); 594 BRect lowerRect(0, (float)CountLines() * fFontInfo.lineHeight, 595 bounds.Width(), bounds.Height()); 596 if (lowerRect.Intersects(updateRect)) { 597 SetHighColor(fBackgroundColor); 598 FillRect(lowerRect); 599 } 600 } 601 602 virtual BSize MinSize() 603 { 604 return BSize(100, TotalHeight()); 605 } 606 607 virtual BSize PreferredSize() 608 { 609 return BSize(250, TotalHeight()); 610 } 611 612 virtual BSize MaxSize() 613 { 614 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 615 } 616 }; 617 618 619 class MainWindow::SchedulingPage::SchedulingView : public LineBaseView { 620 public: 621 struct Listener { 622 virtual ~Listener() 623 { 624 } 625 626 virtual void DataWidthChanged() = 0; 627 virtual void DataRangeChanged() = 0; 628 }; 629 630 private: 631 enum { 632 COLOR_RUNNING = 0, 633 COLOR_PREEMPTED, 634 COLOR_READY, 635 COLOR_IO_REQUEST, 636 COLOR_IO_OPERATION, 637 ACTIVITY_COLOR_COUNT 638 }; 639 640 public: 641 SchedulingView(FontInfo& fontInfo, ListSelectionModel* filterModel, 642 ListSelectionModel* selectionModel) 643 : 644 LineBaseView("scheduling", fontInfo, filterModel, selectionModel), 645 fStartTime(0), 646 fEndTime(0), 647 fNSecsPerPixel(1000000), 648 fLastMousePos(-1, -1), 649 fListener(NULL) 650 { 651 fActivityColors[COLOR_RUNNING].set_to(0, 255, 0); 652 fActivityColors[COLOR_PREEMPTED].set_to(255, 127, 0); 653 fActivityColors[COLOR_READY].set_to(255, 0, 0); 654 fActivityColors[COLOR_IO_REQUEST].set_to(127, 127, 255); 655 fActivityColors[COLOR_IO_OPERATION].set_to(0, 0, 200); 656 657 fActivitySelectedColors[COLOR_RUNNING] = tint_color( 658 fActivityColors[COLOR_RUNNING], B_DARKEN_2_TINT); 659 fActivitySelectedColors[COLOR_PREEMPTED] = tint_color( 660 fActivityColors[COLOR_PREEMPTED], B_DARKEN_2_TINT); 661 fActivitySelectedColors[COLOR_READY] = tint_color( 662 fActivityColors[COLOR_READY], B_DARKEN_2_TINT); 663 fActivitySelectedColors[COLOR_IO_REQUEST] = tint_color( 664 fActivityColors[COLOR_IO_REQUEST], B_DARKEN_2_TINT); 665 fActivitySelectedColors[COLOR_IO_OPERATION] = tint_color( 666 fActivityColors[COLOR_IO_OPERATION], B_DARKEN_2_TINT); 667 } 668 669 ~SchedulingView() 670 { 671 } 672 673 virtual void SetModel(Model* model) 674 { 675 LineBaseView::SetModel(model); 676 fSchedulingData.SetModel(model); 677 fStartTime = 0; 678 fEndTime = 0; 679 680 if (fListener != NULL) { 681 fListener->DataWidthChanged(); 682 fListener->DataRangeChanged(); 683 } 684 685 } 686 687 void SetListener(Listener* listener) 688 { 689 fListener = listener; 690 } 691 692 void UpdateScrollBar() 693 { 694 float width = Frame().Width(); 695 float dataWidth = std::max(width, MinSize().width); 696 697 if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) { 698 float range = dataWidth - width; 699 if (range > 0) { 700 scrollBar->SetRange(0, range); 701 scrollBar->SetProportion((width + 1) / (dataWidth + 1)); 702 scrollBar->SetSteps(fFontInfo.lineHeight, width + 1); 703 } else { 704 scrollBar->SetRange(0, 0); 705 scrollBar->SetProportion(1); 706 } 707 } 708 } 709 710 void GetDataRange(nanotime_t& _startTime, nanotime_t& _endTime) 711 { 712 _GetEventTimeRange(_startTime, _endTime); 713 } 714 715 virtual BSize MinSize() 716 { 717 nanotime_t timeSpan = fModel != NULL ? fModel->LastEventTime() : 0; 718 float width = std::max(float(timeSpan / fNSecsPerPixel), 100.0f); 719 return BSize(width, TotalHeight()); 720 } 721 722 virtual BSize MaxSize() 723 { 724 return BSize(MinSize().width, B_SIZE_UNLIMITED); 725 } 726 727 virtual void ScrollTo(BPoint where) 728 { 729 LineBaseView::ScrollTo(where); 730 fStartTime = 0; 731 fEndTime = 0; 732 733 if (fListener != NULL) 734 fListener->DataRangeChanged(); 735 } 736 737 virtual void MessageReceived(BMessage* message) 738 { 739 switch (message->what) { 740 case B_MOUSE_WHEEL_CHANGED: 741 { 742 // We're only interested in Shift + vertical wheel. 743 float deltaY; 744 if ((modifiers() & B_SHIFT_KEY) == 0 745 || message->FindFloat("be:wheel_delta_y", &deltaY) 746 != B_OK) { 747 break; 748 } 749 750 _Zoom(fLastMousePos.x, deltaY); 751 752 return; 753 } 754 } 755 756 LineBaseView::MessageReceived(message); 757 } 758 759 virtual void MouseMoved(BPoint where, uint32 code, 760 const BMessage* dragMessage) 761 { 762 fLastMousePos = where - LeftTop(); 763 764 // if (fDraggingStartPos.x < 0) 765 // return; 766 // 767 // ScrollBar(B_HORIZONTAL)->SetValue(fDraggingStartScrollValue 768 // + fDraggingStartPos.x - where.x); 769 } 770 771 virtual bool GetToolTipAt(BPoint point, BToolTip** _tip) 772 { 773 Model::Thread* thread; 774 nanotime_t time; 775 _GetThreadAndTimeAt(point, thread, time); 776 if (thread == NULL) 777 return false; 778 779 // get the thread's state 780 ThreadState threadState = UNKNOWN; 781 nanotime_t threadStateTime = time; 782 nanotime_t threadStateEndTime = time; 783 Model::ThreadWaitObjectGroup* threadWaitObject = NULL; 784 { 785 const Array<SchedulingEvent>& events 786 = fSchedulingData.EventsForThread(thread->Index()); 787 int32 eventCount = events.Size(); 788 789 int32 lower = 0; 790 int32 upper = eventCount - 1; 791 while (lower < upper) { 792 int32 mid = (lower + upper + 1) / 2; 793 const SchedulingEvent& event = events[mid]; 794 if (event.time > time) 795 upper = mid - 1; 796 else 797 lower = mid; 798 } 799 800 if (lower >= 0 && lower < eventCount) { 801 threadState = events[lower].state; 802 threadStateTime = events[lower].time; 803 threadWaitObject = events[lower].waitObject; 804 if (lower + 1 < eventCount) 805 threadStateEndTime = events[lower + 1].time; 806 else 807 threadStateEndTime = fModel->LastEventTime(); 808 } 809 } 810 811 // get the thread's I/O state 812 BObjectList<Model::IORequest> ioRequests; 813 _GetIORequests(thread, time, ioRequests); 814 815 // find out which threads are running 816 system_profiler_event_header** events = fModel->Events(); 817 size_t eventIndex = fModel->ClosestEventIndex(time + 1); 818 // find the first event after the one we're interested in; we'll 819 // skip it in the loop 820 821 int32 cpuCount = fModel->CountCPUs(); 822 int32 missingThreads = cpuCount; 823 Model::Thread* runningThreads[cpuCount]; 824 memset(runningThreads, 0, sizeof(Model::Thread*) * cpuCount); 825 826 while (missingThreads > 0 && eventIndex > 0) { 827 eventIndex--; 828 system_profiler_event_header* header = events[eventIndex]; 829 if (header->event != B_SYSTEM_PROFILER_THREAD_SCHEDULED 830 || runningThreads[header->cpu] != NULL) { 831 continue; 832 } 833 834 system_profiler_thread_scheduled* event 835 = (system_profiler_thread_scheduled*)(header + 1); 836 if (Model::Thread* thread = fModel->ThreadByID(event->thread)) { 837 runningThreads[header->cpu] = thread; 838 missingThreads--; 839 } 840 } 841 842 // create the tool tip 843 BString text; 844 text << "Time: " << format_nanotime(time) << "\n"; 845 text << "Thread: " << thread->Name() << " (" << thread->ID() << ")\n"; 846 text << "State: " << thread_state_name(threadState); 847 if (threadWaitObject != NULL) { 848 char objectName[32]; 849 snprintf(objectName, sizeof(objectName), "%#lx", 850 threadWaitObject->Object()); 851 text << " at " << wait_object_type_name(threadWaitObject->Type()) 852 << " \"" << threadWaitObject->Name() << "\" " 853 << objectName << "\n"; 854 } else 855 text << "\n"; 856 text << "For: " << format_nanotime(time - threadStateTime) << " of " 857 << format_nanotime(threadStateEndTime - threadStateTime); 858 859 if (!ioRequests.IsEmpty()) { 860 int32 scheduler = 0; 861 for (int32 i = 0; Model::IORequest* request = ioRequests.ItemAt(i); 862 i++) { 863 if (i == 0 || scheduler != request->Scheduler()) { 864 scheduler = request->Scheduler(); 865 text << "\nI/O channel " << scheduler << ":"; 866 } 867 text << "\n " << (request->IsWrite() ? "write" : "read") 868 << " request: " << request->BytesTransferred() << "/" 869 << request->Length() << " "; 870 if (request->IsFinished()) { 871 text << (request->Status() == B_OK ? "ok" : "failed"); 872 text << ", " 873 << format_nanotime( 874 request->FinishedTime() - request->ScheduledTime()); 875 } else 876 text << "unfinished"; 877 878 for (size_t k = 0; k < request->operationCount; k++) { 879 Model::IOOperation& operation = request->operations[k]; 880 if (operation.startedEvent->time > fModel->BaseTime() + time 881 || (operation.IsFinished() 882 && operation.FinishedTime() 883 <= fModel->BaseTime() + time)) { 884 continue; 885 } 886 887 text << "\n " << (operation.IsWrite() ? "write" : "read") 888 << ": " << operation.BytesTransferred() << "/" 889 << operation.Length() << " "; 890 if (operation.IsFinished()) { 891 text << (request->Status() == B_OK ? "ok" : "failed"); 892 text << ", " 893 << format_nanotime(operation.FinishedTime() 894 - operation.StartedTime()); 895 } else 896 text << "unfinished"; 897 } 898 } 899 } 900 901 text << "\n\n"; 902 text << "Running threads:"; 903 for (int32 i = 0; i < cpuCount; i++) { 904 text << "\n " << i << ": "; 905 if (Model::Thread* thread = runningThreads[i]) 906 text << thread->Name() << " (" << thread->ID() << ")"; 907 else 908 text << "?"; 909 } 910 911 *_tip = new(std::nothrow) BTextToolTip(text); 912 return *_tip != NULL; 913 } 914 915 virtual void Draw(BRect updateRect) 916 { 917 if (fModel == NULL || fSchedulingData.InitCheck() != B_OK) 918 return; 919 920 _UpdateData(); 921 922 // draw the events 923 924 // get the lines intersecting with the update rect 925 int32 minLine, maxLine; 926 GetLineRange(updateRect, minLine, maxLine); 927 928 for (int32 i = minLine; i <= maxLine; i++) { 929 // draw the background 930 const rgb_color* activityColors; 931 if (fSelectionModel->IsItemSelected(i)) { 932 activityColors = fActivitySelectedColors; 933 SetLowColor(fSelectedBackgroundColor); 934 } else { 935 activityColors = fActivityColors; 936 SetLowColor(fBackgroundColor); 937 } 938 BRect lineRect = LineRect(i); 939 FillRect(lineRect, B_SOLID_LOW); 940 941 Model::Thread* thread = fModel->ThreadAt( 942 fFilterModel->SelectedItemAt(i)); 943 if (thread == NULL) 944 continue; 945 946 BRect schedulingLineRect = lineRect; 947 schedulingLineRect.bottom -= lineRect.IntegerHeight() / 2; 948 BRect ioLineRect = lineRect; 949 ioLineRect.top = schedulingLineRect.bottom + 1; 950 951 const Array<SchedulingEvent>& events 952 = fSchedulingData.EventsForThread(thread->Index()); 953 954 int32 eventCount = events.Size(); 955 //printf("drawing events for thread %ld: %ld events\n", thread->Index(), eventCount); 956 for (int32 k = 0; k < eventCount; k++) { 957 const SchedulingEvent& event = events[k]; 958 nanotime_t startTime = std::max(event.time, fStartTime); 959 nanotime_t endTime = k + 1 < eventCount 960 ? std::min(events[k + 1].time, fEndTime) : fEndTime; 961 962 switch (event.state) { 963 case RUNNING: 964 case STILL_RUNNING: 965 SetHighColor(activityColors[COLOR_RUNNING]); 966 break; 967 case PREEMPTED: 968 SetHighColor(activityColors[COLOR_PREEMPTED]); 969 break; 970 case READY: 971 SetHighColor(activityColors[COLOR_READY]); 972 break; 973 case WAITING: 974 case UNKNOWN: 975 default: 976 continue; 977 } 978 979 BRect rect = schedulingLineRect; 980 rect.left = startTime / fNSecsPerPixel; 981 rect.right = endTime / fNSecsPerPixel - 1; 982 FillRect(rect); 983 } 984 985 const Array<IOSchedulingEvent>& ioEvents 986 = fSchedulingData.IOEventsForThread(thread->Index()); 987 eventCount = ioEvents.Size(); 988 989 for (int32 k = 0; k < eventCount; k++) { 990 const IOSchedulingEvent& event = ioEvents[k]; 991 nanotime_t startTime = std::max(event.time, fStartTime); 992 nanotime_t endTime = k + 1 < eventCount 993 ? std::min(ioEvents[k + 1].time, fEndTime) : fEndTime; 994 995 switch (event.state) { 996 case IO_SCHEDULING_STATE_PENDING_REQUEST: 997 SetHighColor(activityColors[COLOR_IO_REQUEST]); 998 break; 999 case IO_SCHEDULING_STATE_PENDING_OPERATION: 1000 SetHighColor(activityColors[COLOR_IO_OPERATION]); 1001 break; 1002 case IO_SCHEDULING_STATE_IDLE: 1003 default: 1004 continue; 1005 } 1006 1007 BRect rect = ioLineRect; 1008 rect.left = startTime / fNSecsPerPixel; 1009 rect.right = endTime / fNSecsPerPixel - 1; 1010 FillRect(rect); 1011 } 1012 } 1013 } 1014 1015 private: 1016 // shorthands for the longish structure names 1017 typedef system_profiler_thread_enqueued_in_run_queue 1018 thread_enqueued_in_run_queue; 1019 typedef system_profiler_thread_removed_from_run_queue 1020 thread_removed_from_run_queue; 1021 1022 private: 1023 void _UpdateData() 1024 { 1025 // get the interesting event time range 1026 nanotime_t startTime; 1027 nanotime_t endTime; 1028 _GetEventTimeRange(startTime, endTime); 1029 1030 if (startTime == fStartTime && endTime == fEndTime) 1031 return; 1032 fStartTime = startTime; 1033 fEndTime = endTime; 1034 1035 fSchedulingData.Clear(); 1036 1037 //printf("MainWindow::SchedulingPage::SchedulingView::_UpdateData()\n"); 1038 //printf(" time range: %lld - %lld\n", startTime, endTime); 1039 1040 // get a scheduling state close to our start time 1041 const Model::CompactSchedulingState* compactState 1042 = fModel->ClosestSchedulingState(startTime); 1043 //printf(" compactState: %p\n", compactState); 1044 fState.Clear(); 1045 status_t error = fState.Init(compactState); 1046 if (error != B_OK) 1047 return; 1048 1049 // init the event stream 1050 BDebugEventInputStream input; 1051 error = input.SetTo((uint8*)fModel->EventData(), 1052 fModel->EventDataSize(), false); 1053 if (error == B_OK && compactState != NULL) 1054 error = input.Seek(compactState->EventOffset()); 1055 //printf(" event offset: %lld, input init error: %s\n", compactState != NULL ? compactState->EventOffset() : 0, strerror(error)); 1056 if (error != B_OK) 1057 return; 1058 1059 fSchedulingData.SetRecordingEnabled( 1060 fState.LastEventTime() >= startTime); 1061 1062 // add the initial thread states to the scheduling data 1063 if (compactState != NULL) { 1064 int32 threadStateCount = compactState->CountThreadsStates(); 1065 for (int32 i = 0; i < threadStateCount; i++) { 1066 const Model::CompactThreadSchedulingState* threadState 1067 = compactState->ThreadStateAt(i); 1068 switch (threadState->state) { 1069 case RUNNING: 1070 case STILL_RUNNING: 1071 fSchedulingData.AddRun(threadState->thread, 1072 threadState->lastTime); 1073 break; 1074 case PREEMPTED: 1075 fSchedulingData.AddPreemption(threadState->thread, 1076 threadState->lastTime); 1077 break; 1078 case READY: 1079 fSchedulingData.AddLatency(threadState->thread, 1080 threadState->lastTime); 1081 break; 1082 case WAITING: 1083 { 1084 Model::ThreadWaitObjectGroup* group = NULL; 1085 if (threadState->waitObject != NULL) { 1086 group = fModel->ThreadWaitObjectGroupFor( 1087 threadState->ID(), 1088 threadState->waitObject->Type(), 1089 threadState->waitObject->Object()); 1090 } 1091 fSchedulingData.AddWait(threadState->thread, 1092 threadState->lastTime, group); 1093 break; 1094 } 1095 case UNKNOWN: 1096 default: 1097 break; 1098 } 1099 } 1100 } 1101 1102 // process the events 1103 while (true) { 1104 // get next event 1105 uint32 event; 1106 uint32 cpu; 1107 const void* buffer; 1108 off_t offset; 1109 ssize_t bufferSize = input.ReadNextEvent(&event, &cpu, &buffer, 1110 &offset); 1111 if (bufferSize < 0) 1112 { 1113 printf("failed to read event!\n"); 1114 return; 1115 } 1116 if (buffer == NULL) 1117 break; 1118 1119 // process the event 1120 error = _ProcessEvent(event, cpu, buffer, bufferSize); 1121 if (error != B_OK) 1122 return; 1123 1124 if (fState.LastEventTime() >= startTime) 1125 fSchedulingData.SetRecordingEnabled(true); 1126 if (fState.LastEventTime() >= endTime) 1127 break; 1128 } 1129 1130 // process each thread's I/O events 1131 nanotime_t lastEventTime = fModel->BaseTime() - fModel->LastEventTime(); 1132 int32 threadCount = fModel->CountThreads(); 1133 for (int32 i = 0; i < threadCount; i++) { 1134 Model::Thread* thread = fModel->ThreadAt(i); 1135 Model::IORequest** requests = thread->IORequests(); 1136 size_t requestCount = thread->CountIORequests(); 1137 if (requestCount == 0) 1138 continue; 1139 1140 // first request 1141 nanotime_t clusterStart = requests[0]->scheduledEvent->time; 1142 nanotime_t clusterEnd = requests[0]->finishedEvent != NULL 1143 ? requests[0]->finishedEvent->time : lastEventTime; 1144 BObjectList<Model::IOOperation> operations; 1145 1146 // add first request operations 1147 for (size_t l = 0; l < requests[0]->operationCount; l++) 1148 operations.AddItem(&requests[0]->operations[l]); 1149 1150 for (size_t k = 1; k < requestCount; k++) { 1151 nanotime_t requestStart = requests[k]->scheduledEvent->time; 1152 nanotime_t requestEnd = requests[k]->finishedEvent != NULL 1153 ? requests[k]->finishedEvent->time : lastEventTime; 1154 1155 if (requestStart >= endTime) 1156 break; 1157 1158 if (requestStart > clusterEnd) { 1159 if (clusterEnd > startTime) { 1160 _AddThreadIOData(thread, clusterStart, clusterEnd, 1161 operations); 1162 } 1163 operations.MakeEmpty(); 1164 clusterStart = requestStart; 1165 clusterEnd = requestEnd; 1166 } else 1167 clusterEnd = std::max(clusterEnd, requestEnd); 1168 1169 // add request operations 1170 for (size_t l = 0; l < requests[k]->operationCount; l++) 1171 operations.AddItem(&requests[k]->operations[l]); 1172 } 1173 1174 if (clusterEnd > startTime) 1175 _AddThreadIOData(thread, clusterStart, clusterEnd, operations); 1176 } 1177 } 1178 1179 void _AddThreadIOData(Model::Thread* thread, nanotime_t startTime, 1180 nanotime_t endTime, BObjectList<Model::IOOperation>& operations) 1181 { 1182 //printf(" IORequest cluster: %lld - %lld\n", startTime, endTime); 1183 // start in pending request state 1184 fSchedulingData.AddIOState(thread, startTime - fModel->BaseTime(), 1185 IO_SCHEDULING_STATE_PENDING_REQUEST); 1186 1187 int32 operationCount = operations.CountItems(); 1188 if (operationCount == 0) 1189 return; 1190 1191 // sort the operations 1192 if (operationCount > 1) 1193 operations.SortItems(Model::IOOperation::CompareByTime); 1194 1195 nanotime_t lastEventTime = fModel->BaseTime() + fModel->LastEventTime(); 1196 1197 // process the operations 1198 Model::IOOperation* operation = operations.ItemAt(0); 1199 nanotime_t clusterStart = operation->startedEvent->time; 1200 nanotime_t clusterEnd = operation->finishedEvent != NULL 1201 ? operation->finishedEvent->time : lastEventTime; 1202 1203 for (int32 i = 1; i < operationCount; i++) { 1204 operation = operations.ItemAt(i); 1205 nanotime_t operationStart = operation->startedEvent->time; 1206 nanotime_t operationEnd = operation->finishedEvent != NULL 1207 ? operation->finishedEvent->time : lastEventTime; 1208 1209 if (operationStart > clusterEnd) { 1210 fSchedulingData.AddIOState(thread, 1211 clusterStart - fModel->BaseTime(), 1212 IO_SCHEDULING_STATE_PENDING_OPERATION); 1213 fSchedulingData.AddIOState(thread, 1214 clusterEnd - fModel->BaseTime(), 1215 IO_SCHEDULING_STATE_PENDING_REQUEST); 1216 1217 clusterStart = operationStart; 1218 clusterEnd = operationEnd; 1219 } else 1220 clusterEnd = std::max(clusterEnd, operationEnd); 1221 } 1222 1223 // add the last cluster 1224 fSchedulingData.AddIOState(thread, clusterStart - fModel->BaseTime(), 1225 IO_SCHEDULING_STATE_PENDING_OPERATION); 1226 fSchedulingData.AddIOState(thread, clusterEnd - fModel->BaseTime(), 1227 IO_SCHEDULING_STATE_PENDING_REQUEST); 1228 1229 // after the end of the request cluster, state is idle again 1230 fSchedulingData.AddIOState(thread, endTime - fModel->BaseTime(), 1231 IO_SCHEDULING_STATE_IDLE); 1232 } 1233 1234 void _GetEventTimeRange(nanotime_t& _startTime, nanotime_t& _endTime) 1235 { 1236 if (fModel != NULL) { 1237 float scrollOffset = _ScrollOffset(); 1238 _startTime = (nanotime_t)scrollOffset * fNSecsPerPixel; 1239 _endTime = (nanotime_t)(scrollOffset + Bounds().Width() + 1) 1240 * fNSecsPerPixel; 1241 } else { 1242 _startTime = 0; 1243 _endTime = 1; 1244 } 1245 } 1246 1247 void _GetThreadAndTimeAt(BPoint point, Model::Thread*& _thread, 1248 nanotime_t& _time) 1249 { 1250 _thread = fModel->ThreadAt( 1251 fFilterModel->SelectedItemAt(LineAt(point))); 1252 _time = (nanotime_t)point.x * fNSecsPerPixel; 1253 } 1254 1255 void _GetIORequests(Model::Thread* thread, nanotime_t time, 1256 BObjectList<Model::IORequest>& ioRequests) 1257 { 1258 // find the time in the event data 1259 const Array<IOSchedulingEvent>& events 1260 = fSchedulingData.IOEventsForThread(thread->Index()); 1261 int32 eventCount = events.Size(); 1262 1263 int32 lower = 0; 1264 int32 upper = eventCount - 1; 1265 while (lower < upper) { 1266 int32 mid = (lower + upper + 1) / 2; 1267 const IOSchedulingEvent& event = events[mid]; 1268 if (event.time > time) 1269 upper = mid - 1; 1270 else 1271 lower = mid; 1272 } 1273 1274 if (lower < 0 || lower >= eventCount) 1275 return; 1276 1277 if (events[lower].state == IO_SCHEDULING_STATE_IDLE) 1278 return; 1279 1280 // find the beginning and end of the I/O request cluster 1281 while (lower > 0) { 1282 if (events[lower - 1].state == IO_SCHEDULING_STATE_IDLE) 1283 break; 1284 lower--; 1285 } 1286 1287 while (upper + 1 < eventCount) { 1288 upper++; 1289 if (events[upper].state == IO_SCHEDULING_STATE_IDLE) 1290 break; 1291 } 1292 1293 nanotime_t startTime = events[lower].time; 1294 nanotime_t endTime = events[upper].state == IO_SCHEDULING_STATE_IDLE 1295 ? events[upper].time : fEndTime; 1296 1297 // convert to absolute time -- as used by the I/O requests 1298 startTime += fModel->BaseTime(); 1299 endTime += fModel->BaseTime(); 1300 time += fModel->BaseTime(); 1301 1302 // collect the requests in the range 1303 Model::IORequest** requests = thread->IORequests(); 1304 size_t requestCount = thread->CountIORequests(); 1305 size_t index = thread->ClosestRequestStartIndex(startTime); 1306 for (; index < requestCount; index++) { 1307 Model::IORequest* request = requests[index]; 1308 1309 if (request->ScheduledTime() >= endTime) 1310 break; 1311 1312 if (request->ScheduledTime() > time 1313 || (request->finishedEvent != NULL 1314 && request->finishedEvent->time <= time)) { 1315 continue; 1316 } 1317 1318 ioRequests.AddItem(request); 1319 } 1320 1321 if (ioRequests.CountItems() > 1) 1322 ioRequests.SortItems(Model::IORequest::CompareSchedulerTime); 1323 } 1324 1325 float _ScrollOffset() const 1326 { 1327 if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) 1328 return scrollBar->Value(); 1329 return 0; 1330 } 1331 1332 void _Zoom(float x, float steps) 1333 { 1334 if (steps == 0 || fModel == NULL) 1335 return; 1336 1337 // compute the domain point where to zoom in 1338 float scrollOffset = _ScrollOffset(); 1339 double timeForX = (scrollOffset + x) * fNSecsPerPixel; 1340 1341 uint32 factor = 4; 1342 if (steps < 0) { 1343 steps = -steps; 1344 factor = 1; 1345 } 1346 1347 uint32 oldNsecPerPixel = fNSecsPerPixel; 1348 for (; steps > 0; steps--) 1349 fNSecsPerPixel = fNSecsPerPixel * factor / 2; 1350 1351 if (fNSecsPerPixel < 1) 1352 fNSecsPerPixel = 1; 1353 else if (fNSecsPerPixel > 1000000) 1354 fNSecsPerPixel = 1000000; 1355 1356 if (fNSecsPerPixel == oldNsecPerPixel) 1357 return; 1358 1359 Invalidate(); 1360 1361 UpdateScrollBar(); 1362 if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) 1363 scrollBar->SetValue(timeForX / fNSecsPerPixel - x); 1364 1365 if (fListener != NULL) { 1366 fListener->DataWidthChanged(); 1367 fListener->DataRangeChanged(); 1368 } 1369 } 1370 1371 inline void _UpdateLastEventTime(nanotime_t time) 1372 { 1373 fState.SetLastEventTime(time - fModel->BaseTime()); 1374 } 1375 1376 status_t _ProcessEvent(uint32 event, uint32 cpu, const void* buffer, 1377 size_t size) 1378 { 1379 switch (event) { 1380 case B_SYSTEM_PROFILER_THREAD_ADDED: 1381 _HandleThreadAdded((system_profiler_thread_added*)buffer); 1382 break; 1383 1384 case B_SYSTEM_PROFILER_THREAD_REMOVED: 1385 _HandleThreadRemoved((system_profiler_thread_removed*)buffer); 1386 break; 1387 1388 case B_SYSTEM_PROFILER_THREAD_SCHEDULED: 1389 _HandleThreadScheduled( 1390 (system_profiler_thread_scheduled*)buffer); 1391 break; 1392 1393 case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE: 1394 _HandleThreadEnqueuedInRunQueue( 1395 (thread_enqueued_in_run_queue*)buffer); 1396 break; 1397 1398 case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE: 1399 _HandleThreadRemovedFromRunQueue( 1400 (thread_removed_from_run_queue*)buffer); 1401 break; 1402 1403 default: 1404 break; 1405 } 1406 1407 return B_OK; 1408 } 1409 1410 void _HandleThreadAdded(system_profiler_thread_added* event) 1411 { 1412 //printf(" thread added: %ld\n", event->thread); 1413 // do we know the thread already? 1414 Model::ThreadSchedulingState* info = fState.LookupThread(event->thread); 1415 if (info != NULL) { 1416 // TODO: ? 1417 return; 1418 } 1419 1420 Model::Thread* thread = fModel->ThreadByID(event->thread); 1421 if (thread == NULL) 1422 return; 1423 1424 // create and add a ThreadSchedulingState 1425 info = new(std::nothrow) Model::ThreadSchedulingState(thread); 1426 if (info == NULL) 1427 return; 1428 1429 fState.InsertThread(info); 1430 } 1431 1432 void _HandleThreadRemoved(system_profiler_thread_removed* event) 1433 { 1434 //printf(" thread removed: %ld\n", event->thread); 1435 // Model::ThreadSchedulingState* thread = fState.LookupThread( 1436 // event->thread); 1437 // if (thread != NULL) { 1438 // fState.RemoveThread(thread); 1439 // delete thread; 1440 //// TODO: The thread will be unscheduled in a moment and cause a warning! So 1441 //// maybe keep it around in a separate hash table a bit longer? 1442 // } 1443 } 1444 1445 void _HandleThreadScheduled(system_profiler_thread_scheduled* event) 1446 { 1447 _UpdateLastEventTime(event->time); 1448 1449 Model::ThreadSchedulingState* thread = fState.LookupThread( 1450 event->thread); 1451 if (thread == NULL) { 1452 printf("Schedule event for unknown thread: %" B_PRId32 "\n", 1453 event->thread); 1454 return; 1455 } 1456 1457 thread->lastTime = fState.LastEventTime(); 1458 thread->state = RUNNING; 1459 fSchedulingData.AddRun(thread->thread, fState.LastEventTime()); 1460 1461 // unscheduled thread 1462 1463 if (event->thread == event->previous_thread) 1464 return; 1465 1466 thread = fState.LookupThread(event->previous_thread); 1467 if (thread == NULL) { 1468 printf("Schedule event for unknown previous thread: %" B_PRId32 1469 "\n", event->previous_thread); 1470 return; 1471 } 1472 1473 if (thread->state == STILL_RUNNING) { 1474 // thread preempted 1475 fSchedulingData.AddPreemption(thread->thread, 1476 fState.LastEventTime()); 1477 1478 thread->lastTime = fState.LastEventTime(); 1479 thread->state = PREEMPTED; 1480 } else if (thread->state == RUNNING) { 1481 // thread starts waiting (it hadn't been added to the run 1482 // queue before being unscheduled) 1483 if (event->previous_thread_state == B_THREAD_WAITING) { 1484 addr_t waitObject = event->previous_thread_wait_object; 1485 switch (event->previous_thread_wait_object_type) { 1486 case THREAD_BLOCK_TYPE_SNOOZE: 1487 case THREAD_BLOCK_TYPE_SIGNAL: 1488 waitObject = 0; 1489 break; 1490 case THREAD_BLOCK_TYPE_SEMAPHORE: 1491 case THREAD_BLOCK_TYPE_CONDITION_VARIABLE: 1492 case THREAD_BLOCK_TYPE_MUTEX: 1493 case THREAD_BLOCK_TYPE_RW_LOCK: 1494 case THREAD_BLOCK_TYPE_OTHER: 1495 case THREAD_BLOCK_TYPE_OTHER_OBJECT: 1496 default: 1497 break; 1498 } 1499 1500 fSchedulingData.AddWait(thread->thread, fState.LastEventTime(), 1501 fModel->ThreadWaitObjectGroupFor(thread->ID(), 1502 event->previous_thread_wait_object_type, waitObject)); 1503 } else { 1504 fSchedulingData.AddUnspecifiedWait(thread->thread, 1505 fState.LastEventTime()); 1506 } 1507 1508 thread->lastTime = fState.LastEventTime(); 1509 thread->state = WAITING; 1510 } else if (thread->state == UNKNOWN) { 1511 uint32 threadState = event->previous_thread_state; 1512 if (threadState == B_THREAD_WAITING 1513 || threadState == B_THREAD_SUSPENDED) { 1514 fSchedulingData.AddWait(thread->thread, fState.LastEventTime(), 1515 NULL); 1516 thread->lastTime = fState.LastEventTime(); 1517 thread->state = WAITING; 1518 } else if (threadState == B_THREAD_READY) { 1519 thread->lastTime = fState.LastEventTime(); 1520 thread->state = PREEMPTED; 1521 fSchedulingData.AddPreemption(thread->thread, 1522 fState.LastEventTime()); 1523 } 1524 } 1525 } 1526 1527 void _HandleThreadEnqueuedInRunQueue(thread_enqueued_in_run_queue* event) 1528 { 1529 _UpdateLastEventTime(event->time); 1530 1531 Model::ThreadSchedulingState* thread = fState.LookupThread( 1532 event->thread); 1533 if (thread == NULL) { 1534 printf("Enqueued in run queue event for unknown thread: %" B_PRId32 1535 "\n", event->thread); 1536 return; 1537 } 1538 1539 if (thread->state == RUNNING || thread->state == STILL_RUNNING) { 1540 // Thread was running and is reentered into the run queue. This 1541 // is done by the scheduler, if the thread remains ready. 1542 thread->state = STILL_RUNNING; 1543 } else { 1544 // Thread was waiting and is ready now. 1545 nanotime_t diffTime = fState.LastEventTime() - thread->lastTime; 1546 if (thread->waitObject != NULL) { 1547 thread->waitObject->AddWait(diffTime); 1548 thread->waitObject = NULL; 1549 } 1550 1551 fSchedulingData.AddLatency(thread->thread, fState.LastEventTime()); 1552 thread->lastTime = fState.LastEventTime(); 1553 thread->state = READY; 1554 } 1555 } 1556 1557 void _HandleThreadRemovedFromRunQueue(thread_removed_from_run_queue* event) 1558 { 1559 _UpdateLastEventTime(event->time); 1560 1561 Model::ThreadSchedulingState* thread = fState.LookupThread( 1562 event->thread); 1563 if (thread == NULL) { 1564 printf("Removed from run queue event for unknown thread: %" B_PRId32 1565 "\n", event->thread); 1566 return; 1567 } 1568 1569 // This really only happens when the thread priority is changed 1570 // while the thread is ready. 1571 fSchedulingData.AddUnspecifiedWait(thread->thread, 1572 fState.LastEventTime()); 1573 1574 thread->lastTime = fState.LastEventTime(); 1575 thread->state = WAITING; 1576 } 1577 1578 private: 1579 Model::SchedulingState fState; 1580 SchedulingData fSchedulingData; 1581 nanotime_t fStartTime; 1582 nanotime_t fEndTime; 1583 uint32 fNSecsPerPixel; 1584 BPoint fLastMousePos; 1585 Listener* fListener; 1586 1587 rgb_color fActivityColors[ACTIVITY_COLOR_COUNT]; 1588 rgb_color fActivitySelectedColors[ACTIVITY_COLOR_COUNT]; 1589 }; 1590 1591 1592 class MainWindow::SchedulingPage::ViewPort : public BaseView, 1593 private HeaderListener, private SchedulingView::Listener { 1594 public: 1595 ViewPort(HeaderView* headerView, ThreadsView* threadsView, 1596 SchedulingView* schedulingView, FontInfo& fontInfo, 1597 ListSelectionModel* filterModel) 1598 : 1599 BaseView("viewport", fontInfo, filterModel), 1600 fHeaderView(headerView), 1601 fThreadsView(threadsView), 1602 fSchedulingView(schedulingView) 1603 { 1604 AddChild(threadsView); 1605 AddChild(schedulingView); 1606 1607 fSchedulingView->SetListener(this); 1608 1609 HeaderModel* headerModel = fHeaderView->Model(); 1610 1611 Header* header = new Header((int32)fThreadsView->PreferredSize().width, 1612 (int32)fThreadsView->MinSize().width, 1613 (int32)fThreadsView->MaxSize().width, 1614 (int32)fThreadsView->PreferredSize().width, 0); 1615 header->SetValue("Thread"); 1616 headerModel->AddHeader(header); 1617 header->AddListener(this); 1618 1619 header = new Header(100, 100, 10000, 200, 1); 1620 // TODO: Set header width correctly! 1621 header->SetValue("Activity"); 1622 header->SetHeaderRenderer(new TimelineHeaderRenderer); 1623 headerModel->AddHeader(header); 1624 // header->AddListener(this); 1625 } 1626 1627 ~ViewPort() 1628 { 1629 } 1630 1631 void RemoveListeners() 1632 { 1633 fHeaderView->Model()->HeaderAt(0)->RemoveListener(this); 1634 } 1635 1636 void UpdateScrollBars() 1637 { 1638 float height = Frame().Height(); 1639 float dataHeight = std::max(height, fSchedulingView->MinSize().height); 1640 1641 fSchedulingView->UpdateScrollBar(); 1642 1643 if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) { 1644 float range = dataHeight - height; 1645 if (range > 0) { 1646 scrollBar->SetRange(0, range); 1647 scrollBar->SetProportion( 1648 (height + 1) / (dataHeight + 1)); 1649 scrollBar->SetSteps(fFontInfo.lineHeight, height + 1); 1650 } else { 1651 scrollBar->SetRange(0, 0); 1652 scrollBar->SetProportion(1); 1653 } 1654 } 1655 } 1656 1657 virtual BSize MinSize() 1658 { 1659 return BSize(10, 10); 1660 } 1661 1662 virtual BSize MaxSize() 1663 { 1664 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 1665 } 1666 1667 BSize PreferredSize() 1668 { 1669 BSize threadsViewSize(fThreadsView->PreferredSize()); 1670 BSize schedulingViewSize(fSchedulingView->PreferredSize()); 1671 return BSize(BLayoutUtils::AddDistances( 1672 threadsViewSize.width + kViewSeparationMargin, 1673 schedulingViewSize.width), 1674 std::max(threadsViewSize.height, schedulingViewSize.height)); 1675 } 1676 1677 void DoLayout() 1678 { 1679 float width = Bounds().Width(); 1680 float height = fThreadsView->MinSize().Height(); 1681 1682 float threadsViewWidth = 0; 1683 if (fHeaderView->Model()->HeaderAt(0) != NULL) 1684 threadsViewWidth = fHeaderView->HeaderFrame(0).Width(); 1685 threadsViewWidth = std::max(threadsViewWidth, 1686 fThreadsView->MinSize().width); 1687 threadsViewWidth = std::min(threadsViewWidth, 1688 fThreadsView->MaxSize().width); 1689 1690 float schedulingViewLeft = threadsViewWidth + 1 + kViewSeparationMargin; 1691 float schedulingViewWidth = width - schedulingViewLeft; 1692 1693 fThreadsView->MoveTo(0, 0); 1694 fThreadsView->ResizeTo(threadsViewWidth, height); 1695 1696 fSchedulingView->MoveTo(schedulingViewLeft, 0); 1697 fSchedulingView->ResizeTo(schedulingViewWidth, height); 1698 1699 if (Header* header = fHeaderView->Model()->HeaderAt(1)) { 1700 float headerWidth = schedulingViewWidth + 1 + kViewSeparationMargin; 1701 header->SetMinWidth(headerWidth); 1702 header->SetMaxWidth(headerWidth); 1703 header->SetPreferredWidth(headerWidth); 1704 header->SetWidth(headerWidth); 1705 } 1706 1707 UpdateScrollBars(); 1708 } 1709 1710 private: 1711 virtual void HeaderWidthChanged(Header* header) 1712 { 1713 if (header->ModelIndex() == 0) 1714 InvalidateLayout(); 1715 } 1716 1717 virtual void DataWidthChanged() 1718 { 1719 } 1720 1721 virtual void DataRangeChanged() 1722 { 1723 Header* header = fHeaderView->Model()->HeaderAt(1); 1724 if (header == NULL) 1725 return; 1726 1727 nanotime_t startTime; 1728 nanotime_t endTime; 1729 fSchedulingView->GetDataRange(startTime, endTime); 1730 TimeRange* range = new(std::nothrow) TimeRange(startTime, endTime); 1731 if (range != NULL) { 1732 header->SetValue(BVariant(range, 'time')); 1733 range->ReleaseReference(); 1734 } 1735 } 1736 1737 private: 1738 HeaderView* fHeaderView; 1739 ThreadsView* fThreadsView; 1740 SchedulingView* fSchedulingView; 1741 }; 1742 1743 1744 MainWindow::SchedulingPage::SchedulingPage(MainWindow* parent) 1745 : 1746 BGroupView(B_VERTICAL), 1747 fParent(parent), 1748 fModel(NULL), 1749 fScrollView(NULL), 1750 fViewPort(NULL), 1751 fThreadsView(NULL), 1752 fSchedulingView(NULL), 1753 fFilterModel(), 1754 fSelectionModel() 1755 { 1756 SetName("Scheduling"); 1757 1758 be_plain_font->GetHeight(&fFontInfo.fontHeight); 1759 fFontInfo.lineHeight = ceilf(fFontInfo.fontHeight.ascent) 1760 + ceilf(fFontInfo.fontHeight.descent); 1761 1762 HeaderView* headerView = new HeaderView; 1763 BView* scrollChild = BLayoutBuilder::Group<>(B_VERTICAL) 1764 .Add(headerView) 1765 .Add(fViewPort = new ViewPort(headerView, 1766 fThreadsView = new ThreadsView(fFontInfo, &fFilterModel, 1767 &fSelectionModel), 1768 fSchedulingView = new SchedulingView(fFontInfo, &fFilterModel, 1769 &fSelectionModel), 1770 fFontInfo, &fFilterModel)).View(); 1771 ; 1772 1773 AddChild(fScrollView = new BScrollView("scroll", scrollChild, 0, true, 1774 true)); 1775 1776 fScrollView->ScrollBar(B_HORIZONTAL)->SetTarget(fSchedulingView); 1777 fScrollView->ScrollBar(B_VERTICAL)->SetTarget(fViewPort); 1778 fViewPort->UpdateScrollBars(); 1779 } 1780 1781 1782 MainWindow::SchedulingPage::~SchedulingPage() 1783 { 1784 fViewPort->RemoveListeners(); 1785 fThreadsView->RemoveListeners(); 1786 fSchedulingView->RemoveListeners(); 1787 } 1788 1789 1790 void 1791 MainWindow::SchedulingPage::SetModel(Model* model) 1792 { 1793 if (model == fModel) 1794 return; 1795 1796 fSelectionModel.Clear(); 1797 fFilterModel.Clear(); 1798 1799 if (fModel != NULL) { 1800 } 1801 1802 fModel = model; 1803 1804 if (fModel != NULL) { 1805 fFilterModel.SelectItems(0, fModel->CountThreads(), false); 1806 } 1807 1808 fViewPort->SetModel(fModel); 1809 fThreadsView->SetModel(fModel); 1810 fSchedulingView->SetModel(fModel); 1811 } 1812