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