2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5 //*****************************************************************************
6 // File: breakpoint.cpp
10 //*****************************************************************************
13 /* ------------------------------------------------------------------------- *
15 * ------------------------------------------------------------------------- */
17 CordbBreakpoint::CordbBreakpoint(CordbProcess * pProcess, CordbBreakpointType bpType)
18 : CordbBase(pProcess, 0, enumCordbBreakpoint),
19 m_active(false), m_type(bpType)
23 // Neutered by CordbAppDomain
24 void CordbBreakpoint::Neuter()
26 m_pAppDomain = NULL; // clear ref
30 HRESULT CordbBreakpoint::QueryInterface(REFIID id, void **pInterface)
32 if (id == IID_ICorDebugBreakpoint)
34 *pInterface = static_cast<ICorDebugBreakpoint*>(this);
36 else if (id == IID_IUnknown)
38 *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugBreakpoint*>(this));
49 HRESULT CordbBreakpoint::BaseIsActive(BOOL *pbActive)
51 *pbActive = m_active ? TRUE : FALSE;
56 /* ------------------------------------------------------------------------- *
57 * Function Breakpoint class
58 * ------------------------------------------------------------------------- */
60 CordbFunctionBreakpoint::CordbFunctionBreakpoint(CordbCode *code,
62 : CordbBreakpoint(code->GetProcess(), CBT_FUNCTION),
63 m_code(code), m_offset(offset)
65 // Remember the app domain we came from so that breakpoints can be
66 // deactivated from within the ExitAppdomain callback.
67 m_pAppDomain = m_code->GetAppDomain();
68 _ASSERTE(m_pAppDomain != NULL);
71 CordbFunctionBreakpoint::~CordbFunctionBreakpoint()
73 // @todo- eventually get CordbFunctionBreakpoint rooted and enable this.
74 //_ASSERTE(this->IsNeutered());
75 //_ASSERTE(m_code == NULL);
78 void CordbFunctionBreakpoint::Neuter()
81 CordbBreakpoint::Neuter();
84 HRESULT CordbFunctionBreakpoint::QueryInterface(REFIID id, void **pInterface)
86 if (id == IID_ICorDebugFunctionBreakpoint)
88 *pInterface = static_cast<ICorDebugFunctionBreakpoint*>(this);
92 // Not looking for a function breakpoint? See if the base class handles
93 // this interface. (issue 143976)
94 return CordbBreakpoint::QueryInterface(id, pInterface);
101 HRESULT CordbFunctionBreakpoint::GetFunction(ICorDebugFunction **ppFunction)
103 PUBLIC_API_ENTRY(this);
104 FAIL_IF_NEUTERED(this);
105 VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugFunction **);
109 return CORDBG_E_PROCESS_TERMINATED;
111 if (m_code->IsNeutered())
113 return CORDBG_E_CODE_NOT_AVAILABLE;
116 *ppFunction = static_cast<ICorDebugFunction *> (m_code->GetFunction());
117 (*ppFunction)->AddRef();
122 // m_id is actually a LSPTR_BREAKPOINT. Get it as a type-safe member.
123 LSPTR_BREAKPOINT CordbFunctionBreakpoint::GetLsPtrBP()
130 HRESULT CordbFunctionBreakpoint::GetOffset(ULONG32 *pnOffset)
132 //REVISIT_TODO: is this casting correct for ia64?
133 PUBLIC_API_ENTRY(this);
134 FAIL_IF_NEUTERED(this);
135 VALIDATE_POINTER_TO_OBJECT(pnOffset, SIZE_T *);
137 *pnOffset = (ULONG32)m_offset;
142 //---------------------------------------------------------------------------------------
144 // Activates or removes a breakpoint
147 // fActivate - TRUE if to activate the breakpoint, else FALSE.
150 // S_OK if successful, else a specific error code detailing the type of failure.
152 //---------------------------------------------------------------------------------------
153 HRESULT CordbFunctionBreakpoint::Activate(BOOL fActivate)
155 PUBLIC_REENTRANT_API_ENTRY(this);
156 OK_IF_NEUTERED(this); // we'll check again later
158 if (fActivate == (m_active == true) )
163 // For backwards compat w/ everett, we let the other error codes
164 // take precedence over neutering error codes.
165 if ((m_code == NULL) || this->IsNeutered())
167 return CORDBG_E_PROCESS_TERMINATED;
171 ATT_ALLOW_LIVE_DO_STOPGO(GetProcess());
173 // For legacy, check this error condition. We must do this under the stop-go lock to ensure
174 // that the m_code object was not deleted out from underneath us.
176 // 6/23/09 - This isn't just for legacy anymore, collectible types should be able to hit this
177 // by unloading the module containing the code this breakpoint is bound to.
178 if (m_code->IsNeutered())
180 return CORDBG_E_CODE_NOT_AVAILABLE;
185 // <REVISIT_TODO>@todo: when we implement module and value breakpoints, then
186 // we'll want to factor some of this code out.</REVISIT_TODO>
188 CordbProcess * pProcess = GetProcess();
190 RSLockHolder lockHolder(pProcess->GetProcessLock());
191 pProcess->ClearPatchTable(); // if we add something, then the right side
192 // view of the patch table is no longer valid
194 DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE);
196 CordbAppDomain * pAppDomain = GetAppDomain();
197 _ASSERTE (pAppDomain != NULL);
201 pProcess->InitIPCEvent(pEvent, DB_IPCE_BREAKPOINT_ADD, true, pAppDomain->GetADToken());
203 pEvent->BreakpointData.funcMetadataToken = m_code->GetMetadataToken();
204 pEvent->BreakpointData.vmDomainFile = m_code->GetModule()->GetRuntimeDomainFile();
205 pEvent->BreakpointData.encVersion = m_code->GetVersion();
207 BOOL fIsIL = m_code->IsIL();
209 pEvent->BreakpointData.isIL = fIsIL ? true : false;
210 pEvent->BreakpointData.offset = m_offset;
213 pEvent->BreakpointData.nativeCodeMethodDescToken = pEvent->BreakpointData.nativeCodeMethodDescToken.NullPtr();
217 pEvent->BreakpointData.nativeCodeMethodDescToken =
218 (m_code.GetValue()->AsNativeCode())->GetVMNativeCodeMethodDescToken().ToLsPtr();
221 // Note: we're sending a two-way event, so it blocks here
222 // until the breakpoint is really added and the reply event is
223 // copied over the event we sent.
224 lockHolder.Release();
225 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
226 lockHolder.Acquire();
228 hr = WORST_HR(hr, pEvent->hr);
236 m_id = LsPtrToCookie(pEvent->BreakpointData.breakpointToken);
238 // If we weren't able to allocate the BP, we should have set the
239 // hr on the left side.
243 pAppDomain->m_breakpoints.AddBase(this);
246 // Continue called automatically by StopContinueHolder
250 _ASSERTE (pAppDomain != NULL);
252 if (pProcess->IsSafeToSendEvents())
254 pProcess->InitIPCEvent(pEvent, DB_IPCE_BREAKPOINT_REMOVE, false, pAppDomain->GetADToken());
256 pEvent->BreakpointData.breakpointToken = GetLsPtrBP();
258 lockHolder.Release();
259 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
260 lockHolder.Acquire();
262 hr = WORST_HR(hr, pEvent->hr);
266 hr = CORDBHRFromProcessState(pProcess, pAppDomain);
269 pAppDomain->m_breakpoints.RemoveBase(LsPtrToCookie(GetLsPtrBP()));
276 void CordbFunctionBreakpoint::Disconnect()
281 /* ------------------------------------------------------------------------- *
283 * ------------------------------------------------------------------------- */
285 CordbStepper::CordbStepper(CordbThread *thread, CordbFrame *frame)
286 : CordbBase(thread->GetProcess(), 0, enumCordbStepper),
287 m_thread(thread), m_frame(frame),
288 m_stepperToken(0), m_active(false),
290 m_fIsJMCStepper(false),
291 m_rgfMappingStop(STOP_OTHER_UNMAPPED),
292 m_rgfInterceptStop(INTERCEPT_NONE)
296 HRESULT CordbStepper::QueryInterface(REFIID id, void **pInterface)
298 if (id == IID_ICorDebugStepper)
299 *pInterface = static_cast<ICorDebugStepper *>(this);
300 else if (id == IID_ICorDebugStepper2)
301 *pInterface = static_cast<ICorDebugStepper2 *>(this);
302 else if (id == IID_IUnknown)
303 *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugStepper *>(this));
305 return E_NOINTERFACE;
311 HRESULT CordbStepper::SetRangeIL(BOOL bIL)
313 PUBLIC_API_ENTRY(this);
314 FAIL_IF_NEUTERED(this);
315 m_rangeIL = (bIL != FALSE);
320 HRESULT CordbStepper::SetJMC(BOOL fIsJMCStepper)
322 PUBLIC_API_ENTRY(this);
323 FAIL_IF_NEUTERED(this);
324 // Can't have JMC and stopping with anything else.
325 if (m_rgfMappingStop & STOP_ALL)
328 m_fIsJMCStepper = (fIsJMCStepper != FALSE);
332 HRESULT CordbStepper::IsActive(BOOL *pbActive)
334 PUBLIC_API_ENTRY(this);
335 FAIL_IF_NEUTERED(this);
336 VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *);
338 *pbActive = m_active;
343 // M_id is a ptr to the stepper in the LS process.
344 LSPTR_STEPPER CordbStepper::GetLsPtrStepper()
351 HRESULT CordbStepper::Deactivate()
353 PUBLIC_REENTRANT_API_ENTRY(this);
357 FAIL_IF_NEUTERED(this);
359 if (m_thread == NULL)
360 return CORDBG_E_PROCESS_TERMINATED;
363 CordbProcess *process = GetProcess();
364 ATT_ALLOW_LIVE_DO_STOPGO(process);
368 if (!m_active) // another thread may be deactivating (e.g. step complete event)
374 CordbAppDomain *pAppDomain = GetAppDomain();
375 _ASSERTE (pAppDomain != NULL);
377 DebuggerIPCEvent event;
378 process->InitIPCEvent(&event,
381 pAppDomain->GetADToken());
383 event.StepData.stepperToken = GetLsPtrStepper();
386 hr = process->SendIPCEvent(&event, sizeof(DebuggerIPCEvent));
387 hr = WORST_HR(hr, event.hr);
391 process->m_steppers.RemoveBase((ULONG_PTR)m_id);
399 HRESULT CordbStepper::SetInterceptMask(CorDebugIntercept mask)
401 PUBLIC_API_ENTRY(this);
402 FAIL_IF_NEUTERED(this);
403 m_rgfInterceptStop = mask;
407 HRESULT CordbStepper::SetUnmappedStopMask(CorDebugUnmappedStop mask)
409 PUBLIC_API_ENTRY(this);
410 FAIL_IF_NEUTERED(this);
412 // You must be Win32 attached to stop in unmanaged code.
413 if ((mask & STOP_UNMANAGED) && !GetProcess()->IsInteropDebugging())
416 // Limitations on JMC Stepping - if JMC stepping is active,
417 // all other stop masks must be disabled.
418 // The jit can't place JMC probes before the prolog, so if we're
419 // we're JMC stepping, we'll stop after the prolog.
420 // The implementation for JMC stepping also doesn't let us stop in
421 // unmanaged code. (because there are no probes there).
422 // So enforce those implementation limitations here.
429 // @todo- Ensure that we only set valid bits.
432 m_rgfMappingStop = mask;
436 HRESULT CordbStepper::Step(BOOL bStepIn)
438 PUBLIC_API_ENTRY(this);
439 FAIL_IF_NEUTERED(this);
440 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
442 if (m_thread == NULL)
443 return CORDBG_E_PROCESS_TERMINATED;
445 return StepRange(bStepIn, NULL, 0);
448 //---------------------------------------------------------------------------------------
450 // Ships off a step-range command to the left-side. On the next continue the LS will
451 // step across one range at a time.
454 // fStepIn - TRUE if this stepper should execute a step-in, else FALSE
455 // rgRanges - Array of ranges that define a single step.
456 // cRanges - Count of number of elements in rgRanges.
459 // S_OK if the stepper is successfully set-up, else an appropriate error code.
461 HRESULT CordbStepper::StepRange(BOOL fStepIn,
462 COR_DEBUG_STEP_RANGE rgRanges[],
465 PUBLIC_REENTRANT_API_ENTRY(this);
466 FAIL_IF_NEUTERED(this);
467 VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(rgRanges, COR_DEBUG_STEP_RANGE, cRanges, true, true);
469 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
471 if (m_thread == NULL)
473 return CORDBG_E_PROCESS_TERMINATED;
481 // Deactivate the current stepping.
482 // or return an error???
492 // Validate step-ranges. Ranges are exclusive, so end offset
493 // should always be greater than start offset.
494 // Ranges don't have to be sorted.
495 // Zero ranges is ok; though they ought to just call Step() in that case.
496 for (ULONG32 i = 0; i < cRanges; i++)
498 if (rgRanges[i].startOffset >= rgRanges[i].endOffset)
500 STRESS_LOG2(LF_CORDB, LL_INFO10, "Illegal step range. 0x%x-0x%x\n", rgRanges[i].startOffset, rgRanges[i].endOffset);
501 return ErrWrapper(E_INVALIDARG);
505 CordbProcess * pProcess = GetProcess();
511 DebuggerIPCEvent * pEvent = reinterpret_cast<DebuggerIPCEvent *>(_alloca(CorDBIPC_BUFFER_SIZE));
513 pProcess->InitIPCEvent(pEvent, DB_IPCE_STEP, true, GetAppDomain()->GetADToken());
515 pEvent->StepData.vmThreadToken = m_thread->m_vmThreadToken;
516 pEvent->StepData.rgfMappingStop = m_rgfMappingStop;
517 pEvent->StepData.rgfInterceptStop = m_rgfInterceptStop;
518 pEvent->StepData.IsJMCStop = !!m_fIsJMCStepper;
523 pEvent->StepData.frameToken = LEAF_MOST_FRAME;
527 pEvent->StepData.frameToken = m_frame->GetFramePointer();
530 pEvent->StepData.stepIn = (fStepIn != 0);
531 pEvent->StepData.totalRangeCount = cRanges;
532 pEvent->StepData.rangeIL = m_rangeIL;
535 // Send ranges. We may have to send > 1 message.
538 COR_DEBUG_STEP_RANGE * pRangeStart = &(pEvent->StepData.range);
539 COR_DEBUG_STEP_RANGE * pRangeEnd = (reinterpret_cast<COR_DEBUG_STEP_RANGE *> (((BYTE *)pEvent) + CorDBIPC_BUFFER_SIZE)) - 1;
541 int cRangesToGo = cRanges;
545 while (cRangesToGo > 0)
548 // Find the number of ranges we can copy this time thru the loop
552 if (cRangesToGo < (pRangeEnd - pRangeStart))
554 cRangesToCopy = cRangesToGo;
558 cRangesToCopy = (unsigned int)(pRangeEnd - pRangeStart);
562 // Copy the ranges into the IPC block now, 1-by-1
564 int cRangesCopied = 0;
566 while (cRangesCopied != cRangesToCopy)
568 pRangeStart[cRangesCopied] = rgRanges[cRanges - cRangesToGo + cRangesCopied];
572 pEvent->StepData.rangeCount = cRangesCopied;
574 cRangesToGo -= cRangesCopied;
577 // Send step event (two-way event here...)
580 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
582 hr = WORST_HR(hr, pEvent->hr);
593 // Send step event without any ranges (two-way event here...)
596 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
598 hr = WORST_HR(hr, pEvent->hr);
606 m_id = LsPtrToCookie(pEvent->StepData.stepperToken);
608 LOG((LF_CORDB,LL_INFO10000, "CS::SR: m_id:0x%x | 0x%x \n",
610 LsPtrToCookie(pEvent->StepData.stepperToken)));
613 CordbAppDomain *pAppDomain = GetAppDomain();
615 _ASSERTE (pAppDomain != NULL);
619 pProcess->m_steppers.AddBase(this);
627 //---------------------------------------------------------------------------------------
629 // Ships off a step-out command to the left-side. On the next continue the LS will
630 // execute a step-out
633 // S_OK if the stepper is successfully set-up, else an appropriate error code.
635 HRESULT CordbStepper::StepOut()
637 PUBLIC_API_ENTRY(this);
638 FAIL_IF_NEUTERED(this);
639 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
641 if (m_thread == NULL)
643 return CORDBG_E_PROCESS_TERMINATED;
651 // Deactivate the current stepping.
652 // or return an error???
663 CordbProcess * pProcess = GetProcess();
665 // We don't do native step-out.
666 if (pProcess->SupportsVersion(ver_ICorDebugProcess2))
668 if ((m_rgfMappingStop & STOP_UNMANAGED) != 0)
670 return ErrWrapper(CORDBG_E_CANT_INTEROP_STEP_OUT);
678 DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE);
680 pProcess->InitIPCEvent(pEvent, DB_IPCE_STEP_OUT, true, GetAppDomain()->GetADToken());
682 pEvent->StepData.vmThreadToken = m_thread->m_vmThreadToken;
683 pEvent->StepData.rgfMappingStop = m_rgfMappingStop;
684 pEvent->StepData.rgfInterceptStop = m_rgfInterceptStop;
685 pEvent->StepData.IsJMCStop = !!m_fIsJMCStepper;
689 pEvent->StepData.frameToken = LEAF_MOST_FRAME;
693 pEvent->StepData.frameToken = m_frame->GetFramePointer();
696 pEvent->StepData.totalRangeCount = 0;
698 // Note: two-way event here...
699 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
701 hr = WORST_HR(hr, pEvent->hr);
708 m_id = LsPtrToCookie(pEvent->StepData.stepperToken);
711 CordbAppDomain * pAppDomain = GetAppDomain();
713 _ASSERTE (pAppDomain != NULL);
717 pProcess->m_steppers.AddBase(this);