1 /* 2 * Copyright 2012-2016, Rene Gollent, rene@gollent.com. 3 * Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "CliContext.h" 9 10 #include <AutoDeleter.h> 11 #include <AutoLocker.h> 12 13 #include "StackTrace.h" 14 #include "UserInterface.h" 15 #include "Value.h" 16 #include "ValueNodeManager.h" 17 #include "Variable.h" 18 19 20 // NOTE: This is a simple work-around for EditLine not having any kind of user 21 // data field. Hence in _GetPrompt() we don't have access to the context object. 22 // ATM only one CLI is possible in Debugger, so a static variable works well 23 // enough. Should that ever change, we would need a thread-safe 24 // EditLine* -> CliContext* map. 25 static CliContext* sCurrentContext; 26 27 28 // #pragma mark - Event 29 30 31 struct CliContext::Event : DoublyLinkedListLinkImpl<CliContext::Event> { 32 Event(int type, Thread* thread = NULL, TeamMemoryBlock* block = NULL, 33 ExpressionInfo* info = NULL, status_t expressionResult = B_OK, 34 ExpressionResult* expressionValue = NULL) 35 : 36 fType(type), 37 fThreadReference(thread), 38 fMemoryBlockReference(block), 39 fExpressionInfo(info), 40 fExpressionResult(expressionResult), 41 fExpressionValue(expressionValue) 42 { 43 } 44 45 int Type() const 46 { 47 return fType; 48 } 49 50 Thread* GetThread() const 51 { 52 return fThreadReference.Get(); 53 } 54 55 TeamMemoryBlock* GetMemoryBlock() const 56 { 57 return fMemoryBlockReference.Get(); 58 } 59 60 ExpressionInfo* GetExpressionInfo() const 61 { 62 return fExpressionInfo; 63 } 64 65 status_t GetExpressionResult() const 66 { 67 return fExpressionResult; 68 } 69 70 ExpressionResult* GetExpressionValue() const 71 { 72 return fExpressionValue.Get(); 73 } 74 75 76 private: 77 int fType; 78 BReference<Thread> fThreadReference; 79 BReference<TeamMemoryBlock> fMemoryBlockReference; 80 BReference<ExpressionInfo> fExpressionInfo; 81 status_t fExpressionResult; 82 BReference<ExpressionResult> fExpressionValue; 83 }; 84 85 86 // #pragma mark - CliContext 87 88 89 CliContext::CliContext() 90 : 91 fLock("CliContext"), 92 fTeam(NULL), 93 fListener(NULL), 94 fNodeManager(NULL), 95 fEditLine(NULL), 96 fHistory(NULL), 97 fPrompt(NULL), 98 fBlockingSemaphore(-1), 99 fInputLoopWaitingForEvents(0), 100 fEventsOccurred(0), 101 fInputLoopWaiting(false), 102 fTerminating(false), 103 fCurrentThread(NULL), 104 fCurrentStackTrace(NULL), 105 fCurrentStackFrameIndex(-1), 106 fCurrentBlock(NULL), 107 fExpressionInfo(NULL), 108 fExpressionResult(B_OK), 109 fExpressionValue(NULL) 110 { 111 sCurrentContext = this; 112 } 113 114 115 CliContext::~CliContext() 116 { 117 Cleanup(); 118 sCurrentContext = NULL; 119 120 if (fBlockingSemaphore >= 0) 121 delete_sem(fBlockingSemaphore); 122 } 123 124 125 status_t 126 CliContext::Init(Team* team, UserInterfaceListener* listener) 127 { 128 fTeam = team; 129 fListener = listener; 130 131 fTeam->AddListener(this); 132 133 status_t error = fLock.InitCheck(); 134 if (error != B_OK) 135 return error; 136 137 fBlockingSemaphore = create_sem(0, "CliContext block"); 138 if (fBlockingSemaphore < 0) 139 return fBlockingSemaphore; 140 141 fEditLine = el_init("Debugger", stdin, stdout, stderr); 142 if (fEditLine == NULL) 143 return B_ERROR; 144 145 fHistory = history_init(); 146 if (fHistory == NULL) 147 return B_ERROR; 148 149 HistEvent historyEvent; 150 history(fHistory, &historyEvent, H_SETSIZE, 100); 151 152 el_set(fEditLine, EL_HIST, &history, fHistory); 153 el_set(fEditLine, EL_EDITOR, "emacs"); 154 el_set(fEditLine, EL_PROMPT, &_GetPrompt); 155 156 fNodeManager = new(std::nothrow) ValueNodeManager(); 157 if (fNodeManager == NULL) 158 return B_NO_MEMORY; 159 fNodeManager->AddListener(this); 160 161 fExpressionInfo = new(std::nothrow) ExpressionInfo(); 162 if (fExpressionInfo == NULL) 163 return B_NO_MEMORY; 164 fExpressionInfo->AddListener(this); 165 166 return B_OK; 167 } 168 169 170 void 171 CliContext::Cleanup() 172 { 173 Terminating(); 174 175 while (Event* event = fPendingEvents.RemoveHead()) 176 delete event; 177 178 if (fEditLine != NULL) { 179 el_end(fEditLine); 180 fEditLine = NULL; 181 } 182 183 if (fHistory != NULL) { 184 history_end(fHistory); 185 fHistory = NULL; 186 } 187 188 if (fTeam != NULL) { 189 fTeam->RemoveListener(this); 190 fTeam = NULL; 191 } 192 193 if (fNodeManager != NULL) { 194 fNodeManager->ReleaseReference(); 195 fNodeManager = NULL; 196 } 197 198 if (fCurrentBlock != NULL) { 199 fCurrentBlock->ReleaseReference(); 200 fCurrentBlock = NULL; 201 } 202 203 if (fExpressionInfo != NULL) { 204 fExpressionInfo->ReleaseReference(); 205 fExpressionInfo = NULL; 206 } 207 } 208 209 210 void 211 CliContext::Terminating() 212 { 213 AutoLocker<BLocker> locker(fLock); 214 215 fTerminating = true; 216 _SignalInputLoop(EVENT_QUIT); 217 218 // TODO: Signal the input loop, should it be in PromptUser()! 219 } 220 221 222 thread_id 223 CliContext::CurrentThreadID() const 224 { 225 return fCurrentThread != NULL ? fCurrentThread->ID() : -1; 226 } 227 228 229 void 230 CliContext::SetCurrentThread(Thread* thread) 231 { 232 AutoLocker<BLocker> locker(fLock); 233 234 if (fCurrentThread != NULL) 235 fCurrentThread->ReleaseReference(); 236 237 fCurrentThread = thread; 238 239 if (fCurrentStackTrace != NULL) { 240 fCurrentStackTrace->ReleaseReference(); 241 fCurrentStackTrace = NULL; 242 fCurrentStackFrameIndex = -1; 243 fNodeManager->SetStackFrame(NULL, NULL); 244 } 245 246 if (fCurrentThread != NULL) { 247 fCurrentThread->AcquireReference(); 248 StackTrace* stackTrace = fCurrentThread->GetStackTrace(); 249 // if the thread's stack trace has already been loaded, 250 // set it, otherwise we'll set it when we process the thread's 251 // stack trace changed event. 252 if (stackTrace != NULL) { 253 fCurrentStackTrace = stackTrace; 254 fCurrentStackTrace->AcquireReference(); 255 SetCurrentStackFrameIndex(0); 256 } 257 } 258 } 259 260 261 void 262 CliContext::PrintCurrentThread() 263 { 264 AutoLocker<Team> teamLocker(fTeam); 265 266 if (fCurrentThread != NULL) { 267 printf("current thread: %" B_PRId32 " \"%s\"\n", fCurrentThread->ID(), 268 fCurrentThread->Name()); 269 } else 270 printf("no current thread\n"); 271 } 272 273 274 void 275 CliContext::SetCurrentStackFrameIndex(int32 index) 276 { 277 AutoLocker<BLocker> locker(fLock); 278 279 if (fCurrentStackTrace == NULL) 280 return; 281 else if (index < 0 || index >= fCurrentStackTrace->CountFrames()) 282 return; 283 284 fCurrentStackFrameIndex = index; 285 286 StackFrame* frame = fCurrentStackTrace->FrameAt(index); 287 if (frame != NULL) 288 fNodeManager->SetStackFrame(fCurrentThread, frame); 289 } 290 291 292 status_t 293 CliContext::EvaluateExpression(const char* expression, 294 SourceLanguage* language, target_addr_t& address) 295 { 296 fExpressionInfo->SetTo(expression); 297 298 fListener->ExpressionEvaluationRequested( 299 language, fExpressionInfo); 300 WaitForEvents(CliContext::EVENT_EXPRESSION_EVALUATED); 301 if (fTerminating) 302 return B_INTERRUPTED; 303 304 BString errorMessage; 305 if (fExpressionValue != NULL) { 306 if (fExpressionValue->Kind() == EXPRESSION_RESULT_KIND_PRIMITIVE) { 307 Value* value = fExpressionValue->PrimitiveValue(); 308 BVariant variantValue; 309 value->ToVariant(variantValue); 310 if (variantValue.Type() == B_STRING_TYPE) 311 errorMessage.SetTo(variantValue.ToString()); 312 else 313 address = variantValue.ToUInt64(); 314 } 315 } else 316 errorMessage = strerror(fExpressionResult); 317 318 if (!errorMessage.IsEmpty()) { 319 printf("Unable to evaluate expression: %s\n", 320 errorMessage.String()); 321 return B_ERROR; 322 } 323 324 return B_OK; 325 } 326 327 328 status_t 329 CliContext::GetMemoryBlock(target_addr_t address, TeamMemoryBlock*& block) 330 { 331 if (fCurrentBlock == NULL || !fCurrentBlock->Contains(address)) { 332 GetUserInterfaceListener()->InspectRequested(address, this); 333 WaitForEvents(CliContext::EVENT_TEAM_MEMORY_BLOCK_RETRIEVED); 334 if (fTerminating) 335 return B_INTERRUPTED; 336 } 337 338 block = fCurrentBlock; 339 return B_OK; 340 } 341 342 343 const char* 344 CliContext::PromptUser(const char* prompt) 345 { 346 fPrompt = prompt; 347 348 int count; 349 const char* line = el_gets(fEditLine, &count); 350 351 fPrompt = NULL; 352 353 ProcessPendingEvents(); 354 355 return line; 356 } 357 358 359 void 360 CliContext::AddLineToInputHistory(const char* line) 361 { 362 HistEvent historyEvent; 363 history(fHistory, &historyEvent, H_ENTER, line); 364 } 365 366 367 void 368 CliContext::QuitSession(bool killTeam) 369 { 370 _PrepareToWaitForEvents(EVENT_QUIT); 371 372 fListener->UserInterfaceQuitRequested( 373 killTeam 374 ? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM 375 : UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM); 376 377 _WaitForEvents(); 378 } 379 380 381 void 382 CliContext::WaitForThreadOrUser() 383 { 384 ProcessPendingEvents(); 385 386 // TODO: Deal with SIGINT as well! 387 for (;;) { 388 _PrepareToWaitForEvents( 389 EVENT_USER_INTERRUPT | EVENT_THREAD_STOPPED); 390 391 // check whether there are any threads stopped already 392 Thread* stoppedThread = NULL; 393 BReference<Thread> stoppedThreadReference; 394 395 AutoLocker<Team> teamLocker(fTeam); 396 397 for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator(); 398 Thread* thread = it.Next();) { 399 if (thread->State() == THREAD_STATE_STOPPED) { 400 stoppedThread = thread; 401 stoppedThreadReference.SetTo(thread); 402 break; 403 } 404 } 405 406 teamLocker.Unlock(); 407 408 if (stoppedThread != NULL) { 409 if (fCurrentThread == NULL) 410 SetCurrentThread(stoppedThread); 411 412 _SignalInputLoop(EVENT_THREAD_STOPPED); 413 } 414 415 uint32 events = _WaitForEvents(); 416 if ((events & EVENT_QUIT) != 0 || stoppedThread != NULL) { 417 ProcessPendingEvents(); 418 return; 419 } 420 } 421 } 422 423 424 void 425 CliContext::WaitForEvents(int32 eventMask) 426 { 427 for (;;) { 428 _PrepareToWaitForEvents(eventMask | EVENT_USER_INTERRUPT); 429 uint32 events = fEventsOccurred; 430 if ((events & eventMask) == 0) { 431 events = _WaitForEvents(); 432 } 433 434 if ((events & EVENT_QUIT) != 0 || (events & eventMask) != 0) { 435 _SignalInputLoop(eventMask); 436 ProcessPendingEvents(); 437 return; 438 } 439 } 440 } 441 442 443 void 444 CliContext::ProcessPendingEvents() 445 { 446 AutoLocker<Team> teamLocker(fTeam); 447 448 for (;;) { 449 // get the next event 450 AutoLocker<BLocker> locker(fLock); 451 Event* event = fPendingEvents.RemoveHead(); 452 locker.Unlock(); 453 if (event == NULL) 454 break; 455 ObjectDeleter<Event> eventDeleter(event); 456 457 // process the event 458 Thread* thread = event->GetThread(); 459 460 switch (event->Type()) { 461 case EVENT_QUIT: 462 case EVENT_DEBUG_REPORT_CHANGED: 463 case EVENT_USER_INTERRUPT: 464 break; 465 case EVENT_THREAD_ADDED: 466 printf("[new thread: %" B_PRId32 " \"%s\"]\n", thread->ID(), 467 thread->Name()); 468 break; 469 case EVENT_THREAD_REMOVED: 470 printf("[thread terminated: %" B_PRId32 " \"%s\"]\n", 471 thread->ID(), thread->Name()); 472 break; 473 case EVENT_THREAD_STOPPED: 474 printf("[thread stopped: %" B_PRId32 " \"%s\"]\n", 475 thread->ID(), thread->Name()); 476 break; 477 case EVENT_THREAD_STACK_TRACE_CHANGED: 478 if (thread == fCurrentThread) { 479 fCurrentStackTrace = thread->GetStackTrace(); 480 fCurrentStackTrace->AcquireReference(); 481 SetCurrentStackFrameIndex(0); 482 } 483 break; 484 case EVENT_TEAM_MEMORY_BLOCK_RETRIEVED: 485 if (fCurrentBlock != NULL) { 486 fCurrentBlock->ReleaseReference(); 487 fCurrentBlock = NULL; 488 } 489 fCurrentBlock = event->GetMemoryBlock(); 490 break; 491 case EVENT_EXPRESSION_EVALUATED: 492 fExpressionResult = event->GetExpressionResult(); 493 if (fExpressionValue != NULL) { 494 fExpressionValue->ReleaseReference(); 495 fExpressionValue = NULL; 496 } 497 fExpressionValue = event->GetExpressionValue(); 498 if (fExpressionValue != NULL) 499 fExpressionValue->AcquireReference(); 500 break; 501 } 502 } 503 } 504 505 506 void 507 CliContext::ThreadAdded(const Team::ThreadEvent& threadEvent) 508 { 509 _QueueEvent( 510 new(std::nothrow) Event(EVENT_THREAD_ADDED, threadEvent.GetThread())); 511 _SignalInputLoop(EVENT_THREAD_ADDED); 512 } 513 514 515 void 516 CliContext::ThreadRemoved(const Team::ThreadEvent& threadEvent) 517 { 518 _QueueEvent( 519 new(std::nothrow) Event(EVENT_THREAD_REMOVED, threadEvent.GetThread())); 520 _SignalInputLoop(EVENT_THREAD_REMOVED); 521 } 522 523 524 void 525 CliContext::ThreadStateChanged(const Team::ThreadEvent& threadEvent) 526 { 527 if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED) 528 return; 529 530 _QueueEvent( 531 new(std::nothrow) Event(EVENT_THREAD_STOPPED, threadEvent.GetThread())); 532 _SignalInputLoop(EVENT_THREAD_STOPPED); 533 } 534 535 536 void 537 CliContext::ThreadStackTraceChanged(const Team::ThreadEvent& threadEvent) 538 { 539 if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED) 540 return; 541 542 _QueueEvent( 543 new(std::nothrow) Event(EVENT_THREAD_STACK_TRACE_CHANGED, 544 threadEvent.GetThread())); 545 _SignalInputLoop(EVENT_THREAD_STACK_TRACE_CHANGED); 546 } 547 548 549 void 550 CliContext::ExpressionEvaluated(ExpressionInfo* info, status_t result, 551 ExpressionResult* value) 552 { 553 _QueueEvent( 554 new(std::nothrow) Event(EVENT_EXPRESSION_EVALUATED, 555 NULL, NULL, info, result, value)); 556 _SignalInputLoop(EVENT_EXPRESSION_EVALUATED); 557 } 558 559 560 void 561 CliContext::DebugReportChanged(const Team::DebugReportEvent& event) 562 { 563 if (event.GetFinalStatus() == B_OK) { 564 printf("Successfully saved debug report to %s\n", 565 event.GetReportPath()); 566 } else { 567 fprintf(stderr, "Failed to write debug report: %s\n", strerror( 568 event.GetFinalStatus())); 569 } 570 571 _QueueEvent(new(std::nothrow) Event(EVENT_DEBUG_REPORT_CHANGED)); 572 _SignalInputLoop(EVENT_DEBUG_REPORT_CHANGED); 573 } 574 575 576 void 577 CliContext::CoreFileChanged(const Team::CoreFileChangedEvent& event) 578 { 579 printf("Successfully saved core file to %s\n", 580 event.GetTargetPath()); 581 582 _QueueEvent(new(std::nothrow) Event(EVENT_CORE_FILE_CHANGED)); 583 _SignalInputLoop(EVENT_CORE_FILE_CHANGED); 584 } 585 586 587 void 588 CliContext::MemoryBlockRetrieved(TeamMemoryBlock* block) 589 { 590 _QueueEvent( 591 new(std::nothrow) Event(EVENT_TEAM_MEMORY_BLOCK_RETRIEVED, 592 NULL, block)); 593 _SignalInputLoop(EVENT_TEAM_MEMORY_BLOCK_RETRIEVED); 594 } 595 596 597 void 598 CliContext::ValueNodeChanged(ValueNodeChild* nodeChild, ValueNode* oldNode, 599 ValueNode* newNode) 600 { 601 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED); 602 } 603 604 605 void 606 CliContext::ValueNodeChildrenCreated(ValueNode* node) 607 { 608 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED); 609 } 610 611 612 void 613 CliContext::ValueNodeChildrenDeleted(ValueNode* node) 614 { 615 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED); 616 } 617 618 619 void 620 CliContext::ValueNodeValueChanged(ValueNode* oldNode) 621 { 622 _SignalInputLoop(EVENT_VALUE_NODE_CHANGED); 623 } 624 625 626 void 627 CliContext::_QueueEvent(Event* event) 628 { 629 if (event == NULL) { 630 // no memory -- can't do anything about it 631 return; 632 } 633 634 AutoLocker<BLocker> locker(fLock); 635 fPendingEvents.Add(event); 636 } 637 638 639 void 640 CliContext::_PrepareToWaitForEvents(uint32 eventMask) 641 { 642 // Set the events we're going to wait for -- always wait for "quit". 643 AutoLocker<BLocker> locker(fLock); 644 fInputLoopWaitingForEvents = eventMask | EVENT_QUIT; 645 fEventsOccurred = fTerminating ? EVENT_QUIT : 0; 646 } 647 648 649 uint32 650 CliContext::_WaitForEvents() 651 { 652 AutoLocker<BLocker> locker(fLock); 653 654 if (fEventsOccurred == 0) { 655 sem_id blockingSemaphore = fBlockingSemaphore; 656 fInputLoopWaiting = true; 657 658 locker.Unlock(); 659 660 while (acquire_sem(blockingSemaphore) == B_INTERRUPTED) { 661 } 662 663 locker.Lock(); 664 } 665 666 uint32 events = fEventsOccurred; 667 fEventsOccurred = 0; 668 return events; 669 } 670 671 672 void 673 CliContext::_SignalInputLoop(uint32 events) 674 { 675 AutoLocker<BLocker> locker(fLock); 676 677 if ((fInputLoopWaitingForEvents & events) == 0) 678 return; 679 680 fEventsOccurred = fInputLoopWaitingForEvents & events; 681 fInputLoopWaitingForEvents = 0; 682 683 if (fInputLoopWaiting) { 684 fInputLoopWaiting = false; 685 release_sem(fBlockingSemaphore); 686 } 687 } 688 689 690 /*static*/ const char* 691 CliContext::_GetPrompt(EditLine* editLine) 692 { 693 return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL; 694 } 695