Merge pull request #15595 from mikedn/rc-binop
[platform/upstream/coreclr.git] / src / debug / di / breakpoint.cpp
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
6 // 
7
8 //
9 //*****************************************************************************
10 #include "stdafx.h"
11
12 /* ------------------------------------------------------------------------- *
13  * Breakpoint class
14  * ------------------------------------------------------------------------- */
15
16 CordbBreakpoint::CordbBreakpoint(CordbProcess * pProcess, CordbBreakpointType bpType)
17   : CordbBase(pProcess, 0, enumCordbBreakpoint), 
18   m_active(false), m_pAppDomain(NULL), m_type(bpType)
19 {
20 }
21
22 // Neutered by CordbAppDomain
23 void CordbBreakpoint::Neuter()
24 {
25     m_pAppDomain = NULL; // clear ref
26     CordbBase::Neuter();
27 }
28
29 HRESULT CordbBreakpoint::QueryInterface(REFIID id, void **pInterface)
30 {
31     if (id == IID_ICorDebugBreakpoint)
32     {
33         *pInterface = static_cast<ICorDebugBreakpoint*>(this);
34     }
35     else if (id == IID_IUnknown)
36     {
37         *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugBreakpoint*>(this));
38     }
39     else
40     {
41         return E_NOINTERFACE;
42     }
43
44     ExternalAddRef();
45     return S_OK;
46 }
47
48 HRESULT CordbBreakpoint::BaseIsActive(BOOL *pbActive)
49 {
50     *pbActive = m_active ? TRUE : FALSE;
51
52     return S_OK;
53 }
54
55 /* ------------------------------------------------------------------------- *
56  * Function Breakpoint class
57  * ------------------------------------------------------------------------- */
58
59 CordbFunctionBreakpoint::CordbFunctionBreakpoint(CordbCode *code,
60                                                  SIZE_T offset,
61                                                  BOOL offsetIsIl)
62   : CordbBreakpoint(code->GetProcess(), CBT_FUNCTION), 
63   m_code(code), m_offset(offset),
64   m_offsetIsIl(offsetIsIl)
65 {
66     // Remember the app domain we came from so that breakpoints can be
67     // deactivated from within the ExitAppdomain callback.
68     m_pAppDomain = m_code->GetAppDomain();
69     _ASSERTE(m_pAppDomain != NULL);
70 }
71
72 CordbFunctionBreakpoint::~CordbFunctionBreakpoint()
73 {
74     // @todo- eventually get CordbFunctionBreakpoint rooted and enable this.
75     //_ASSERTE(this->IsNeutered());
76     //_ASSERTE(m_code == NULL);
77 }
78
79 void CordbFunctionBreakpoint::Neuter()
80 {
81     Disconnect();
82     CordbBreakpoint::Neuter();
83 }
84
85 HRESULT CordbFunctionBreakpoint::QueryInterface(REFIID id, void **pInterface)
86 {
87     if (id == IID_ICorDebugFunctionBreakpoint)
88     {
89         *pInterface = static_cast<ICorDebugFunctionBreakpoint*>(this);
90     }
91     else
92     {
93         // Not looking for a function breakpoint? See if the base class handles
94         // this interface. (issue 143976)
95         return CordbBreakpoint::QueryInterface(id, pInterface);
96     }
97
98     ExternalAddRef();
99     return S_OK;
100 }
101
102 HRESULT CordbFunctionBreakpoint::GetFunction(ICorDebugFunction **ppFunction)
103 {
104     PUBLIC_API_ENTRY(this);
105     FAIL_IF_NEUTERED(this);
106     VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugFunction **);
107
108     if (m_code == NULL)
109     {
110         return CORDBG_E_PROCESS_TERMINATED;
111     }        
112     if (m_code->IsNeutered())
113     {
114         return CORDBG_E_CODE_NOT_AVAILABLE;
115     }
116
117     *ppFunction = static_cast<ICorDebugFunction *> (m_code->GetFunction());
118     (*ppFunction)->AddRef();
119
120     return S_OK;
121 }
122
123 // m_id is actually a LSPTR_BREAKPOINT. Get it as a type-safe member.
124 LSPTR_BREAKPOINT CordbFunctionBreakpoint::GetLsPtrBP()
125 {
126     LSPTR_BREAKPOINT p;
127     p.Set((void*) m_id);
128     return p;
129 }
130
131 HRESULT CordbFunctionBreakpoint::GetOffset(ULONG32 *pnOffset)
132 {
133   //REVISIT_TODO: is this casting correct for ia64?
134     PUBLIC_API_ENTRY(this);
135     FAIL_IF_NEUTERED(this);
136     VALIDATE_POINTER_TO_OBJECT(pnOffset, SIZE_T *);
137     
138     *pnOffset = (ULONG32)m_offset;
139
140     return S_OK;
141 }
142
143 //---------------------------------------------------------------------------------------
144 //
145 // Activates or removes a breakpoint 
146 //
147 // Arguments:
148 //    fActivate - TRUE if to activate the breakpoint, else FALSE.
149 //
150 // Return Value:
151 //    S_OK if successful, else a specific error code detailing the type of failure.
152 //
153 //---------------------------------------------------------------------------------------
154 HRESULT CordbFunctionBreakpoint::Activate(BOOL fActivate)
155 {
156     PUBLIC_REENTRANT_API_ENTRY(this);
157     OK_IF_NEUTERED(this); // we'll check again later
158
159     if (fActivate == (m_active == true) )
160     {
161         return S_OK;
162     }
163
164     // For backwards compat w/ everett, we let the other error codes
165     // take precedence over neutering error codes.
166     if ((m_code == NULL) || this->IsNeutered())
167     {
168         return CORDBG_E_PROCESS_TERMINATED;
169     }
170
171     HRESULT hr;
172     ATT_ALLOW_LIVE_DO_STOPGO(GetProcess());
173
174     // For legacy, check this error condition. We must do this under the stop-go lock to ensure
175     // that the m_code object was not deleted out from underneath us.
176     //
177     // 6/23/09 - This isn't just for legacy anymore, collectible types should be able to hit this
178     // by unloading the module containing the code this breakpoint is bound to.
179     if (m_code->IsNeutered())
180     {
181         return CORDBG_E_CODE_NOT_AVAILABLE;
182     }
183
184         
185     //
186     // <REVISIT_TODO>@todo: when we implement module and value breakpoints, then
187     // we'll want to factor some of this code out.</REVISIT_TODO>
188     //
189     CordbProcess * pProcess = GetProcess();
190
191     RSLockHolder lockHolder(pProcess->GetProcessLock());
192     pProcess->ClearPatchTable(); // if we add something, then the right side 
193                                 // view of the patch table is no longer valid
194
195     DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE);
196
197     CordbAppDomain * pAppDomain = GetAppDomain();
198     _ASSERTE (pAppDomain != NULL);
199
200     if (fActivate)
201     {
202         pProcess->InitIPCEvent(pEvent, DB_IPCE_BREAKPOINT_ADD, true, pAppDomain->GetADToken());
203
204         pEvent->BreakpointData.funcMetadataToken = m_code->GetMetadataToken();
205         pEvent->BreakpointData.vmDomainFile = m_code->GetModule()->GetRuntimeDomainFile();
206         pEvent->BreakpointData.encVersion = m_code->GetVersion();
207
208         BOOL codeIsIL = m_code->IsIL();
209
210         pEvent->BreakpointData.isIL = m_offsetIsIl;
211         pEvent->BreakpointData.offset = m_offset;
212         if (codeIsIL)
213         {
214             pEvent->BreakpointData.nativeCodeMethodDescToken = pEvent->BreakpointData.nativeCodeMethodDescToken.NullPtr();
215         }
216         else
217         {
218             pEvent->BreakpointData.nativeCodeMethodDescToken = 
219                 (m_code.GetValue()->AsNativeCode())->GetVMNativeCodeMethodDescToken().ToLsPtr();
220         }
221
222         // Note: we're sending a two-way event, so it blocks here
223         // until the breakpoint is really added and the reply event is
224         // copied over the event we sent.
225         lockHolder.Release();
226         hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
227         lockHolder.Acquire();
228
229         hr = WORST_HR(hr, pEvent->hr);
230
231         if (FAILED(hr))
232         {
233             return hr;
234         }
235
236             
237         m_id = LsPtrToCookie(pEvent->BreakpointData.breakpointToken);
238
239         // If we weren't able to allocate the BP, we should have set the
240         // hr on the left side.
241         _ASSERTE(m_id != 0);
242
243
244         pAppDomain->m_breakpoints.AddBase(this);
245         m_active = true;
246
247         // Continue called automatically by StopContinueHolder
248     }
249     else
250     {
251         _ASSERTE (pAppDomain != NULL);
252
253         if (pProcess->IsSafeToSendEvents())
254         {            
255             pProcess->InitIPCEvent(pEvent, DB_IPCE_BREAKPOINT_REMOVE, false, pAppDomain->GetADToken());
256
257             pEvent->BreakpointData.breakpointToken = GetLsPtrBP(); 
258
259             lockHolder.Release();
260             hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);            
261             lockHolder.Acquire();
262
263             hr = WORST_HR(hr, pEvent->hr);
264         }
265         else
266         {
267             hr = CORDBHRFromProcessState(pProcess, pAppDomain);
268         }            
269         
270         pAppDomain->m_breakpoints.RemoveBase(LsPtrToCookie(GetLsPtrBP()));
271         m_active = false;
272     }
273
274     return hr;
275 }
276
277 void CordbFunctionBreakpoint::Disconnect()
278 {
279     m_code.Clear();
280 }
281
282 /* ------------------------------------------------------------------------- *
283  * Stepper class
284  * ------------------------------------------------------------------------- */
285
286 CordbStepper::CordbStepper(CordbThread *thread, CordbFrame *frame)
287   : CordbBase(thread->GetProcess(), 0, enumCordbStepper), 
288     m_thread(thread), m_frame(frame),
289     m_stepperToken(0), m_active(false),
290     m_rangeIL(TRUE),
291     m_fIsJMCStepper(false),
292     m_rgfMappingStop(STOP_OTHER_UNMAPPED),
293     m_rgfInterceptStop(INTERCEPT_NONE)
294 {
295 }
296
297 HRESULT CordbStepper::QueryInterface(REFIID id, void **pInterface)
298 {
299     if (id == IID_ICorDebugStepper)
300         *pInterface = static_cast<ICorDebugStepper *>(this);
301     else if (id == IID_ICorDebugStepper2)
302         *pInterface = static_cast<ICorDebugStepper2 *>(this);
303     else if (id == IID_IUnknown)
304         *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugStepper *>(this));
305     else
306         return E_NOINTERFACE;
307
308     ExternalAddRef();
309     return S_OK;
310 }
311
312 HRESULT CordbStepper::SetRangeIL(BOOL bIL)
313 {
314     PUBLIC_API_ENTRY(this);
315     FAIL_IF_NEUTERED(this);
316     m_rangeIL = (bIL != FALSE);
317
318     return S_OK;
319 }
320
321 HRESULT CordbStepper::SetJMC(BOOL fIsJMCStepper)
322 {
323     PUBLIC_API_ENTRY(this);
324     FAIL_IF_NEUTERED(this);
325     // Can't have JMC and stopping with anything else.
326     if (m_rgfMappingStop & STOP_ALL)
327         return E_INVALIDARG;
328             
329     m_fIsJMCStepper = (fIsJMCStepper != FALSE);
330     return S_OK;
331 }
332
333 HRESULT CordbStepper::IsActive(BOOL *pbActive)
334 {
335     PUBLIC_API_ENTRY(this);
336     FAIL_IF_NEUTERED(this);
337     VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *);
338     
339     *pbActive = m_active;
340
341     return S_OK;
342 }
343
344 // M_id is a ptr to the stepper in the LS process.
345 LSPTR_STEPPER CordbStepper::GetLsPtrStepper()
346 {
347     LSPTR_STEPPER p;
348     p.Set((void*) m_id);
349     return p;
350 }
351
352 HRESULT CordbStepper::Deactivate()
353 {
354     PUBLIC_REENTRANT_API_ENTRY(this);
355     if (!m_active)
356         return S_OK;
357         
358     FAIL_IF_NEUTERED(this);
359
360     if (m_thread == NULL)
361         return CORDBG_E_PROCESS_TERMINATED;
362
363     HRESULT hr;
364     CordbProcess *process = GetProcess();
365     ATT_ALLOW_LIVE_DO_STOPGO(process);
366     
367     process->Lock();
368
369     if (!m_active) // another thread may be deactivating (e.g. step complete event)
370     {
371         process->Unlock();
372         return S_OK;
373     }
374
375     CordbAppDomain *pAppDomain = GetAppDomain();
376     _ASSERTE (pAppDomain != NULL);
377
378     DebuggerIPCEvent event;
379     process->InitIPCEvent(&event, 
380                           DB_IPCE_STEP_CANCEL, 
381                           false,
382                           pAppDomain->GetADToken());
383
384     event.StepData.stepperToken = GetLsPtrStepper(); 
385
386     process->Unlock();
387     hr = process->SendIPCEvent(&event, sizeof(DebuggerIPCEvent));
388     hr = WORST_HR(hr, event.hr);
389     process->Lock();
390
391
392     process->m_steppers.RemoveBase((ULONG_PTR)m_id);
393     m_active = false;
394
395     process->Unlock();
396
397     return hr;
398 }
399
400 HRESULT CordbStepper::SetInterceptMask(CorDebugIntercept mask)
401 {
402     PUBLIC_API_ENTRY(this);
403     FAIL_IF_NEUTERED(this);
404     m_rgfInterceptStop = mask;
405     return S_OK;
406 }
407
408 HRESULT CordbStepper::SetUnmappedStopMask(CorDebugUnmappedStop mask)
409 {
410     PUBLIC_API_ENTRY(this);
411     FAIL_IF_NEUTERED(this);
412     
413     // You must be Win32 attached to stop in unmanaged code.
414     if ((mask & STOP_UNMANAGED) && !GetProcess()->IsInteropDebugging())
415         return E_INVALIDARG;
416
417     // Limitations on JMC Stepping - if JMC stepping is active,
418     // all other stop masks must be disabled.
419     // The jit can't place JMC probes before the prolog, so if we're 
420     // we're JMC stepping, we'll stop after the prolog. 
421     // The implementation for JMC stepping also doesn't let us stop in
422     // unmanaged code. (because there are no probes there).
423     // So enforce those implementation limitations here.
424     if (m_fIsJMCStepper)
425     {
426         if (mask & STOP_ALL)
427             return E_INVALIDARG;
428     }
429
430     // @todo- Ensure that we only set valid bits.
431     
432     
433     m_rgfMappingStop = mask;
434     return S_OK;
435 }
436
437 HRESULT CordbStepper::Step(BOOL bStepIn)
438 {
439     PUBLIC_API_ENTRY(this);
440     FAIL_IF_NEUTERED(this);
441     ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
442     
443     if (m_thread == NULL)
444         return CORDBG_E_PROCESS_TERMINATED;
445
446     return StepRange(bStepIn, NULL, 0);
447 }
448
449 //---------------------------------------------------------------------------------------
450 //
451 // Ships off a step-range command to the left-side.  On the next continue the LS will
452 // step across one range at a time.
453 //
454 // Arguments:
455 //    fStepIn - TRUE if this stepper should execute a step-in, else FALSE
456 //    rgRanges - Array of ranges that define a single step.
457 //    cRanges - Count of number of elements in rgRanges.
458 //
459 // Returns:
460 //    S_OK if the stepper is successfully set-up, else an appropriate error code.
461 //  
462 HRESULT CordbStepper::StepRange(BOOL fStepIn, 
463                                 COR_DEBUG_STEP_RANGE rgRanges[], 
464                                 ULONG32 cRanges)
465 {
466     PUBLIC_REENTRANT_API_ENTRY(this);
467     FAIL_IF_NEUTERED(this);
468     VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(rgRanges, COR_DEBUG_STEP_RANGE, cRanges, true, true);
469
470     ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
471
472     if (m_thread == NULL)
473     {
474         return CORDBG_E_PROCESS_TERMINATED;
475     }
476
477     HRESULT hr = S_OK;
478
479     if (m_active)
480     {
481         //
482         // Deactivate the current stepping. 
483         // or return an error???
484         //
485         hr = Deactivate();
486
487         if (FAILED(hr))
488         {
489             return hr;
490         }
491     }
492
493     // Validate step-ranges. Ranges are exclusive, so end offset
494     // should always be greater than start offset.
495     // Ranges don't have to be sorted.
496     // Zero ranges is ok; though they ought to just call Step() in that case.
497     for (ULONG32 i = 0; i < cRanges; i++)
498     {
499         if (rgRanges[i].startOffset >= rgRanges[i].endOffset)
500         {
501             STRESS_LOG2(LF_CORDB, LL_INFO10, "Illegal step range. 0x%x-0x%x\n", rgRanges[i].startOffset, rgRanges[i].endOffset);
502             return ErrWrapper(E_INVALIDARG);
503         }
504     }
505     
506     CordbProcess * pProcess = GetProcess();
507     
508     //
509     // Build step event
510     //
511
512     DebuggerIPCEvent * pEvent = reinterpret_cast<DebuggerIPCEvent *>(_alloca(CorDBIPC_BUFFER_SIZE));
513
514     pProcess->InitIPCEvent(pEvent, DB_IPCE_STEP, true, GetAppDomain()->GetADToken());
515
516     pEvent->StepData.vmThreadToken = m_thread->m_vmThreadToken;
517     pEvent->StepData.rgfMappingStop = m_rgfMappingStop;
518     pEvent->StepData.rgfInterceptStop = m_rgfInterceptStop;
519     pEvent->StepData.IsJMCStop = !!m_fIsJMCStepper;
520
521         
522     if (m_frame == NULL)
523     {
524         pEvent->StepData.frameToken = LEAF_MOST_FRAME;
525     }
526     else
527     {
528         pEvent->StepData.frameToken = m_frame->GetFramePointer();
529     }
530
531     pEvent->StepData.stepIn = (fStepIn != 0);
532     pEvent->StepData.totalRangeCount = cRanges;
533     pEvent->StepData.rangeIL = m_rangeIL;
534
535     //
536     // Send ranges.  We may have to send > 1 message.
537     //
538
539     COR_DEBUG_STEP_RANGE * pRangeStart = &(pEvent->StepData.range);
540     COR_DEBUG_STEP_RANGE * pRangeEnd = (reinterpret_cast<COR_DEBUG_STEP_RANGE *> (((BYTE *)pEvent) + CorDBIPC_BUFFER_SIZE)) - 1;
541
542     int cRangesToGo = cRanges;
543
544     if (cRangesToGo > 0)
545     {
546         while (cRangesToGo > 0)
547         {
548             //
549             // Find the number of ranges we can copy this time thru the loop
550             //
551             int cRangesToCopy;
552
553             if (cRangesToGo < (pRangeEnd - pRangeStart))
554             {
555                 cRangesToCopy = cRangesToGo;
556             }
557             else
558             {
559                 cRangesToCopy = (unsigned int)(pRangeEnd - pRangeStart);
560             }
561
562             //
563             // Copy the ranges into the IPC block now, 1-by-1
564             //
565             int cRangesCopied = 0;
566
567             while (cRangesCopied != cRangesToCopy)
568             {
569                 pRangeStart[cRangesCopied] = rgRanges[cRanges - cRangesToGo + cRangesCopied];
570                 cRangesCopied++; 
571             }
572
573             pEvent->StepData.rangeCount = cRangesCopied;
574
575             cRangesToGo -= cRangesCopied;
576
577             //
578             // Send step event (two-way event here...)
579             //
580
581             hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
582
583             hr = WORST_HR(hr, pEvent->hr);
584             
585             if (FAILED(hr))
586             {
587                 return hr;
588             }
589         }
590     }
591     else
592     {
593         //
594         // Send step event without any ranges (two-way event here...)
595         //
596
597         hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
598
599         hr = WORST_HR(hr, pEvent->hr);
600
601         if (FAILED(hr))
602         {
603             return hr;
604         }
605     }
606
607     m_id = LsPtrToCookie(pEvent->StepData.stepperToken);
608
609     LOG((LF_CORDB,LL_INFO10000, "CS::SR: m_id:0x%x | 0x%x \n", 
610          m_id, 
611          LsPtrToCookie(pEvent->StepData.stepperToken)));
612
613 #ifdef _DEBUG
614     CordbAppDomain *pAppDomain = GetAppDomain();
615 #endif
616     _ASSERTE (pAppDomain != NULL);
617
618     pProcess->Lock();
619
620     pProcess->m_steppers.AddBase(this);
621     m_active = true;
622
623     pProcess->Unlock();
624
625     return hr;
626 }
627
628 //---------------------------------------------------------------------------------------
629 //
630 // Ships off a step-out command to the left-side.  On the next continue the LS will
631 // execute a step-out
632 //
633 // Returns:
634 //    S_OK if the stepper is successfully set-up, else an appropriate error code.
635 //  
636 HRESULT CordbStepper::StepOut()
637 {
638     PUBLIC_API_ENTRY(this);
639     FAIL_IF_NEUTERED(this);
640     ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
641         
642     if (m_thread == NULL)
643     {
644         return CORDBG_E_PROCESS_TERMINATED;
645     }
646
647     HRESULT hr;
648
649     if (m_active)
650     {
651         //
652         // Deactivate the current stepping. 
653         // or return an error???
654         //
655
656         hr = Deactivate();
657
658         if (FAILED(hr))
659         {
660             return hr;
661         }
662     }
663
664     CordbProcess * pProcess = GetProcess();
665
666     // We don't do native step-out.
667     if (pProcess->SupportsVersion(ver_ICorDebugProcess2))
668     {
669         if ((m_rgfMappingStop & STOP_UNMANAGED) != 0)
670         {
671             return ErrWrapper(CORDBG_E_CANT_INTEROP_STEP_OUT);
672         }
673     }
674     
675     //
676     // Build step event
677     //
678
679     DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE);
680
681     pProcess->InitIPCEvent(pEvent, DB_IPCE_STEP_OUT, true, GetAppDomain()->GetADToken());
682
683     pEvent->StepData.vmThreadToken = m_thread->m_vmThreadToken;
684     pEvent->StepData.rgfMappingStop = m_rgfMappingStop;
685     pEvent->StepData.rgfInterceptStop = m_rgfInterceptStop;
686     pEvent->StepData.IsJMCStop = !!m_fIsJMCStepper;
687
688     if (m_frame == NULL)
689     {
690         pEvent->StepData.frameToken = LEAF_MOST_FRAME;
691     }
692     else
693     {
694         pEvent->StepData.frameToken = m_frame->GetFramePointer();
695     }
696
697     pEvent->StepData.totalRangeCount = 0;
698
699     // Note: two-way event here...
700     hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
701     
702     hr = WORST_HR(hr, pEvent->hr);
703     
704     if (FAILED(hr))
705     {
706         return hr;
707     }
708
709     m_id = LsPtrToCookie(pEvent->StepData.stepperToken);
710
711 #ifdef _DEBUG
712     CordbAppDomain * pAppDomain = GetAppDomain();
713 #endif
714     _ASSERTE (pAppDomain != NULL);
715
716     pProcess->Lock();
717
718     pProcess->m_steppers.AddBase(this);
719     m_active = true;
720
721     pProcess->Unlock();
722     
723     return S_OK;
724 }