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 %" B_PRIu32 ", size: %" B_PRIuSIZE 725 "\n", event, size); 726 return B_BAD_DATA; 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: %" B_PRId32 "\n", 894 event->team); 895 } 896 } 897 898 899 void 900 ModelLoader::_HandleTeamExec(system_profiler_team_exec* event) 901 { 902 // TODO:... 903 } 904 905 906 void 907 ModelLoader::_HandleThreadAdded(system_profiler_thread_added* event) 908 { 909 _AddThread(event)->IncrementEventCount(); 910 } 911 912 913 void 914 ModelLoader::_HandleThreadRemoved(system_profiler_thread_removed* event) 915 { 916 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 917 if (thread == NULL) { 918 printf("Warning: Removed event for unknown thread: %" B_PRId32 "\n", 919 event->thread); 920 thread = _AddUnknownThread(event->thread); 921 } 922 923 thread->thread->SetDeletionTime(fState->LastEventTime()); 924 thread->IncrementEventCount(); 925 } 926 927 928 void 929 ModelLoader::_HandleThreadScheduled(uint32 cpu, 930 system_profiler_thread_scheduled* event) 931 { 932 _UpdateLastEventTime(event->time); 933 934 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 935 if (thread == NULL) { 936 printf("Warning: Schedule event for unknown thread: %" B_PRId32 "\n", 937 event->thread); 938 thread = _AddUnknownThread(event->thread); 939 return; 940 } 941 942 nanotime_t diffTime = fState->LastEventTime() - thread->lastTime; 943 944 if (thread->state == READY) { 945 // thread scheduled after having been woken up 946 thread->thread->AddLatency(diffTime); 947 } else if (thread->state == PREEMPTED) { 948 // thread scheduled after having been preempted before 949 thread->thread->AddRerun(diffTime); 950 } 951 952 if (thread->state == STILL_RUNNING) { 953 // Thread was running and continues to run. 954 thread->state = RUNNING; 955 } 956 957 if (thread->state != RUNNING) { 958 thread->lastTime = fState->LastEventTime(); 959 thread->state = RUNNING; 960 } 961 962 thread->IncrementEventCount(); 963 964 // unscheduled thread 965 966 if (event->thread == event->previous_thread) 967 return; 968 969 thread = fState->LookupThread(event->previous_thread); 970 if (thread == NULL) { 971 printf("Warning: Schedule event for unknown previous thread: %" B_PRId32 972 "\n", event->previous_thread); 973 thread = _AddUnknownThread(event->previous_thread); 974 } 975 976 diffTime = fState->LastEventTime() - thread->lastTime; 977 978 if (thread->state == STILL_RUNNING) { 979 // thread preempted 980 thread->thread->AddPreemption(diffTime); 981 thread->thread->AddRun(diffTime); 982 if (thread->priority == 0) 983 _AddIdleTime(cpu, diffTime); 984 985 thread->lastTime = fState->LastEventTime(); 986 thread->state = PREEMPTED; 987 } else if (thread->state == RUNNING) { 988 // thread starts waiting (it hadn't been added to the run 989 // queue before being unscheduled) 990 thread->thread->AddRun(diffTime); 991 if (thread->priority == 0) 992 _AddIdleTime(cpu, diffTime); 993 994 if (event->previous_thread_state == B_THREAD_WAITING) { 995 addr_t waitObject = event->previous_thread_wait_object; 996 switch (event->previous_thread_wait_object_type) { 997 case THREAD_BLOCK_TYPE_SNOOZE: 998 case THREAD_BLOCK_TYPE_SIGNAL: 999 waitObject = 0; 1000 break; 1001 case THREAD_BLOCK_TYPE_SEMAPHORE: 1002 case THREAD_BLOCK_TYPE_CONDITION_VARIABLE: 1003 case THREAD_BLOCK_TYPE_MUTEX: 1004 case THREAD_BLOCK_TYPE_RW_LOCK: 1005 case THREAD_BLOCK_TYPE_OTHER: 1006 default: 1007 break; 1008 } 1009 1010 _AddThreadWaitObject(thread, 1011 event->previous_thread_wait_object_type, waitObject); 1012 } 1013 1014 thread->lastTime = fState->LastEventTime(); 1015 thread->state = WAITING; 1016 } else if (thread->state == UNKNOWN) { 1017 uint32 threadState = event->previous_thread_state; 1018 if (threadState == B_THREAD_WAITING 1019 || threadState == B_THREAD_SUSPENDED) { 1020 thread->lastTime = fState->LastEventTime(); 1021 thread->state = WAITING; 1022 } else if (threadState == B_THREAD_READY) { 1023 thread->lastTime = fState->LastEventTime(); 1024 thread->state = PREEMPTED; 1025 } 1026 } 1027 1028 thread->IncrementEventCount(); 1029 } 1030 1031 1032 void 1033 ModelLoader::_HandleThreadEnqueuedInRunQueue( 1034 thread_enqueued_in_run_queue* event) 1035 { 1036 _UpdateLastEventTime(event->time); 1037 1038 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 1039 if (thread == NULL) { 1040 printf("Warning: Enqueued in run queue event for unknown thread: %" 1041 B_PRId32 "\n", event->thread); 1042 thread = _AddUnknownThread(event->thread); 1043 } 1044 1045 if (thread->state == RUNNING || thread->state == STILL_RUNNING) { 1046 // Thread was running and is reentered into the run queue. This 1047 // is done by the scheduler, if the thread remains ready. 1048 thread->state = STILL_RUNNING; 1049 } else { 1050 // Thread was waiting and is ready now. 1051 nanotime_t diffTime = fState->LastEventTime() - thread->lastTime; 1052 if (thread->waitObject != NULL) { 1053 thread->waitObject->AddWait(diffTime); 1054 thread->waitObject = NULL; 1055 thread->thread->AddWait(diffTime); 1056 } else if (thread->state != UNKNOWN) 1057 thread->thread->AddUnspecifiedWait(diffTime); 1058 1059 thread->lastTime = fState->LastEventTime(); 1060 thread->state = READY; 1061 } 1062 1063 thread->priority = event->priority; 1064 1065 thread->IncrementEventCount(); 1066 } 1067 1068 1069 void 1070 ModelLoader::_HandleThreadRemovedFromRunQueue(uint32 cpu, 1071 thread_removed_from_run_queue* event) 1072 { 1073 _UpdateLastEventTime(event->time); 1074 1075 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 1076 if (thread == NULL) { 1077 printf("Warning: Removed from run queue event for unknown thread: " 1078 "%" B_PRId32 "\n", event->thread); 1079 thread = _AddUnknownThread(event->thread); 1080 } 1081 1082 // This really only happens when the thread priority is changed 1083 // while the thread is ready. 1084 1085 nanotime_t diffTime = fState->LastEventTime() - thread->lastTime; 1086 if (thread->state == RUNNING) { 1087 // This should never happen. 1088 thread->thread->AddRun(diffTime); 1089 if (thread->priority == 0) 1090 _AddIdleTime(cpu, diffTime); 1091 } else if (thread->state == READY || thread->state == PREEMPTED) { 1092 // Not really correct, but the case is rare and we keep it 1093 // simple. 1094 thread->thread->AddUnspecifiedWait(diffTime); 1095 } 1096 1097 thread->lastTime = fState->LastEventTime(); 1098 thread->state = WAITING; 1099 1100 thread->IncrementEventCount(); 1101 } 1102 1103 1104 void 1105 ModelLoader::_HandleWaitObjectInfo(system_profiler_wait_object_info* event) 1106 { 1107 if (fModel->AddWaitObject(event, NULL) == NULL) 1108 throw std::bad_alloc(); 1109 } 1110 1111 1112 void 1113 ModelLoader::_HandleIOSchedulerAdded(system_profiler_io_scheduler_added* event) 1114 { 1115 Model::IOScheduler* scheduler = fModel->IOSchedulerByID(event->scheduler); 1116 if (scheduler != NULL) { 1117 printf("Warning: Duplicate added event for I/O scheduler %" B_PRId32 1118 "\n", event->scheduler); 1119 return; 1120 } 1121 1122 if (fModel->AddIOScheduler(event) == NULL) 1123 throw std::bad_alloc(); 1124 } 1125 1126 1127 void 1128 ModelLoader::_HandleIORequestScheduled(io_request_scheduled* event) 1129 { 1130 IORequest* request = fIORequests->Lookup(event->request); 1131 if (request != NULL) { 1132 printf("Warning: Duplicate schedule event for I/O request %p\n", 1133 event->request); 1134 return; 1135 } 1136 1137 ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread); 1138 if (thread == NULL) { 1139 printf("Warning: I/O request for unknown thread %" B_PRId32 "\n", 1140 event->thread); 1141 thread = _AddUnknownThread(event->thread); 1142 } 1143 1144 if (fModel->IOSchedulerByID(event->scheduler) == NULL) { 1145 printf("Warning: I/O requests for unknown scheduler %" B_PRId32 "\n", 1146 event->scheduler); 1147 // TODO: Add state for unknown scheduler, as we do for threads. 1148 return; 1149 } 1150 1151 request = new(std::nothrow) IORequest(event); 1152 if (request == NULL) 1153 throw std::bad_alloc(); 1154 1155 fIORequests->Insert(request); 1156 thread->AddIORequest(request); 1157 } 1158 1159 1160 void 1161 ModelLoader::_HandleIORequestFinished(io_request_finished* event) 1162 { 1163 IORequest* request = fIORequests->Lookup(event->request); 1164 if (request == NULL) 1165 return; 1166 1167 request->finishedEvent = event; 1168 1169 fIORequests->Remove(request); 1170 fState->LookupThread(request->scheduledEvent->thread) 1171 ->IORequestFinished(request); 1172 } 1173 1174 1175 void 1176 ModelLoader::_HandleIOOperationStarted(io_operation_started* event) 1177 { 1178 IORequest* request = fIORequests->Lookup(event->request); 1179 if (request == NULL) { 1180 printf("Warning: I/O request for operation %p not found\n", 1181 event->operation); 1182 return; 1183 } 1184 1185 IOOperation* operation = new(std::nothrow) IOOperation(event); 1186 if (operation == NULL) 1187 throw std::bad_alloc(); 1188 1189 request->AddOperation(operation); 1190 } 1191 1192 1193 void 1194 ModelLoader::_HandleIOOperationFinished(io_operation_finished* event) 1195 { 1196 IORequest* request = fIORequests->Lookup(event->request); 1197 if (request == NULL) { 1198 printf("Warning: I/O request for operation %p not found\n", 1199 event->operation); 1200 return; 1201 } 1202 1203 IOOperation* operation = request->FindOperation(event->operation); 1204 if (operation == NULL) { 1205 printf("Warning: operation %p not found\n", event->operation); 1206 return; 1207 } 1208 1209 operation->finishedEvent = event; 1210 } 1211 1212 1213 ModelLoader::ExtendedThreadSchedulingState* 1214 ModelLoader::_AddThread(system_profiler_thread_added* event) 1215 { 1216 // do we know the thread already? 1217 ExtendedThreadSchedulingState* info = fState->LookupThread(event->thread); 1218 if (info != NULL) { 1219 printf("Warning: Duplicate thread added event for thread %" B_PRId32 1220 "\n", event->thread); 1221 return info; 1222 } 1223 1224 // add the thread to the model 1225 Model::Thread* thread = fModel->AddThread(event, fState->LastEventTime()); 1226 if (thread == NULL) 1227 throw std::bad_alloc(); 1228 1229 // create and add a ThreadSchedulingState 1230 info = new(std::nothrow) ExtendedThreadSchedulingState(thread); 1231 if (info == NULL) 1232 throw std::bad_alloc(); 1233 1234 // TODO: The priority is missing from the system_profiler_thread_added 1235 // struct. For now guess at least whether this is an idle thread. 1236 if (strncmp(event->name, "idle thread", strlen("idle thread")) == 0) 1237 info->priority = 0; 1238 else 1239 info->priority = B_NORMAL_PRIORITY; 1240 1241 fState->InsertThread(info); 1242 1243 return info; 1244 } 1245 1246 1247 ModelLoader::ExtendedThreadSchedulingState* 1248 ModelLoader::_AddUnknownThread(thread_id threadID) 1249 { 1250 // create a dummy "add thread" event 1251 system_profiler_thread_added* event = (system_profiler_thread_added*) 1252 malloc(sizeof(system_profiler_thread_added)); 1253 if (event == NULL) 1254 throw std::bad_alloc(); 1255 1256 if (!fModel->AddAssociatedData(event)) { 1257 free(event); 1258 throw std::bad_alloc(); 1259 } 1260 1261 try { 1262 event->team = _AddUnknownTeam()->ID(); 1263 event->thread = threadID; 1264 snprintf(event->name, sizeof(event->name), "unknown thread %" B_PRId32, 1265 threadID); 1266 1267 // add the thread to the model 1268 ExtendedThreadSchedulingState* state = _AddThread(event); 1269 return state; 1270 } catch (...) { 1271 throw; 1272 } 1273 } 1274 1275 Model::Team* 1276 ModelLoader::_AddUnknownTeam() 1277 { 1278 team_id teamID = 0; 1279 Model::Team* team = fModel->TeamByID(teamID); 1280 if (team != NULL) 1281 return team; 1282 1283 // create a dummy "add team" event 1284 static const char* const kUnknownThreadsTeamName = "unknown threads"; 1285 size_t nameLength = strlen(kUnknownThreadsTeamName); 1286 1287 system_profiler_team_added* event = (system_profiler_team_added*) 1288 malloc(sizeof(system_profiler_team_added) + nameLength); 1289 if (event == NULL) 1290 throw std::bad_alloc(); 1291 1292 event->team = teamID; 1293 event->args_offset = nameLength; 1294 strlcpy(event->name, kUnknownThreadsTeamName, nameLength + 1); 1295 1296 // add the team to the model 1297 team = fModel->AddTeam(event, fState->LastEventTime()); 1298 if (team == NULL) 1299 throw std::bad_alloc(); 1300 1301 return team; 1302 } 1303 1304 1305 void 1306 ModelLoader::_AddThreadWaitObject(ExtendedThreadSchedulingState* thread, 1307 uint32 type, addr_t object) 1308 { 1309 Model::WaitObjectGroup* waitObjectGroup 1310 = fModel->WaitObjectGroupFor(type, object); 1311 if (waitObjectGroup == NULL) { 1312 // The algorithm should prevent this case. 1313 printf("ModelLoader::_AddThreadWaitObject(): Unknown wait object: type:" 1314 " %" B_PRIu32 ", " "object: %#" B_PRIxADDR "\n", type, object); 1315 return; 1316 } 1317 1318 Model::WaitObject* waitObject = waitObjectGroup->MostRecentWaitObject(); 1319 1320 Model::ThreadWaitObjectGroup* threadWaitObjectGroup 1321 = fModel->ThreadWaitObjectGroupFor(thread->ID(), type, object); 1322 1323 if (threadWaitObjectGroup == NULL 1324 || threadWaitObjectGroup->MostRecentWaitObject() != waitObject) { 1325 Model::ThreadWaitObject* threadWaitObject 1326 = fModel->AddThreadWaitObject(thread->ID(), waitObject, 1327 &threadWaitObjectGroup); 1328 if (threadWaitObject == NULL) 1329 throw std::bad_alloc(); 1330 } 1331 1332 thread->waitObject = threadWaitObjectGroup->MostRecentThreadWaitObject(); 1333 } 1334 1335 1336 void 1337 ModelLoader::_AddIdleTime(uint32 cpu, nanotime_t time) 1338 { 1339 fCPUInfos[cpu].idleTime += time; 1340 } 1341