1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ModelLoader.h" 8 9 #include <stdio.h> 10 #include <string.h> 11 12 #include <new> 13 14 #include <AutoDeleter.h> 15 #include <AutoLocker.h> 16 #include <DebugEventStream.h> 17 18 #include <system_profiler_defs.h> 19 #include <thread_defs.h> 20 21 #include "DataSource.h" 22 #include "MessageCodes.h" 23 #include "Model.h" 24 25 26 // add a scheduling state snapshot every x events 27 static const uint32 kSchedulingSnapshotInterval = 1024; 28 29 30 struct SimpleWaitObjectInfo : system_profiler_wait_object_info { 31 SimpleWaitObjectInfo(uint32 type) 32 { 33 this->type = type; 34 object = 0; 35 referenced_object = 0; 36 name[0] = '\0'; 37 } 38 }; 39 40 41 static const SimpleWaitObjectInfo kSnoozeWaitObjectInfo( 42 THREAD_BLOCK_TYPE_SNOOZE); 43 static const SimpleWaitObjectInfo kSignalWaitObjectInfo( 44 THREAD_BLOCK_TYPE_SIGNAL); 45 46 47 // #pragma mark - 48 49 50 inline void 51 ModelLoader::_UpdateLastEventTime(bigtime_t time) 52 { 53 if (fBaseTime < 0) { 54 fBaseTime = time; 55 fModel->SetBaseTime(time); 56 } 57 58 fState.SetLastEventTime(time - fBaseTime); 59 } 60 61 62 ModelLoader::ModelLoader(DataSource* dataSource, 63 const BMessenger& target, void* targetCookie) 64 : 65 AbstractModelLoader(target, targetCookie), 66 fModel(NULL), 67 fDataSource(dataSource) 68 { 69 } 70 71 72 ModelLoader::~ModelLoader() 73 { 74 delete fDataSource; 75 delete fModel; 76 } 77 78 79 Model* 80 ModelLoader::DetachModel() 81 { 82 AutoLocker<BLocker> locker(fLock); 83 84 if (fModel == NULL || fLoading) 85 return NULL; 86 87 Model* model = fModel; 88 fModel = NULL; 89 90 return model; 91 } 92 93 94 status_t 95 ModelLoader::PrepareForLoading() 96 { 97 if (fModel != NULL || fDataSource == NULL) 98 return B_BAD_VALUE; 99 100 // init the state 101 status_t error = fState.Init(); 102 if (error != B_OK) 103 return error; 104 105 return B_OK; 106 } 107 108 109 status_t 110 ModelLoader::Load() 111 { 112 try { 113 return _Load(); 114 } catch(...) { 115 return B_ERROR; 116 } 117 } 118 119 120 void 121 ModelLoader::FinishLoading(bool success) 122 { 123 fState.Clear(); 124 125 if (!success) { 126 delete fModel; 127 fModel = NULL; 128 } 129 } 130 131 132 status_t 133 ModelLoader::_Load() 134 { 135 // read the complete data into memory 136 void* eventData; 137 size_t eventDataSize; 138 status_t error = _ReadDebugEvents(&eventData, &eventDataSize); 139 if (error != B_OK) 140 return error; 141 142 // get the data source name 143 BString dataSourceName; 144 fDataSource->GetName(dataSourceName); 145 146 // create a model 147 fModel = new(std::nothrow) Model(dataSourceName.String(), eventData, 148 eventDataSize); 149 if (fModel == NULL) { 150 free(eventData); 151 return B_NO_MEMORY; 152 } 153 154 // create a debug input stream 155 BDebugEventInputStream* input = new(std::nothrow) BDebugEventInputStream; 156 if (input == NULL) 157 return B_NO_MEMORY; 158 ObjectDeleter<BDebugEventInputStream> inputDeleter(input); 159 160 error = input->SetTo(eventData, eventDataSize, false); 161 if (error != B_OK) 162 return error; 163 164 // add the snooze and signal wait objects to the model 165 if (fModel->AddWaitObject(&kSnoozeWaitObjectInfo, NULL) == NULL 166 || fModel->AddWaitObject(&kSignalWaitObjectInfo, NULL) == NULL) { 167 return B_NO_MEMORY; 168 } 169 170 // process the events 171 fState.Clear(); 172 fBaseTime = -1; 173 uint64 count = 0; 174 175 while (true) { 176 // get next event 177 uint32 event; 178 uint32 cpu; 179 const void* buffer; 180 off_t offset; 181 ssize_t bufferSize = input->ReadNextEvent(&event, &cpu, &buffer, 182 &offset); 183 if (bufferSize < 0) 184 return bufferSize; 185 if (buffer == NULL) 186 break; 187 188 // process the event 189 status_t error = _ProcessEvent(event, cpu, buffer, bufferSize); 190 if (error != B_OK) 191 return error; 192 193 // periodically check whether we're supposed to abort 194 if (++count % 32 == 0) { 195 AutoLocker<BLocker> locker(fLock); 196 if (fAborted) 197 return B_ERROR; 198 } 199 200 // periodically add scheduling snapshots 201 if (count % kSchedulingSnapshotInterval == 0) 202 fModel->AddSchedulingStateSnapshot(fState, offset); 203 } 204 205 fModel->SetLastEventTime(fState.LastEventTime()); 206 fModel->LoadingFinished(); 207 208 return B_OK; 209 } 210 211 212 status_t 213 ModelLoader::_ReadDebugEvents(void** _eventData, size_t* _size) 214 { 215 // get a BDataIO from the data source 216 BDataIO* io; 217 status_t error = fDataSource->CreateDataIO(&io); 218 if (error != B_OK) 219 return error; 220 ObjectDeleter<BDataIO> dataIOtDeleter(io); 221 222 // First we need to find out how large a buffer to allocate. 223 size_t size; 224 225 if (BPositionIO* positionIO = dynamic_cast<BPositionIO*>(io)) { 226 // it's a BPositionIO -- this makes things easier, since we know how 227 // many bytes to read 228 off_t currentPos = positionIO->Position(); 229 if (currentPos < 0) 230 return currentPos; 231 232 off_t fileSize; 233 error = positionIO->GetSize(&fileSize); 234 if (error != B_OK) 235 return error; 236 237 size = fileSize - currentPos; 238 } else { 239 // no BPositionIO -- we need to determine the total size by iteratively 240 // reading the whole data one time 241 242 // allocate a dummy buffer for reading 243 const size_t kBufferSize = 1024 * 1024; 244 void* buffer = malloc(kBufferSize); 245 if (buffer == NULL) 246 return B_NO_MEMORY; 247 MemoryDeleter bufferDeleter(buffer); 248 249 size = 0; 250 while (true) { 251 ssize_t bytesRead = io->Read(buffer, kBufferSize); 252 if (bytesRead < 0) 253 return bytesRead; 254 if (bytesRead == 0) 255 break; 256 257 size += bytesRead; 258 } 259 260 // we've got the size -- recreate the BDataIO 261 dataIOtDeleter.Delete(); 262 error = fDataSource->CreateDataIO(&io); 263 if (error != B_OK) 264 return error; 265 dataIOtDeleter.SetTo(io); 266 } 267 268 // allocate the data buffer 269 void* data = malloc(size); 270 if (data == NULL) 271 return B_NO_MEMORY; 272 MemoryDeleter dataDeleter(data); 273 274 // read the data 275 ssize_t bytesRead = io->Read(data, size); 276 if (bytesRead < 0) 277 return bytesRead; 278 if ((size_t)bytesRead != size) 279 return B_FILE_ERROR; 280 281 dataDeleter.Detach(); 282 *_eventData = data; 283 *_size = size; 284 return B_OK; 285 } 286 287 288 status_t 289 ModelLoader::_ProcessEvent(uint32 event, uint32 cpu, const void* buffer, 290 size_t size) 291 { 292 switch (event) { 293 case B_SYSTEM_PROFILER_TEAM_ADDED: 294 _HandleTeamAdded((system_profiler_team_added*)buffer); 295 break; 296 297 case B_SYSTEM_PROFILER_TEAM_REMOVED: 298 _HandleTeamRemoved((system_profiler_team_removed*)buffer); 299 break; 300 301 case B_SYSTEM_PROFILER_TEAM_EXEC: 302 _HandleTeamExec((system_profiler_team_exec*)buffer); 303 break; 304 305 case B_SYSTEM_PROFILER_THREAD_ADDED: 306 _HandleThreadAdded((system_profiler_thread_added*)buffer); 307 break; 308 309 case B_SYSTEM_PROFILER_THREAD_REMOVED: 310 _HandleThreadRemoved((system_profiler_thread_removed*)buffer); 311 break; 312 313 case B_SYSTEM_PROFILER_THREAD_SCHEDULED: 314 _HandleThreadScheduled((system_profiler_thread_scheduled*)buffer); 315 break; 316 317 case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE: 318 _HandleThreadEnqueuedInRunQueue( 319 (thread_enqueued_in_run_queue*)buffer); 320 break; 321 322 case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE: 323 _HandleThreadRemovedFromRunQueue( 324 (thread_removed_from_run_queue*)buffer); 325 break; 326 327 case B_SYSTEM_PROFILER_WAIT_OBJECT_INFO: 328 _HandleWaitObjectInfo((system_profiler_wait_object_info*)buffer); 329 break; 330 331 default: 332 printf("unsupported event type %lu, size: %lu\n", event, size); 333 return B_BAD_DATA; 334 break; 335 } 336 337 return B_OK; 338 } 339 340 341 void 342 ModelLoader::_HandleTeamAdded(system_profiler_team_added* event) 343 { 344 if (fModel->AddTeam(event, fState.LastEventTime()) == NULL) 345 throw std::bad_alloc(); 346 } 347 348 349 void 350 ModelLoader::_HandleTeamRemoved(system_profiler_team_removed* event) 351 { 352 if (Model::Team* team = fModel->TeamByID(event->team)) 353 team->SetDeletionTime(fState.LastEventTime()); 354 else 355 printf("Removed event for unknown team: %ld\n", event->team); 356 } 357 358 359 void 360 ModelLoader::_HandleTeamExec(system_profiler_team_exec* event) 361 { 362 // TODO:... 363 } 364 365 366 void 367 ModelLoader::_HandleThreadAdded(system_profiler_thread_added* event) 368 { 369 if (_AddThread(event) == NULL) 370 throw std::bad_alloc(); 371 } 372 373 374 void 375 ModelLoader::_HandleThreadRemoved(system_profiler_thread_removed* event) 376 { 377 if (Model::Thread* thread = fModel->ThreadByID(event->thread)) 378 thread->SetDeletionTime(fState.LastEventTime()); 379 else 380 printf("Removed event for unknown team: %ld\n", event->thread); 381 } 382 383 384 void 385 ModelLoader::_HandleThreadScheduled(system_profiler_thread_scheduled* event) 386 { 387 _UpdateLastEventTime(event->time); 388 389 Model::ThreadSchedulingState* thread = fState.LookupThread(event->thread); 390 if (thread == NULL) { 391 printf("Schedule event for unknown thread: %ld\n", event->thread); 392 return; 393 } 394 395 bigtime_t diffTime = fState.LastEventTime() - thread->lastTime; 396 397 if (thread->state == READY) { 398 // thread scheduled after having been woken up 399 thread->thread->AddLatency(diffTime); 400 } else if (thread->state == PREEMPTED) { 401 // thread scheduled after having been preempted before 402 thread->thread->AddRerun(diffTime); 403 } 404 405 if (thread->state == STILL_RUNNING) { 406 // Thread was running and continues to run. 407 thread->state = RUNNING; 408 } 409 410 if (thread->state != RUNNING) { 411 thread->lastTime = fState.LastEventTime(); 412 thread->state = RUNNING; 413 } 414 415 // unscheduled thread 416 417 if (event->thread == event->previous_thread) 418 return; 419 420 thread = fState.LookupThread(event->previous_thread); 421 if (thread == NULL) { 422 printf("Schedule event for unknown previous thread: %ld\n", 423 event->previous_thread); 424 return; 425 } 426 427 diffTime = fState.LastEventTime() - thread->lastTime; 428 429 if (thread->state == STILL_RUNNING) { 430 // thread preempted 431 thread->thread->AddPreemption(diffTime); 432 thread->thread->AddRun(diffTime); 433 434 thread->lastTime = fState.LastEventTime(); 435 thread->state = PREEMPTED; 436 } else if (thread->state == RUNNING) { 437 // thread starts waiting (it hadn't been added to the run 438 // queue before being unscheduled) 439 thread->thread->AddRun(diffTime); 440 441 if (event->previous_thread_state == B_THREAD_WAITING) { 442 addr_t waitObject = event->previous_thread_wait_object; 443 switch (event->previous_thread_wait_object_type) { 444 case THREAD_BLOCK_TYPE_SNOOZE: 445 case THREAD_BLOCK_TYPE_SIGNAL: 446 waitObject = 0; 447 break; 448 case THREAD_BLOCK_TYPE_SEMAPHORE: 449 case THREAD_BLOCK_TYPE_CONDITION_VARIABLE: 450 case THREAD_BLOCK_TYPE_MUTEX: 451 case THREAD_BLOCK_TYPE_RW_LOCK: 452 case THREAD_BLOCK_TYPE_OTHER: 453 default: 454 break; 455 } 456 457 _AddThreadWaitObject(thread, 458 event->previous_thread_wait_object_type, waitObject); 459 } 460 461 thread->lastTime = fState.LastEventTime(); 462 thread->state = WAITING; 463 } else if (thread->state == UNKNOWN) { 464 uint32 threadState = event->previous_thread_state; 465 if (threadState == B_THREAD_WAITING 466 || threadState == B_THREAD_SUSPENDED) { 467 thread->lastTime = fState.LastEventTime(); 468 thread->state = WAITING; 469 } else if (threadState == B_THREAD_READY) { 470 thread->lastTime = fState.LastEventTime(); 471 thread->state = PREEMPTED; 472 } 473 } 474 } 475 476 477 void 478 ModelLoader::_HandleThreadEnqueuedInRunQueue( 479 thread_enqueued_in_run_queue* event) 480 { 481 _UpdateLastEventTime(event->time); 482 483 Model::ThreadSchedulingState* thread = fState.LookupThread(event->thread); 484 if (thread == NULL) { 485 printf("Enqueued in run queue event for unknown thread: %ld\n", 486 event->thread); 487 return; 488 } 489 490 if (thread->state == RUNNING || thread->state == STILL_RUNNING) { 491 // Thread was running and is reentered into the run queue. This 492 // is done by the scheduler, if the thread remains ready. 493 thread->state = STILL_RUNNING; 494 } else { 495 // Thread was waiting and is ready now. 496 bigtime_t diffTime = fState.LastEventTime() - thread->lastTime; 497 if (thread->waitObject != NULL) { 498 thread->waitObject->AddWait(diffTime); 499 thread->waitObject = NULL; 500 thread->thread->AddWait(diffTime); 501 } else if (thread->state != UNKNOWN) 502 thread->thread->AddUnspecifiedWait(diffTime); 503 504 thread->lastTime = fState.LastEventTime(); 505 thread->state = READY; 506 } 507 } 508 509 510 void 511 ModelLoader::_HandleThreadRemovedFromRunQueue( 512 thread_removed_from_run_queue* event) 513 { 514 _UpdateLastEventTime(event->time); 515 516 Model::ThreadSchedulingState* thread = fState.LookupThread(event->thread); 517 if (thread == NULL) { 518 printf("Removed from run queue event for unknown thread: %ld\n", 519 event->thread); 520 return; 521 } 522 523 // This really only happens when the thread priority is changed 524 // while the thread is ready. 525 526 bigtime_t diffTime = fState.LastEventTime() - thread->lastTime; 527 if (thread->state == RUNNING) { 528 // This should never happen. 529 thread->thread->AddRun(diffTime); 530 } else if (thread->state == READY || thread->state == PREEMPTED) { 531 // Not really correct, but the case is rare and we keep it 532 // simple. 533 thread->thread->AddUnspecifiedWait(diffTime); 534 } 535 536 thread->lastTime = fState.LastEventTime(); 537 thread->state = WAITING; 538 } 539 540 541 void 542 ModelLoader::_HandleWaitObjectInfo(system_profiler_wait_object_info* event) 543 { 544 if (fModel->AddWaitObject(event, NULL) == NULL) 545 throw std::bad_alloc(); 546 } 547 548 549 Model::ThreadSchedulingState* 550 ModelLoader::_AddThread(system_profiler_thread_added* event) 551 { 552 // do we know the thread already? 553 Model::ThreadSchedulingState* info = fState.LookupThread(event->thread); 554 if (info != NULL) { 555 // TODO: ? 556 return info; 557 } 558 559 // add the thread to the model 560 Model::Thread* thread = fModel->AddThread(event, fState.LastEventTime()); 561 if (thread == NULL) 562 return NULL; 563 564 // create and add a ThreadSchedulingState 565 info = new(std::nothrow) Model::ThreadSchedulingState(thread); 566 if (info == NULL) 567 return NULL; 568 569 fState.InsertThread(info); 570 571 return info; 572 } 573 574 575 void 576 ModelLoader::_AddThreadWaitObject(Model::ThreadSchedulingState* thread, 577 uint32 type, addr_t object) 578 { 579 Model::WaitObjectGroup* waitObjectGroup 580 = fModel->WaitObjectGroupFor(type, object); 581 if (waitObjectGroup == NULL) { 582 // The algorithm should prevent this case. 583 printf("ModelLoader::_AddThreadWaitObject(): Unknown wait object: type: %lu, " 584 "object: %#lx\n", type, object); 585 return; 586 } 587 588 Model::WaitObject* waitObject = waitObjectGroup->MostRecentWaitObject(); 589 590 Model::ThreadWaitObjectGroup* threadWaitObjectGroup 591 = fModel->ThreadWaitObjectGroupFor(thread->ID(), type, object); 592 593 if (threadWaitObjectGroup == NULL 594 || threadWaitObjectGroup->MostRecentWaitObject() != waitObject) { 595 Model::ThreadWaitObject* threadWaitObject 596 = fModel->AddThreadWaitObject(thread->ID(), waitObject, 597 &threadWaitObjectGroup); 598 if (threadWaitObject == NULL) 599 throw std::bad_alloc(); 600 } 601 602 thread->waitObject = threadWaitObjectGroup->MostRecentThreadWaitObject(); 603 } 604