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
SchedulingEventMainWindow::SchedulingPage::SchedulingEvent59 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
IOSchedulingEventMainWindow::SchedulingPage::IOSchedulingEvent74 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:
SchedulingData()85 SchedulingData()
86 :
87 fModel(NULL),
88 fDataArrays(NULL),
89 fIODataArrays(NULL),
90 fRecordingEnabled(false)
91 {
92 }
93
InitCheck() const94 status_t InitCheck() const
95 {
96 return fDataArrays != NULL && fIODataArrays != NULL
97 ? B_OK : B_NO_MEMORY;
98 }
99
SetModel(Model * model)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
SetRecordingEnabled(bool enabled)116 void SetRecordingEnabled(bool enabled)
117 {
118 fRecordingEnabled = enabled;
119 }
120
Clear()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
EventsForThread(int32 index)136 const Array<SchedulingEvent>& EventsForThread(int32 index)
137 {
138 return fDataArrays[index];
139 }
140
IOEventsForThread(int32 index)141 const Array<IOSchedulingEvent>& IOEventsForThread(int32 index)
142 {
143 return fIODataArrays[index];
144 }
145
AddState(Model::Thread * thread,nanotime_t time,ThreadState state,Model::ThreadWaitObjectGroup * waitObject)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
AddIOState(Model::Thread * thread,nanotime_t time,uint32 state)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
AddRun(Model::Thread * thread,nanotime_t time)174 void AddRun(Model::Thread* thread, nanotime_t time)
175 {
176 AddState(thread, time, RUNNING, NULL);
177 }
178
AddLatency(Model::Thread * thread,nanotime_t time)179 void AddLatency(Model::Thread* thread, nanotime_t time)
180 {
181 AddState(thread, time, READY, NULL);
182 }
183
AddPreemption(Model::Thread * thread,nanotime_t time)184 void AddPreemption(Model::Thread* thread, nanotime_t time)
185 {
186 AddState(thread, time, PREEMPTED, NULL);
187 }
188
AddWait(Model::Thread * thread,nanotime_t time,Model::ThreadWaitObjectGroup * waitObject)189 void AddWait(Model::Thread* thread, nanotime_t time,
190 Model::ThreadWaitObjectGroup* waitObject)
191 {
192 AddState(thread, time, WAITING, waitObject);
193 }
194
AddUnspecifiedWait(Model::Thread * thread,nanotime_t time)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
TimeRangeMainWindow::SchedulingPage::TimeRange216 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:
TimelineHeaderRenderer()228 TimelineHeaderRenderer()
229 :
230 fAxis(new NanotimeChartAxisLegendSource, new StringChartLegendRenderer)
231 {
232 fAxis.SetLocation(CHART_AXIS_TOP);
233 }
234
HeaderHeight(BView * view,const Header * header)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
PreferredHeaderWidth(BView * view,const Header * header)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
DrawHeader(BView * view,BRect frame,BRect updateRect,const Header * header,uint32 flags)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:
_SetupAxis(BView * view,const Header * header)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:
BaseView(const char * name,FontInfo & fontInfo,ListSelectionModel * filterModel,uint32 flags=0)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
SetModel(Model * model)292 virtual void SetModel(Model* model)
293 {
294 fModel = model;
295
296 InvalidateLayout();
297 }
298
299 protected:
CountLines() const300 int32 CountLines() const
301 {
302 return fFilterModel->CountSelectedItems();
303 }
304
TotalHeight() const305 float TotalHeight() const
306 {
307 return fFontInfo.lineHeight * CountLines();
308 }
309
LineAt(BPoint point) const310 int32 LineAt(BPoint point) const
311 {
312 int32 line = (int32)point.y / (int32)fFontInfo.lineHeight;
313 return line < CountLines() ? line : -1;
314 }
315
GetLineRange(BRect rect,int32 & minLine,int32 & maxLine) const316 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
LineRect(uint32 line) const325 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:
LineBaseView(const char * name,FontInfo & fontInfo,ListSelectionModel * filterModel,ListSelectionModel * selectionModel)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
RemoveListeners()355 void RemoveListeners()
356 {
357 fSelectionModel->RemoveListener(this);
358 fFilterModel->RemoveListener(this);
359 }
360
MessageReceived(BMessage * message)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
MouseDown(BPoint where)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
ItemsSelected(ListSelectionModel * model,int32 index,int32 count)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
ItemsDeselected(ListSelectionModel * model,int32 index,int32 count)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
InvalidateLines(int32 index,int32 count)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:
ThreadsView(FontInfo & fontInfo,ListSelectionModel * filterModel,ListSelectionModel * selectionModel)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
~ThreadsView()554 ~ThreadsView()
555 {
556 }
557
Draw(BRect updateRect)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
MinSize()602 virtual BSize MinSize()
603 {
604 return BSize(100, TotalHeight());
605 }
606
PreferredSize()607 virtual BSize PreferredSize()
608 {
609 return BSize(250, TotalHeight());
610 }
611
MaxSize()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 {
~ListenerMainWindow::SchedulingPage::SchedulingView::Listener622 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:
SchedulingView(FontInfo & fontInfo,ListSelectionModel * filterModel,ListSelectionModel * selectionModel)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
~SchedulingView()669 ~SchedulingView()
670 {
671 }
672
SetModel(Model * model)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
SetListener(Listener * listener)687 void SetListener(Listener* listener)
688 {
689 fListener = listener;
690 }
691
UpdateScrollBar()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
GetDataRange(nanotime_t & _startTime,nanotime_t & _endTime)710 void GetDataRange(nanotime_t& _startTime, nanotime_t& _endTime)
711 {
712 _GetEventTimeRange(_startTime, _endTime);
713 }
714
MinSize()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
MaxSize()722 virtual BSize MaxSize()
723 {
724 return BSize(MinSize().width, B_SIZE_UNLIMITED);
725 }
726
ScrollTo(BPoint where)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
MessageReceived(BMessage * message)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
MouseMoved(BPoint where,uint32 code,const BMessage * dragMessage)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
GetToolTipAt(BPoint point,BToolTip ** _tip)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
Draw(BRect updateRect)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:
_UpdateData()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
_AddThreadIOData(Model::Thread * thread,nanotime_t startTime,nanotime_t endTime,BObjectList<Model::IOOperation> & operations)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
_GetEventTimeRange(nanotime_t & _startTime,nanotime_t & _endTime)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
_GetThreadAndTimeAt(BPoint point,Model::Thread * & _thread,nanotime_t & _time)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
_GetIORequests(Model::Thread * thread,nanotime_t time,BObjectList<Model::IORequest> & ioRequests)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
_ScrollOffset() const1325 float _ScrollOffset() const
1326 {
1327 if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL))
1328 return scrollBar->Value();
1329 return 0;
1330 }
1331
_Zoom(float x,float steps)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
_UpdateLastEventTime(nanotime_t time)1371 inline void _UpdateLastEventTime(nanotime_t time)
1372 {
1373 fState.SetLastEventTime(time - fModel->BaseTime());
1374 }
1375
_ProcessEvent(uint32 event,uint32 cpu,const void * buffer,size_t size)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
_HandleThreadAdded(system_profiler_thread_added * event)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
_HandleThreadRemoved(system_profiler_thread_removed * event)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
_HandleThreadScheduled(system_profiler_thread_scheduled * event)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
_HandleThreadEnqueuedInRunQueue(thread_enqueued_in_run_queue * event)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
_HandleThreadRemovedFromRunQueue(thread_removed_from_run_queue * event)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:
ViewPort(HeaderView * headerView,ThreadsView * threadsView,SchedulingView * schedulingView,FontInfo & fontInfo,ListSelectionModel * filterModel)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
~ViewPort()1627 ~ViewPort()
1628 {
1629 }
1630
RemoveListeners()1631 void RemoveListeners()
1632 {
1633 fHeaderView->Model()->HeaderAt(0)->RemoveListener(this);
1634 }
1635
UpdateScrollBars()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
MinSize()1657 virtual BSize MinSize()
1658 {
1659 return BSize(10, 10);
1660 }
1661
MaxSize()1662 virtual BSize MaxSize()
1663 {
1664 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
1665 }
1666
PreferredSize()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
DoLayout()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:
HeaderWidthChanged(Header * header)1711 virtual void HeaderWidthChanged(Header* header)
1712 {
1713 if (header->ModelIndex() == 0)
1714 InvalidateLayout();
1715 }
1716
DataWidthChanged()1717 virtual void DataWidthChanged()
1718 {
1719 }
1720
DataRangeChanged()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
SchedulingPage(MainWindow * parent)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 scrollChild->SetFlags(scrollChild->Flags() | B_SCROLL_VIEW_AWARE);
1773
1774 AddChild(fScrollView = new BScrollView("scroll", scrollChild, 0, true,
1775 true));
1776
1777 fScrollView->ScrollBar(B_HORIZONTAL)->SetTarget(fSchedulingView);
1778 fScrollView->ScrollBar(B_VERTICAL)->SetTarget(fViewPort);
1779 fViewPort->UpdateScrollBars();
1780 }
1781
1782
~SchedulingPage()1783 MainWindow::SchedulingPage::~SchedulingPage()
1784 {
1785 fViewPort->RemoveListeners();
1786 fThreadsView->RemoveListeners();
1787 fSchedulingView->RemoveListeners();
1788 }
1789
1790
1791 void
SetModel(Model * model)1792 MainWindow::SchedulingPage::SetModel(Model* model)
1793 {
1794 if (model == fModel)
1795 return;
1796
1797 fSelectionModel.Clear();
1798 fFilterModel.Clear();
1799
1800 if (fModel != NULL) {
1801 }
1802
1803 fModel = model;
1804
1805 if (fModel != NULL) {
1806 fFilterModel.SelectItems(0, fModel->CountThreads(), false);
1807 }
1808
1809 fViewPort->SetModel(fModel);
1810 fThreadsView->SetModel(fModel);
1811 fSchedulingView->SetModel(fModel);
1812 }
1813