xref: /haiku/src/apps/debuganalyzer/gui/thread_window/ActivityPage.cpp (revision 58481f0f6ef1a61ba07283f012cafbc2ed874ead)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "thread_window/ActivityPage.h"
7 
8 #include <new>
9 
10 #include <GroupLayoutBuilder.h>
11 
12 #include <AutoDeleter.h>
13 
14 #include "ThreadModel.h"
15 
16 #include "chart/BigtimeChartAxisLegendSource.h"
17 #include "chart/Chart.h"
18 #include "chart/ChartDataSource.h"
19 #include "chart/DefaultChartAxisLegendSource.h"
20 #include "chart/LegendChartAxis.h"
21 #include "chart/LineChartRenderer.h"
22 #include "chart/StringChartLegend.h"
23 
24 
25 enum {
26 	RUN_TIME			= 0,
27 	WAIT_TIME			= 1,
28 	PREEMPTION_TIME		= 2,
29 	LATENCY_TIME		= 3,
30 	UNSPECIFIED_TIME	= 4,
31 	TIME_TYPE_COUNT		= 5
32 };
33 
34 
35 class ThreadWindow::ActivityPage::ThreadActivityData
36 	: public ChartDataSource {
37 public:
38 	ThreadActivityData(ThreadModel* model, int32 timeType)
39 		:
40 		fModel(model),
41 		fTimeType(timeType)
42 	{
43 	}
44 
45 	virtual ChartDataRange Domain() const
46 	{
47 		return ChartDataRange(0.0, (double)fModel->GetModel()->LastEventTime());
48 	}
49 
50 	virtual ChartDataRange Range() const
51 	{
52 		return ChartDataRange(0.0, 1.0);
53 	}
54 
55 	virtual void GetSamples(double start, double end, double* samples,
56 		int32 sampleCount)
57 	{
58 		thread_id threadID = fModel->GetThread()->ID();
59 
60 		double sampleLength = (end - start) / (double)sampleCount;
61 
62 		int32 startIndex = fModel->FindSchedulingEvent((bigtime_t)start);
63 		bigtime_t baseTime = fModel->GetModel()->BaseTime();
64 
65 		enum ScheduleState {
66 			RUNNING,
67 			STILL_RUNNING,
68 			PREEMPTED,
69 			READY,
70 			WAITING,
71 			UNKNOWN
72 		};
73 
74 		ScheduleState state = UNKNOWN;
75 
76 		// get the first event and guess the initial state
77 		const system_profiler_event_header* header
78 			= fModel->SchedulingEventAt(startIndex);
79 		if (header == NULL) {
80 			for (int32 i = 0; i < sampleCount; i++)
81 				samples[i] = 0;
82 			return;
83 		}
84 
85 		double previousEventTime = start;
86 
87 		switch (header->event) {
88 			case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
89 			{
90 				system_profiler_thread_scheduled* event
91 					= (system_profiler_thread_scheduled*)(header + 1);
92 				if (event->thread == threadID)
93 					state = READY;
94 				else
95 					state = RUNNING;
96 				previousEventTime = event->time;
97 				break;
98 			}
99 
100 			case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
101 			{
102 				system_profiler_thread_enqueued_in_run_queue* event
103 					= (system_profiler_thread_enqueued_in_run_queue*)
104 						(header + 1);
105 				state = WAITING;
106 				previousEventTime = event->time;
107 				break;
108 			}
109 
110 			case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
111 			{
112 				system_profiler_thread_removed_from_run_queue* event
113 					= (system_profiler_thread_removed_from_run_queue*)
114 						(header + 1);
115 				state = READY;
116 				previousEventTime = event->time;
117 				break;
118 			}
119 		}
120 
121 		double times[TIME_TYPE_COUNT] = { };
122 		int32 sampleIndex = -1;
123 		double nextSampleTime = start;
124 
125 		for (int32 i = startIndex; ; i++) {
126 			header = fModel->SchedulingEventAt(i);
127 			double eventTime = previousEventTime;
128 			int32 timeType = -1;
129 
130 			if (header != NULL) {
131 				switch (header->event) {
132 					case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
133 					{
134 						system_profiler_thread_scheduled* event
135 							= (system_profiler_thread_scheduled*)(header + 1);
136 						eventTime = double(event->time - baseTime);
137 
138 						if (event->thread == threadID) {
139 							// thread scheduled
140 							if (state == READY) {
141 								// thread scheduled after having been woken up
142 								timeType = LATENCY_TIME;
143 							} else if (state == PREEMPTED) {
144 								// thread scheduled after having been preempted
145 								// before
146 								timeType = PREEMPTION_TIME;
147 							}
148 
149 							if (state == STILL_RUNNING) {
150 								// Thread was running and continues to run.
151 								state = RUNNING;
152 								timeType = RUN_TIME;
153 							} else if (state != RUNNING) {
154 								state = RUNNING;
155 							}
156 						} else {
157 							// thread unscheduled
158 							if (state == STILL_RUNNING) {
159 								// thread preempted
160 								state = PREEMPTED;
161 								timeType = RUN_TIME;
162 							} else if (state == RUNNING) {
163 								// thread starts waiting (it hadn't been added
164 								// to the run queue before being unscheduled)
165 								state = WAITING;
166 								timeType = RUN_TIME;
167 							}
168 						}
169 
170 						break;
171 					}
172 
173 					case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
174 					{
175 						system_profiler_thread_enqueued_in_run_queue* event
176 							= (system_profiler_thread_enqueued_in_run_queue*)
177 								(header + 1);
178 						eventTime = double(event->time - baseTime);
179 
180 						if (state == RUNNING || state == STILL_RUNNING) {
181 							// Thread was running and is reentered into the run
182 							// queue. This is done by the scheduler, if the
183 							// thread remains ready.
184 							state = STILL_RUNNING;
185 							timeType = RUN_TIME;
186 						} else if (state == READY || state == PREEMPTED) {
187 							// Happens only after having been removed from the
188 							// run queue for altering the priority.
189 							timeType = state == READY
190 								? LATENCY_TIME : PREEMPTION_TIME;
191 						} else {
192 							// Thread was waiting and is ready now.
193 							state = READY;
194 							timeType = WAIT_TIME;
195 						}
196 
197 						break;
198 					}
199 
200 					case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
201 					{
202 						system_profiler_thread_removed_from_run_queue* event
203 							= (system_profiler_thread_removed_from_run_queue*)
204 								(header + 1);
205 						eventTime = double(event->time - baseTime);
206 
207 						// This really only happens when the thread priority is
208 						// changed while the thread is ready.
209 
210 						if (state == RUNNING) {
211 							// This should never happen.
212 							state = READY;
213 							timeType = RUN_TIME;
214 						} else if (state == READY) {
215 							// Thread was ready after having been woken up.
216 							timeType = LATENCY_TIME;
217 						} else if (state == PREEMPTED) {
218 							// Thread was ready after having been preempted.
219 							timeType = PREEMPTION_TIME;
220 						} else
221 							state = READY;
222 
223 						break;
224 					}
225 				}
226 			} else {
227 				// no more events for this thread -- assume things go on like
228 				// this until the death of the thread
229 				switch (state) {
230 					case RUNNING:
231 					case STILL_RUNNING:
232 						timeType = RUN_TIME;
233 						break;
234 					case PREEMPTED:
235 						timeType = PREEMPTION_TIME;
236 						break;
237 					case READY:
238 						timeType = LATENCY_TIME;
239 						break;
240 					case WAITING:
241 					case UNKNOWN:
242 						timeType = WAIT_TIME;
243 						break;
244 				}
245 
246 				if (fModel->GetThread()->DeletionTime() >= 0) {
247 					eventTime = double(fModel->GetThread()->DeletionTime()
248 						- baseTime);
249 					if (eventTime <= previousEventTime) {
250 						// thread is dead
251 						eventTime = end + 1;
252 						timeType = UNSPECIFIED_TIME;
253 					}
254 				} else
255 					eventTime = end + 1;
256 			}
257 
258 			if (timeType < 0)
259 				continue;
260 
261 			while (eventTime >= nextSampleTime) {
262 				// We've reached the next sample. Partially add this time to the
263 				// current sample.
264 				times[timeType] += nextSampleTime - previousEventTime;
265 				previousEventTime = nextSampleTime;
266 
267 				// write the sample value
268 				if (sampleIndex >= 0) {
269 					samples[sampleIndex] = times[fTimeType] / sampleLength;
270 					if (samples[sampleIndex] > 1.0)
271 						samples[sampleIndex] = 1.0;
272 				}
273 
274 				for (int32 k = 0; k < TIME_TYPE_COUNT; k++)
275 					times[k] = 0;
276 
277 				// compute the time of the next sample
278 				if (++sampleIndex >= sampleCount)
279 					return;
280 				nextSampleTime = start
281 					+ (end - start)
282 						* (double)(sampleIndex + 1) / (double)sampleCount;
283 			}
284 
285 			// next sample not yet reached -- just add the time
286 			times[timeType] += double(eventTime - previousEventTime);
287 			previousEventTime = eventTime;
288 		}
289 	}
290 
291 private:
292 	ThreadModel*	fModel;
293 	int32			fTimeType;
294 };
295 
296 
297 ThreadWindow::ActivityPage::ActivityPage()
298 	:
299 	BGroupView(B_VERTICAL),
300 	fThreadModel(NULL),
301 	fActivityChart(NULL),
302 	fActivityChartRenderer(NULL),
303 	fRunTimeData(NULL),
304 	fWaitTimeData(NULL),
305 	fLatencyTimeData(NULL),
306 	fPreemptionTimeData(NULL)
307 {
308 	SetName("Activity");
309 
310 	GroupLayout()->SetInsets(10, 10, 10, 10);
311 
312 	fActivityChartRenderer = new LineChartRenderer;
313 	ObjectDeleter<ChartRenderer> rendererDeleter(fActivityChartRenderer);
314 
315 	fActivityChart = new Chart(fActivityChartRenderer);
316 
317 	BGroupLayoutBuilder(this)
318 		.Add(fActivityChart)
319 		.AddStrut(20)
320 	;
321 
322 	rendererDeleter.Detach();
323 
324 // TODO: Allocation management...
325 	LegendChartAxis* axis = new LegendChartAxis(
326 		new BigtimeChartAxisLegendSource, new StringChartLegendRenderer);
327 	fActivityChart->SetAxis(CHART_AXIS_BOTTOM, axis);
328 
329 	axis = new LegendChartAxis(
330 	new BigtimeChartAxisLegendSource, new StringChartLegendRenderer);
331 	fActivityChart->SetAxis(CHART_AXIS_TOP, axis);
332 
333 	axis = new LegendChartAxis(
334 	new DefaultChartAxisLegendSource, new StringChartLegendRenderer);
335 	fActivityChart->SetAxis(CHART_AXIS_LEFT, axis);
336 
337 	axis = new LegendChartAxis(
338 	new DefaultChartAxisLegendSource, new StringChartLegendRenderer);
339 	fActivityChart->SetAxis(CHART_AXIS_RIGHT, axis);
340 }
341 
342 
343 ThreadWindow::ActivityPage::~ActivityPage()
344 {
345 	delete fRunTimeData;
346 	delete fWaitTimeData;
347 	delete fLatencyTimeData;
348 	delete fPreemptionTimeData;
349 	delete fActivityChartRenderer;
350 }
351 
352 
353 void
354 ThreadWindow::ActivityPage::SetModel(ThreadModel* model)
355 {
356 	if (model == fThreadModel)
357 		return;
358 
359 	if (fThreadModel != NULL) {
360 		fActivityChart->RemoveAllDataSources();
361 		delete fRunTimeData;
362 		delete fWaitTimeData;
363 		delete fLatencyTimeData;
364 		delete fPreemptionTimeData;
365 		fRunTimeData = NULL;
366 		fWaitTimeData = NULL;
367 		fLatencyTimeData = NULL;
368 		fPreemptionTimeData = NULL;
369 	}
370 
371 	fThreadModel = model;
372 
373 	if (fThreadModel != NULL) {
374 		// run time
375 		LineChartRendererDataSourceConfig runConfig(
376 			rgb_color().set_to(0, 0, 0, 255));
377 
378 		fRunTimeData = new(std::nothrow) ThreadActivityData(fThreadModel,
379 			RUN_TIME);
380 		fActivityChart->AddDataSource(fRunTimeData, &runConfig);
381 
382 		// wait time
383 		LineChartRendererDataSourceConfig waitConfig(
384 			rgb_color().set_to(0, 255, 0, 255));
385 
386 		fWaitTimeData = new(std::nothrow) ThreadActivityData(fThreadModel,
387 			WAIT_TIME);
388 		fActivityChart->AddDataSource(fWaitTimeData, &waitConfig);
389 
390 		// preemption time
391 		LineChartRendererDataSourceConfig preemptionConfig(
392 			rgb_color().set_to(0, 0, 255, 255));
393 
394 		fLatencyTimeData = new(std::nothrow) ThreadActivityData(fThreadModel,
395 			PREEMPTION_TIME);
396 		fActivityChart->AddDataSource(fLatencyTimeData, &preemptionConfig);
397 
398 		// latency time
399 		LineChartRendererDataSourceConfig latencyConfig(
400 			rgb_color().set_to(255, 0, 0, 255));
401 
402 		fPreemptionTimeData = new(std::nothrow) ThreadActivityData(fThreadModel,
403 			LATENCY_TIME);
404 		fActivityChart->AddDataSource(fPreemptionTimeData, &latencyConfig);
405 	}
406 }
407