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 BLooper("CliContext"), 92 fLock("CliContext"), 93 fTeam(NULL), 94 fListener(NULL), 95 fNodeManager(NULL), 96 fEditLine(NULL), 97 fHistory(NULL), 98 fPrompt(NULL), 99 fWaitForEventSemaphore(-1), 100 fEventOccurred(0), 101 fTerminating(false), 102 fStoppedThread(NULL), 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 (fWaitForEventSemaphore >= 0) 121 delete_sem(fWaitForEventSemaphore); 122 } 123 124 125 status_t 126 CliContext::Init(::Team* team, UserInterfaceListener* listener) 127 { 128 AutoLocker<BLocker> locker(fLock); 129 130 fTeam = team; 131 fListener = listener; 132 133 fTeam->AddListener(this); 134 135 status_t error = fLock.InitCheck(); 136 if (error != B_OK) 137 return error; 138 139 fWaitForEventSemaphore = create_sem(0, "CliContext wait for event"); 140 if (fWaitForEventSemaphore < 0) 141 return fWaitForEventSemaphore; 142 143 fEditLine = el_init("Debugger", stdin, stdout, stderr); 144 if (fEditLine == NULL) 145 return B_ERROR; 146 147 fHistory = history_init(); 148 if (fHistory == NULL) 149 return B_ERROR; 150 151 HistEvent historyEvent; 152 history(fHistory, &historyEvent, H_SETSIZE, 100); 153 154 el_set(fEditLine, EL_HIST, &history, fHistory); 155 el_set(fEditLine, EL_EDITOR, "emacs"); 156 el_set(fEditLine, EL_PROMPT, &_GetPrompt); 157 158 fNodeManager = new(std::nothrow) ValueNodeManager(); 159 if (fNodeManager == NULL) 160 return B_NO_MEMORY; 161 fNodeManager->AddListener(this); 162 163 fExpressionInfo = new(std::nothrow) ExpressionInfo(); 164 if (fExpressionInfo == NULL) 165 return B_NO_MEMORY; 166 fExpressionInfo->AddListener(this); 167 168 return B_OK; 169 } 170 171 172 void 173 CliContext::Cleanup() 174 { 175 AutoLocker<BLocker> locker(fLock); 176 Terminating(); 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 // TODO: Use the lifecycle methods of BLooper instead 211 void 212 CliContext::Terminating() 213 { 214 AutoLocker<BLocker> locker(fLock); 215 216 fTerminating = true; 217 218 BMessage message(MSG_QUIT); 219 PostMessage(&message); 220 221 // TODO: Signal the input loop, should it be in PromptUser()! 222 } 223 224 225 thread_id 226 CliContext::CurrentThreadID() const 227 { 228 AutoLocker<BLocker> locker(fLock); 229 return fCurrentThread != NULL ? fCurrentThread->ID() : -1; 230 } 231 232 233 void 234 CliContext::SetCurrentThread(::Thread* thread) 235 { 236 AutoLocker<BLocker> locker(fLock); 237 238 if (fCurrentThread != NULL) 239 fCurrentThread->ReleaseReference(); 240 241 fCurrentThread = thread; 242 243 if (fCurrentStackTrace != NULL) { 244 fCurrentStackTrace->ReleaseReference(); 245 fCurrentStackTrace = NULL; 246 fCurrentStackFrameIndex = -1; 247 fNodeManager->SetStackFrame(NULL, NULL); 248 } 249 250 if (fCurrentThread != NULL) { 251 fCurrentThread->AcquireReference(); 252 StackTrace* stackTrace = fCurrentThread->GetStackTrace(); 253 // if the thread's stack trace has already been loaded, 254 // set it, otherwise we'll set it when we process the thread's 255 // stack trace changed event. 256 if (stackTrace != NULL) { 257 fCurrentStackTrace = stackTrace; 258 fCurrentStackTrace->AcquireReference(); 259 SetCurrentStackFrameIndex(0); 260 } 261 } 262 } 263 264 265 void 266 CliContext::PrintCurrentThread() 267 { 268 AutoLocker< ::Team> teamLocker(fTeam); 269 AutoLocker<BLocker> locker(fLock); 270 271 if (fCurrentThread != NULL) { 272 printf("current thread: %" B_PRId32 " \"%s\"\n", fCurrentThread->ID(), 273 fCurrentThread->Name()); 274 } else 275 printf("no current thread\n"); 276 } 277 278 279 void 280 CliContext::SetCurrentStackFrameIndex(int32 index) 281 { 282 AutoLocker<BLocker> locker(fLock); 283 284 if (fCurrentStackTrace == NULL) 285 return; 286 else if (index < 0 || index >= fCurrentStackTrace->CountFrames()) 287 return; 288 289 fCurrentStackFrameIndex = index; 290 291 StackFrame* frame = fCurrentStackTrace->FrameAt(index); 292 if (frame != NULL) 293 fNodeManager->SetStackFrame(fCurrentThread, frame); 294 } 295 296 297 status_t 298 CliContext::EvaluateExpression(const char* expression, 299 SourceLanguage* language, target_addr_t& address) 300 { 301 AutoLocker<BLocker> locker(fLock); 302 fExpressionInfo->SetTo(expression); 303 304 fListener->ExpressionEvaluationRequested( 305 language, fExpressionInfo); 306 _WaitForEvent(MSG_EXPRESSION_EVALUATED); 307 if (fTerminating) 308 return B_INTERRUPTED; 309 310 BString errorMessage; 311 if (fExpressionValue != NULL) { 312 if (fExpressionValue->Kind() == EXPRESSION_RESULT_KIND_PRIMITIVE) { 313 Value* value = fExpressionValue->PrimitiveValue(); 314 BVariant variantValue; 315 value->ToVariant(variantValue); 316 if (variantValue.Type() == B_STRING_TYPE) 317 errorMessage.SetTo(variantValue.ToString()); 318 else 319 address = variantValue.ToUInt64(); 320 } 321 } else 322 errorMessage = strerror(fExpressionResult); 323 324 if (!errorMessage.IsEmpty()) { 325 printf("Unable to evaluate expression: %s\n", 326 errorMessage.String()); 327 return B_ERROR; 328 } 329 330 return B_OK; 331 } 332 333 334 status_t 335 CliContext::GetMemoryBlock(target_addr_t address, TeamMemoryBlock*& block) 336 { 337 AutoLocker<BLocker> locker(fLock); 338 if (fCurrentBlock == NULL || !fCurrentBlock->Contains(address)) { 339 GetUserInterfaceListener()->InspectRequested(address, this); 340 _WaitForEvent(MSG_TEAM_MEMORY_BLOCK_RETRIEVED); 341 if (fTerminating) 342 return B_INTERRUPTED; 343 } 344 345 block = fCurrentBlock; 346 return B_OK; 347 } 348 349 350 const char* 351 CliContext::PromptUser(const char* prompt) 352 { 353 fPrompt = prompt; 354 355 int count; 356 const char* line = el_gets(fEditLine, &count); 357 358 fPrompt = NULL; 359 360 return line; 361 } 362 363 364 void 365 CliContext::AddLineToInputHistory(const char* line) 366 { 367 HistEvent historyEvent; 368 history(fHistory, &historyEvent, H_ENTER, line); 369 } 370 371 372 void 373 CliContext::QuitSession(bool killTeam) 374 { 375 fListener->UserInterfaceQuitRequested( 376 killTeam 377 ? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM 378 : UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM); 379 380 WaitForEvent(MSG_QUIT); 381 } 382 383 384 void 385 CliContext::WaitForThreadOrUser() 386 { 387 // TODO: Deal with SIGINT as well! 388 389 AutoLocker<BLocker> locker(fLock); 390 391 while (fStoppedThread == NULL) 392 _WaitForEvent(MSG_THREAD_STATE_CHANGED); 393 394 if (fCurrentThread == NULL) 395 SetCurrentThread(fStoppedThread); 396 } 397 398 399 void 400 CliContext::WaitForEvent(uint32 event) { 401 AutoLocker<BLocker> locker(fLock); 402 _WaitForEvent(event); 403 } 404 405 406 void 407 CliContext::MessageReceived(BMessage* message) 408 { 409 fLock.Lock(); 410 411 int32 threadID; 412 message->FindInt32("thread", &threadID); 413 414 const char* threadName; 415 message->FindString("threadName", &threadName); 416 417 switch (message->what) { 418 case MSG_THREAD_ADDED: 419 printf("[new thread: %" B_PRId32 " \"%s\"]\n", threadID, 420 threadName); 421 break; 422 case MSG_THREAD_REMOVED: 423 printf("[thread terminated: %" B_PRId32 " \"%s\"]\n", 424 threadID, threadName); 425 break; 426 case MSG_THREAD_STATE_CHANGED: 427 { 428 AutoLocker< ::Team> locker(fTeam); 429 ::Thread* thread = fTeam->ThreadByID(threadID); 430 431 if (thread->State() == THREAD_STATE_STOPPED) { 432 printf("[thread stopped: %" B_PRId32 " \"%s\"]\n", 433 threadID, threadName); 434 fStoppedThread.SetTo(thread); 435 } else { 436 fStoppedThread = NULL; 437 } 438 break; 439 } 440 case MSG_THREAD_STACK_TRACE_CHANGED: 441 if (threadID == fCurrentThread->ID()) { 442 AutoLocker< ::Team> locker(fTeam); 443 ::Thread* thread = fTeam->ThreadByID(threadID); 444 445 fCurrentStackTrace = thread->GetStackTrace(); 446 fCurrentStackTrace->AcquireReference(); 447 SetCurrentStackFrameIndex(0); 448 } 449 break; 450 case MSG_TEAM_MEMORY_BLOCK_RETRIEVED: 451 { 452 TeamMemoryBlock* block = NULL; 453 if (message->FindPointer("block", 454 reinterpret_cast<void **>(&block)) != B_OK) { 455 break; 456 } 457 458 if (fCurrentBlock != NULL) { 459 fCurrentBlock->ReleaseReference(); 460 } 461 462 // reference acquired in MemoryBlockRetrieved 463 fCurrentBlock = block; 464 break; 465 } 466 case MSG_EXPRESSION_EVALUATED: 467 { 468 status_t result; 469 if (message->FindInt32("result", &result) != B_OK) { 470 break; 471 } 472 473 fExpressionResult = result; 474 475 ExpressionResult* value = NULL; 476 message->FindPointer("value", reinterpret_cast<void**>(&value)); 477 478 if (fExpressionValue != NULL) { 479 fExpressionValue->ReleaseReference(); 480 } 481 482 // reference acquired in ExpressionEvaluated 483 fExpressionValue = value; 484 break; 485 } 486 default: 487 BLooper::MessageReceived(message); 488 break; 489 } 490 491 fEventOccurred = message->what; 492 493 fLock.Unlock(); 494 495 release_sem(fWaitForEventSemaphore); 496 // all of the code that was waiting on the semaphore runs 497 acquire_sem(fWaitForEventSemaphore); 498 499 fLock.Lock(); 500 fEventOccurred = 0; 501 fLock.Unlock(); 502 } 503 504 505 void 506 CliContext::ThreadAdded(const Team::ThreadEvent& threadEvent) 507 { 508 BMessage message(MSG_THREAD_ADDED); 509 message.AddInt32("thread", threadEvent.GetThread()->ID()); 510 message.AddString("threadName", threadEvent.GetThread()->Name()); 511 PostMessage(&message); 512 } 513 514 515 void 516 CliContext::ThreadRemoved(const Team::ThreadEvent& threadEvent) 517 { 518 BMessage message(MSG_THREAD_REMOVED); 519 message.AddInt32("thread", threadEvent.GetThread()->ID()); 520 message.AddString("threadName", threadEvent.GetThread()->Name()); 521 PostMessage(&message); 522 } 523 524 525 void 526 CliContext::ThreadStateChanged(const Team::ThreadEvent& threadEvent) 527 { 528 BMessage message(MSG_THREAD_STATE_CHANGED); 529 message.AddInt32("thread", threadEvent.GetThread()->ID()); 530 message.AddString("threadName", threadEvent.GetThread()->Name()); 531 PostMessage(&message); 532 } 533 534 535 void 536 CliContext::ThreadStackTraceChanged(const Team::ThreadEvent& threadEvent) 537 { 538 if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED) 539 return; 540 541 BMessage message(MSG_THREAD_STACK_TRACE_CHANGED); 542 message.AddInt32("thread", threadEvent.GetThread()->ID()); 543 message.AddString("threadName", threadEvent.GetThread()->Name()); 544 PostMessage(&message); 545 } 546 547 548 void 549 CliContext::ExpressionEvaluated(ExpressionInfo* info, status_t result, 550 ExpressionResult* value) 551 { 552 BMessage message(MSG_EXPRESSION_EVALUATED); 553 message.AddInt32("result", result); 554 555 if (value != NULL) { 556 value->AcquireReference(); 557 message.AddPointer("value", value); 558 } 559 560 PostMessage(&message); 561 } 562 563 564 void 565 CliContext::DebugReportChanged(const Team::DebugReportEvent& event) 566 { 567 if (event.GetFinalStatus() == B_OK) { 568 printf("Successfully saved debug report to %s\n", 569 event.GetReportPath()); 570 } else { 571 fprintf(stderr, "Failed to write debug report: %s\n", strerror( 572 event.GetFinalStatus())); 573 } 574 } 575 576 577 void 578 CliContext::CoreFileChanged(const Team::CoreFileChangedEvent& event) 579 { 580 printf("Successfully saved core file to %s\n", 581 event.GetTargetPath()); 582 } 583 584 585 void 586 CliContext::MemoryBlockRetrieved(TeamMemoryBlock* block) 587 { 588 if (block != NULL) 589 block->AcquireReference(); 590 591 BMessage message(MSG_TEAM_MEMORY_BLOCK_RETRIEVED); 592 message.AddPointer("block", block); 593 PostMessage(&message); 594 } 595 596 597 void 598 CliContext::ValueNodeChanged(ValueNodeChild* nodeChild, ValueNode* oldNode, 599 ValueNode* newNode) 600 { 601 BMessage message(MSG_VALUE_NODE_CHANGED); 602 PostMessage(&message); 603 } 604 605 606 void 607 CliContext::ValueNodeChildrenCreated(ValueNode* node) 608 { 609 BMessage message(MSG_VALUE_NODE_CHANGED); 610 PostMessage(&message); 611 } 612 613 614 void 615 CliContext::ValueNodeChildrenDeleted(ValueNode* node) 616 { 617 BMessage message(MSG_VALUE_NODE_CHANGED); 618 PostMessage(&message); 619 } 620 621 622 void 623 CliContext::ValueNodeValueChanged(ValueNode* oldNode) 624 { 625 BMessage message(MSG_VALUE_NODE_CHANGED); 626 PostMessage(&message); 627 } 628 629 630 /*static*/ const char* 631 CliContext::_GetPrompt(EditLine* editLine) 632 { 633 return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL; 634 } 635 636 637 void 638 CliContext::_WaitForEvent(uint32 event) { 639 if (fTerminating) 640 return; 641 642 do { 643 fLock.Unlock(); 644 while (acquire_sem(fWaitForEventSemaphore) == B_INTERRUPTED) { 645 } 646 fLock.Lock(); 647 release_sem(fWaitForEventSemaphore); 648 } while (fEventOccurred != event && !fTerminating); 649 } 650 651