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. 25static CliContext* sCurrentContext; 26 27 28// #pragma mark - Event 29 30 31struct 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 76private: 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 89CliContext::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 115CliContext::~CliContext() 116{ 117 Cleanup(); 118 sCurrentContext = NULL; 119 120 if (fWaitForEventSemaphore >= 0) 121 delete_sem(fWaitForEventSemaphore); 122} 123 124 125status_t 126CliContext::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 172void 173CliContext::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 211void 212CliContext::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 225thread_id 226CliContext::CurrentThreadID() const 227{ 228 AutoLocker<BLocker> locker(fLock); 229 return fCurrentThread != NULL ? fCurrentThread->ID() : -1; 230} 231 232 233void 234CliContext::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 265void 266CliContext::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 279void 280CliContext::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 297status_t 298CliContext::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 334status_t 335CliContext::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 350const char* 351CliContext::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 364void 365CliContext::AddLineToInputHistory(const char* line) 366{ 367 HistEvent historyEvent; 368 history(fHistory, &historyEvent, H_ENTER, line); 369} 370 371 372void 373CliContext::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 384void 385CliContext::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 399void 400CliContext::WaitForEvent(uint32 event) { 401 AutoLocker<BLocker> locker(fLock); 402 _WaitForEvent(event); 403} 404 405 406void 407CliContext::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 505void 506CliContext::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 515void 516CliContext::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 525void 526CliContext::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 535void 536CliContext::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 548void 549CliContext::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 564void 565CliContext::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 577void 578CliContext::CoreFileChanged(const Team::CoreFileChangedEvent& event) 579{ 580 printf("Successfully saved core file to %s\n", 581 event.GetTargetPath()); 582} 583 584 585void 586CliContext::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 597void 598CliContext::ValueNodeChanged(ValueNodeChild* nodeChild, ValueNode* oldNode, 599 ValueNode* newNode) 600{ 601 BMessage message(MSG_VALUE_NODE_CHANGED); 602 PostMessage(&message); 603} 604 605 606void 607CliContext::ValueNodeChildrenCreated(ValueNode* node) 608{ 609 BMessage message(MSG_VALUE_NODE_CHANGED); 610 PostMessage(&message); 611} 612 613 614void 615CliContext::ValueNodeChildrenDeleted(ValueNode* node) 616{ 617 BMessage message(MSG_VALUE_NODE_CHANGED); 618 PostMessage(&message); 619} 620 621 622void 623CliContext::ValueNodeValueChanged(ValueNode* oldNode) 624{ 625 BMessage message(MSG_VALUE_NODE_CHANGED); 626 PostMessage(&message); 627} 628 629 630/*static*/ const char* 631CliContext::_GetPrompt(EditLine* editLine) 632{ 633 return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL; 634} 635 636 637void 638CliContext::_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