1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //*****************************************************************************
5 // File: breakpoint.cpp
9 //*****************************************************************************
12 /* ------------------------------------------------------------------------- *
14 * ------------------------------------------------------------------------- */
16 CordbBreakpoint::CordbBreakpoint(CordbProcess * pProcess, CordbBreakpointType bpType)
17 : CordbBase(pProcess, 0, enumCordbBreakpoint),
18 m_active(false), m_pAppDomain(NULL), m_type(bpType)
22 // Neutered by CordbAppDomain
23 void CordbBreakpoint::Neuter()
25 m_pAppDomain = NULL; // clear ref
29 HRESULT CordbBreakpoint::QueryInterface(REFIID id, void **pInterface)
31 if (id == IID_ICorDebugBreakpoint)
33 *pInterface = static_cast<ICorDebugBreakpoint*>(this);
35 else if (id == IID_IUnknown)
37 *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugBreakpoint*>(this));
48 HRESULT CordbBreakpoint::BaseIsActive(BOOL *pbActive)
50 *pbActive = m_active ? TRUE : FALSE;
55 /* ------------------------------------------------------------------------- *
56 * Function Breakpoint class
57 * ------------------------------------------------------------------------- */
59 CordbFunctionBreakpoint::CordbFunctionBreakpoint(CordbCode *code,
61 : CordbBreakpoint(code->GetProcess(), CBT_FUNCTION),
62 m_code(code), m_offset(offset)
64 // Remember the app domain we came from so that breakpoints can be
65 // deactivated from within the ExitAppdomain callback.
66 m_pAppDomain = m_code->GetAppDomain();
67 _ASSERTE(m_pAppDomain != NULL);
70 CordbFunctionBreakpoint::~CordbFunctionBreakpoint()
72 // @todo- eventually get CordbFunctionBreakpoint rooted and enable this.
73 //_ASSERTE(this->IsNeutered());
74 //_ASSERTE(m_code == NULL);
77 void CordbFunctionBreakpoint::Neuter()
80 CordbBreakpoint::Neuter();
83 HRESULT CordbFunctionBreakpoint::QueryInterface(REFIID id, void **pInterface)
85 if (id == IID_ICorDebugFunctionBreakpoint)
87 *pInterface = static_cast<ICorDebugFunctionBreakpoint*>(this);
91 // Not looking for a function breakpoint? See if the base class handles
92 // this interface. (issue 143976)
93 return CordbBreakpoint::QueryInterface(id, pInterface);
100 HRESULT CordbFunctionBreakpoint::GetFunction(ICorDebugFunction **ppFunction)
102 PUBLIC_API_ENTRY(this);
103 FAIL_IF_NEUTERED(this);
104 VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugFunction **);
108 return CORDBG_E_PROCESS_TERMINATED;
110 if (m_code->IsNeutered())
112 return CORDBG_E_CODE_NOT_AVAILABLE;
115 *ppFunction = static_cast<ICorDebugFunction *> (m_code->GetFunction());
116 (*ppFunction)->AddRef();
121 // m_id is actually a LSPTR_BREAKPOINT. Get it as a type-safe member.
122 LSPTR_BREAKPOINT CordbFunctionBreakpoint::GetLsPtrBP()
129 HRESULT CordbFunctionBreakpoint::GetOffset(ULONG32 *pnOffset)
131 //REVISIT_TODO: is this casting correct for ia64?
132 PUBLIC_API_ENTRY(this);
133 FAIL_IF_NEUTERED(this);
134 VALIDATE_POINTER_TO_OBJECT(pnOffset, SIZE_T *);
136 *pnOffset = (ULONG32)m_offset;
141 //---------------------------------------------------------------------------------------
143 // Activates or removes a breakpoint
146 // fActivate - TRUE if to activate the breakpoint, else FALSE.
149 // S_OK if successful, else a specific error code detailing the type of failure.
151 //---------------------------------------------------------------------------------------
152 HRESULT CordbFunctionBreakpoint::Activate(BOOL fActivate)
154 PUBLIC_REENTRANT_API_ENTRY(this);
155 OK_IF_NEUTERED(this); // we'll check again later
157 if (fActivate == (m_active == true) )
162 // For backwards compat w/ everett, we let the other error codes
163 // take precedence over neutering error codes.
164 if ((m_code == NULL) || this->IsNeutered())
166 return CORDBG_E_PROCESS_TERMINATED;
170 ATT_ALLOW_LIVE_DO_STOPGO(GetProcess());
172 // For legacy, check this error condition. We must do this under the stop-go lock to ensure
173 // that the m_code object was not deleted out from underneath us.
175 // 6/23/09 - This isn't just for legacy anymore, collectible types should be able to hit this
176 // by unloading the module containing the code this breakpoint is bound to.
177 if (m_code->IsNeutered())
179 return CORDBG_E_CODE_NOT_AVAILABLE;
184 // <REVISIT_TODO>@todo: when we implement module and value breakpoints, then
185 // we'll want to factor some of this code out.</REVISIT_TODO>
187 CordbProcess * pProcess = GetProcess();
189 RSLockHolder lockHolder(pProcess->GetProcessLock());
190 pProcess->ClearPatchTable(); // if we add something, then the right side
191 // view of the patch table is no longer valid
193 DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE);
195 CordbAppDomain * pAppDomain = GetAppDomain();
196 _ASSERTE (pAppDomain != NULL);
200 pProcess->InitIPCEvent(pEvent, DB_IPCE_BREAKPOINT_ADD, true, pAppDomain->GetADToken());
202 pEvent->BreakpointData.funcMetadataToken = m_code->GetMetadataToken();
203 pEvent->BreakpointData.vmDomainFile = m_code->GetModule()->GetRuntimeDomainFile();
204 pEvent->BreakpointData.encVersion = m_code->GetVersion();
206 BOOL fIsIL = m_code->IsIL();
208 pEvent->BreakpointData.isIL = fIsIL ? true : false;
209 pEvent->BreakpointData.offset = m_offset;
212 pEvent->BreakpointData.nativeCodeMethodDescToken = pEvent->BreakpointData.nativeCodeMethodDescToken.NullPtr();
216 pEvent->BreakpointData.nativeCodeMethodDescToken =
217 (m_code.GetValue()->AsNativeCode())->GetVMNativeCodeMethodDescToken().ToLsPtr();
220 // Note: we're sending a two-way event, so it blocks here
221 // until the breakpoint is really added and the reply event is
222 // copied over the event we sent.
223 lockHolder.Release();
224 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
225 lockHolder.Acquire();
227 hr = WORST_HR(hr, pEvent->hr);
235 m_id = LsPtrToCookie(pEvent->BreakpointData.breakpointToken);
237 // If we weren't able to allocate the BP, we should have set the
238 // hr on the left side.
242 pAppDomain->m_breakpoints.AddBase(this);
245 // Continue called automatically by StopContinueHolder
249 _ASSERTE (pAppDomain != NULL);
251 if (pProcess->IsSafeToSendEvents())
253 pProcess->InitIPCEvent(pEvent, DB_IPCE_BREAKPOINT_REMOVE, false, pAppDomain->GetADToken());
255 pEvent->BreakpointData.breakpointToken = GetLsPtrBP();
257 lockHolder.Release();
258 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
259 lockHolder.Acquire();
261 hr = WORST_HR(hr, pEvent->hr);
265 hr = CORDBHRFromProcessState(pProcess, pAppDomain);
268 pAppDomain->m_breakpoints.RemoveBase(LsPtrToCookie(GetLsPtrBP()));
275 void CordbFunctionBreakpoint::Disconnect()
280 /* ------------------------------------------------------------------------- *
282 * ------------------------------------------------------------------------- */
284 CordbStepper::CordbStepper(CordbThread *thread, CordbFrame *frame)
285 : CordbBase(thread->GetProcess(), 0, enumCordbStepper),
286 m_thread(thread), m_frame(frame),
287 m_stepperToken(0), m_active(false),
289 m_fIsJMCStepper(false),
290 m_rgfMappingStop(STOP_OTHER_UNMAPPED),
291 m_rgfInterceptStop(INTERCEPT_NONE)
295 HRESULT CordbStepper::QueryInterface(REFIID id, void **pInterface)
297 if (id == IID_ICorDebugStepper)
298 *pInterface = static_cast<ICorDebugStepper *>(this);
299 else if (id == IID_ICorDebugStepper2)
300 *pInterface = static_cast<ICorDebugStepper2 *>(this);
301 else if (id == IID_IUnknown)
302 *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugStepper *>(this));
304 return E_NOINTERFACE;
310 HRESULT CordbStepper::SetRangeIL(BOOL bIL)
312 PUBLIC_API_ENTRY(this);
313 FAIL_IF_NEUTERED(this);
314 m_rangeIL = (bIL != FALSE);
319 HRESULT CordbStepper::SetJMC(BOOL fIsJMCStepper)
321 PUBLIC_API_ENTRY(this);
322 FAIL_IF_NEUTERED(this);
323 // Can't have JMC and stopping with anything else.
324 if (m_rgfMappingStop & STOP_ALL)
327 m_fIsJMCStepper = (fIsJMCStepper != FALSE);
331 HRESULT CordbStepper::IsActive(BOOL *pbActive)
333 PUBLIC_API_ENTRY(this);
334 FAIL_IF_NEUTERED(this);
335 VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *);
337 *pbActive = m_active;
342 // M_id is a ptr to the stepper in the LS process.
343 LSPTR_STEPPER CordbStepper::GetLsPtrStepper()
350 HRESULT CordbStepper::Deactivate()
352 PUBLIC_REENTRANT_API_ENTRY(this);
356 FAIL_IF_NEUTERED(this);
358 if (m_thread == NULL)
359 return CORDBG_E_PROCESS_TERMINATED;
362 CordbProcess *process = GetProcess();
363 ATT_ALLOW_LIVE_DO_STOPGO(process);
367 if (!m_active) // another thread may be deactivating (e.g. step complete event)
373 CordbAppDomain *pAppDomain = GetAppDomain();
374 _ASSERTE (pAppDomain != NULL);
376 DebuggerIPCEvent event;
377 process->InitIPCEvent(&event,
380 pAppDomain->GetADToken());
382 event.StepData.stepperToken = GetLsPtrStepper();
385 hr = process->SendIPCEvent(&event, sizeof(DebuggerIPCEvent));
386 hr = WORST_HR(hr, event.hr);
390 process->m_steppers.RemoveBase((ULONG_PTR)m_id);
398 HRESULT CordbStepper::SetInterceptMask(CorDebugIntercept mask)
400 PUBLIC_API_ENTRY(this);
401 FAIL_IF_NEUTERED(this);
402 m_rgfInterceptStop = mask;
406 HRESULT CordbStepper::SetUnmappedStopMask(CorDebugUnmappedStop mask)
408 PUBLIC_API_ENTRY(this);
409 FAIL_IF_NEUTERED(this);
411 // You must be Win32 attached to stop in unmanaged code.
412 if ((mask & STOP_UNMANAGED) && !GetProcess()->IsInteropDebugging())
415 // Limitations on JMC Stepping - if JMC stepping is active,
416 // all other stop masks must be disabled.
417 // The jit can't place JMC probes before the prolog, so if we're
418 // we're JMC stepping, we'll stop after the prolog.
419 // The implementation for JMC stepping also doesn't let us stop in
420 // unmanaged code. (because there are no probes there).
421 // So enforce those implementation limitations here.
428 // @todo- Ensure that we only set valid bits.
431 m_rgfMappingStop = mask;
435 HRESULT CordbStepper::Step(BOOL bStepIn)
437 PUBLIC_API_ENTRY(this);
438 FAIL_IF_NEUTERED(this);
439 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
441 if (m_thread == NULL)
442 return CORDBG_E_PROCESS_TERMINATED;
444 return StepRange(bStepIn, NULL, 0);
447 //---------------------------------------------------------------------------------------
449 // Ships off a step-range command to the left-side. On the next continue the LS will
450 // step across one range at a time.
453 // fStepIn - TRUE if this stepper should execute a step-in, else FALSE
454 // rgRanges - Array of ranges that define a single step.
455 // cRanges - Count of number of elements in rgRanges.
458 // S_OK if the stepper is successfully set-up, else an appropriate error code.
460 HRESULT CordbStepper::StepRange(BOOL fStepIn,
461 COR_DEBUG_STEP_RANGE rgRanges[],
464 PUBLIC_REENTRANT_API_ENTRY(this);
465 FAIL_IF_NEUTERED(this);
466 VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(rgRanges, COR_DEBUG_STEP_RANGE, cRanges, true, true);
468 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
470 if (m_thread == NULL)
472 return CORDBG_E_PROCESS_TERMINATED;
480 // Deactivate the current stepping.
481 // or return an error???
491 // Validate step-ranges. Ranges are exclusive, so end offset
492 // should always be greater than start offset.
493 // Ranges don't have to be sorted.
494 // Zero ranges is ok; though they ought to just call Step() in that case.
495 for (ULONG32 i = 0; i < cRanges; i++)
497 if (rgRanges[i].startOffset >= rgRanges[i].endOffset)
499 STRESS_LOG2(LF_CORDB, LL_INFO10, "Illegal step range. 0x%x-0x%x\n", rgRanges[i].startOffset, rgRanges[i].endOffset);
500 return ErrWrapper(E_INVALIDARG);
504 CordbProcess * pProcess = GetProcess();
510 DebuggerIPCEvent * pEvent = reinterpret_cast<DebuggerIPCEvent *>(_alloca(CorDBIPC_BUFFER_SIZE));
512 pProcess->InitIPCEvent(pEvent, DB_IPCE_STEP, true, GetAppDomain()->GetADToken());
514 pEvent->StepData.vmThreadToken = m_thread->m_vmThreadToken;
515 pEvent->StepData.rgfMappingStop = m_rgfMappingStop;
516 pEvent->StepData.rgfInterceptStop = m_rgfInterceptStop;
517 pEvent->StepData.IsJMCStop = !!m_fIsJMCStepper;
522 pEvent->StepData.frameToken = LEAF_MOST_FRAME;
526 pEvent->StepData.frameToken = m_frame->GetFramePointer();
529 pEvent->StepData.stepIn = (fStepIn != 0);
530 pEvent->StepData.totalRangeCount = cRanges;
531 pEvent->StepData.rangeIL = m_rangeIL;
534 // Send ranges. We may have to send > 1 message.
537 COR_DEBUG_STEP_RANGE * pRangeStart = &(pEvent->StepData.range);
538 COR_DEBUG_STEP_RANGE * pRangeEnd = (reinterpret_cast<COR_DEBUG_STEP_RANGE *> (((BYTE *)pEvent) + CorDBIPC_BUFFER_SIZE)) - 1;
540 int cRangesToGo = cRanges;
544 while (cRangesToGo > 0)
547 // Find the number of ranges we can copy this time thru the loop
551 if (cRangesToGo < (pRangeEnd - pRangeStart))
553 cRangesToCopy = cRangesToGo;
557 cRangesToCopy = (unsigned int)(pRangeEnd - pRangeStart);
561 // Copy the ranges into the IPC block now, 1-by-1
563 int cRangesCopied = 0;
565 while (cRangesCopied != cRangesToCopy)
567 pRangeStart[cRangesCopied] = rgRanges[cRanges - cRangesToGo + cRangesCopied];
571 pEvent->StepData.rangeCount = cRangesCopied;
573 cRangesToGo -= cRangesCopied;
576 // Send step event (two-way event here...)
579 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
581 hr = WORST_HR(hr, pEvent->hr);
592 // Send step event without any ranges (two-way event here...)
595 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
597 hr = WORST_HR(hr, pEvent->hr);
605 m_id = LsPtrToCookie(pEvent->StepData.stepperToken);
607 LOG((LF_CORDB,LL_INFO10000, "CS::SR: m_id:0x%x | 0x%x \n",
609 LsPtrToCookie(pEvent->StepData.stepperToken)));
612 CordbAppDomain *pAppDomain = GetAppDomain();
614 _ASSERTE (pAppDomain != NULL);
618 pProcess->m_steppers.AddBase(this);
626 //---------------------------------------------------------------------------------------
628 // Ships off a step-out command to the left-side. On the next continue the LS will
629 // execute a step-out
632 // S_OK if the stepper is successfully set-up, else an appropriate error code.
634 HRESULT CordbStepper::StepOut()
636 PUBLIC_API_ENTRY(this);
637 FAIL_IF_NEUTERED(this);
638 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
640 if (m_thread == NULL)
642 return CORDBG_E_PROCESS_TERMINATED;
650 // Deactivate the current stepping.
651 // or return an error???
662 CordbProcess * pProcess = GetProcess();
664 // We don't do native step-out.
665 if (pProcess->SupportsVersion(ver_ICorDebugProcess2))
667 if ((m_rgfMappingStop & STOP_UNMANAGED) != 0)
669 return ErrWrapper(CORDBG_E_CANT_INTEROP_STEP_OUT);
677 DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE);
679 pProcess->InitIPCEvent(pEvent, DB_IPCE_STEP_OUT, true, GetAppDomain()->GetADToken());
681 pEvent->StepData.vmThreadToken = m_thread->m_vmThreadToken;
682 pEvent->StepData.rgfMappingStop = m_rgfMappingStop;
683 pEvent->StepData.rgfInterceptStop = m_rgfInterceptStop;
684 pEvent->StepData.IsJMCStop = !!m_fIsJMCStepper;
688 pEvent->StepData.frameToken = LEAF_MOST_FRAME;
692 pEvent->StepData.frameToken = m_frame->GetFramePointer();
695 pEvent->StepData.totalRangeCount = 0;
697 // Note: two-way event here...
698 hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
700 hr = WORST_HR(hr, pEvent->hr);
707 m_id = LsPtrToCookie(pEvent->StepData.stepperToken);
710 CordbAppDomain * pAppDomain = GetAppDomain();
712 _ASSERTE (pAppDomain != NULL);
716 pProcess->m_steppers.AddBase(this);