1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "Model.h" 7 8 #include <new> 9 10 #include <stdio.h> 11 #include <stdlib.h> 12 13 #include <AutoDeleter.h> 14 15 16 // #pragma mark - WaitObject 17 18 19 Model::WaitObject::WaitObject(const system_profiler_wait_object_info* event) 20 : 21 fEvent(event) 22 { 23 } 24 25 26 Model::WaitObject::~WaitObject() 27 { 28 } 29 30 31 // #pragma mark - WaitObjectGroup 32 33 34 Model::WaitObjectGroup::WaitObjectGroup(WaitObject* waitObject) 35 { 36 fWaitObjects.Add(waitObject); 37 } 38 39 40 Model::WaitObjectGroup::~WaitObjectGroup() 41 { 42 } 43 44 45 // #pragma mark - ThreadWaitObject 46 47 48 Model::ThreadWaitObject::ThreadWaitObject(WaitObject* waitObject) 49 : 50 fWaitObject(waitObject), 51 fWaits(0), 52 fTotalWaitTime(0) 53 { 54 } 55 56 57 Model::ThreadWaitObject::~ThreadWaitObject() 58 { 59 } 60 61 62 void 63 Model::ThreadWaitObject::AddWait(bigtime_t waitTime) 64 { 65 fWaits++; 66 fTotalWaitTime += waitTime; 67 } 68 69 70 // #pragma mark - ThreadWaitObjectGroup 71 72 73 Model::ThreadWaitObjectGroup::ThreadWaitObjectGroup( 74 ThreadWaitObject* threadWaitObject) 75 { 76 fWaitObjects.Add(threadWaitObject); 77 } 78 79 80 Model::ThreadWaitObjectGroup::~ThreadWaitObjectGroup() 81 { 82 } 83 84 85 bool 86 Model::ThreadWaitObjectGroup::GetThreadWaitObjects( 87 BObjectList<ThreadWaitObject>& objects) 88 { 89 ThreadWaitObjectList::Iterator it = fWaitObjects.GetIterator(); 90 while (ThreadWaitObject* object = it.Next()) { 91 if (!objects.AddItem(object)) 92 return false; 93 } 94 95 return true; 96 } 97 98 99 // #pragma mark - Team 100 101 102 Model::Team::Team(const system_profiler_team_added* event, bigtime_t time) 103 : 104 fCreationEvent(event), 105 fCreationTime(time), 106 fDeletionTime(-1), 107 fThreads(10) 108 { 109 } 110 111 112 Model::Team::~Team() 113 { 114 } 115 116 117 bool 118 Model::Team::AddThread(Thread* thread) 119 { 120 return fThreads.BinaryInsert(thread, &Thread::CompareByCreationTimeID); 121 } 122 123 124 // #pragma mark - Thread 125 126 127 Model::Thread::Thread(Team* team, const system_profiler_thread_added* event, 128 bigtime_t time) 129 : 130 fTeam(team), 131 fCreationEvent(event), 132 fCreationTime(time), 133 fDeletionTime(-1), 134 fRuns(0), 135 fTotalRunTime(0), 136 fMinRunTime(-1), 137 fMaxRunTime(-1), 138 fLatencies(0), 139 fTotalLatency(0), 140 fMinLatency(-1), 141 fMaxLatency(-1), 142 fReruns(0), 143 fTotalRerunTime(0), 144 fMinRerunTime(-1), 145 fMaxRerunTime(-1), 146 fWaits(0), 147 fTotalWaitTime(0), 148 fUnspecifiedWaitTime(0), 149 fPreemptions(0), 150 fIndex(-1), 151 fWaitObjectGroups(20, true) 152 { 153 } 154 155 156 Model::Thread::~Thread() 157 { 158 } 159 160 161 Model::ThreadWaitObjectGroup* 162 Model::Thread::ThreadWaitObjectGroupFor(uint32 type, addr_t object) const 163 { 164 type_and_object key; 165 key.type = type; 166 key.object = object; 167 168 return fWaitObjectGroups.BinarySearchByKey(key, 169 &ThreadWaitObjectGroup::CompareWithTypeObject); 170 } 171 172 173 void 174 Model::Thread::AddRun(bigtime_t runTime) 175 { 176 fRuns++; 177 fTotalRunTime += runTime; 178 179 if (fMinRunTime < 0 || runTime < fMinRunTime) 180 fMinRunTime = runTime; 181 if (runTime > fMaxRunTime) 182 fMaxRunTime = runTime; 183 } 184 185 186 void 187 Model::Thread::AddRerun(bigtime_t runTime) 188 { 189 fReruns++; 190 fTotalRerunTime += runTime; 191 192 if (fMinRerunTime < 0 || runTime < fMinRerunTime) 193 fMinRerunTime = runTime; 194 if (runTime > fMaxRerunTime) 195 fMaxRerunTime = runTime; 196 } 197 198 199 void 200 Model::Thread::AddLatency(bigtime_t latency) 201 { 202 fLatencies++; 203 fTotalLatency += latency; 204 205 if (fMinLatency < 0 || latency < fMinLatency) 206 fMinLatency = latency; 207 if (latency > fMaxLatency) 208 fMaxLatency = latency; 209 } 210 211 212 void 213 Model::Thread::AddPreemption(bigtime_t runTime) 214 { 215 fPreemptions++; 216 } 217 218 219 void 220 Model::Thread::AddWait(bigtime_t waitTime) 221 { 222 fWaits++; 223 fTotalWaitTime += waitTime; 224 } 225 226 227 void 228 Model::Thread::AddUnspecifiedWait(bigtime_t waitTime) 229 { 230 fUnspecifiedWaitTime += waitTime; 231 } 232 233 234 Model::ThreadWaitObject* 235 Model::Thread::AddThreadWaitObject(WaitObject* waitObject, 236 ThreadWaitObjectGroup** _threadWaitObjectGroup) 237 { 238 // create a thread wait object 239 ThreadWaitObject* threadWaitObject 240 = new(std::nothrow) ThreadWaitObject(waitObject); 241 if (threadWaitObject == NULL) 242 return NULL; 243 244 // find the thread wait object group 245 ThreadWaitObjectGroup* threadWaitObjectGroup 246 = ThreadWaitObjectGroupFor(waitObject->Type(), waitObject->Object()); 247 if (threadWaitObjectGroup == NULL) { 248 // doesn't exist yet -- create 249 threadWaitObjectGroup = new(std::nothrow) ThreadWaitObjectGroup( 250 threadWaitObject); 251 if (threadWaitObjectGroup == NULL) { 252 delete threadWaitObject; 253 return NULL; 254 } 255 256 // add to the list 257 if (!fWaitObjectGroups.BinaryInsert(threadWaitObjectGroup, 258 &ThreadWaitObjectGroup::CompareByTypeObject)) { 259 delete threadWaitObjectGroup; 260 return NULL; 261 } 262 } else { 263 // exists -- just add the object 264 threadWaitObjectGroup->AddWaitObject(threadWaitObject); 265 } 266 267 if (_threadWaitObjectGroup != NULL) 268 *_threadWaitObjectGroup = threadWaitObjectGroup; 269 270 return threadWaitObject; 271 } 272 273 274 // #pragma mark - SchedulingState 275 276 277 Model::SchedulingState::~SchedulingState() 278 { 279 Clear(); 280 } 281 282 283 status_t 284 Model::SchedulingState::Init() 285 { 286 status_t error = fThreadStates.Init(); 287 if (error != B_OK) 288 return error; 289 290 return B_OK; 291 } 292 293 294 status_t 295 Model::SchedulingState::Init(const CompactSchedulingState* state) 296 { 297 status_t error = Init(); 298 if (error != B_OK) 299 return error; 300 301 if (state == NULL) 302 return B_OK; 303 304 fLastEventTime = state->LastEventTime(); 305 for (int32 i = 0; const CompactThreadSchedulingState* compactThreadState 306 = state->ThreadStateAt(i); i++) { 307 ThreadSchedulingState* threadState 308 = new(std::nothrow) ThreadSchedulingState(*compactThreadState); 309 if (threadState == NULL) 310 return B_NO_MEMORY; 311 312 fThreadStates.Insert(threadState); 313 } 314 315 return B_OK; 316 } 317 318 319 void 320 Model::SchedulingState::Clear() 321 { 322 ThreadSchedulingState* state = fThreadStates.Clear(true); 323 while (state != NULL) { 324 ThreadSchedulingState* next = state->next; 325 delete state; 326 state = next; 327 } 328 329 fLastEventTime = -1; 330 } 331 332 333 // #pragma mark - CompactSchedulingState 334 335 336 /*static*/ Model::CompactSchedulingState* 337 Model::CompactSchedulingState::Create(const SchedulingState& state, 338 off_t eventOffset) 339 { 340 bigtime_t lastEventTime = state.LastEventTime(); 341 342 // count the active threads 343 int32 threadCount = 0; 344 for (ThreadSchedulingStateTable::Iterator it 345 = state.ThreadStates().GetIterator(); 346 ThreadSchedulingState* threadState = it.Next();) { 347 Thread* thread = threadState->thread; 348 if (thread->CreationTime() <= lastEventTime 349 && (thread->DeletionTime() == -1 350 || thread->DeletionTime() >= lastEventTime)) { 351 threadCount++; 352 } 353 } 354 355 CompactSchedulingState* compactState = (CompactSchedulingState*)malloc( 356 sizeof(CompactSchedulingState) 357 + threadCount * sizeof(CompactThreadSchedulingState)); 358 if (compactState == NULL) 359 return NULL; 360 361 // copy the state info 362 compactState->fEventOffset = eventOffset; 363 compactState->fThreadCount = threadCount; 364 compactState->fLastEventTime = lastEventTime; 365 366 int32 threadIndex = 0; 367 for (ThreadSchedulingStateTable::Iterator it 368 = state.ThreadStates().GetIterator(); 369 ThreadSchedulingState* threadState = it.Next();) { 370 Thread* thread = threadState->thread; 371 if (thread->CreationTime() <= lastEventTime 372 && (thread->DeletionTime() == -1 373 || thread->DeletionTime() >= lastEventTime)) { 374 compactState->fThreadStates[threadIndex++] = *threadState; 375 } 376 } 377 378 return compactState; 379 } 380 381 382 void 383 Model::CompactSchedulingState::Delete() 384 { 385 free(this); 386 } 387 388 389 // #pragma mark - Model 390 391 392 Model::Model(const char* dataSourceName, void* eventData, size_t eventDataSize) 393 : 394 fDataSourceName(dataSourceName), 395 fEventData(eventData), 396 fEventDataSize(eventDataSize), 397 fBaseTime(0), 398 fLastEventTime(0), 399 fTeams(20, true), 400 fThreads(20, true), 401 fWaitObjectGroups(20, true), 402 fSchedulingStates(100) 403 { 404 } 405 406 407 Model::~Model() 408 { 409 for (int32 i = 0; CompactSchedulingState* state 410 = fSchedulingStates.ItemAt(i); i++) { 411 state->Delete(); 412 } 413 414 free(fEventData); 415 } 416 417 418 void 419 Model::LoadingFinished() 420 { 421 for (int32 i = 0; Thread* thread = fThreads.ItemAt(i); i++) 422 thread->SetIndex(i); 423 } 424 425 426 void 427 Model::SetBaseTime(bigtime_t time) 428 { 429 fBaseTime = time; 430 } 431 432 433 void 434 Model::SetLastEventTime(bigtime_t time) 435 { 436 fLastEventTime = time; 437 } 438 439 440 int32 441 Model::CountTeams() const 442 { 443 return fTeams.CountItems(); 444 } 445 446 447 Model::Team* 448 Model::TeamAt(int32 index) const 449 { 450 return fTeams.ItemAt(index); 451 } 452 453 454 Model::Team* 455 Model::TeamByID(team_id id) const 456 { 457 return fTeams.BinarySearchByKey(id, &Team::CompareWithID); 458 } 459 460 461 Model::Team* 462 Model::AddTeam(const system_profiler_team_added* event, bigtime_t time) 463 { 464 Team* team = TeamByID(event->team); 465 if (team != NULL) { 466 fprintf(stderr, "Duplicate team: %ld\n", event->team); 467 // TODO: User feedback! 468 return team; 469 } 470 471 team = new(std::nothrow) Team(event, time); 472 if (team == NULL) 473 return NULL; 474 475 if (!fTeams.BinaryInsert(team, &Team::CompareByID)) { 476 delete team; 477 return NULL; 478 } 479 480 return team; 481 } 482 483 484 int32 485 Model::CountThreads() const 486 { 487 return fThreads.CountItems(); 488 } 489 490 491 Model::Thread* 492 Model::ThreadAt(int32 index) const 493 { 494 return fThreads.ItemAt(index); 495 } 496 497 498 Model::Thread* 499 Model::ThreadByID(thread_id id) const 500 { 501 return fThreads.BinarySearchByKey(id, &Thread::CompareWithID); 502 } 503 504 505 Model::Thread* 506 Model::AddThread(const system_profiler_thread_added* event, bigtime_t time) 507 { 508 // check whether we do already know the thread 509 Thread* thread = ThreadByID(event->thread); 510 if (thread != NULL) { 511 fprintf(stderr, "Duplicate thread: %ld\n", event->thread); 512 // TODO: User feedback! 513 return thread; 514 } 515 516 // get its team 517 Team* team = TeamByID(event->team); 518 if (team == NULL) { 519 fprintf(stderr, "No team for thread: %ld\n", event->thread); 520 return NULL; 521 } 522 523 // create the thread and add it 524 thread = new(std::nothrow) Thread(team, event, time); 525 if (thread == NULL) 526 return NULL; 527 ObjectDeleter<Thread> threadDeleter(thread); 528 529 if (!fThreads.BinaryInsert(thread, &Thread::CompareByID)) 530 return NULL; 531 532 if (!team->AddThread(thread)) { 533 fThreads.RemoveItem(thread); 534 return NULL; 535 } 536 537 threadDeleter.Detach(); 538 return thread; 539 } 540 541 542 Model::WaitObject* 543 Model::AddWaitObject(const system_profiler_wait_object_info* event, 544 WaitObjectGroup** _waitObjectGroup) 545 { 546 // create a wait object 547 WaitObject* waitObject = new(std::nothrow) WaitObject(event); 548 if (waitObject == NULL) 549 return NULL; 550 551 // find the wait object group 552 WaitObjectGroup* waitObjectGroup 553 = WaitObjectGroupFor(waitObject->Type(), waitObject->Object()); 554 if (waitObjectGroup == NULL) { 555 // doesn't exist yet -- create 556 waitObjectGroup = new(std::nothrow) WaitObjectGroup(waitObject); 557 if (waitObjectGroup == NULL) { 558 delete waitObject; 559 return NULL; 560 } 561 562 // add to the list 563 if (!fWaitObjectGroups.BinaryInsert(waitObjectGroup, 564 &WaitObjectGroup::CompareByTypeObject)) { 565 delete waitObjectGroup; 566 return NULL; 567 } 568 } else { 569 // exists -- just add the object 570 waitObjectGroup->AddWaitObject(waitObject); 571 } 572 573 if (_waitObjectGroup != NULL) 574 *_waitObjectGroup = waitObjectGroup; 575 576 return waitObject; 577 } 578 579 580 Model::WaitObjectGroup* 581 Model::WaitObjectGroupFor(uint32 type, addr_t object) const 582 { 583 type_and_object key; 584 key.type = type; 585 key.object = object; 586 587 return fWaitObjectGroups.BinarySearchByKey(key, 588 &WaitObjectGroup::CompareWithTypeObject); 589 } 590 591 592 Model::ThreadWaitObject* 593 Model::AddThreadWaitObject(thread_id threadID, WaitObject* waitObject, 594 ThreadWaitObjectGroup** _threadWaitObjectGroup) 595 { 596 Thread* thread = ThreadByID(threadID); 597 if (thread == NULL) 598 return NULL; 599 600 return thread->AddThreadWaitObject(waitObject, _threadWaitObjectGroup); 601 } 602 603 604 Model::ThreadWaitObjectGroup* 605 Model::ThreadWaitObjectGroupFor(thread_id threadID, uint32 type, addr_t object) const 606 { 607 Thread* thread = ThreadByID(threadID); 608 if (thread == NULL) 609 return NULL; 610 611 return thread->ThreadWaitObjectGroupFor(type, object); 612 } 613 614 615 bool 616 Model::AddSchedulingStateSnapshot(const SchedulingState& state, 617 off_t eventOffset) 618 { 619 CompactSchedulingState* compactState = CompactSchedulingState::Create(state, 620 eventOffset); 621 if (compactState == NULL) 622 return false; 623 624 if (!fSchedulingStates.AddItem(compactState)) { 625 compactState->Delete(); 626 return false; 627 } 628 629 return true; 630 } 631 632 633 const Model::CompactSchedulingState* 634 Model::ClosestSchedulingState(bigtime_t eventTime) const 635 { 636 int32 index = fSchedulingStates.BinarySearchIndexByKey(eventTime, 637 &_CompareEventTimeSchedulingState); 638 if (index >= 0) 639 return fSchedulingStates.ItemAt(index); 640 641 // no exact match 642 index = -index - 1; 643 return index > 0 ? fSchedulingStates.ItemAt(index - 1) : NULL; 644 } 645 646 647 /*static*/ int 648 Model::_CompareEventTimeSchedulingState(const bigtime_t* time, 649 const CompactSchedulingState* state) 650 { 651 if (*time < state->LastEventTime()) 652 return -1; 653 return *time == state->LastEventTime() ? 0 : 1; 654 } 655 656