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 <algorithm> 13 #include <new> 14 15 #include <AutoDeleter.h> 16 #include <AutoLocker.h> 17 #include <DebugEventStream.h> 18 19 #include <system_profiler_defs.h> 20 #include <thread_defs.h> 21 22 #include "DataSource.h" 23 #include "MessageCodes.h" 24 #include "Model.h" 25 26 27 // add a scheduling state snapshot every x events 28 static const uint32 kSchedulingSnapshotInterval = 1024; 29 30 static const uint32 kMaxCPUCount = 1024; 31 32 33 struct SimpleWaitObjectInfo : system_profiler_wait_object_info { 34 SimpleWaitObjectInfo(uint32 type) 35 { 36 this->type = type; 37 object = 0; 38 referenced_object = 0; 39 name[0] = '\0'; 40 } 41 }; 42 43 44 static const SimpleWaitObjectInfo kSnoozeWaitObjectInfo( 45 THREAD_BLOCK_TYPE_SNOOZE); 46 static const SimpleWaitObjectInfo kSignalWaitObjectInfo( 47 THREAD_BLOCK_TYPE_SIGNAL); 48 49 50 // #pragma mark - CPUInfo 51 52 53 struct ModelLoader::CPUInfo { 54 nanotime_t idleTime; 55 56 CPUInfo() 57 : 58 idleTime(0) 59 { 60 } 61 }; 62 63 64 // #pragma mark - IOOperation 65 66 67 struct ModelLoader::IOOperation : DoublyLinkedListLinkImpl<IOOperation> { 68 io_operation_started* startedEvent; 69 io_operation_finished* finishedEvent; 70 71 IOOperation(io_operation_started* startedEvent) 72 : 73 startedEvent(startedEvent), 74 finishedEvent(NULL) 75 { 76 } 77 }; 78 79 80 // #pragma mark - IORequest 81 82 83 struct ModelLoader::IORequest : DoublyLinkedListLinkImpl<IORequest> { 84 io_request_scheduled* scheduledEvent; 85 io_request_finished* finishedEvent; 86 IOOperationList operations; 87 size_t operationCount; 88 IORequest* hashNext; 89 90 IORequest(io_request_scheduled* scheduledEvent) 91 : 92 scheduledEvent(scheduledEvent), 93 finishedEvent(NULL), 94 operationCount(0) 95 { 96 } 97 98 ~IORequest() 99 { 100 while (IOOperation* operation = operations.RemoveHead()) 101 delete operation; 102 } 103 104 void AddOperation(IOOperation* operation) 105 { 106 operations.Add(operation); 107 operationCount++; 108 } 109 110 IOOperation* FindOperation(void* address) const 111 { 112 for (IOOperationList::ConstReverseIterator it 113 = operations.GetReverseIterator(); 114 IOOperation* operation = it.Next();) { 115 if (operation->startedEvent->operation == address) 116 return operation; 117 } 118 119 return NULL; 120 } 121 122 Model::IORequest* CreateModelRequest() const 123 { 124 size_t operationCount = operations.Count(); 125 126 Model::IORequest* modelRequest = Model::IORequest::Create( 127 scheduledEvent, finishedEvent, operationCount); 128 if (modelRequest == NULL) 129 return NULL; 130 131 size_t index = 0; 132 for (IOOperationList::ConstIterator it = operations.GetIterator(); 133 IOOperation* operation = it.Next();) { 134 Model::IOOperation& modelOperation 135 = modelRequest->operations[index++]; 136 modelOperation.startedEvent = operation->startedEvent; 137 modelOperation.finishedEvent = operation->finishedEvent; 138 } 139 140 return modelRequest; 141 } 142 }; 143 144 145 // #pragma mark - IORequestHashDefinition 146 147 148 struct ModelLoader::IORequestHashDefinition { 149 typedef void* KeyType; 150 typedef IORequest ValueType; 151 152 size_t HashKey(KeyType key) const 153 { return (size_t)key; } 154 155 size_t Hash(const IORequest* value) const 156 { return HashKey(value->scheduledEvent->request); } 157 158 bool Compare(KeyType key, const IORequest* value) const 159 { return key == value->scheduledEvent->request; } 160 161 IORequest*& GetLink(IORequest* value) const 162 { return value->hashNext; } 163 }; 164 165 166 // #pragma mark - ExtendedThreadSchedulingState 167 168 169 struct ModelLoader::ExtendedThreadSchedulingState 170 : Model::ThreadSchedulingState { 171 172 ExtendedThreadSchedulingState(Model::Thread* thread) 173 : 174 Model::ThreadSchedulingState(thread), 175 fEvents(NULL), 176 fEventIndex(0), 177 fEventCount(0) 178 { 179 } 180 181 ~ExtendedThreadSchedulingState() 182 { 183 delete[] fEvents; 184 185 while (IORequest* request = fIORequests.RemoveHead()) 186 delete request; 187 while (IORequest* request = fPendingIORequests.RemoveHead()) 188 delete request; 189 } 190 191 system_profiler_event_header** Events() const 192 { 193 return fEvents; 194 } 195 196 size_t CountEvents() const 197 { 198 return fEventCount; 199 } 200 201 system_profiler_event_header** DetachEvents() 202 { 203 system_profiler_event_header** events = fEvents; 204 fEvents = NULL; 205 return events; 206 } 207 208 void IncrementEventCount() 209 { 210 fEventCount++; 211 } 212 213 void AddEvent(system_profiler_event_header* event) 214 { 215 fEvents[fEventIndex++] = event; 216 } 217 218 bool AllocateEventArray() 219 { 220 if (fEventCount == 0) 221 return true; 222 223 fEvents = new(std::nothrow) system_profiler_event_header*[fEventCount]; 224 if (fEvents == NULL) 225 return false; 226 227 return true; 228 } 229 230 void AddIORequest(IORequest* request) 231 { 232 fPendingIORequests.Add(request); 233 } 234 235 void IORequestFinished(IORequest* request) 236 { 237 fPendingIORequests.Remove(request); 238 fIORequests.Add(request); 239 } 240 241 bool PrepareThreadIORequests(Model::IORequest**& _requests, 242 size_t& _requestCount) 243 { 244 fIORequests.MoveFrom(&fPendingIORequests); 245 size_t requestCount = fIORequests.Count(); 246 247 if (requestCount == 0) { 248 _requests = NULL; 249 _requestCount = 0; 250 return true; 251 } 252 253 Model::IORequest** requests 254 = new(std::nothrow) Model::IORequest*[requestCount]; 255 if (requests == NULL) 256 return false; 257 258 size_t index = 0; 259 while (IORequest* request = fIORequests.RemoveHead()) { 260 ObjectDeleter<IORequest> requestDeleter(request); 261 262 Model::IORequest* modelRequest = request->CreateModelRequest(); 263 if (modelRequest == NULL) { 264 for (size_t i = 0; i < index; i++) 265 requests[i]->Delete(); 266 return false; 267 } 268 269 requests[index++] = modelRequest; 270 } 271 272 _requests = requests; 273 _requestCount = requestCount; 274 return true; 275 } 276 277 private: 278 system_profiler_event_header** fEvents; 279 size_t fEventIndex; 280 size_t fEventCount; 281 IORequestList fIORequests; 282 IORequestList fPendingIORequests; 283 }; 284 285 286 // #pragma mark - ExtendedSchedulingState 287 288 289 struct ModelLoader::ExtendedSchedulingState : Model::SchedulingState { 290 inline ExtendedThreadSchedulingState* LookupThread(thread_id threadID) const 291 { 292 Model::ThreadSchedulingState* thread 293 = Model::SchedulingState::LookupThread(threadID); 294 return thread != NULL 295 ? static_cast<ExtendedThreadSchedulingState*>(thread) : NULL; 296 } 297 298 299 protected: 300 virtual void DeleteThread(Model::ThreadSchedulingState* thread) 301 { 302 delete static_cast<ExtendedThreadSchedulingState*>(thread); 303 } 304 }; 305 306 307 // #pragma mark - ModelLoader 308 309 310 inline void 311 ModelLoader::_UpdateLastEventTime(nanotime_t time) 312 { 313 if (fBaseTime < 0) { 314 fBaseTime = time; 315 fModel->SetBaseTime(time); 316 } 317 318 fState->SetLastEventTime(time - fBaseTime); 319 } 320 321 322 ModelLoader::ModelLoader(DataSource* dataSource, 323 const BMessenger& target, void* targetCookie) 324 : 325 AbstractModelLoader(target, targetCookie), 326 fModel(NULL), 327 fDataSource(dataSource), 328 fCPUInfos(NULL), 329 fState(NULL), 330 fIORequests(NULL) 331 { 332 } 333 334 335 ModelLoader::~ModelLoader() 336 { 337 delete[] fCPUInfos; 338 delete fDataSource; 339 delete fModel; 340 delete fState; 341 delete fIORequests; 342 } 343 344 345 Model* 346 ModelLoader::DetachModel() 347 { 348 AutoLocker<BLocker> locker(fLock); 349 350 if (fModel == NULL || fLoading) 351 return NULL; 352 353 Model* model = fModel; 354 fModel = NULL; 355 356 return model; 357 } 358 359 360 status_t 361 ModelLoader::PrepareForLoading() 362 { 363 if (fModel != NULL || fDataSource == NULL) 364 return B_BAD_VALUE; 365 366 // create and init the state 367 fState = new(std::nothrow) ExtendedSchedulingState; 368 if (fState == NULL) 369 return B_NO_MEMORY; 370 371 status_t error = fState->Init(); 372 if (error != B_OK) 373 return error; 374 375 // create CPU info array 376 fCPUInfos = new(std::nothrow) CPUInfo[kMaxCPUCount]; 377 if (fCPUInfos == NULL) 378 return B_NO_MEMORY; 379 380 // create IORequest hash table 381 fIORequests = new(std::nothrow) IORequestTable; 382 if (fIORequests == NULL || fIORequests->Init() != B_OK) 383 return B_NO_MEMORY; 384 385 return B_OK; 386 } 387 388 389 status_t 390 ModelLoader::Load() 391 { 392 try { 393 return _Load(); 394 } catch(...) { 395 return B_ERROR; 396 } 397 } 398 399 400 void 401 ModelLoader::FinishLoading(bool success) 402 { 403 delete fState; 404 fState = NULL; 405 406 if (!success) { 407 delete fModel; 408 fModel = NULL; 409 } 410 411 delete[] fCPUInfos; 412 fCPUInfos = NULL; 413 414 delete fIORequests; 415 fIORequests = NULL; 416 } 417 418 419 status_t 420 ModelLoader::_Load() 421 { 422 // read the complete data into memory 423 void* eventData; 424 size_t eventDataSize; 425 status_t error = _ReadDebugEvents(&eventData, &eventDataSize); 426 if (error != B_OK) 427 return error; 428 MemoryDeleter eventDataDeleter(eventData); 429 430 // create a debug event array 431 system_profiler_event_header** events; 432 size_t eventCount; 433 error = _CreateDebugEventArray(eventData, eventDataSize, events, 434 eventCount); 435 if (error != B_OK) 436 return error; 437 ArrayDeleter<system_profiler_event_header*> eventsDeleter(events); 438 439 // get the data source name 440 BString dataSourceName; 441 fDataSource->GetName(dataSourceName); 442 443 // create a model 444 fModel = new(std::nothrow) Model(dataSourceName.String(), eventData, 445 eventDataSize, events, eventCount); 446 if (fModel == NULL) 447 return B_NO_MEMORY; 448 eventDataDeleter.Detach(); 449 eventsDeleter.Detach(); 450 451 // create a debug input stream 452 BDebugEventInputStream input; 453 error = input.SetTo(eventData, eventDataSize, false); 454 if (error != B_OK) 455 return error; 456 457 // add the snooze and signal wait objects to the model 458 if (fModel->AddWaitObject(&kSnoozeWaitObjectInfo, NULL) == NULL 459 || fModel->AddWaitObject(&kSignalWaitObjectInfo, NULL) == NULL) { 460 return B_NO_MEMORY; 461 } 462 463 // process the events 464 fMaxCPUIndex = 0; 465 fState->Clear(); 466 fBaseTime = -1; 467 uint64 count = 0; 468 469 while (true) { 470 // get next event 471 uint32 event; 472 uint32 cpu; 473 const void* buffer; 474 off_t offset; 475 ssize_t bufferSize = input.ReadNextEvent(&event, &cpu, &buffer, 476 &offset); 477 if (bufferSize < 0) 478 return bufferSize; 479 if (buffer == NULL) 480 break; 481 482 // process the event 483 status_t error = _ProcessEvent(event, cpu, buffer, bufferSize); 484 if (error != B_OK) 485 return error; 486 487 if (cpu > fMaxCPUIndex) { 488 if (cpu + 1 > kMaxCPUCount) 489 return B_BAD_DATA; 490 fMaxCPUIndex = cpu; 491 } 492 493 // periodically check whether we're supposed to abort 494 if (++count % 32 == 0) { 495 AutoLocker<BLocker> locker(fLock); 496 if (fAborted) 497 return B_ERROR; 498 } 499 500 // periodically add scheduling snapshots 501 if (count % kSchedulingSnapshotInterval == 0) 502 fModel->AddSchedulingStateSnapshot(*fState, offset); 503 } 504 505 if (!fModel->SetCPUCount(fMaxCPUIndex + 1)) 506 return B_NO_MEMORY; 507 508 for (uint32 i = 0; i <= fMaxCPUIndex; i++) 509 fModel->CPUAt(i)->SetIdleTime(fCPUInfos[i].idleTime); 510 511 fModel->SetLastEventTime(fState->LastEventTime()); 512 513 if (!_SetThreadEvents() || !_SetThreadIORequests()) 514 return B_NO_MEMORY; 515 516 fModel->LoadingFinished(); 517 518 return B_OK; 519 } 520 521 522 status_t 523 ModelLoader::_ReadDebugEvents(void** _eventData, size_t* _size) 524 { 525 // get a BDataIO from the data source 526 BDataIO* io; 527 status_t error = fDataSource->CreateDataIO(&io); 528 if (error != B_OK) 529 return error; 530 ObjectDeleter<BDataIO> dataIOtDeleter(io); 531 532 // First we need to find out how large a buffer to allocate. 533 size_t size; 534 535 if (BPositionIO* positionIO = dynamic_cast<BPositionIO*>(io)) { 536 // it's a BPositionIO -- this makes things easier, since we know how 537 // many bytes to read 538 off_t currentPos = positionIO->Position(); 539 if (currentPos < 0) 540 return currentPos; 541 542 off_t fileSize; 543 error = positionIO->GetSize(&fileSize); 544 if (error != B_OK) 545 return error; 546 547 size = fileSize - currentPos; 548 } else { 549 // no BPositionIO -- we need to determine the total size by iteratively 550 // reading the whole data one time 551 552 // allocate a dummy buffer for reading 553 const size_t kBufferSize = 1024 * 1024; 554 void* buffer = malloc(kBufferSize); 555 if (buffer == NULL) 556 return B_NO_MEMORY; 557 MemoryDeleter bufferDeleter(buffer); 558 559 size = 0; 560 while (true) { 561 ssize_t bytesRead = io->Read(buffer, kBufferSize); 562 if (bytesRead < 0) 563 return bytesRead; 564 if (bytesRead == 0) 565 break; 566 567 size += bytesRead; 568 } 569 570 // we've got the size -- recreate the BDataIO 571 dataIOtDeleter.Delete(); 572 error = fDataSource->CreateDataIO(&io); 573 if (error != B_OK) 574 return error; 575 dataIOtDeleter.SetTo(io); 576 } 577 578 // allocate the data buffer 579 void* data = malloc(size); 580 if (data == NULL) 581 return B_NO_MEMORY; 582 MemoryDeleter dataDeleter(data); 583 584 // read the data 585 ssize_t bytesRead = io->Read(data, size); 586 if (bytesRead < 0) 587 return bytesRead; 588 if ((size_t)bytesRead != size) 589 return B_FILE_ERROR; 590 591 dataDeleter.Detach(); 592 *_eventData = data; 593 *_size = size; 594 return B_OK; 595 } 596 597 598 status_t 599 ModelLoader::_CreateDebugEventArray(void* eventData, size_t eventDataSize, 600 system_profiler_event_header**& _events, size_t& _eventCount) 601 { 602 // count the events 603 BDebugEventInputStream input; 604 status_t error = input.SetTo(eventData, eventDataSize, false); 605 if (error != B_OK) 606 return error; 607 608 size_t eventCount = 0; 609 while (true) { 610 // get next event 611 uint32 event; 612 uint32 cpu; 613 const void* buffer; 614 ssize_t bufferSize = input.ReadNextEvent(&event, &cpu, &buffer, NULL); 615 if (bufferSize < 0) 616 return bufferSize; 617 if (buffer == NULL) 618 break; 619 620 eventCount++; 621 } 622 623 // create the array 624 system_profiler_event_header** events = new(std::nothrow) 625 system_profiler_event_header*[eventCount]; 626 if (events == NULL) 627 return B_NO_MEMORY; 628 629 // populate the array 630 error = input.SetTo(eventData, eventDataSize, false); 631 if (error != B_OK) { 632 delete[] events; 633 return error; 634 } 635 636 size_t eventIndex = 0; 637 while (true) { 638 // get next event 639 uint32 event; 640 uint32 cpu; 641 const void* buffer; 642 off_t offset; 643 input.ReadNextEvent(&event, &cpu, &buffer, &offset); 644 if (buffer == NULL) 645 break; 646 647 events[eventIndex++] 648 = (system_profiler_event_header*)((uint8*)eventData + offset); 649 } 650 651 _events = events; 652 _eventCount = eventCount; 653 return B_OK; 654 } 655 656 657 status_t 658 ModelLoader::_ProcessEvent(uint32 event, uint32 cpu, const void* buffer, 659 size_t size) 660 { 661 switch (event) { 662 case B_SYSTEM_PROFILER_TEAM_ADDED: 663 _HandleTeamAdded((system_profiler_team_added*)buffer); 664 break; 665 666 case B_SYSTEM_PROFILER_TEAM_REMOVED: 667 _HandleTeamRemoved((system_profiler_team_removed*)buffer); 668 break; 669 670 case B_SYSTEM_PROFILER_TEAM_EXEC: 671 _HandleTeamExec((system_profiler_team_exec*)buffer); 672 break; 673 674 case B_SYSTEM_PROFILER_THREAD_ADDED: 675 _HandleThreadAdded((system_profiler_thread_added*)buffer); 676 break; 677 678 case B_SYSTEM_PROFILER_THREAD_REMOVED: 679 _HandleThreadRemoved((system_profiler_thread_removed*)buffer); 680 break; 681 682 case B_SYSTEM_PROFILER_THREAD_SCHEDULED: 683 _HandleThreadScheduled(cpu, 684 (system_profiler_thread_scheduled*)buffer); 685 break; 686 687 case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE: 688 _HandleThreadEnqueuedInRunQueue( 689 (thread_enqueued_in_run_queue*)buffer); 690 break; 691 692 case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE: 693 _HandleThreadRemovedFromRunQueue(cpu, 694 (thread_removed_from_run_queue*)buffer); 695 break; 696 697 case B_SYSTEM_PROFILER_WAIT_OBJECT_INFO: 698 _HandleWaitObjectInfo((system_profiler_wait_object_info*)buffer); 699 break; 700 701 case B_SYSTEM_PROFILER_IO_SCHEDULER_ADDED: 702 _HandleIOSchedulerAdded( 703 (system_profiler_io_scheduler_added*)buffer); 704 break; 705 706 case B_SYSTEM_PROFILER_IO_SCHEDULER_REMOVED: 707 // not so interesting 708 break; 709 710 case B_SYSTEM_PROFILER_IO_REQUEST_SCHEDULED: 711 _HandleIORequestScheduled((io_request_scheduled*)buffer); 712 break; 713 case B_SYSTEM_PROFILER_IO_REQUEST_FINISHED: 714 _HandleIORequestFinished((io_request_finished*)buffer); 715 break; 716 case B_SYSTEM_PROFILER_IO_OPERATION_STARTED: 717 _HandleIOOperationStarted((io_operation_started*)buffer); 718 break; 719 case B_SYSTEM_PROFILER_IO_OPERATION_FINISHED: 720 _HandleIOOperationFinished((io_operation_finished*)buffer); 721 break; 722 723 default: 724 printf("unsupported event type %lu, size: %lu\n", event, size); 725 return B_BAD_DATA; 726 break; 727 } 728 729 return B_OK; 730 } 731 732 733 bool 734 ModelLoader::_SetThreadEvents() 735 { 736 // allocate the threads' events arrays 737 for (int32 i = 0; Model::Thread* thread = fModel->ThreadAt(i); i++) { 738 ExtendedThreadSchedulingState* state 739 = fState->LookupThread(thread->ID()); 740 if (!state->AllocateEventArray()) 741 return false; 742 } 743 744 // fill the threads' event arrays 745 system_profiler_event_header** events = fModel->Events(); 746 size_t eventCount = fModel->CountEvents(); 747 for (size_t i = 0; i < eventCount; i++) { 748 system_profiler_event_header* header = events[i]; 749 void* buffer = header + 1; 750 751 switch (header->event) { 752 case B_SYSTEM_PROFILER_THREAD_ADDED: 753 { 754 system_profiler_thread_added* event 755 = (system_profiler_thread_added*)buffer; 756 fState->LookupThread(event->thread)->AddEvent(header); 757 break; 758 } 759 760 case B_SYSTEM_PROFILER_THREAD_REMOVED: 761 { 762 system_profiler_thread_removed* event 763 = (system_profiler_thread_removed*)buffer; 764 fState->LookupThread(event->thread)->AddEvent(header); 765 break; 766 } 767 768 case B_SYSTEM_PROFILER_THREAD_SCHEDULED: 769 { 770 system_profiler_thread_scheduled* event 771 = (system_profiler_thread_scheduled*)buffer; 772 fState->LookupThread(event->thread)->AddEvent(header); 773 774 if (event->thread != event->previous_thread) { 775 fState->LookupThread(event->previous_thread) 776 ->AddEvent(header); 777 } 778 break; 779 } 780 781 case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE: 782 { 783 thread_enqueued_in_run_queue* event 784 = (thread_enqueued_in_run_queue*)buffer; 785 fState->LookupThread(event->thread)->AddEvent(header); 786 break; 787 } 788 789 case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE: 790 { 791 thread_removed_from_run_queue* event 792 = (thread_removed_from_run_queue*)buffer; 793 fState->LookupThread(event->thread)->AddEvent(header); 794 break; 795 } 796 797 default: 798 break; 799 } 800 } 801 802 // transfer the events arrays from the scheduling states to the thread 803 // objects 804 for (int32 i = 0; Model::Thread* thread = fModel->ThreadAt(i); i++) { 805 ExtendedThreadSchedulingState* state 806 = fState->LookupThread(thread->ID()); 807 thread->SetEvents(state->Events(), state->CountEvents()); 808 state->DetachEvents(); 809 } 810 811 return true; 812 } 813 814 815 bool 816 ModelLoader::_SetThreadIORequests() 817 { 818 for (int32 i = 0; Model::Thread* thread = fModel->ThreadAt(i); i++) { 819 ExtendedThreadSchedulingState* state 820 = fState->LookupThread(thread->ID()); 821 Model::IORequest** requests; 822 size_t requestCount; 823 if (!state->PrepareThreadIORequests(requests, requestCount)) 824 return false; 825 if (requestCount > 0) 826 _SetThreadIORequests(thread, requests, requestCount); 827 } 828 829 return true; 830 } 831 832 833 void 834 ModelLoader::_SetThreadIORequests(Model::Thread* thread, 835 Model::IORequest** requests, size_t requestCount) 836 { 837 // compute some totals 838 int64 ioCount = 0; 839 nanotime_t ioTime = 0; 840 841 // sort requests by scheduler and start time 842 std::sort(requests, requests + requestCount, 843 Model::IORequest::SchedulerTimeLess); 844 845 nanotime_t endTime = fBaseTime + fModel->LastEventTime(); 846 847 // compute the summed up I/O times 848 nanotime_t ioStart = requests[0]->scheduledEvent->time; 849 nanotime_t previousEnd = requests[0]->finishedEvent != NULL 850 ? requests[0]->finishedEvent->time : endTime; 851 int32 scheduler = requests[0]->scheduledEvent->scheduler; 852 853 for (size_t i = 1; i < requestCount; i++) { 854 system_profiler_io_request_scheduled* scheduledEvent 855 = requests[i]->scheduledEvent; 856 if (scheduledEvent->scheduler != scheduler 857 || scheduledEvent->time >= previousEnd) { 858 ioCount++; 859 ioTime += previousEnd - ioStart; 860 ioStart = scheduledEvent->time; 861 } 862 863 previousEnd = requests[i]->finishedEvent != NULL 864 ? requests[i]->finishedEvent->time : endTime; 865 } 866 867 ioCount++; 868 ioTime += previousEnd - ioStart; 869 870 // sort requests by start time 871 std::sort(requests, requests + requestCount, Model::IORequest::TimeLess); 872 873 // set the computed values 874 thread->SetIORequests(requests, requestCount); 875 thread->SetIOs(ioCount, ioTime); 876 } 877 878 879 void 880 ModelLoader::_HandleTeamAdded(system_profiler_team_added* event) 881 { 882 if (fModel->AddTeam(event, fState->LastEventTime()) == NULL) 883 throw std::bad_alloc(); 884 } 885 886 887 void 888 ModelLoader::_HandleTeamRemoved(system_profiler_team_removed* event) 889 { 890 if (Model::Team* team = fModel->TeamByID(event->team)) 891 team->SetDeletionTime(fState->LastEventTime()); 892 else 893 printf("Warning: Removed event for unknown team: %ld\n", event->team); 894 } 895 896 897 void 898 ModelLoader::_HandleTeamExec(system_profiler_team_exec* event) 899 { 900 // TODO:... 901 } 902 903 904 void 905 ModelLoader::_HandleThreadAdded(system_profiler_thread_added* event) 906 { 907 _AddThread(event)->IncrementEventCount(); 908 } 909 910 911 void 912 ModelLoader::_HandleThreadRemoved(system_profiler_thread_removed* event) 913 { 914 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 915 if (thread == NULL) { 916 printf("Warning: Removed event for unknown thread: %ld\n", 917 event->thread); 918 thread = _AddUnknownThread(event->thread); 919 } 920 921 thread->thread->SetDeletionTime(fState->LastEventTime()); 922 thread->IncrementEventCount(); 923 } 924 925 926 void 927 ModelLoader::_HandleThreadScheduled(uint32 cpu, 928 system_profiler_thread_scheduled* event) 929 { 930 _UpdateLastEventTime(event->time); 931 932 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 933 if (thread == NULL) { 934 printf("Warning: Schedule event for unknown thread: %ld\n", 935 event->thread); 936 thread = _AddUnknownThread(event->thread); 937 return; 938 } 939 940 nanotime_t diffTime = fState->LastEventTime() - thread->lastTime; 941 942 if (thread->state == READY) { 943 // thread scheduled after having been woken up 944 thread->thread->AddLatency(diffTime); 945 } else if (thread->state == PREEMPTED) { 946 // thread scheduled after having been preempted before 947 thread->thread->AddRerun(diffTime); 948 } 949 950 if (thread->state == STILL_RUNNING) { 951 // Thread was running and continues to run. 952 thread->state = RUNNING; 953 } 954 955 if (thread->state != RUNNING) { 956 thread->lastTime = fState->LastEventTime(); 957 thread->state = RUNNING; 958 } 959 960 thread->IncrementEventCount(); 961 962 // unscheduled thread 963 964 if (event->thread == event->previous_thread) 965 return; 966 967 thread = fState->LookupThread(event->previous_thread); 968 if (thread == NULL) { 969 printf("Warning: Schedule event for unknown previous thread: %ld\n", 970 event->previous_thread); 971 thread = _AddUnknownThread(event->previous_thread); 972 } 973 974 diffTime = fState->LastEventTime() - thread->lastTime; 975 976 if (thread->state == STILL_RUNNING) { 977 // thread preempted 978 thread->thread->AddPreemption(diffTime); 979 thread->thread->AddRun(diffTime); 980 if (thread->priority == 0) 981 _AddIdleTime(cpu, diffTime); 982 983 thread->lastTime = fState->LastEventTime(); 984 thread->state = PREEMPTED; 985 } else if (thread->state == RUNNING) { 986 // thread starts waiting (it hadn't been added to the run 987 // queue before being unscheduled) 988 thread->thread->AddRun(diffTime); 989 if (thread->priority == 0) 990 _AddIdleTime(cpu, diffTime); 991 992 if (event->previous_thread_state == B_THREAD_WAITING) { 993 addr_t waitObject = event->previous_thread_wait_object; 994 switch (event->previous_thread_wait_object_type) { 995 case THREAD_BLOCK_TYPE_SNOOZE: 996 case THREAD_BLOCK_TYPE_SIGNAL: 997 waitObject = 0; 998 break; 999 case THREAD_BLOCK_TYPE_SEMAPHORE: 1000 case THREAD_BLOCK_TYPE_CONDITION_VARIABLE: 1001 case THREAD_BLOCK_TYPE_MUTEX: 1002 case THREAD_BLOCK_TYPE_RW_LOCK: 1003 case THREAD_BLOCK_TYPE_OTHER: 1004 default: 1005 break; 1006 } 1007 1008 _AddThreadWaitObject(thread, 1009 event->previous_thread_wait_object_type, waitObject); 1010 } 1011 1012 thread->lastTime = fState->LastEventTime(); 1013 thread->state = WAITING; 1014 } else if (thread->state == UNKNOWN) { 1015 uint32 threadState = event->previous_thread_state; 1016 if (threadState == B_THREAD_WAITING 1017 || threadState == B_THREAD_SUSPENDED) { 1018 thread->lastTime = fState->LastEventTime(); 1019 thread->state = WAITING; 1020 } else if (threadState == B_THREAD_READY) { 1021 thread->lastTime = fState->LastEventTime(); 1022 thread->state = PREEMPTED; 1023 } 1024 } 1025 1026 thread->IncrementEventCount(); 1027 } 1028 1029 1030 void 1031 ModelLoader::_HandleThreadEnqueuedInRunQueue( 1032 thread_enqueued_in_run_queue* event) 1033 { 1034 _UpdateLastEventTime(event->time); 1035 1036 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 1037 if (thread == NULL) { 1038 printf("Warning: Enqueued in run queue event for unknown thread: %ld\n", 1039 event->thread); 1040 thread = _AddUnknownThread(event->thread); 1041 } 1042 1043 if (thread->state == RUNNING || thread->state == STILL_RUNNING) { 1044 // Thread was running and is reentered into the run queue. This 1045 // is done by the scheduler, if the thread remains ready. 1046 thread->state = STILL_RUNNING; 1047 } else { 1048 // Thread was waiting and is ready now. 1049 nanotime_t diffTime = fState->LastEventTime() - thread->lastTime; 1050 if (thread->waitObject != NULL) { 1051 thread->waitObject->AddWait(diffTime); 1052 thread->waitObject = NULL; 1053 thread->thread->AddWait(diffTime); 1054 } else if (thread->state != UNKNOWN) 1055 thread->thread->AddUnspecifiedWait(diffTime); 1056 1057 thread->lastTime = fState->LastEventTime(); 1058 thread->state = READY; 1059 } 1060 1061 thread->priority = event->priority; 1062 1063 thread->IncrementEventCount(); 1064 } 1065 1066 1067 void 1068 ModelLoader::_HandleThreadRemovedFromRunQueue(uint32 cpu, 1069 thread_removed_from_run_queue* event) 1070 { 1071 _UpdateLastEventTime(event->time); 1072 1073 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 1074 if (thread == NULL) { 1075 printf("Warning: Removed from run queue event for unknown thread: " 1076 "%ld\n", event->thread); 1077 thread = _AddUnknownThread(event->thread); 1078 } 1079 1080 // This really only happens when the thread priority is changed 1081 // while the thread is ready. 1082 1083 nanotime_t diffTime = fState->LastEventTime() - thread->lastTime; 1084 if (thread->state == RUNNING) { 1085 // This should never happen. 1086 thread->thread->AddRun(diffTime); 1087 if (thread->priority == 0) 1088 _AddIdleTime(cpu, diffTime); 1089 } else if (thread->state == READY || thread->state == PREEMPTED) { 1090 // Not really correct, but the case is rare and we keep it 1091 // simple. 1092 thread->thread->AddUnspecifiedWait(diffTime); 1093 } 1094 1095 thread->lastTime = fState->LastEventTime(); 1096 thread->state = WAITING; 1097 1098 thread->IncrementEventCount(); 1099 } 1100 1101 1102 void 1103 ModelLoader::_HandleWaitObjectInfo(system_profiler_wait_object_info* event) 1104 { 1105 if (fModel->AddWaitObject(event, NULL) == NULL) 1106 throw std::bad_alloc(); 1107 } 1108 1109 1110 void 1111 ModelLoader::_HandleIOSchedulerAdded(system_profiler_io_scheduler_added* event) 1112 { 1113 Model::IOScheduler* scheduler = fModel->IOSchedulerByID(event->scheduler); 1114 if (scheduler != NULL) { 1115 printf("Warning: Duplicate added event for I/O scheduler %ld\n", 1116 event->scheduler); 1117 return; 1118 } 1119 1120 if (fModel->AddIOScheduler(event) == NULL) 1121 throw std::bad_alloc(); 1122 } 1123 1124 1125 void 1126 ModelLoader::_HandleIORequestScheduled(io_request_scheduled* event) 1127 { 1128 IORequest* request = fIORequests->Lookup(event->request); 1129 if (request != NULL) { 1130 printf("Warning: Duplicate schedule event for I/O request %p\n", 1131 event->request); 1132 return; 1133 } 1134 1135 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 1136 if (thread == NULL) { 1137 printf("Warning: I/O request for unknown thread %ld\n", event->thread); 1138 thread = _AddUnknownThread(event->thread); 1139 } 1140 1141 if (fModel->IOSchedulerByID(event->scheduler) == NULL) { 1142 printf("Warning: I/O requests for unknown scheduler %ld\n", 1143 event->scheduler); 1144 // TODO: Add state for unknown scheduler, as we do for threads. 1145 return; 1146 } 1147 1148 request = new(std::nothrow) IORequest(event); 1149 if (request == NULL) 1150 throw std::bad_alloc(); 1151 1152 fIORequests->Insert(request); 1153 thread->AddIORequest(request); 1154 } 1155 1156 1157 void 1158 ModelLoader::_HandleIORequestFinished(io_request_finished* event) 1159 { 1160 IORequest* request = fIORequests->Lookup(event->request); 1161 if (request == NULL) 1162 return; 1163 1164 request->finishedEvent = event; 1165 1166 fIORequests->Remove(request); 1167 fState->LookupThread(request->scheduledEvent->thread) 1168 ->IORequestFinished(request); 1169 } 1170 1171 1172 void 1173 ModelLoader::_HandleIOOperationStarted(io_operation_started* event) 1174 { 1175 IORequest* request = fIORequests->Lookup(event->request); 1176 if (request == NULL) { 1177 printf("Warning: I/O request for operation %p not found\n", 1178 event->operation); 1179 return; 1180 } 1181 1182 IOOperation* operation = new(std::nothrow) IOOperation(event); 1183 if (operation == NULL) 1184 throw std::bad_alloc(); 1185 1186 request->AddOperation(operation); 1187 } 1188 1189 1190 void 1191 ModelLoader::_HandleIOOperationFinished(io_operation_finished* event) 1192 { 1193 IORequest* request = fIORequests->Lookup(event->request); 1194 if (request == NULL) { 1195 printf("Warning: I/O request for operation %p not found\n", 1196 event->operation); 1197 return; 1198 } 1199 1200 IOOperation* operation = request->FindOperation(event->operation); 1201 if (operation == NULL) { 1202 printf("Warning: operation %p not found\n", event->operation); 1203 return; 1204 } 1205 1206 operation->finishedEvent = event; 1207 } 1208 1209 1210 ModelLoader::ExtendedThreadSchedulingState* 1211 ModelLoader::_AddThread(system_profiler_thread_added* event) 1212 { 1213 // do we know the thread already? 1214 ExtendedThreadSchedulingState* info = fState->LookupThread(event->thread); 1215 if (info != NULL) { 1216 printf("Warning: Duplicate thread added event for thread %" B_PRId32 1217 "\n", event->thread); 1218 return info; 1219 } 1220 1221 // add the thread to the model 1222 Model::Thread* thread = fModel->AddThread(event, fState->LastEventTime()); 1223 if (thread == NULL) 1224 throw std::bad_alloc(); 1225 1226 // create and add a ThreadSchedulingState 1227 info = new(std::nothrow) ExtendedThreadSchedulingState(thread); 1228 if (info == NULL) 1229 throw std::bad_alloc(); 1230 1231 // TODO: The priority is missing from the system_profiler_thread_added 1232 // struct. For now guess at least whether this is an idle thread. 1233 if (strncmp(event->name, "idle thread", strlen("idle thread")) == 0) 1234 info->priority = 0; 1235 else 1236 info->priority = B_NORMAL_PRIORITY; 1237 1238 fState->InsertThread(info); 1239 1240 return info; 1241 } 1242 1243 1244 ModelLoader::ExtendedThreadSchedulingState* 1245 ModelLoader::_AddUnknownThread(thread_id threadID) 1246 { 1247 // create a dummy "add thread" event 1248 system_profiler_thread_added* event = (system_profiler_thread_added*) 1249 malloc(sizeof(system_profiler_thread_added)); 1250 if (event == NULL) 1251 throw std::bad_alloc(); 1252 1253 if (!fModel->AddAssociatedData(event)) { 1254 free(event); 1255 throw std::bad_alloc(); 1256 } 1257 1258 try { 1259 event->team = _AddUnknownTeam()->ID(); 1260 event->thread = threadID; 1261 snprintf(event->name, sizeof(event->name), "unknown thread %" B_PRId32, 1262 threadID); 1263 1264 // add the thread to the model 1265 ExtendedThreadSchedulingState* state = _AddThread(event); 1266 return state; 1267 } catch (...) { 1268 throw; 1269 } 1270 } 1271 1272 Model::Team* 1273 ModelLoader::_AddUnknownTeam() 1274 { 1275 team_id teamID = 0; 1276 Model::Team* team = fModel->TeamByID(teamID); 1277 if (team != NULL) 1278 return team; 1279 1280 // create a dummy "add team" event 1281 static const char* const kUnknownThreadsTeamName = "unknown threads"; 1282 size_t nameLength = strlen(kUnknownThreadsTeamName); 1283 1284 system_profiler_team_added* event = (system_profiler_team_added*) 1285 malloc(sizeof(system_profiler_team_added) + nameLength); 1286 if (event == NULL) 1287 throw std::bad_alloc(); 1288 1289 event->team = teamID; 1290 event->args_offset = nameLength; 1291 strlcpy(event->name, kUnknownThreadsTeamName, nameLength + 1); 1292 1293 // add the team to the model 1294 team = fModel->AddTeam(event, fState->LastEventTime()); 1295 if (team == NULL) 1296 throw std::bad_alloc(); 1297 1298 return team; 1299 } 1300 1301 1302 void 1303 ModelLoader::_AddThreadWaitObject(ExtendedThreadSchedulingState* thread, 1304 uint32 type, addr_t object) 1305 { 1306 Model::WaitObjectGroup* waitObjectGroup 1307 = fModel->WaitObjectGroupFor(type, object); 1308 if (waitObjectGroup == NULL) { 1309 // The algorithm should prevent this case. 1310 printf("ModelLoader::_AddThreadWaitObject(): Unknown wait object: type: %lu, " 1311 "object: %#lx\n", type, object); 1312 return; 1313 } 1314 1315 Model::WaitObject* waitObject = waitObjectGroup->MostRecentWaitObject(); 1316 1317 Model::ThreadWaitObjectGroup* threadWaitObjectGroup 1318 = fModel->ThreadWaitObjectGroupFor(thread->ID(), type, object); 1319 1320 if (threadWaitObjectGroup == NULL 1321 || threadWaitObjectGroup->MostRecentWaitObject() != waitObject) { 1322 Model::ThreadWaitObject* threadWaitObject 1323 = fModel->AddThreadWaitObject(thread->ID(), waitObject, 1324 &threadWaitObjectGroup); 1325 if (threadWaitObject == NULL) 1326 throw std::bad_alloc(); 1327 } 1328 1329 thread->waitObject = threadWaitObjectGroup->MostRecentThreadWaitObject(); 1330 } 1331 1332 1333 void 1334 ModelLoader::_AddIdleTime(uint32 cpu, nanotime_t time) 1335 { 1336 fCPUInfos[cpu].idleTime += time; 1337 } 1338