diff --git a/src/apps/debuganalyzer/gui/thread_window/ActivityPage.cpp b/src/apps/debuganalyzer/gui/thread_window/ActivityPage.cpp new file mode 100644 index 0000000000..20d1b13e42 --- /dev/null +++ b/src/apps/debuganalyzer/gui/thread_window/ActivityPage.cpp @@ -0,0 +1,393 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "thread_window/ActivityPage.h" + +#include + +#include + +#include + +#include "ThreadModel.h" + +#include "chart/BigtimeChartAxisLegendSource.h" +#include "chart/Chart.h" +#include "chart/ChartDataSource.h" +#include "chart/LegendChartAxis.h" +#include "chart/LineChartRenderer.h" +#include "chart/StringChartLegend.h" + + +enum { + RUN_TIME = 0, + WAIT_TIME = 1, + PREEMPTION_TIME = 2, + LATENCY_TIME = 3, + UNSPECIFIED_TIME = 4, + TIME_TYPE_COUNT = 5 +}; + + +class ThreadWindow::ActivityPage::ThreadActivityData + : public ChartDataSource { +public: + ThreadActivityData(ThreadModel* model, int32 timeType) + : + fModel(model), + fTimeType(timeType) + { + } + + virtual ChartDataRange Domain() const + { + return ChartDataRange(0.0, (double)fModel->GetModel()->LastEventTime()); + } + + virtual ChartDataRange Range() const + { + return ChartDataRange(0.0, 1.0); + } + + virtual void GetSamples(double start, double end, double* samples, + int32 sampleCount) + { + thread_id threadID = fModel->GetThread()->ID(); + + double sampleLength = (end - start) / (double)sampleCount; + + int32 startIndex = fModel->FindSchedulingEvent((bigtime_t)start); + bigtime_t baseTime = fModel->GetModel()->BaseTime(); + + enum ScheduleState { + RUNNING, + STILL_RUNNING, + PREEMPTED, + READY, + WAITING, + UNKNOWN + }; + + ScheduleState state = UNKNOWN; + + // get the first event and guess the initial state + const system_profiler_event_header* header + = fModel->SchedulingEventAt(startIndex); + if (header == NULL) { + for (int32 i = 0; i < sampleCount; i++) + samples[i] = 0; + return; + } + + double previousEventTime = start; + + switch (header->event) { + case B_SYSTEM_PROFILER_THREAD_SCHEDULED: + { + system_profiler_thread_scheduled* event + = (system_profiler_thread_scheduled*)(header + 1); + if (event->thread == threadID) + state = READY; + else + state = RUNNING; + previousEventTime = event->time; + break; + } + + case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE: + { + system_profiler_thread_enqueued_in_run_queue* event + = (system_profiler_thread_enqueued_in_run_queue*) + (header + 1); + state = WAITING; + previousEventTime = event->time; + break; + } + + case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE: + { + system_profiler_thread_removed_from_run_queue* event + = (system_profiler_thread_removed_from_run_queue*) + (header + 1); + state = READY; + previousEventTime = event->time; + break; + } + } + + double times[TIME_TYPE_COUNT] = { }; + int32 sampleIndex = -1; + double nextSampleTime = start; + + for (int32 i = startIndex; ; i++) { + header = fModel->SchedulingEventAt(i); + double eventTime = previousEventTime; + int32 timeType = -1; + + if (header != NULL) { + switch (header->event) { + case B_SYSTEM_PROFILER_THREAD_SCHEDULED: + { + system_profiler_thread_scheduled* event + = (system_profiler_thread_scheduled*)(header + 1); + eventTime = double(event->time - baseTime); + + if (event->thread == threadID) { + // thread scheduled + if (state == READY) { + // thread scheduled after having been woken up + timeType = LATENCY_TIME; + } else if (state == PREEMPTED) { + // thread scheduled after having been preempted + // before + timeType = PREEMPTION_TIME; + } + + if (state == STILL_RUNNING) { + // Thread was running and continues to run. + state = RUNNING; + timeType = RUN_TIME; + } else if (state != RUNNING) { + state = RUNNING; + } + } else { + // thread unscheduled + if (state == STILL_RUNNING) { + // thread preempted + state = PREEMPTED; + timeType = RUN_TIME; + } else if (state == RUNNING) { + // thread starts waiting (it hadn't been added + // to the run queue before being unscheduled) + state = WAITING; + timeType = RUN_TIME; + } + } + + break; + } + + case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE: + { + system_profiler_thread_enqueued_in_run_queue* event + = (system_profiler_thread_enqueued_in_run_queue*) + (header + 1); + eventTime = double(event->time - baseTime); + + if (state == RUNNING || state == STILL_RUNNING) { + // Thread was running and is reentered into the run + // queue. This is done by the scheduler, if the + // thread remains ready. + state = STILL_RUNNING; + timeType = RUN_TIME; + } else if (state == READY || state == PREEMPTED) { + // Happens only after having been removed from the + // run queue for altering the priority. + timeType = state == READY + ? LATENCY_TIME : PREEMPTION_TIME; + } else { + // Thread was waiting and is ready now. + state = READY; + timeType = WAIT_TIME; + } + + break; + } + + case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE: + { + system_profiler_thread_removed_from_run_queue* event + = (system_profiler_thread_removed_from_run_queue*) + (header + 1); + eventTime = double(event->time - baseTime); + + // This really only happens when the thread priority is + // changed while the thread is ready. + + if (state == RUNNING) { + // This should never happen. + state = READY; + timeType = RUN_TIME; + } else if (state == READY) { + // Thread was ready after having been woken up. + timeType = LATENCY_TIME; + } else if (state == PREEMPTED) { + // Thread was ready after having been preempted. + timeType = PREEMPTION_TIME; + } else + state = READY; + + break; + } + } + } else { + // no more events for this thread -- assume things go on like + // this until the death of the thread + switch (state) { + case RUNNING: + case STILL_RUNNING: + timeType = RUN_TIME; + break; + case PREEMPTED: + timeType = PREEMPTION_TIME; + break; + case READY: + timeType = LATENCY_TIME; + break; + case WAITING: + case UNKNOWN: + timeType = WAIT_TIME; + break; + } + + if (fModel->GetThread()->DeletionTime() >= 0) { + eventTime = double(fModel->GetThread()->DeletionTime() + - baseTime); + if (eventTime <= previousEventTime) { + // thread is dead + eventTime = end + 1; + timeType = UNSPECIFIED_TIME; + } + } else + eventTime = end + 1; + } + + if (timeType < 0) + continue; + + while (eventTime >= nextSampleTime) { + // We've reached the next sample. Partially add this time to the + // current sample. + times[timeType] += nextSampleTime - previousEventTime; + previousEventTime = nextSampleTime; + + // write the sample value + if (sampleIndex >= 0) { + samples[sampleIndex] = times[fTimeType] / sampleLength; + if (samples[sampleIndex] > 1.0) + samples[sampleIndex] = 1.0; + } + + for (int32 k = 0; k < TIME_TYPE_COUNT; k++) + times[k] = 0; + + // compute the time of the next sample + if (++sampleIndex >= sampleCount) + return; + nextSampleTime = start + + (end - start) + * (double)(sampleIndex + 1) / (double)sampleCount; + } + + // next sample not yet reached -- just add the time + times[timeType] += double(eventTime - previousEventTime); + previousEventTime = eventTime; + } + } + +private: + ThreadModel* fModel; + int32 fTimeType; +}; + + +ThreadWindow::ActivityPage::ActivityPage() + : + BGroupView(B_VERTICAL), + fThreadModel(NULL), + fActivityChart(NULL), + fActivityChartRenderer(NULL), + fRunTimeData(NULL), + fWaitTimeData(NULL), + fLatencyTimeData(NULL), + fPreemptionTimeData(NULL) +{ + SetName("Activity"); + + GroupLayout()->SetInsets(10, 10, 10, 10); + + fActivityChartRenderer = new LineChartRenderer; + ObjectDeleter rendererDeleter(fActivityChartRenderer); + + fActivityChart = new Chart(fActivityChartRenderer); + + BGroupLayoutBuilder(this) + .Add(fActivityChart) + .AddStrut(20) + ; + + rendererDeleter.Detach(); + +// TODO: Allocation management... + LegendChartAxis* axis = new LegendChartAxis( + new BigtimeChartAxisLegendSource, new StringChartLegendRenderer); + fActivityChart->SetAxis(CHART_AXIS_BOTTOM, axis); +} + + +ThreadWindow::ActivityPage::~ActivityPage() +{ + delete fRunTimeData; + delete fWaitTimeData; + delete fLatencyTimeData; + delete fPreemptionTimeData; + delete fActivityChartRenderer; +} + + +void +ThreadWindow::ActivityPage::SetModel(ThreadModel* model) +{ + if (model == fThreadModel) + return; + + if (fThreadModel != NULL) { + fActivityChart->RemoveAllDataSources(); + delete fRunTimeData; + delete fWaitTimeData; + delete fLatencyTimeData; + delete fPreemptionTimeData; + fRunTimeData = NULL; + fWaitTimeData = NULL; + fLatencyTimeData = NULL; + fPreemptionTimeData = NULL; + } + + fThreadModel = model; + + if (fThreadModel != NULL) { + // run time + LineChartRendererDataSourceConfig runConfig( + rgb_color().set_to(0, 0, 0, 255)); + + fRunTimeData = new(std::nothrow) ThreadActivityData(fThreadModel, + RUN_TIME); + fActivityChart->AddDataSource(fRunTimeData, &runConfig); + + // wait time + LineChartRendererDataSourceConfig waitConfig( + rgb_color().set_to(0, 255, 0, 255)); + + fWaitTimeData = new(std::nothrow) ThreadActivityData(fThreadModel, + WAIT_TIME); + fActivityChart->AddDataSource(fWaitTimeData, &waitConfig); + + // preemption time + LineChartRendererDataSourceConfig preemptionConfig( + rgb_color().set_to(0, 0, 255, 255)); + + fLatencyTimeData = new(std::nothrow) ThreadActivityData(fThreadModel, + PREEMPTION_TIME); + fActivityChart->AddDataSource(fLatencyTimeData, &preemptionConfig); + + // latency time + LineChartRendererDataSourceConfig latencyConfig( + rgb_color().set_to(255, 0, 0, 255)); + + fPreemptionTimeData = new(std::nothrow) ThreadActivityData(fThreadModel, + LATENCY_TIME); + fActivityChart->AddDataSource(fPreemptionTimeData, &latencyConfig); + } +} diff --git a/src/apps/debuganalyzer/gui/thread_window/ActivityPage.h b/src/apps/debuganalyzer/gui/thread_window/ActivityPage.h new file mode 100644 index 0000000000..432b8f9f87 --- /dev/null +++ b/src/apps/debuganalyzer/gui/thread_window/ActivityPage.h @@ -0,0 +1,39 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef THREAD_ACTIVITY_PAGE_H +#define THREAD_ACTIVITY_PAGE_H + +#include + +#include "thread_window/ThreadWindow.h" + + +class Chart; +class ChartRenderer; + + +class ThreadWindow::ActivityPage : public BGroupView { +public: + ActivityPage(); + virtual ~ActivityPage(); + + void SetModel(ThreadModel* model); + +private: + class ThreadActivityData; + +private: + ThreadModel* fThreadModel; + Chart* fActivityChart; + ChartRenderer* fActivityChartRenderer; + ThreadActivityData* fRunTimeData; + ThreadActivityData* fWaitTimeData; + ThreadActivityData* fLatencyTimeData; + ThreadActivityData* fPreemptionTimeData; +}; + + + +#endif // THREAD_ACTIVITY_PAGE_H diff --git a/src/apps/debuganalyzer/gui/thread_window/Jamfile b/src/apps/debuganalyzer/gui/thread_window/Jamfile index a5348a8e4e..ea0a0ee99e 100644 --- a/src/apps/debuganalyzer/gui/thread_window/Jamfile +++ b/src/apps/debuganalyzer/gui/thread_window/Jamfile @@ -8,7 +8,8 @@ UseHeaders $(HAIKU_DEBUG_ANALYZER_HEADERS) ; MergeObject DebugAnalyzer_gui_thread_window.o : - ThreadWindow.cpp + ActivityPage.cpp GeneralPage.cpp + ThreadWindow.cpp WaitObjectsPage.cpp ; diff --git a/src/apps/debuganalyzer/gui/thread_window/ThreadWindow.cpp b/src/apps/debuganalyzer/gui/thread_window/ThreadWindow.cpp index f00a1be0ea..0b5a6560cc 100644 --- a/src/apps/debuganalyzer/gui/thread_window/ThreadWindow.cpp +++ b/src/apps/debuganalyzer/gui/thread_window/ThreadWindow.cpp @@ -18,6 +18,7 @@ #include "ThreadModel.h" #include "ThreadModelLoader.h" +#include "thread_window/ActivityPage.h" #include "thread_window/GeneralPage.h" #include "thread_window/WaitObjectsPage.h" @@ -41,6 +42,7 @@ ThreadWindow::ThreadWindow(SubWindowManager* manager, Model* model, fMainTabView(NULL), fGeneralPage(NULL), fWaitObjectsPage(NULL), + fActivityPage(NULL), fModel(model), fThread(thread), fThreadModel(NULL), @@ -56,6 +58,7 @@ ThreadWindow::ThreadWindow(SubWindowManager* manager, Model* model, fMainTabView->AddTab(fGeneralPage = new GeneralPage); fMainTabView->AddTab(fWaitObjectsPage = new WaitObjectsPage); + fMainTabView->AddTab(fActivityPage = new ActivityPage); fGeneralPage->SetModel(fModel, fThread); @@ -146,4 +149,5 @@ ThreadWindow::_SetModel(ThreadModel* model) fThreadModel = model; fWaitObjectsPage->SetModel(fThreadModel); + fActivityPage->SetModel(fThreadModel); } diff --git a/src/apps/debuganalyzer/gui/thread_window/ThreadWindow.h b/src/apps/debuganalyzer/gui/thread_window/ThreadWindow.h index 752b1acbb7..0ff1f24902 100644 --- a/src/apps/debuganalyzer/gui/thread_window/ThreadWindow.h +++ b/src/apps/debuganalyzer/gui/thread_window/ThreadWindow.h @@ -26,6 +26,7 @@ public: virtual void Show(); private: + class ActivityPage; class GeneralPage; class WaitObjectsPage; @@ -36,6 +37,7 @@ private: BTabView* fMainTabView; GeneralPage* fGeneralPage; WaitObjectsPage* fWaitObjectsPage; + ActivityPage* fActivityPage; Model* fModel; Model::Thread* fThread; ThreadModel* fThreadModel;