1/* 2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2010-2011, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8#include "ThreadHandler.h" 9 10#include <stdio.h> 11 12#include <new> 13 14#include <AutoLocker.h> 15 16#include "Architecture.h" 17#include "BreakpointManager.h" 18#include "CpuState.h" 19#include "DebuggerInterface.h" 20#include "FunctionInstance.h" 21#include "ImageDebugInfo.h" 22#include "InstructionInfo.h" 23#include "Jobs.h" 24#include "MessageCodes.h" 25#include "Register.h" 26#include "SourceCode.h" 27#include "SpecificImageDebugInfo.h" 28#include "StackTrace.h" 29#include "Statement.h" 30#include "Team.h" 31#include "Tracing.h" 32#include "Worker.h" 33 34 35// step modes 36enum { 37 STEP_NONE, 38 STEP_OVER, 39 STEP_INTO, 40 STEP_OUT 41}; 42 43 44ThreadHandler::ThreadHandler(Thread* thread, Worker* worker, 45 DebuggerInterface* debuggerInterface, 46 BreakpointManager* breakpointManager) 47 : 48 fThread(thread), 49 fWorker(worker), 50 fDebuggerInterface(debuggerInterface), 51 fBreakpointManager(breakpointManager), 52 fStepMode(STEP_NONE), 53 fStepStatement(NULL), 54 fBreakpointAddress(0), 55 fPreviousInstructionPointer(0), 56 fPreviousFrameAddress(0), 57 fSingleStepping(false) 58{ 59 fDebuggerInterface->AcquireReference(); 60} 61 62 63ThreadHandler::~ThreadHandler() 64{ 65 _ClearContinuationState(); 66 fDebuggerInterface->ReleaseReference(); 67} 68 69 70void 71ThreadHandler::Init() 72{ 73 fWorker->ScheduleJob(new(std::nothrow) GetThreadStateJob(fDebuggerInterface, 74 fThread)); 75} 76 77 78status_t 79ThreadHandler::SetBreakpointAndRun(target_addr_t address) 80{ 81 status_t error = _InstallTemporaryBreakpoint(address); 82 if (error != B_OK) 83 return error; 84 85 fPreviousInstructionPointer = 0; 86 resume_thread(ThreadID()); 87 // TODO: This should probably better be a DebuggerInterface method, 88 // but this method is used only when debugging a local team anyway. 89 // Pretend "step out" mode, so that the temporary breakpoint hit will not 90 // be ignored. 91 fStepMode = STEP_OUT; 92 fSingleStepping = false; 93 94 return B_OK; 95} 96 97 98bool 99ThreadHandler::HandleThreadDebugged(ThreadDebuggedEvent* event) 100{ 101 return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGED); 102} 103 104 105bool 106ThreadHandler::HandleDebuggerCall(DebuggerCallEvent* event) 107{ 108 BString message; 109 fDebuggerInterface->ReadMemoryString(event->Message(), 1024, message); 110 return _HandleThreadStopped(NULL, THREAD_STOPPED_DEBUGGER_CALL, message); 111} 112 113 114bool 115ThreadHandler::HandleBreakpointHit(BreakpointHitEvent* event) 116{ 117 CpuState* cpuState = event->GetCpuState(); 118 target_addr_t instructionPointer = cpuState->InstructionPointer(); 119 120 TRACE_EVENTS("ThreadHandler::HandleBreakpointHit(): ip: %" B_PRIx64 "\n", 121 instructionPointer); 122 123 // check whether this is a temporary breakpoint we're waiting for 124 if (fBreakpointAddress != 0 && instructionPointer == fBreakpointAddress 125 && fStepMode != STEP_NONE) { 126 if (_HandleBreakpointHitStep(cpuState)) 127 return true; 128 } else { 129 // Might be a user breakpoint, but could as well be a temporary 130 // breakpoint of another thread. 131 AutoLocker<Team> locker(fThread->GetTeam()); 132 Breakpoint* breakpoint = fThread->GetTeam()->BreakpointAtAddress( 133 cpuState->InstructionPointer()); 134 bool continueThread = false; 135 if (breakpoint == NULL) { 136 // spurious breakpoint -- might be a temporary breakpoint, that has 137 // already been uninstalled 138 continueThread = true; 139 } else if (!breakpoint->HasEnabledUserBreakpoint()) { 140 // breakpoint of another thread or one that has been disabled in 141 // the meantime 142 continueThread = true; 143 } 144 145 if (continueThread) { 146 if (fSingleStepping) { 147 // We might have hit a just-installed software breakpoint and 148 // thus haven't stepped at all. Just try again. 149 if (fPreviousInstructionPointer == instructionPointer) { 150 fDebuggerInterface->SingleStepThread(ThreadID()); 151 return true; 152 } 153 154 // That shouldn't happen. Try something reasonable anyway. 155 if (fStepMode != STEP_NONE) { 156 if (_HandleSingleStepStep(cpuState)) 157 return true; 158 } 159 } 160 161 return false; 162 } 163 } 164 165 return _HandleThreadStopped(cpuState, THREAD_STOPPED_BREAKPOINT); 166} 167 168 169bool 170ThreadHandler::HandleWatchpointHit(WatchpointHitEvent* event) 171{ 172 return _HandleThreadStopped(event->GetCpuState(), 173 THREAD_STOPPED_WATCHPOINT); 174} 175 176 177bool 178ThreadHandler::HandleSingleStep(SingleStepEvent* event) 179{ 180 // Check whether we're stepping automatically. 181 if (fStepMode != STEP_NONE) { 182 if (_HandleSingleStepStep(event->GetCpuState())) 183 return true; 184 } 185 186 return _HandleThreadStopped(event->GetCpuState(), 187 THREAD_STOPPED_SINGLE_STEP); 188} 189 190 191bool 192ThreadHandler::HandleExceptionOccurred(ExceptionOccurredEvent* event) 193{ 194 char buffer[256]; 195 get_debug_exception_string(event->Exception(), buffer, sizeof(buffer)); 196 return _HandleThreadStopped(NULL, THREAD_STOPPED_EXCEPTION, buffer); 197} 198 199 200void 201ThreadHandler::HandleThreadAction(uint32 action) 202{ 203 AutoLocker<Team> locker(fThread->GetTeam()); 204 205 if (fThread->State() == THREAD_STATE_UNKNOWN) 206 return; 207 208 // When stop is requested, thread must be running, otherwise stopped. 209 if (action == MSG_THREAD_STOP 210 ? fThread->State() != THREAD_STATE_RUNNING 211 : fThread->State() != THREAD_STATE_STOPPED) { 212 return; 213 } 214 215 // When stepping we need a stack trace. Save it before unsetting the state. 216 CpuState* cpuState = fThread->GetCpuState(); 217 StackTrace* stackTrace = fThread->GetStackTrace(); 218 BReference<CpuState> cpuStateReference(cpuState); 219 BReference<StackTrace> stackTraceReference(stackTrace); 220 221 // When continuing the thread update thread state before actually issuing 222 // the command, since we need to unlock. 223 if (action != MSG_THREAD_STOP) { 224 _SetThreadState(THREAD_STATE_RUNNING, NULL, THREAD_STOPPED_UNKNOWN, 225 BString()); 226 } 227 228 locker.Unlock(); 229 230 switch (action) { 231 case MSG_THREAD_RUN: 232 fStepMode = STEP_NONE; 233 _RunThread(0); 234 return; 235 case MSG_THREAD_STOP: 236 fStepMode = STEP_NONE; 237 fDebuggerInterface->StopThread(ThreadID()); 238 return; 239 case MSG_THREAD_STEP_OVER: 240 case MSG_THREAD_STEP_INTO: 241 case MSG_THREAD_STEP_OUT: 242 break; 243 } 244 245 TRACE_CONTROL("ThreadHandler::HandleThreadAction(MSG_THREAD_STEP_*)\n"); 246 247 // We want to step. We need a stack trace for that purpose. If we don't 248 // have one yet, get it. Start with the CPU state. 249 if (stackTrace == NULL && cpuState == NULL) { 250 if (fDebuggerInterface->GetCpuState(fThread->ID(), cpuState) == B_OK) 251 cpuStateReference.SetTo(cpuState, true); 252 } 253 254 if (stackTrace == NULL && cpuState != NULL) { 255 if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( 256 fThread->GetTeam(), this, cpuState, stackTrace, 0, 1, 257 false, false) == B_OK) { 258 stackTraceReference.SetTo(stackTrace, true); 259 } 260 } 261 262 if (stackTrace == NULL || stackTrace->CountFrames() == 0) { 263 _StepFallback(); 264 return; 265 } 266 267 StackFrame* frame = stackTrace->FrameAt(0); 268 269 TRACE_CONTROL(" ip: %#" B_PRIx64 "\n", frame->InstructionPointer()); 270 271 // When the thread is in a syscall, do the same for all step kinds: Stop it 272 // when it returns by means of a breakpoint. 273 if (frame->Type() == STACK_FRAME_TYPE_SYSCALL) { 274 // set a breakpoint at the CPU state's instruction pointer (points to 275 // the return address, unlike the stack frame's instruction pointer) 276// TODO: This is doesn't work correctly anymore. When stepping over a "syscall" 277// instruction the thread is stopped twice. The after the first step the PC is 278// incorrectly shown at the "syscall" instruction. Then we step again and are 279// stopped at the temporary breakpoint after the "syscall" instruction. There 280// are two problems. The first one is that we don't (cannot?) discriminate 281// between the thread being in a syscall (like in a blocking syscall) and the 282// thread having been stopped (or singled-stepped) at the end of the syscall. 283// The second issue is that the temporary breakpoint is probably not necessary 284// anymore, since single-stepping over "syscall" instructions should just work 285// as expected. 286 status_t error = _InstallTemporaryBreakpoint( 287 frame->GetCpuState()->InstructionPointer()); 288 if (error != B_OK) { 289 _StepFallback(); 290 return; 291 } 292 293 fStepMode = STEP_OUT; 294 _RunThread(frame->GetCpuState()->InstructionPointer()); 295 return; 296 } 297 298 // For "step out" just set a temporary breakpoint on the return address. 299 if (action == MSG_THREAD_STEP_OUT) { 300 status_t error = _InstallTemporaryBreakpoint(frame->ReturnAddress()); 301 if (error != B_OK) { 302 _StepFallback(); 303 return; 304 } 305 fPreviousFrameAddress = frame->FrameAddress(); 306 fStepMode = STEP_OUT; 307 _RunThread(frame->GetCpuState()->InstructionPointer()); 308 return; 309 } 310 311 // For "step in" and "step over" we also need the source code statement at 312 // the current instruction pointer. 313 fStepStatement = _GetStatementAtInstructionPointer(frame); 314 if (fStepStatement == NULL) { 315 _StepFallback(); 316 return; 317 } 318 319 TRACE_CONTROL(" statement: %#" B_PRIx64 " - %#" B_PRIx64 "\n", 320 fStepStatement->CoveringAddressRange().Start(), 321 fStepStatement->CoveringAddressRange().End()); 322 323 if (action == MSG_THREAD_STEP_INTO) { 324 // step into 325 fStepMode = STEP_INTO; 326 _SingleStepThread(frame->GetCpuState()->InstructionPointer()); 327 } else { 328 fPreviousFrameAddress = frame->FrameAddress(); 329 // step over 330 fStepMode = STEP_OVER; 331 if (!_DoStepOver(frame->GetCpuState())) 332 _StepFallback(); 333 } 334} 335 336 337void 338ThreadHandler::HandleThreadStateChanged() 339{ 340 AutoLocker<Team> locker(fThread->GetTeam()); 341 342 // cancel jobs for this thread 343 fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_CPU_STATE)); 344 fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_STACK_TRACE)); 345 346 // If the thread is stopped and has no CPU state yet, schedule a job. 347 if (fThread->State() == THREAD_STATE_STOPPED 348 && fThread->GetCpuState() == NULL) { 349 fWorker->ScheduleJob( 350 new(std::nothrow) GetCpuStateJob(fDebuggerInterface, fThread)); 351 } 352} 353 354 355void 356ThreadHandler::HandleCpuStateChanged() 357{ 358 AutoLocker<Team> locker(fThread->GetTeam()); 359 360 // cancel stack trace job for this thread 361 fWorker->AbortJob(SimpleJobKey(fThread, JOB_TYPE_GET_STACK_TRACE)); 362 363 // If the thread has a CPU state, but no stack trace yet, schedule a job. 364 if (fThread->GetCpuState() != NULL && fThread->GetStackTrace() == NULL) { 365 fWorker->ScheduleJob( 366 new(std::nothrow) GetStackTraceJob(fDebuggerInterface, 367 fDebuggerInterface->GetArchitecture(), fThread)); 368 } 369} 370 371 372void 373ThreadHandler::HandleStackTraceChanged() 374{ 375} 376 377 378status_t 379ThreadHandler::GetImageDebugInfo(Image* image, ImageDebugInfo*& _info) 380{ 381 AutoLocker<Team> teamLocker(fThread->GetTeam()); 382 383 if (image->GetImageDebugInfo() != NULL) { 384 _info = image->GetImageDebugInfo(); 385 _info->AcquireReference(); 386 return B_OK; 387 } 388 389 // Let's be lazy. If the image debug info has not been loaded yet, the user 390 // can't have seen any source code either. 391 return B_ENTRY_NOT_FOUND; 392} 393 394 395bool 396ThreadHandler::_HandleThreadStopped(CpuState* cpuState, uint32 stoppedReason, 397 const BString& stoppedReasonInfo) 398{ 399 _ClearContinuationState(); 400 401 AutoLocker<Team> locker(fThread->GetTeam()); 402 403 _SetThreadState(THREAD_STATE_STOPPED, cpuState, stoppedReason, 404 stoppedReasonInfo); 405 406 return true; 407} 408 409 410void 411ThreadHandler::_SetThreadState(uint32 state, CpuState* cpuState, 412 uint32 stoppedReason, const BString& stoppedReasonInfo) 413{ 414 fThread->SetState(state, stoppedReason, stoppedReasonInfo); 415 fThread->SetCpuState(cpuState); 416} 417 418 419Statement* 420ThreadHandler::_GetStatementAtInstructionPointer(StackFrame* frame) 421{ 422 AutoLocker<Team> locker(fThread->GetTeam()); 423 424 FunctionInstance* functionInstance = frame->Function(); 425 if (functionInstance == NULL) 426 return NULL; 427 FunctionDebugInfo* function = functionInstance->GetFunctionDebugInfo(); 428 429 // If there's source code attached to the function, we can just get the 430 // statement. 431// SourceCode* sourceCode = function->GetSourceCode(); 432// if (sourceCode != NULL) { 433// Statement* statement = sourceCode->StatementAtAddress( 434// frame->InstructionPointer()); 435// if (statement != NULL) 436// statement->AcquireReference(); 437// return statement; 438// } 439 440 locker.Unlock(); 441 442 // We need to get the statement from the debug info of the function. 443 Statement* statement; 444 if (function->GetSpecificImageDebugInfo()->GetStatement(function, 445 frame->InstructionPointer(), statement) != B_OK) { 446 return NULL; 447 } 448 449 return statement; 450} 451 452 453void 454ThreadHandler::_StepFallback() 455{ 456 fStepMode = STEP_NONE; 457 _SingleStepThread(0); 458} 459 460 461bool 462ThreadHandler::_DoStepOver(CpuState* cpuState) 463{ 464 TRACE_CONTROL("ThreadHandler::_DoStepOver()\n"); 465 466 // The basic strategy is to single-step out of the statement like for 467 // "step into", only we have to avoid stepping into subroutines. Hence we 468 // check whether the current instruction is a subroutine call. If not, we 469 // just single-step, otherwise we set a breakpoint after the instruction. 470 InstructionInfo info; 471 if (fDebuggerInterface->GetArchitecture()->GetInstructionInfo( 472 cpuState->InstructionPointer(), info, cpuState) != B_OK) { 473 TRACE_CONTROL(" failed to get instruction info\n"); 474 return false; 475 } 476 477 if (info.Type() != INSTRUCTION_TYPE_SUBROUTINE_CALL) { 478 _SingleStepThread(cpuState->InstructionPointer()); 479 480 TRACE_CONTROL(" not a subroutine call\n"); 481 return true; 482 } 483 484 TRACE_CONTROL(" subroutine call -- installing breakpoint at address " 485 "%#" B_PRIx64 "\n", info.Address() + info.Size()); 486 487 fThread->SetExecutedSubroutine(info.TargetAddress()); 488 if (_InstallTemporaryBreakpoint(info.Address() + info.Size()) != B_OK) 489 return false; 490 491 _RunThread(cpuState->InstructionPointer()); 492 return true; 493} 494 495 496status_t 497ThreadHandler::_InstallTemporaryBreakpoint(target_addr_t address) 498{ 499 _UninstallTemporaryBreakpoint(); 500 501 status_t error = fBreakpointManager->InstallTemporaryBreakpoint(address, 502 this); 503 if (error != B_OK) 504 return error; 505 506 fBreakpointAddress = address; 507 return B_OK; 508} 509 510 511void 512ThreadHandler::_UninstallTemporaryBreakpoint() 513{ 514 if (fBreakpointAddress == 0) 515 return; 516 517 fBreakpointManager->UninstallTemporaryBreakpoint(fBreakpointAddress, this); 518 fBreakpointAddress = 0; 519} 520 521 522void 523ThreadHandler::_ClearContinuationState() 524{ 525 _UninstallTemporaryBreakpoint(); 526 527 if (fStepStatement != NULL) { 528 fStepStatement->ReleaseReference(); 529 fStepStatement = NULL; 530 } 531 532 fStepMode = STEP_NONE; 533 fSingleStepping = false; 534} 535 536 537void 538ThreadHandler::_RunThread(target_addr_t instructionPointer) 539{ 540 fPreviousInstructionPointer = instructionPointer; 541 fDebuggerInterface->ContinueThread(ThreadID()); 542 fSingleStepping = false; 543} 544 545 546void 547ThreadHandler::_SingleStepThread(target_addr_t instructionPointer) 548{ 549 fPreviousInstructionPointer = instructionPointer; 550 fDebuggerInterface->SingleStepThread(ThreadID()); 551 fSingleStepping = true; 552} 553 554 555bool 556ThreadHandler::_HandleBreakpointHitStep(CpuState* cpuState) 557{ 558 // in any case uninstall the temporary breakpoint 559 _UninstallTemporaryBreakpoint(); 560 561 switch (fStepMode) { 562 case STEP_OVER: 563 { 564 StackTrace* stackTrace = fThread->GetStackTrace(); 565 BReference<StackTrace> stackTraceReference(stackTrace); 566 567 if (stackTrace == NULL && cpuState != NULL) { 568 if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( 569 fThread->GetTeam(), this, cpuState, stackTrace, 0, 1, 570 false, false) == B_OK) { 571 stackTraceReference.SetTo(stackTrace, true); 572 } 573 } 574 if (stackTrace != NULL) { 575 StackFrame* frame = stackTrace->FrameAt(0); 576 // If we're not in the same frame we started in, 577 // keep executing. 578 if (frame != NULL && fPreviousFrameAddress 579 != frame->FrameAddress()) { 580 status_t error = _InstallTemporaryBreakpoint( 581 cpuState->InstructionPointer()); 582 if (error != B_OK) 583 _StepFallback(); 584 else 585 _RunThread(cpuState->InstructionPointer()); 586 return true; 587 } 588 } 589 590 // If we're still in the statement, we continue single-stepping, 591 // otherwise we're done. 592 if (fStepStatement->ContainsAddress( 593 cpuState->InstructionPointer())) { 594 if (!_DoStepOver(cpuState)) 595 _StepFallback(); 596 return true; 597 } 598 fPreviousFrameAddress = 0; 599 return false; 600 } 601 602 case STEP_INTO: 603 // Should never happen -- we don't set a breakpoint in this case. 604 return false; 605 606 case STEP_OUT: 607 { 608 // That's the return address, so we're done in theory, 609 // unless we're a recursive function. Check if we've actually 610 // exited the previous stack frame or not. 611 fThread->SetExecutedSubroutine(cpuState->InstructionPointer()); 612 target_addr_t framePointer = cpuState->StackFramePointer(); 613 bool hasExitedFrame = fDebuggerInterface->GetArchitecture() 614 ->StackGrowthDirection() == STACK_GROWTH_DIRECTION_POSITIVE 615 ? framePointer < fPreviousFrameAddress 616 : framePointer > fPreviousFrameAddress; 617 618 if (!hasExitedFrame) { 619 status_t error = _InstallTemporaryBreakpoint( 620 cpuState->InstructionPointer()); 621 if (error != B_OK) 622 _StepFallback(); 623 else 624 _RunThread(cpuState->InstructionPointer()); 625 return true; 626 } 627 fPreviousFrameAddress = 0; 628 } 629 630 default: 631 return false; 632 } 633} 634 635 636bool 637ThreadHandler::_HandleSingleStepStep(CpuState* cpuState) 638{ 639 TRACE_CONTROL("ThreadHandler::_HandleSingleStepStep(): ip: %" B_PRIx64 "\n", 640 cpuState->InstructionPointer()); 641 642 switch (fStepMode) { 643 case STEP_INTO: 644 { 645 // We continue stepping as long as we're in the statement. 646 if (fStepStatement->ContainsAddress(cpuState->InstructionPointer())) { 647 _SingleStepThread(cpuState->InstructionPointer()); 648 return true; 649 } 650 651 StackTrace* stackTrace = fThread->GetStackTrace(); 652 BReference<StackTrace> stackTraceReference(stackTrace); 653 654 if (stackTrace == NULL && cpuState != NULL) { 655 if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( 656 fThread->GetTeam(), this, cpuState, stackTrace, 0, 1, 657 false, false) == B_OK) { 658 stackTraceReference.SetTo(stackTrace, true); 659 } 660 } 661 662 if (stackTrace != NULL) { 663 StackFrame* frame = stackTrace->FrameAt(0); 664 Image* image = frame->GetImage(); 665 ImageDebugInfo* info = NULL; 666 if (GetImageDebugInfo(image, info) != B_OK) 667 return false; 668 669 BReference<ImageDebugInfo>(info, true); 670 if (info->GetAddressSectionType( 671 cpuState->InstructionPointer()) 672 == ADDRESS_SECTION_TYPE_PLT) { 673 _SingleStepThread(cpuState->InstructionPointer()); 674 return true; 675 } 676 } 677 return false; 678 } 679 680 case STEP_OVER: 681 { 682 // If we have stepped out of the statement, we're done. 683 if (!fStepStatement->ContainsAddress(cpuState->InstructionPointer())) { 684 StackTrace* stackTrace = fThread->GetStackTrace(); 685 BReference<StackTrace> stackTraceReference(stackTrace); 686 if (stackTrace == NULL && cpuState != NULL) { 687 if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( 688 fThread->GetTeam(), this, cpuState, stackTrace, 0, 689 1, false, false) == B_OK) { 690 stackTraceReference.SetTo(stackTrace, true); 691 } 692 } 693 694 if (stackTrace != NULL && stackTrace->FrameAt(0) 695 ->FrameAddress() != fPreviousFrameAddress) { 696 fThread->SetExecutedSubroutine( 697 cpuState->InstructionPointer()); 698 } 699 700 return false; 701 } 702 return _DoStepOver(cpuState); 703 } 704 705 case STEP_OUT: 706 // We never single-step in this case. 707 default: 708 return false; 709 } 710} 711