From d4d63490afaa02ff9413bc9d8181bbede81b0610 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 28 Jul 2009 18:57:57 +0000 Subject: [PATCH] * Moved ThreadInfo from ModelLoader to Model. And renamed it to ThreadSchedulingState. Also moved the management of the thread scheduling states into a new model class SchedulingState, which is now used by ModelLoader. * Added scheduling state snapshots to Model. The ModelLoader adds a complete snapshot every 1024 events, so that seeking to scheduling state at a time can be done quickly. * [Featuring stippi] Added actual functionality to the scheduling page. It shows the scheduling activity of all threads and one can zoom in and out. Looks somewhat ugly and is work in progress. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31856 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../debuganalyzer/gui/main_window/Jamfile | 2 + .../gui/main_window/SchedulingPage.cpp | 870 +++++++++++++++++- .../gui/main_window/SchedulingPage.h | 23 +- .../debuganalyzer/gui/table/TreeTable.cpp | 28 + src/apps/debuganalyzer/gui/table/TreeTable.h | 3 + src/apps/debuganalyzer/model/Model.cpp | 177 +++- src/apps/debuganalyzer/model/Model.h | 260 ++++++ .../model_loader/ModelLoader.cpp | 98 +- .../debuganalyzer/model_loader/ModelLoader.h | 49 +- 9 files changed, 1399 insertions(+), 111 deletions(-) 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; };