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