diff --git a/src/apps/debuganalyzer/gui/main_window/Jamfile b/src/apps/debuganalyzer/gui/main_window/Jamfile index eca21d7863..b4e6555d77 100644 --- a/src/apps/debuganalyzer/gui/main_window/Jamfile +++ b/src/apps/debuganalyzer/gui/main_window/Jamfile @@ -5,6 +5,8 @@ UsePrivateSystemHeaders ; UseHeaders $(HAIKU_DEBUG_ANALYZER_HEADERS) ; +UseHeaders [ FDirName $(HAIKU_TOP) src apps debugger util ] ; + MergeObject DebugAnalyzer_gui_main_window.o : diff --git a/src/apps/debuganalyzer/gui/main_window/SchedulingPage.cpp b/src/apps/debuganalyzer/gui/main_window/SchedulingPage.cpp index e9aeef40e3..c7df15baf0 100644 --- a/src/apps/debuganalyzer/gui/main_window/SchedulingPage.cpp +++ b/src/apps/debuganalyzer/gui/main_window/SchedulingPage.cpp @@ -3,20 +3,876 @@ * Distributed under the terms of the MIT License. */ + #include "main_window/SchedulingPage.h" #include +#include #include +#include +#include +#include +#include + +#include + +#include + +#include "Array.h" + +#include "Model.h" + + +static const float kThreadNameMargin = 3.0f; +static const float kViewSeparationMargin = 5.0f; + + +struct MainWindow::SchedulingPage::SchedulingEvent { + bigtime_t time; + Model::ThreadWaitObjectGroup* waitObject; + ThreadState state; + + SchedulingEvent(bigtime_t time, ThreadState state, + Model::ThreadWaitObjectGroup* waitObject) + : + time(time), + waitObject(waitObject), + state(state) + { + } +}; + + +class MainWindow::SchedulingPage::SchedulingData { +public: + SchedulingData() + : + fModel(NULL), + fDataArrays(NULL), + fRecordingEnabled(false) + { + } + + status_t InitCheck() const + { + return fDataArrays != NULL ? B_OK : B_NO_MEMORY; + } + + void SetModel(Model* model) + { + delete[] fDataArrays; + + fModel = model; + fDataArrays = NULL; + + if (fModel != NULL) + fDataArrays = new(std::nothrow) DataArray[fModel->CountThreads()]; + } + + void SetRecordingEnabled(bool enabled) + { + fRecordingEnabled = enabled; + } + + void Clear() + { + if (fDataArrays == NULL) + return; + + int32 count = fModel->CountThreads(); + for (int32 i = 0; i < count; i++) + fDataArrays[i].Clear(); + } + + const Array& EventsForThread(int32 index) + { + return fDataArrays[index]; + } + + void AddState(Model::Thread* thread, bigtime_t time, ThreadState state, + Model::ThreadWaitObjectGroup* waitObject) + { + DataArray& array = fDataArrays[thread->Index()]; + if (!array.IsEmpty()) { + SchedulingEvent& lastEvent = array[array.Size() - 1]; + if (fRecordingEnabled) { + if (lastEvent.state == state + && lastEvent.waitObject == waitObject) { + return; + } + } else { + // recording not enabled yet -- overwrite the last event + lastEvent = SchedulingEvent(time, state, waitObject); + return; + } + } + + SchedulingEvent event(time, state, waitObject); + array.Add(event); + } + + void AddRun(Model::Thread* thread, bigtime_t time) + { + AddState(thread, time, RUNNING, NULL); + } + + void AddLatency(Model::Thread* thread, bigtime_t time) + { + AddState(thread, time, READY, NULL); + } + + void AddPreemption(Model::Thread* thread, bigtime_t time) + { + AddState(thread, time, PREEMPTED, NULL); + } + + void AddWait(Model::Thread* thread, bigtime_t time, + Model::ThreadWaitObjectGroup* waitObject) + { + AddState(thread, time, WAITING, waitObject); + } + + void AddUnspecifiedWait(Model::Thread* thread, bigtime_t time) + { + AddState(thread, time, WAITING, NULL); + } + +private: + typedef Array DataArray; + +private: + Model* fModel; + DataArray* fDataArrays; + bool fRecordingEnabled; +}; + + +class MainWindow::SchedulingPage::BaseView : public BView { +public: + BaseView(const char* name, FontInfo& fontInfo) + : + BView(name, B_WILL_DRAW), + fFontInfo(fontInfo) + { + } + + virtual void SetModel(Model* model) + { + fModel = model; + + InvalidateLayout(); + } + +protected: + int32 _CountLines() const + { + return fModel != NULL ? fModel->CountThreads() : 0; + } + + float TotalHeight() const + { + return fFontInfo.lineHeight * _CountLines(); + } + + void GetLineRange(BRect rect, int32& minLine, int32& maxLine) const + { + int32 lineHeight = (int32)fFontInfo.lineHeight; + minLine = (int32)rect.top / lineHeight; + maxLine = ((int32)ceilf(rect.bottom) + lineHeight - 1) / lineHeight; + minLine = std::max(minLine, 0L); + maxLine = std::min(maxLine, _CountLines() - 1); + } + + BRect LineRect(uint32 line) const + { + float y = (float)line * fFontInfo.lineHeight; + return BRect(0, y, Bounds().right, y + fFontInfo.lineHeight - 1); + } + +protected: + Model* fModel; + FontInfo& fFontInfo; +}; + + +class MainWindow::SchedulingPage::ThreadsView : public BaseView { +public: + ThreadsView(FontInfo& fontInfo) + : + BaseView("threads", fontInfo) + { + } + + virtual void Draw(BRect updateRect) + { + if (fModel == NULL) + return; + + // get the lines intersecting with the update rect + int32 minLine, maxLine; + GetLineRange(updateRect, minLine, maxLine); + + for (int32 i = minLine; i <= maxLine; i++) { + float y = (float)(i + 1) * fFontInfo.lineHeight + - fFontInfo.fontHeight.descent; + DrawString(fModel->ThreadAt(i)->Name(), + BPoint(kThreadNameMargin, y)); + } + } + + virtual BSize MinSize() + { + return BSize(100, TotalHeight()); + } + + virtual BSize MaxSize() + { + return BSize(MinSize().width, B_SIZE_UNLIMITED); + } +}; + + +class MainWindow::SchedulingPage::SchedulingView : public BaseView { +public: + SchedulingView(FontInfo& fontInfo) + : + BaseView("scheduling", fontInfo), + fStartTime(0), + fEndTime(0), + fUSecsPerPixel(1000), + fLastMousePos(-1, -1) + { + } + + virtual void SetModel(Model* model) + { + BaseView::SetModel(model); + fSchedulingData.SetModel(model); + fStartTime = 0; + fEndTime = 0; + } + + void UpdateScrollBar() + { + float width = Frame().Width(); + float dataWidth = std::max(width, MinSize().width); + + if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) { + float range = dataWidth - width; + if (range > 0) { + scrollBar->SetRange(0, range); + scrollBar->SetProportion((width + 1) / (dataWidth + 1)); + scrollBar->SetSteps(fFontInfo.lineHeight, width + 1); + } else { + scrollBar->SetRange(0, 0); + scrollBar->SetProportion(1); + } + } + } + + virtual BSize MinSize() + { + bigtime_t timeSpan = fModel != NULL ? fModel->LastEventTime() : 0; + float width = std::max(float(timeSpan / fUSecsPerPixel), 100.0f); + return BSize(width, TotalHeight()); + } + + virtual BSize MaxSize() + { + return BSize(MinSize().width, B_SIZE_UNLIMITED); + } + + virtual void ScrollTo(BPoint where) + { + BaseView::ScrollTo(where); + fStartTime = 0; + fEndTime = 0; + } + + void MessageReceived(BMessage* message) + { + switch (message->what) { + case B_MOUSE_WHEEL_CHANGED: + { + // We're only interested in Shift + vertical wheel. + float deltaY; + if ((modifiers() & B_SHIFT_KEY) == 0 + || message->FindFloat("be:wheel_delta_y", &deltaY) + != B_OK) { + break; + } + + _Zoom(fLastMousePos.x, deltaY); + + return; + } + } + + BView::MessageReceived(message); + } + + void MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage) + { + fLastMousePos = where - LeftTop(); + +// if (fDraggingStartPos.x < 0) +// return; +// +// ScrollBar(B_HORIZONTAL)->SetValue(fDraggingStartScrollValue +// + fDraggingStartPos.x - where.x); + } + + virtual void Draw(BRect updateRect) + { + if (fModel == NULL || fSchedulingData.InitCheck() != B_OK) + return; + + _UpdateData(); + + // draw the events +// TODO: Draw only the threads currently visible. + int32 threadCount = fModel->CountThreads(); + for (int32 i = 0; i < threadCount; i++) { + Model::Thread* thread = fModel->ThreadAt(i); + const Array& events + = fSchedulingData.EventsForThread(thread->Index()); + + int32 eventCount = events.Size(); +//printf("drawing events for thread %ld: %ld events\n", thread->Index(), eventCount); + for (int32 k = 0; k < eventCount; k++) { + const SchedulingEvent& event = events[k]; + bigtime_t startTime = std::max(event.time, fStartTime); + bigtime_t endTime = k + 1 < eventCount + ? std::min(events[k + 1].time, fEndTime) : fEndTime; + + rgb_color color; + switch (event.state) { + case RUNNING: + case STILL_RUNNING: + color.set_to(0, 255, 0); + break; + case PREEMPTED: + color.set_to(255, 127, 0); + break; + case READY: + color.set_to(255, 0, 0); + break; + case WAITING: + case UNKNOWN: + default: + continue; + } + + SetHighColor(color); + BRect rect = LineRect(i); + rect.left = startTime / fUSecsPerPixel; + rect.right = endTime / fUSecsPerPixel - 1; + FillRect(rect); + } + } + } + +private: + // shorthands for the longish structure names + typedef system_profiler_thread_enqueued_in_run_queue + thread_enqueued_in_run_queue; + typedef system_profiler_thread_removed_from_run_queue + thread_removed_from_run_queue; + +private: + void _UpdateData() + { + // get the interesting event time range + bigtime_t startTime; + bigtime_t endTime; + _GetEventTimeRange(startTime, endTime); + + if (startTime == fStartTime && endTime == fEndTime) + return; + fStartTime = startTime; + fEndTime = endTime; + + fSchedulingData.Clear(); + +//printf("MainWindow::SchedulingPage::SchedulingView::_UpdateData()\n"); +//printf(" time range: %lld - %lld\n", startTime, endTime); + + // get a scheduling state close to our start time + const Model::CompactSchedulingState* compactState + = fModel->ClosestSchedulingState(startTime); +//printf(" compactState: %p\n", compactState); + fState.Clear(); + status_t error = fState.Init(compactState); + if (error != B_OK) + return; + + // init the event stream + BDebugEventInputStream input; + error = input.SetTo((uint8*)fModel->EventData(), + fModel->EventDataSize(), false); + if (error == B_OK && compactState != NULL) + error = input.Seek(compactState->EventOffset()); +//printf(" event offset: %lld, input init error: %s\n", compactState != NULL ? compactState->EventOffset() : 0, strerror(error)); + if (error != B_OK) + return; + + fSchedulingData.SetRecordingEnabled( + fState.LastEventTime() >= startTime); + + // add the initial thread states to the scheduling data + if (compactState != NULL) { + int32 threadStateCount = compactState->CountThreadsStates(); + for (int32 i = 0; i < threadStateCount; i++) { + const Model::CompactThreadSchedulingState* threadState + = compactState->ThreadStateAt(i); + switch (threadState->state) { + case RUNNING: + case STILL_RUNNING: + fSchedulingData.AddRun(threadState->thread, + threadState->lastTime); + break; + case PREEMPTED: + fSchedulingData.AddPreemption(threadState->thread, + threadState->lastTime); + break; + case READY: + fSchedulingData.AddLatency(threadState->thread, + threadState->lastTime); + break; + case WAITING: + { + Model::ThreadWaitObjectGroup* group = NULL; + if (threadState->waitObject != NULL) { + group = fModel->ThreadWaitObjectGroupFor( + threadState->ID(), + threadState->waitObject->Type(), + threadState->waitObject->Object()); + } + fSchedulingData.AddWait(threadState->thread, + threadState->lastTime, group); + break; + } + case UNKNOWN: + default: + break; + } + } + } + + // process the events + while (true) { + // get next event + uint32 event; + uint32 cpu; + const void* buffer; + off_t offset; + ssize_t bufferSize = input.ReadNextEvent(&event, &cpu, &buffer, + &offset); + if (bufferSize < 0) +{ +printf("failed to read event!\n"); + return; +} + if (buffer == NULL) + break; + + // process the event + error = _ProcessEvent(event, cpu, buffer, bufferSize); + if (error != B_OK) + return; + + if (fState.LastEventTime() >= startTime) + fSchedulingData.SetRecordingEnabled(true); + if (fState.LastEventTime() >= endTime) + break; + } + } + + void _GetEventTimeRange(bigtime_t& _startTime, bigtime_t& _endTime) + { + if (fModel != NULL) { + float scrollOffset = _ScrollOffset(); + _startTime = (bigtime_t)scrollOffset * fUSecsPerPixel; + _endTime = (bigtime_t)(scrollOffset + Bounds().Width() + 1) + * fUSecsPerPixel; + } else { + _startTime = 0; + _endTime = 1; + } + } + + float _ScrollOffset() const + { + if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) + return scrollBar->Value(); + return 0; + } + + void _Zoom(float x, float steps) + { + if (steps == 0 || fModel == NULL) + return; + + // compute the domain point where to zoom in + float scrollOffset = _ScrollOffset(); + double timeForX = (scrollOffset + x) * fUSecsPerPixel; + + double factor = 4; + if (steps < 0) { + steps = -steps; + factor = 1; + } + + uint32 oldUsecPerPixel = fUSecsPerPixel; + for (; steps > 0; steps--) + fUSecsPerPixel = fUSecsPerPixel * factor / 2; + + if (fUSecsPerPixel < 1) + fUSecsPerPixel = 1; + else if (fUSecsPerPixel > 1000) + fUSecsPerPixel = 1000; + + if (fUSecsPerPixel == oldUsecPerPixel) + return; + + Invalidate(); + + UpdateScrollBar(); + if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) + scrollBar->SetValue(timeForX / fUSecsPerPixel - x); + } + + inline void _UpdateLastEventTime(bigtime_t time) + { + fState.SetLastEventTime(time - fModel->BaseTime()); + } + + status_t _ProcessEvent(uint32 event, uint32 cpu, const void* buffer, + size_t size) + { + switch (event) { + case B_SYSTEM_PROFILER_TEAM_ADDED: + case B_SYSTEM_PROFILER_TEAM_REMOVED: + case B_SYSTEM_PROFILER_TEAM_EXEC: + break; + + case B_SYSTEM_PROFILER_THREAD_ADDED: + _HandleThreadAdded((system_profiler_thread_added*)buffer); + break; + + case B_SYSTEM_PROFILER_THREAD_REMOVED: + _HandleThreadRemoved((system_profiler_thread_removed*)buffer); + break; + + case B_SYSTEM_PROFILER_THREAD_SCHEDULED: + _HandleThreadScheduled( + (system_profiler_thread_scheduled*)buffer); + break; + + case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE: + _HandleThreadEnqueuedInRunQueue( + (thread_enqueued_in_run_queue*)buffer); + break; + + case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE: + _HandleThreadRemovedFromRunQueue( + (thread_removed_from_run_queue*)buffer); + break; + + case B_SYSTEM_PROFILER_WAIT_OBJECT_INFO: + break; + + default: +printf("unsupported event type %lu, size: %lu\n", event, size); +return B_BAD_DATA; + break; + } + + return B_OK; + } + + void _HandleThreadAdded(system_profiler_thread_added* event) + { +//printf(" thread added: %ld\n", event->thread); + // do we know the thread already? + Model::ThreadSchedulingState* info = fState.LookupThread(event->thread); + if (info != NULL) { + // TODO: ? + return; + } + + Model::Thread* thread = fModel->ThreadByID(event->thread); + if (thread == NULL) + return; + + // create and add a ThreadSchedulingState + info = new(std::nothrow) Model::ThreadSchedulingState(thread); + if (info == NULL) + return; + + fState.InsertThread(info); + } + + void _HandleThreadRemoved(system_profiler_thread_removed* event) + { +//printf(" thread removed: %ld\n", event->thread); +// Model::ThreadSchedulingState* thread = fState.LookupThread( +// event->thread); +// if (thread != NULL) { +// fState.RemoveThread(thread); +// delete thread; +//// TODO: The thread will be unscheduled in a moment and cause a warning! So +//// maybe keep it around in a separate hash table a bit longer? +// } + } + + void _HandleThreadScheduled(system_profiler_thread_scheduled* event) + { + _UpdateLastEventTime(event->time); + + Model::ThreadSchedulingState* thread = fState.LookupThread( + event->thread); + if (thread == NULL) { + printf("Schedule event for unknown thread: %ld\n", event->thread); + return; + } + + thread->lastTime = fState.LastEventTime(); + thread->state = RUNNING; + fSchedulingData.AddRun(thread->thread, fState.LastEventTime()); + + // unscheduled thread + + if (event->thread == event->previous_thread) + return; + + thread = fState.LookupThread(event->previous_thread); + if (thread == NULL) { + printf("Schedule event for unknown previous thread: %ld\n", + event->previous_thread); + return; + } + + if (thread->state == STILL_RUNNING) { + // thread preempted + fSchedulingData.AddPreemption(thread->thread, + fState.LastEventTime()); + + thread->lastTime = fState.LastEventTime(); + thread->state = PREEMPTED; + } else if (thread->state == RUNNING) { + // thread starts waiting (it hadn't been added to the run + // queue before being unscheduled) + if (event->previous_thread_state == B_THREAD_WAITING) { + addr_t waitObject = event->previous_thread_wait_object; + switch (event->previous_thread_wait_object_type) { + case THREAD_BLOCK_TYPE_SNOOZE: + case THREAD_BLOCK_TYPE_SIGNAL: + waitObject = 0; + break; + case THREAD_BLOCK_TYPE_SEMAPHORE: + case THREAD_BLOCK_TYPE_CONDITION_VARIABLE: + case THREAD_BLOCK_TYPE_MUTEX: + case THREAD_BLOCK_TYPE_RW_LOCK: + case THREAD_BLOCK_TYPE_OTHER: + default: + break; + } + + fSchedulingData.AddWait(thread->thread, fState.LastEventTime(), + fModel->ThreadWaitObjectGroupFor(thread->ID(), + event->previous_thread_wait_object_type, waitObject)); + } else { + fSchedulingData.AddUnspecifiedWait(thread->thread, + fState.LastEventTime()); + } + + thread->lastTime = fState.LastEventTime(); + thread->state = WAITING; + } else if (thread->state == UNKNOWN) { + uint32 threadState = event->previous_thread_state; + if (threadState == B_THREAD_WAITING + || threadState == B_THREAD_SUSPENDED) { + fSchedulingData.AddWait(thread->thread, fState.LastEventTime(), + NULL); + thread->lastTime = fState.LastEventTime(); + thread->state = WAITING; + } else if (threadState == B_THREAD_READY) { + thread->lastTime = fState.LastEventTime(); + thread->state = PREEMPTED; + fSchedulingData.AddPreemption(thread->thread, + fState.LastEventTime()); + } + } + } + + void _HandleThreadEnqueuedInRunQueue(thread_enqueued_in_run_queue* event) + { + _UpdateLastEventTime(event->time); + + Model::ThreadSchedulingState* thread = fState.LookupThread( + event->thread); + if (thread == NULL) { + printf("Enqueued in run queue event for unknown thread: %ld\n", + event->thread); + return; + } + + if (thread->state == RUNNING || thread->state == STILL_RUNNING) { + // Thread was running and is reentered into the run queue. This + // is done by the scheduler, if the thread remains ready. + thread->state = STILL_RUNNING; + } else { + // Thread was waiting and is ready now. + bigtime_t diffTime = fState.LastEventTime() - thread->lastTime; + if (thread->waitObject != NULL) { + thread->waitObject->AddWait(diffTime); + thread->waitObject = NULL; + } + + fSchedulingData.AddLatency(thread->thread, fState.LastEventTime()); + thread->lastTime = fState.LastEventTime(); + thread->state = READY; + } + } + + void _HandleThreadRemovedFromRunQueue(thread_removed_from_run_queue* event) + { + _UpdateLastEventTime(event->time); + + Model::ThreadSchedulingState* thread = fState.LookupThread( + event->thread); + if (thread == NULL) { + printf("Removed from run queue event for unknown thread: %ld\n", + event->thread); + return; + } + + // This really only happens when the thread priority is changed + // while the thread is ready. + fSchedulingData.AddUnspecifiedWait(thread->thread, + fState.LastEventTime()); + + thread->lastTime = fState.LastEventTime(); + thread->state = WAITING; + } + +private: + Model::SchedulingState fState; + SchedulingData fSchedulingData; + bigtime_t fStartTime; + bigtime_t fEndTime; + uint32 fUSecsPerPixel; + BPoint fLastMousePos; +}; + + +class MainWindow::SchedulingPage::ViewPort : public BaseView { +public: + ViewPort(ThreadsView* threadsView, SchedulingView* schedulingView, + FontInfo& fontInfo) + : + BaseView("viewport", fontInfo), + fThreadsView(threadsView), + fSchedulingView(schedulingView) + { + AddChild(threadsView); + AddChild(schedulingView); + } + + void TargetedByScrollView(BScrollView* scrollView) + { + _UpdateScrollBars(); + } + + virtual BSize MinSize() + { + return BSize(10, 10); + } + + virtual BSize MaxSize() + { + return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); + } + + BSize PreferredSize() + { + BSize threadsViewSize(fThreadsView->PreferredSize()); + BSize schedulingViewSize(fSchedulingView->PreferredSize()); + return BSize(BLayoutUtils::AddDistances( + threadsViewSize.width + kViewSeparationMargin, + schedulingViewSize.width), + std::max(threadsViewSize.height, schedulingViewSize.height)); + } + + void DoLayout() + { + float width = Bounds().Width(); + float height = fThreadsView->MinSize().Height(); + float threadsViewWidth = fThreadsView->MinSize().width; + float schedulingViewLeft = threadsViewWidth + 1 + kViewSeparationMargin; + + fThreadsView->MoveTo(0, 0); + fThreadsView->ResizeTo(threadsViewWidth, height); + + fSchedulingView->MoveTo(schedulingViewLeft, 0); + fSchedulingView->ResizeTo(width - schedulingViewLeft, height); + + _UpdateScrollBars(); + } + +private: + void _UpdateScrollBars() + { + float height = Frame().Height(); + float dataHeight = std::max(height, fSchedulingView->MinSize().height); + + fSchedulingView->UpdateScrollBar(); + + if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) { + float range = dataHeight - height; + if (range > 0) { + scrollBar->SetRange(0, range); + scrollBar->SetProportion( + (height + 1) / (dataHeight + 1)); + scrollBar->SetSteps(fFontInfo.lineHeight, height + 1); + } else { + scrollBar->SetRange(0, 0); + scrollBar->SetProportion(1); + } + } + } + +private: + ThreadsView* fThreadsView; + SchedulingView* fSchedulingView; +}; + MainWindow::SchedulingPage::SchedulingPage(MainWindow* parent) : BGroupView(B_VERTICAL), fParent(parent), - fModel(NULL) + fModel(NULL), + fScrollView(NULL), + fViewPort(NULL), + fThreadsView(NULL), + fSchedulingView(NULL) { SetName("Scheduling"); + + be_plain_font->GetHeight(&fFontInfo.fontHeight); + fFontInfo.lineHeight = ceilf(fFontInfo.fontHeight.ascent) + + ceilf(fFontInfo.fontHeight.descent); + + fViewPort = new ViewPort(fThreadsView = new ThreadsView(fFontInfo), + fSchedulingView = new SchedulingView(fFontInfo), fFontInfo); + + AddChild(fScrollView = new BScrollView("scroll", fViewPort, 0, true, true)); + + fScrollView->ScrollBar(B_HORIZONTAL)->SetTarget(fSchedulingView); } @@ -38,12 +894,8 @@ MainWindow::SchedulingPage::SetModel(Model* model) if (fModel != NULL) { } + + fViewPort->SetModel(fModel); + fThreadsView->SetModel(fModel); + fSchedulingView->SetModel(fModel); } - - -void -MainWindow::SchedulingPage::TableRowInvoked(Table* table, int32 rowIndex) -{ - // TODO: ... -} - diff --git a/src/apps/debuganalyzer/gui/main_window/SchedulingPage.h b/src/apps/debuganalyzer/gui/main_window/SchedulingPage.h index ee72ef9fd3..587f1e8237 100644 --- a/src/apps/debuganalyzer/gui/main_window/SchedulingPage.h +++ b/src/apps/debuganalyzer/gui/main_window/SchedulingPage.h @@ -5,6 +5,7 @@ #ifndef MAIN_SCHEDULING_PAGE_H #define MAIN_SCHEDULING_PAGE_H + #include #include "table/Table.h" @@ -12,6 +13,9 @@ #include "main_window/MainWindow.h" +class BScrollView; + + class MainWindow::SchedulingPage : public BGroupView, private TableListener { public: SchedulingPage(MainWindow* parent); @@ -20,14 +24,27 @@ public: void SetModel(Model* model); private: - // TableListener - virtual void TableRowInvoked(Table* table, int32 rowIndex); + struct SchedulingEvent; + class SchedulingData; + class BaseView; + class ThreadsView; + class SchedulingView; + class ViewPort; + + struct FontInfo { + font_height fontHeight; + float lineHeight; + }; private: MainWindow* fParent; Model* fModel; + BScrollView* fScrollView; + ViewPort* fViewPort; + ThreadsView* fThreadsView; + SchedulingView* fSchedulingView; + FontInfo fFontInfo; }; - #endif // MAIN_SCHEDULING_PAGE_H diff --git a/src/apps/debuganalyzer/gui/table/TreeTable.cpp b/src/apps/debuganalyzer/gui/table/TreeTable.cpp index 9c3827ca4c..77411d05e2 100644 --- a/src/apps/debuganalyzer/gui/table/TreeTable.cpp +++ b/src/apps/debuganalyzer/gui/table/TreeTable.cpp @@ -247,6 +247,13 @@ TreeTableListener::TreeTableNodeInvoked(TreeTable* table, } +void +TreeTableListener::TreeTableNodeExpandedChanged(TreeTable* table, + const TreeTablePath& path, bool expanded) +{ +} + + // #pragma mark - Column @@ -850,6 +857,27 @@ TreeTable::TableNodesChanged(TreeTableModel* model, const TreeTablePath& path, } +void +TreeTable::ExpandOrCollapse(BRow* _row, bool expand) +{ + TreeTableRow* row = dynamic_cast(_row); + if (row == NULL || row->IsExpanded() == expand) + return; + + AbstractTable::ExpandOrCollapse(row, expand); + + if (row->IsExpanded() != expand) + return; + + TreeTablePath path; + _GetPathForNode(row->Node(), path); + + int32 listenerCount = fListeners.CountItems(); + for (int32 i = listenerCount - 1; i >= 0; i--) + fListeners.ItemAt(i)->TreeTableNodeExpandedChanged(this, path, expand); +} + + void TreeTable::ItemInvoked() { diff --git a/src/apps/debuganalyzer/gui/table/TreeTable.h b/src/apps/debuganalyzer/gui/table/TreeTable.h index 7acde6740f..31e590e8d9 100644 --- a/src/apps/debuganalyzer/gui/table/TreeTable.h +++ b/src/apps/debuganalyzer/gui/table/TreeTable.h @@ -129,6 +129,8 @@ public: virtual void TreeTableSelectionChanged(TreeTable* table); virtual void TreeTableNodeInvoked(TreeTable* table, const TreeTablePath& path); + virtual void TreeTableNodeExpandedChanged(TreeTable* table, + const TreeTablePath& path, bool expanded); }; @@ -189,6 +191,7 @@ private: typedef BObjectList ListenerList; private: + virtual void ExpandOrCollapse(BRow* row, bool expand); virtual void ItemInvoked(); bool _AddChildRows(TreeTableNode* parentNode, diff --git a/src/apps/debuganalyzer/model/Model.cpp b/src/apps/debuganalyzer/model/Model.cpp index 8225775bf9..87c532afe2 100644 --- a/src/apps/debuganalyzer/model/Model.cpp +++ b/src/apps/debuganalyzer/model/Model.cpp @@ -147,6 +147,7 @@ Model::Thread::Thread(Team* team, const system_profiler_thread_added* event, fTotalWaitTime(0), fUnspecifiedWaitTime(0), fPreemptions(0), + fIndex(-1), fWaitObjectGroups(20, true) { } @@ -270,6 +271,121 @@ Model::Thread::AddThreadWaitObject(WaitObject* waitObject, } +// #pragma mark - SchedulingState + + +Model::SchedulingState::~SchedulingState() +{ + Clear(); +} + + +status_t +Model::SchedulingState::Init() +{ + status_t error = fThreadStates.Init(); + if (error != B_OK) + return error; + + return B_OK; +} + + +status_t +Model::SchedulingState::Init(const CompactSchedulingState* state) +{ + status_t error = Init(); + if (error != B_OK) + return error; + + if (state == NULL) + return B_OK; + + fLastEventTime = state->LastEventTime(); + for (int32 i = 0; const CompactThreadSchedulingState* compactThreadState + = state->ThreadStateAt(i); i++) { + ThreadSchedulingState* threadState + = new(std::nothrow) ThreadSchedulingState(*compactThreadState); + if (threadState == NULL) + return B_NO_MEMORY; + + fThreadStates.Insert(threadState); + } + + return B_OK; +} + + +void +Model::SchedulingState::Clear() +{ + ThreadSchedulingState* state = fThreadStates.Clear(true); + while (state != NULL) { + ThreadSchedulingState* next = state->next; + delete state; + state = next; + } + + fLastEventTime = -1; +} + + +// #pragma mark - CompactSchedulingState + + +/*static*/ Model::CompactSchedulingState* +Model::CompactSchedulingState::Create(const SchedulingState& state, + off_t eventOffset) +{ + bigtime_t lastEventTime = state.LastEventTime(); + + // count the active threads + int32 threadCount = 0; + for (ThreadSchedulingStateTable::Iterator it + = state.ThreadStates().GetIterator(); + ThreadSchedulingState* threadState = it.Next();) { + Thread* thread = threadState->thread; + if (thread->CreationTime() <= lastEventTime + && (thread->DeletionTime() == -1 + || thread->DeletionTime() >= lastEventTime)) { + threadCount++; + } + } + + CompactSchedulingState* compactState = (CompactSchedulingState*)malloc( + sizeof(CompactSchedulingState) + + threadCount * sizeof(CompactThreadSchedulingState)); + if (compactState == NULL) + return NULL; + + // copy the state info + compactState->fEventOffset = eventOffset; + compactState->fThreadCount = threadCount; + compactState->fLastEventTime = lastEventTime; + + int32 threadIndex = 0; + for (ThreadSchedulingStateTable::Iterator it + = state.ThreadStates().GetIterator(); + ThreadSchedulingState* threadState = it.Next();) { + Thread* thread = threadState->thread; + if (thread->CreationTime() <= lastEventTime + && (thread->DeletionTime() == -1 + || thread->DeletionTime() >= lastEventTime)) { + compactState->fThreadStates[threadIndex++] = *threadState; + } + } + + return compactState; +} + + +void +Model::CompactSchedulingState::Delete() +{ + free(this); +} + + // #pragma mark - Model @@ -282,22 +398,36 @@ Model::Model(const char* dataSourceName, void* eventData, size_t eventDataSize) fLastEventTime(0), fTeams(20, true), fThreads(20, true), - fWaitObjectGroups(20, true) + fWaitObjectGroups(20, true), + fSchedulingStates(100) { } Model::~Model() { + for (int32 i = 0; CompactSchedulingState* state + = fSchedulingStates.ItemAt(i); i++) { + state->Delete(); + } + free(fEventData); } +void +Model::LoadingFinished() +{ + for (int32 i = 0; Thread* thread = fThreads.ItemAt(i); i++) + thread->SetIndex(i); +} + + void Model::SetBaseTime(bigtime_t time) { fBaseTime = time; -} +} void @@ -480,3 +610,46 @@ Model::ThreadWaitObjectGroupFor(thread_id threadID, uint32 type, addr_t object) return thread->ThreadWaitObjectGroupFor(type, object); } + + +bool +Model::AddSchedulingStateSnapshot(const SchedulingState& state, + off_t eventOffset) +{ + CompactSchedulingState* compactState = CompactSchedulingState::Create(state, + eventOffset); + if (compactState == NULL) + return false; + + if (!fSchedulingStates.AddItem(compactState)) { + compactState->Delete(); + return false; + } + + return true; +} + + +const Model::CompactSchedulingState* +Model::ClosestSchedulingState(bigtime_t eventTime) const +{ + int32 index = fSchedulingStates.BinarySearchIndexByKey(eventTime, + &_CompareEventTimeSchedulingState); + if (index >= 0) + return fSchedulingStates.ItemAt(index); + + // no exact match + index = -index - 1; + return index > 0 ? fSchedulingStates.ItemAt(index - 1) : NULL; +} + + +/*static*/ int +Model::_CompareEventTimeSchedulingState(const bigtime_t* time, + const CompactSchedulingState* state) +{ + if (*time < state->LastEventTime()) + return -1; + return *time == state->LastEventTime() ? 0 : 1; +} + diff --git a/src/apps/debuganalyzer/model/Model.h b/src/apps/debuganalyzer/model/Model.h index 3e31282e60..cbc9f77f4a 100644 --- a/src/apps/debuganalyzer/model/Model.h +++ b/src/apps/debuganalyzer/model/Model.h @@ -5,16 +5,30 @@ #ifndef MODEL_H #define MODEL_H + +#include + #include #include #include #include +#include #include #include +enum ThreadState { + RUNNING, + STILL_RUNNING, + PREEMPTED, + READY, + WAITING, + UNKNOWN +}; + + class Model : public Referenceable { public: struct creation_time_id; @@ -25,6 +39,13 @@ public: class ThreadWaitObjectGroup; class Team; class Thread; + struct CompactThreadSchedulingState; + struct ThreadSchedulingState; + struct ThreadSchedulingStateDefinition; + typedef BOpenHashTable + ThreadSchedulingStateTable; + class SchedulingState; + class CompactSchedulingState; public: Model(const char* dataSourceName, @@ -35,6 +56,8 @@ public: inline void* EventData() const; inline size_t EventDataSize() const; + void LoadingFinished(); + inline bigtime_t BaseTime() const; void SetBaseTime(bigtime_t time); @@ -70,10 +93,23 @@ public: thread_id threadID, uint32 type, addr_t object) const; + bool AddSchedulingStateSnapshot( + const SchedulingState& state, + off_t eventOffset); + // must be added in order (of time) + const CompactSchedulingState* ClosestSchedulingState( + bigtime_t eventTime) const; + // returns the closest previous state + private: typedef BObjectList TeamList; typedef BObjectList ThreadList; typedef BObjectList WaitObjectGroupList; + typedef BObjectList SchedulingStateList; + +private: + static int _CompareEventTimeSchedulingState(const bigtime_t* time, + const CompactSchedulingState* state); private: BString fDataSourceName; @@ -84,6 +120,7 @@ private: TeamList fTeams; // sorted by ID ThreadList fThreads; // sorted by ID WaitObjectGroupList fWaitObjectGroups; + SchedulingStateList fSchedulingStates; }; @@ -246,6 +283,9 @@ public: inline const char* Name() const; inline Team* GetTeam() const; + inline int32 Index() const; + inline void SetIndex(int32 index); + inline bigtime_t CreationTime() const; inline bigtime_t DeletionTime() const; @@ -319,10 +359,106 @@ private: int64 fPreemptions; + int32 fIndex; + ThreadWaitObjectGroupList fWaitObjectGroups; }; +struct Model::CompactThreadSchedulingState { + bigtime_t lastTime; + Model::Thread* thread; + ThreadWaitObject* waitObject; + ThreadState state; + +public: + thread_id ID() const { return thread->ID(); } + + inline CompactThreadSchedulingState& operator=( + const CompactThreadSchedulingState& other); +}; + + +struct Model::ThreadSchedulingState : CompactThreadSchedulingState { + ThreadSchedulingState* next; + +public: + inline ThreadSchedulingState( + const CompactThreadSchedulingState& other); + inline ThreadSchedulingState(Thread* thread); +}; + + +struct Model::ThreadSchedulingStateDefinition { + typedef thread_id KeyType; + typedef ThreadSchedulingState ValueType; + + size_t HashKey(thread_id key) const + { return (size_t)key; } + + size_t Hash(const ThreadSchedulingState* value) const + { return (size_t)value->ID(); } + + bool Compare(thread_id key, const ThreadSchedulingState* value) const + { return key == value->ID(); } + + ThreadSchedulingState*& GetLink(ThreadSchedulingState* value) const + { return value->next; } +}; + + +class Model::SchedulingState { +public: + inline SchedulingState(); + ~SchedulingState(); + + status_t Init(); + status_t Init(const CompactSchedulingState* state); + void Clear(); + + inline bigtime_t LastEventTime() const { return fLastEventTime; } + inline void SetLastEventTime(bigtime_t time); + + inline ThreadSchedulingState* LookupThread(thread_id threadID) const; + inline void InsertThread(ThreadSchedulingState* thread); + inline void RemoveThread(ThreadSchedulingState* thread); + inline const ThreadSchedulingStateTable& ThreadStates() const; + +private: + bigtime_t fLastEventTime; + ThreadSchedulingStateTable fThreadStates; +}; + + +class Model::CompactSchedulingState { +public: + static CompactSchedulingState* Create(const SchedulingState& state, + off_t eventOffset); + void Delete(); + + inline off_t EventOffset() const; + inline bigtime_t LastEventTime() const; + + inline int32 CountThreadsStates() const; + inline const CompactThreadSchedulingState* ThreadStateAt(int32 index) + const; + +private: + friend class BObjectList; + // work-around for our private destructor + +private: + CompactSchedulingState(); + inline ~CompactSchedulingState() {} + +private: + bigtime_t fLastEventTime; + off_t fEventOffset; + int32 fThreadCount; + CompactThreadSchedulingState fThreadStates[0]; +}; + + // #pragma mark - Model @@ -658,6 +794,20 @@ Model::Thread::DeletionTime() const } +int32 +Model::Thread::Index() const +{ + return fIndex; +} + + +void +Model::Thread::SetIndex(int32 index) +{ + fIndex = index; +} + + int64 Model::Thread::Runs() const { @@ -784,4 +934,114 @@ Model::Thread::CompareWithCreationTimeID(const creation_time_id* key, } +// #pragma mark - CompactThreadSchedulingState + + +Model::CompactThreadSchedulingState& +Model::CompactThreadSchedulingState::operator=( + const CompactThreadSchedulingState& other) +{ + lastTime = other.lastTime; + thread = other.thread; + waitObject = other.waitObject; + state = other.state; + return *this; +} + + +// #pragma mark - ThreadSchedulingState + + +Model::ThreadSchedulingState::ThreadSchedulingState( + const CompactThreadSchedulingState& other) +{ + this->CompactThreadSchedulingState::operator=(other); +} + + +Model::ThreadSchedulingState::ThreadSchedulingState(Thread* thread) +{ + lastTime = 0; + this->thread = thread; + waitObject = NULL; + state = UNKNOWN; +} + + +// #pragma mark - SchedulingState + + +Model::SchedulingState::SchedulingState() + : + fLastEventTime(-1) +{ +} + + +void +Model::SchedulingState::SetLastEventTime(bigtime_t time) +{ + fLastEventTime = time; +} + + +Model::ThreadSchedulingState* +Model::SchedulingState::LookupThread(thread_id threadID) const +{ + return fThreadStates.Lookup(threadID); +} + + +void +Model::SchedulingState::InsertThread(ThreadSchedulingState* thread) +{ + fThreadStates.Insert(thread); +} + + +void +Model::SchedulingState::RemoveThread(ThreadSchedulingState* thread) +{ + fThreadStates.Remove(thread); +} + + +const Model::ThreadSchedulingStateTable& +Model::SchedulingState::ThreadStates() const +{ + return fThreadStates; +} + + +// #pragma mark - CompactSchedulingState + + +off_t +Model::CompactSchedulingState::EventOffset() const +{ + return fEventOffset; +} + + +bigtime_t +Model::CompactSchedulingState::LastEventTime() const +{ + return fLastEventTime; +} + + +int32 +Model::CompactSchedulingState::CountThreadsStates() const +{ + return fThreadCount; +} + + +const Model::CompactThreadSchedulingState* +Model::CompactSchedulingState::ThreadStateAt(int32 index) const +{ + return index >= 0 && index < fThreadCount ? &fThreadStates[index] : NULL; +} + + #endif // MODEL_H diff --git a/src/apps/debuganalyzer/model_loader/ModelLoader.cpp b/src/apps/debuganalyzer/model_loader/ModelLoader.cpp index de7e316f9f..245007688f 100644 --- a/src/apps/debuganalyzer/model_loader/ModelLoader.cpp +++ b/src/apps/debuganalyzer/model_loader/ModelLoader.cpp @@ -3,6 +3,7 @@ * Distributed under the terms of the MIT License. */ + #include "ModelLoader.h" #include @@ -22,6 +23,10 @@ #include "Model.h" +// add a scheduling state snapshot every x events +static const uint32 kSchedulingSnapshotInterval = 1024; + + struct SimpleWaitObjectInfo : system_profiler_wait_object_info { SimpleWaitObjectInfo(uint32 type) { @@ -39,19 +44,6 @@ static const SimpleWaitObjectInfo kSignalWaitObjectInfo( THREAD_BLOCK_TYPE_SIGNAL); -// #pragma mark - ThreadInfo - - -ModelLoader::ThreadInfo::ThreadInfo(Model::Thread* thread) - : - thread(thread), - state(UNKNOWN), - lastTime(0), - waitObject(NULL) -{ -} - - // #pragma mark - @@ -63,7 +55,7 @@ ModelLoader::_UpdateLastEventTime(bigtime_t time) fModel->SetBaseTime(time); } - fLastEventTime = time - fBaseTime; + fState.SetLastEventTime(time - fBaseTime); } @@ -105,8 +97,8 @@ ModelLoader::PrepareForLoading() if (fModel != NULL || fDataSource == NULL) return B_BAD_VALUE; - // init the hash tables - status_t error = fThreads.Init(); + // init the state + status_t error = fState.Init(); if (error != B_OK) return error; @@ -128,12 +120,7 @@ ModelLoader::Load() void ModelLoader::FinishLoading(bool success) { - ThreadInfo* threadInfo = fThreads.Clear(true); - while (threadInfo != NULL) { - ThreadInfo* nextInfo = threadInfo->next; - delete threadInfo; - threadInfo = nextInfo; - } + fState.Clear(); if (!success) { delete fModel; @@ -181,16 +168,18 @@ ModelLoader::_Load() } // process the events + fState.Clear(); fBaseTime = -1; - fLastEventTime = -1; - uint32 count = 0; + uint64 count = 0; while (true) { // get next event uint32 event; uint32 cpu; const void* buffer; - ssize_t bufferSize = input->ReadNextEvent(&event, &cpu, &buffer); + off_t offset; + ssize_t bufferSize = input->ReadNextEvent(&event, &cpu, &buffer, + &offset); if (bufferSize < 0) return bufferSize; if (buffer == NULL) @@ -207,9 +196,14 @@ ModelLoader::_Load() if (fAborted) return B_ERROR; } + + // periodically add scheduling snapshots + if (count % kSchedulingSnapshotInterval == 0) + fModel->AddSchedulingStateSnapshot(fState, offset); } - fModel->SetLastEventTime(fLastEventTime); + fModel->SetLastEventTime(fState.LastEventTime()); + fModel->LoadingFinished(); return B_OK; } @@ -347,7 +341,7 @@ return B_BAD_DATA; void ModelLoader::_HandleTeamAdded(system_profiler_team_added* event) { - if (fModel->AddTeam(event, fLastEventTime) == NULL) + if (fModel->AddTeam(event, fState.LastEventTime()) == NULL) throw std::bad_alloc(); } @@ -356,7 +350,7 @@ void ModelLoader::_HandleTeamRemoved(system_profiler_team_removed* event) { if (Model::Team* team = fModel->TeamByID(event->team)) - team->SetDeletionTime(fLastEventTime); + team->SetDeletionTime(fState.LastEventTime()); else printf("Removed event for unknown team: %ld\n", event->team); } @@ -381,7 +375,7 @@ void ModelLoader::_HandleThreadRemoved(system_profiler_thread_removed* event) { if (Model::Thread* thread = fModel->ThreadByID(event->thread)) - thread->SetDeletionTime(fLastEventTime); + thread->SetDeletionTime(fState.LastEventTime()); else printf("Removed event for unknown team: %ld\n", event->thread); } @@ -392,13 +386,13 @@ ModelLoader::_HandleThreadScheduled(system_profiler_thread_scheduled* event) { _UpdateLastEventTime(event->time); - ThreadInfo* thread = fThreads.Lookup(event->thread); + Model::ThreadSchedulingState* thread = fState.LookupThread(event->thread); if (thread == NULL) { printf("Schedule event for unknown thread: %ld\n", event->thread); return; } - bigtime_t diffTime = fLastEventTime - thread->lastTime; + bigtime_t diffTime = fState.LastEventTime() - thread->lastTime; if (thread->state == READY) { // thread scheduled after having been woken up @@ -414,7 +408,7 @@ ModelLoader::_HandleThreadScheduled(system_profiler_thread_scheduled* event) } if (thread->state != RUNNING) { - thread->lastTime = fLastEventTime; + thread->lastTime = fState.LastEventTime(); thread->state = RUNNING; } @@ -423,21 +417,21 @@ ModelLoader::_HandleThreadScheduled(system_profiler_thread_scheduled* event) if (event->thread == event->previous_thread) return; - thread = fThreads.Lookup(event->previous_thread); + thread = fState.LookupThread(event->previous_thread); if (thread == NULL) { printf("Schedule event for unknown previous thread: %ld\n", event->previous_thread); return; } - diffTime = fLastEventTime - thread->lastTime; + diffTime = fState.LastEventTime() - thread->lastTime; if (thread->state == STILL_RUNNING) { // thread preempted thread->thread->AddPreemption(diffTime); thread->thread->AddRun(diffTime); - thread->lastTime = fLastEventTime; + thread->lastTime = fState.LastEventTime(); thread->state = PREEMPTED; } else if (thread->state == RUNNING) { // thread starts waiting (it hadn't been added to the run @@ -464,16 +458,16 @@ ModelLoader::_HandleThreadScheduled(system_profiler_thread_scheduled* event) event->previous_thread_wait_object_type, waitObject); } - thread->lastTime = fLastEventTime; + thread->lastTime = fState.LastEventTime(); thread->state = WAITING; } else if (thread->state == UNKNOWN) { uint32 threadState = event->previous_thread_state; if (threadState == B_THREAD_WAITING || threadState == B_THREAD_SUSPENDED) { - thread->lastTime = fLastEventTime; + thread->lastTime = fState.LastEventTime(); thread->state = WAITING; } else if (threadState == B_THREAD_READY) { - thread->lastTime = fLastEventTime; + thread->lastTime = fState.LastEventTime(); thread->state = PREEMPTED; } } @@ -486,7 +480,7 @@ ModelLoader::_HandleThreadEnqueuedInRunQueue( { _UpdateLastEventTime(event->time); - ThreadInfo* thread = fThreads.Lookup(event->thread); + Model::ThreadSchedulingState* thread = fState.LookupThread(event->thread); if (thread == NULL) { printf("Enqueued in run queue event for unknown thread: %ld\n", event->thread); @@ -499,7 +493,7 @@ ModelLoader::_HandleThreadEnqueuedInRunQueue( thread->state = STILL_RUNNING; } else { // Thread was waiting and is ready now. - bigtime_t diffTime = fLastEventTime - thread->lastTime; + bigtime_t diffTime = fState.LastEventTime() - thread->lastTime; if (thread->waitObject != NULL) { thread->waitObject->AddWait(diffTime); thread->waitObject = NULL; @@ -507,7 +501,7 @@ ModelLoader::_HandleThreadEnqueuedInRunQueue( } else if (thread->state != UNKNOWN) thread->thread->AddUnspecifiedWait(diffTime); - thread->lastTime = fLastEventTime; + thread->lastTime = fState.LastEventTime(); thread->state = READY; } } @@ -519,7 +513,7 @@ ModelLoader::_HandleThreadRemovedFromRunQueue( { _UpdateLastEventTime(event->time); - ThreadInfo* thread = fThreads.Lookup(event->thread); + Model::ThreadSchedulingState* thread = fState.LookupThread(event->thread); if (thread == NULL) { printf("Removed from run queue event for unknown thread: %ld\n", event->thread); @@ -529,7 +523,7 @@ ModelLoader::_HandleThreadRemovedFromRunQueue( // This really only happens when the thread priority is changed // while the thread is ready. - bigtime_t diffTime = fLastEventTime - thread->lastTime; + bigtime_t diffTime = fState.LastEventTime() - thread->lastTime; if (thread->state == RUNNING) { // This should never happen. thread->thread->AddRun(diffTime); @@ -539,7 +533,7 @@ ModelLoader::_HandleThreadRemovedFromRunQueue( thread->thread->AddUnspecifiedWait(diffTime); } - thread->lastTime = fLastEventTime; + thread->lastTime = fState.LastEventTime(); thread->state = WAITING; } @@ -552,35 +546,35 @@ ModelLoader::_HandleWaitObjectInfo(system_profiler_wait_object_info* event) } -ModelLoader::ThreadInfo* +Model::ThreadSchedulingState* ModelLoader::_AddThread(system_profiler_thread_added* event) { // do we know the thread already? - ThreadInfo* info = fThreads.Lookup(event->thread); + Model::ThreadSchedulingState* info = fState.LookupThread(event->thread); if (info != NULL) { // TODO: ? return info; } // add the thread to the model - Model::Thread* thread = fModel->AddThread(event, fLastEventTime); + Model::Thread* thread = fModel->AddThread(event, fState.LastEventTime()); if (thread == NULL) return NULL; - // create and add a ThreadInfo - info = new(std::nothrow) ThreadInfo(thread); + // create and add a ThreadSchedulingState + info = new(std::nothrow) Model::ThreadSchedulingState(thread); if (info == NULL) return NULL; - fThreads.Insert(info); + fState.InsertThread(info); return info; } void -ModelLoader::_AddThreadWaitObject(ThreadInfo* thread, uint32 type, - addr_t object) +ModelLoader::_AddThreadWaitObject(Model::ThreadSchedulingState* thread, + uint32 type, addr_t object) { Model::WaitObjectGroup* waitObjectGroup = fModel->WaitObjectGroupFor(type, object); diff --git a/src/apps/debuganalyzer/model_loader/ModelLoader.h b/src/apps/debuganalyzer/model_loader/ModelLoader.h index 7342cdffbc..9d95eeb320 100644 --- a/src/apps/debuganalyzer/model_loader/ModelLoader.h +++ b/src/apps/debuganalyzer/model_loader/ModelLoader.h @@ -5,7 +5,6 @@ #ifndef MAIN_MODEL_LOADER_H #define MAIN_MODEL_LOADER_H -#include #include "AbstractModelLoader.h" #include "Model.h" @@ -35,46 +34,6 @@ protected: virtual void FinishLoading(bool success); private: - enum ScheduleState { - RUNNING, - STILL_RUNNING, - PREEMPTED, - READY, - WAITING, - UNKNOWN - }; - - struct ThreadInfo { - Model::Thread* thread; - ScheduleState state; - bigtime_t lastTime; - Model::ThreadWaitObject* waitObject; - ThreadInfo* next; - - ThreadInfo(Model::Thread* thread); - - thread_id ID() const { return thread->ID(); } - }; - - struct ThreadTableDefinition { - typedef thread_id KeyType; - typedef ThreadInfo ValueType; - - size_t HashKey(thread_id key) const - { return (size_t)key; } - - size_t Hash(const ThreadInfo* value) const - { return (size_t)value->ID(); } - - bool Compare(thread_id key, const ThreadInfo* value) const - { return key == value->ID(); } - - ThreadInfo*& GetLink(ThreadInfo* value) const - { return value->next; } - }; - - typedef BOpenHashTable ThreadTable; - // shorthands for the longish structure names typedef system_profiler_thread_enqueued_in_run_queue thread_enqueued_in_run_queue; @@ -109,16 +68,16 @@ private: void _HandleWaitObjectInfo( system_profiler_wait_object_info* event); - ThreadInfo* _AddThread(system_profiler_thread_added* event); - void _AddThreadWaitObject(ThreadInfo* thread, + Model::ThreadSchedulingState* _AddThread( + system_profiler_thread_added* event); + void _AddThreadWaitObject(Model::ThreadSchedulingState* thread, uint32 type, addr_t object); private: Model* fModel; DataSource* fDataSource; bigtime_t fBaseTime; - bigtime_t fLastEventTime; - ThreadTable fThreads; + Model::SchedulingState fState; };