Merge pull request #1668 from Dmitry-Me/assignmentOpReturningUnsuitableType
[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_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   : CordbBreakpoint(code->GetProcess(), CBT_FUNCTION), 
62   m_code(code), m_offset(offset)
63 {
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);
68 }
69
70 CordbFunctionBreakpoint::~CordbFunctionBreakpoint()
71 {
72     // @todo- eventually get CordbFunctionBreakpoint rooted and enable this.
73     //_ASSERTE(this->IsNeutered());
74     //_ASSERTE(m_code == NULL);
75 }
76
77 void CordbFunctionBreakpoint::Neuter()
78 {
79     Disconnect();
80     CordbBreakpoint::Neuter();
81 }
82
83 HRESULT CordbFunctionBreakpoint::QueryInterface(REFIID id, void **pInterface)
84 {
85     if (id == IID_ICorDebugFunctionBreakpoint)
86     {
87         *pInterface = static_cast<ICorDebugFunctionBreakpoint*>(this);
88     }
89     else
90     {
91         // Not looking for a function breakpoint? See if the base class handles
92         // this interface. (issue 143976)
93         return CordbBreakpoint::QueryInterface(id, pInterface);
94     }
95
96     ExternalAddRef();
97     return S_OK;
98 }
99
100 HRESULT CordbFunctionBreakpoint::GetFunction(ICorDebugFunction **ppFunction)
101 {
102     PUBLIC_API_ENTRY(this);
103     FAIL_IF_NEUTERED(this);
104     VALIDATE_POINTER_TO_OBJECT(ppFunction, ICorDebugFunction **);
105
106     if (m_code == NULL)
107     {
108         return CORDBG_E_PROCESS_TERMINATED;
109     }        
110     if (m_code->IsNeutered())
111     {
112         return CORDBG_E_CODE_NOT_AVAILABLE;
113     }
114
115     *ppFunction = static_cast<ICorDebugFunction *> (m_code->GetFunction());
116     (*ppFunction)->AddRef();
117
118     return S_OK;
119 }
120
121 // m_id is actually a LSPTR_BREAKPOINT. Get it as a type-safe member.
122 LSPTR_BREAKPOINT CordbFunctionBreakpoint::GetLsPtrBP()
123 {
124     LSPTR_BREAKPOINT p;
125     p.Set((void*) m_id);
126     return p;
127 }
128
129 HRESULT CordbFunctionBreakpoint::GetOffset(ULONG32 *pnOffset)
130 {
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 *);
135     
136     *pnOffset = (ULONG32)m_offset;
137
138     return S_OK;
139 }
140
141 //---------------------------------------------------------------------------------------
142 //
143 // Activates or removes a breakpoint 
144 //
145 // Arguments:
146 //    fActivate - TRUE if to activate the breakpoint, else FALSE.
147 //
148 // Return Value:
149 //    S_OK if successful, else a specific error code detailing the type of failure.
150 //
151 //---------------------------------------------------------------------------------------
152 HRESULT CordbFunctionBreakpoint::Activate(BOOL fActivate)
153 {
154     PUBLIC_REENTRANT_API_ENTRY(this);
155     OK_IF_NEUTERED(this); // we'll check again later
156
157     if (fActivate == (m_active == true) )
158     {
159         return S_OK;
160     }
161
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())
165     {
166         return CORDBG_E_PROCESS_TERMINATED;
167     }
168
169     HRESULT hr;
170     ATT_ALLOW_LIVE_DO_STOPGO(GetProcess());
171
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.
174     //
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())
178     {
179         return CORDBG_E_CODE_NOT_AVAILABLE;
180     }
181
182         
183     //
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>
186     //
187     CordbProcess * pProcess = GetProcess();
188
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
192
193     DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE);
194
195     CordbAppDomain * pAppDomain = GetAppDomain();
196     _ASSERTE (pAppDomain != NULL);
197
198     if (fActivate)
199     {
200         pProcess->InitIPCEvent(pEvent, DB_IPCE_BREAKPOINT_ADD, true, pAppDomain->GetADToken());
201
202         pEvent->BreakpointData.funcMetadataToken = m_code->GetMetadataToken();
203         pEvent->BreakpointData.vmDomainFile = m_code->GetModule()->GetRuntimeDomainFile();
204         pEvent->BreakpointData.encVersion = m_code->GetVersion();
205
206         BOOL fIsIL = m_code->IsIL();
207
208         pEvent->BreakpointData.isIL = fIsIL ? true : false;
209         pEvent->BreakpointData.offset = m_offset;
210         if (fIsIL)
211         {
212             pEvent->BreakpointData.nativeCodeMethodDescToken = pEvent->BreakpointData.nativeCodeMethodDescToken.NullPtr();
213         }
214         else
215         {
216             pEvent->BreakpointData.nativeCodeMethodDescToken = 
217                 (m_code.GetValue()->AsNativeCode())->GetVMNativeCodeMethodDescToken().ToLsPtr();
218         }
219
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();
226
227         hr = WORST_HR(hr, pEvent->hr);
228
229         if (FAILED(hr))
230         {
231             return hr;
232         }
233
234             
235         m_id = LsPtrToCookie(pEvent->BreakpointData.breakpointToken);
236
237         // If we weren't able to allocate the BP, we should have set the
238         // hr on the left side.
239         _ASSERTE(m_id != 0);
240
241
242         pAppDomain->m_breakpoints.AddBase(this);
243         m_active = true;
244
245         // Continue called automatically by StopContinueHolder
246     }
247     else
248     {
249         _ASSERTE (pAppDomain != NULL);
250
251         if (pProcess->IsSafeToSendEvents())
252         {            
253             pProcess->InitIPCEvent(pEvent, DB_IPCE_BREAKPOINT_REMOVE, false, pAppDomain->GetADToken());
254
255             pEvent->BreakpointData.breakpointToken = GetLsPtrBP(); 
256
257             lockHolder.Release();
258             hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);            
259             lockHolder.Acquire();
260
261             hr = WORST_HR(hr, pEvent->hr);
262         }
263         else
264         {
265             hr = CORDBHRFromProcessState(pProcess, pAppDomain);
266         }            
267         
268         pAppDomain->m_breakpoints.RemoveBase(LsPtrToCookie(GetLsPtrBP()));
269         m_active = false;
270     }
271
272     return hr;
273 }
274
275 void CordbFunctionBreakpoint::Disconnect()
276 {
277     m_code.Clear();
278 }
279
280 /* ------------------------------------------------------------------------- *
281  * Stepper class
282  * ------------------------------------------------------------------------- */
283
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),
288     m_rangeIL(TRUE),
289     m_fIsJMCStepper(false),
290     m_rgfMappingStop(STOP_OTHER_UNMAPPED),
291     m_rgfInterceptStop(INTERCEPT_NONE)
292 {
293 }
294
295 HRESULT CordbStepper::QueryInterface(REFIID id, void **pInterface)
296 {
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));
303     else
304         return E_NOINTERFACE;
305
306     ExternalAddRef();
307     return S_OK;
308 }
309
310 HRESULT CordbStepper::SetRangeIL(BOOL bIL)
311 {
312     PUBLIC_API_ENTRY(this);
313     FAIL_IF_NEUTERED(this);
314     m_rangeIL = (bIL != FALSE);
315
316     return S_OK;
317 }
318
319 HRESULT CordbStepper::SetJMC(BOOL fIsJMCStepper)
320 {
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)
325         return E_INVALIDARG;
326             
327     m_fIsJMCStepper = (fIsJMCStepper != FALSE);
328     return S_OK;
329 }
330
331 HRESULT CordbStepper::IsActive(BOOL *pbActive)
332 {
333     PUBLIC_API_ENTRY(this);
334     FAIL_IF_NEUTERED(this);
335     VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *);
336     
337     *pbActive = m_active;
338
339     return S_OK;
340 }
341
342 // M_id is a ptr to the stepper in the LS process.
343 LSPTR_STEPPER CordbStepper::GetLsPtrStepper()
344 {
345     LSPTR_STEPPER p;
346     p.Set((void*) m_id);
347     return p;
348 }
349
350 HRESULT CordbStepper::Deactivate()
351 {
352     PUBLIC_REENTRANT_API_ENTRY(this);
353     if (!m_active)
354         return S_OK;
355         
356     FAIL_IF_NEUTERED(this);
357
358     if (m_thread == NULL)
359         return CORDBG_E_PROCESS_TERMINATED;
360
361     HRESULT hr;
362     CordbProcess *process = GetProcess();
363     ATT_ALLOW_LIVE_DO_STOPGO(process);
364     
365     process->Lock();
366
367     if (!m_active) // another thread may be deactivating (e.g. step complete event)
368     {
369         process->Unlock();
370         return S_OK;
371     }
372
373     CordbAppDomain *pAppDomain = GetAppDomain();
374     _ASSERTE (pAppDomain != NULL);
375
376     DebuggerIPCEvent event;
377     process->InitIPCEvent(&event, 
378                           DB_IPCE_STEP_CANCEL, 
379                           false,
380                           pAppDomain->GetADToken());
381
382     event.StepData.stepperToken = GetLsPtrStepper(); 
383
384     process->Unlock();
385     hr = process->SendIPCEvent(&event, sizeof(DebuggerIPCEvent));
386     hr = WORST_HR(hr, event.hr);
387     process->Lock();
388
389
390     process->m_steppers.RemoveBase((ULONG_PTR)m_id);
391     m_active = false;
392
393     process->Unlock();
394
395     return hr;
396 }
397
398 HRESULT CordbStepper::SetInterceptMask(CorDebugIntercept mask)
399 {
400     PUBLIC_API_ENTRY(this);
401     FAIL_IF_NEUTERED(this);
402     m_rgfInterceptStop = mask;
403     return S_OK;
404 }
405
406 HRESULT CordbStepper::SetUnmappedStopMask(CorDebugUnmappedStop mask)
407 {
408     PUBLIC_API_ENTRY(this);
409     FAIL_IF_NEUTERED(this);
410     
411     // You must be Win32 attached to stop in unmanaged code.
412     if ((mask & STOP_UNMANAGED) && !GetProcess()->IsInteropDebugging())
413         return E_INVALIDARG;
414
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.
422     if (m_fIsJMCStepper)
423     {
424         if (mask & STOP_ALL)
425             return E_INVALIDARG;
426     }
427
428     // @todo- Ensure that we only set valid bits.
429     
430     
431     m_rgfMappingStop = mask;
432     return S_OK;
433 }
434
435 HRESULT CordbStepper::Step(BOOL bStepIn)
436 {
437     PUBLIC_API_ENTRY(this);
438     FAIL_IF_NEUTERED(this);
439     ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
440     
441     if (m_thread == NULL)
442         return CORDBG_E_PROCESS_TERMINATED;
443
444     return StepRange(bStepIn, NULL, 0);
445 }
446
447 //---------------------------------------------------------------------------------------
448 //
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.
451 //
452 // Arguments:
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.
456 //
457 // Returns:
458 //    S_OK if the stepper is successfully set-up, else an appropriate error code.
459 //  
460 HRESULT CordbStepper::StepRange(BOOL fStepIn, 
461                                 COR_DEBUG_STEP_RANGE rgRanges[], 
462                                 ULONG32 cRanges)
463 {
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);
467
468     ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
469
470     if (m_thread == NULL)
471     {
472         return CORDBG_E_PROCESS_TERMINATED;
473     }
474
475     HRESULT hr = S_OK;
476
477     if (m_active)
478     {
479         //
480         // Deactivate the current stepping. 
481         // or return an error???
482         //
483         hr = Deactivate();
484
485         if (FAILED(hr))
486         {
487             return hr;
488         }
489     }
490
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++)
496     {
497         if (rgRanges[i].startOffset >= rgRanges[i].endOffset)
498         {
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);
501         }
502     }
503     
504     CordbProcess * pProcess = GetProcess();
505     
506     //
507     // Build step event
508     //
509
510     DebuggerIPCEvent * pEvent = reinterpret_cast<DebuggerIPCEvent *>(_alloca(CorDBIPC_BUFFER_SIZE));
511
512     pProcess->InitIPCEvent(pEvent, DB_IPCE_STEP, true, GetAppDomain()->GetADToken());
513
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;
518
519         
520     if (m_frame == NULL)
521     {
522         pEvent->StepData.frameToken = LEAF_MOST_FRAME;
523     }
524     else
525     {
526         pEvent->StepData.frameToken = m_frame->GetFramePointer();
527     }
528
529     pEvent->StepData.stepIn = (fStepIn != 0);
530     pEvent->StepData.totalRangeCount = cRanges;
531     pEvent->StepData.rangeIL = m_rangeIL;
532
533     //
534     // Send ranges.  We may have to send > 1 message.
535     //
536
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;
539
540     int cRangesToGo = cRanges;
541
542     if (cRangesToGo > 0)
543     {
544         while (cRangesToGo > 0)
545         {
546             //
547             // Find the number of ranges we can copy this time thru the loop
548             //
549             int cRangesToCopy;
550
551             if (cRangesToGo < (pRangeEnd - pRangeStart))
552             {
553                 cRangesToCopy = cRangesToGo;
554             }
555             else
556             {
557                 cRangesToCopy = (unsigned int)(pRangeEnd - pRangeStart);
558             }
559
560             //
561             // Copy the ranges into the IPC block now, 1-by-1
562             //
563             int cRangesCopied = 0;
564
565             while (cRangesCopied != cRangesToCopy)
566             {
567                 pRangeStart[cRangesCopied] = rgRanges[cRanges - cRangesToGo + cRangesCopied];
568                 cRangesCopied++; 
569             }
570
571             pEvent->StepData.rangeCount = cRangesCopied;
572
573             cRangesToGo -= cRangesCopied;
574
575             //
576             // Send step event (two-way event here...)
577             //
578
579             hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
580
581             hr = WORST_HR(hr, pEvent->hr);
582             
583             if (FAILED(hr))
584             {
585                 return hr;
586             }
587         }
588     }
589     else
590     {
591         //
592         // Send step event without any ranges (two-way event here...)
593         //
594
595         hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
596
597         hr = WORST_HR(hr, pEvent->hr);
598
599         if (FAILED(hr))
600         {
601             return hr;
602         }
603     }
604
605     m_id = LsPtrToCookie(pEvent->StepData.stepperToken);
606
607     LOG((LF_CORDB,LL_INFO10000, "CS::SR: m_id:0x%x | 0x%x \n", 
608          m_id, 
609          LsPtrToCookie(pEvent->StepData.stepperToken)));
610
611 #ifdef _DEBUG
612     CordbAppDomain *pAppDomain = GetAppDomain();
613 #endif
614     _ASSERTE (pAppDomain != NULL);
615
616     pProcess->Lock();
617
618     pProcess->m_steppers.AddBase(this);
619     m_active = true;
620
621     pProcess->Unlock();
622
623     return hr;
624 }
625
626 //---------------------------------------------------------------------------------------
627 //
628 // Ships off a step-out command to the left-side.  On the next continue the LS will
629 // execute a step-out
630 //
631 // Returns:
632 //    S_OK if the stepper is successfully set-up, else an appropriate error code.
633 //  
634 HRESULT CordbStepper::StepOut()
635 {
636     PUBLIC_API_ENTRY(this);
637     FAIL_IF_NEUTERED(this);
638     ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
639         
640     if (m_thread == NULL)
641     {
642         return CORDBG_E_PROCESS_TERMINATED;
643     }
644
645     HRESULT hr;
646
647     if (m_active)
648     {
649         //
650         // Deactivate the current stepping. 
651         // or return an error???
652         //
653
654         hr = Deactivate();
655
656         if (FAILED(hr))
657         {
658             return hr;
659         }
660     }
661
662     CordbProcess * pProcess = GetProcess();
663
664     // We don't do native step-out.
665     if (pProcess->SupportsVersion(ver_ICorDebugProcess2))
666     {
667         if ((m_rgfMappingStop & STOP_UNMANAGED) != 0)
668         {
669             return ErrWrapper(CORDBG_E_CANT_INTEROP_STEP_OUT);
670         }
671     }
672     
673     //
674     // Build step event
675     //
676
677     DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE);
678
679     pProcess->InitIPCEvent(pEvent, DB_IPCE_STEP_OUT, true, GetAppDomain()->GetADToken());
680
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;
685
686     if (m_frame == NULL)
687     {
688         pEvent->StepData.frameToken = LEAF_MOST_FRAME;
689     }
690     else
691     {
692         pEvent->StepData.frameToken = m_frame->GetFramePointer();
693     }
694
695     pEvent->StepData.totalRangeCount = 0;
696
697     // Note: two-way event here...
698     hr = pProcess->SendIPCEvent(pEvent, CorDBIPC_BUFFER_SIZE);
699     
700     hr = WORST_HR(hr, pEvent->hr);
701     
702     if (FAILED(hr))
703     {
704         return hr;
705     }
706
707     m_id = LsPtrToCookie(pEvent->StepData.stepperToken);
708
709 #ifdef _DEBUG
710     CordbAppDomain * pAppDomain = GetAppDomain();
711 #endif
712     _ASSERTE (pAppDomain != NULL);
713
714     pProcess->Lock();
715
716     pProcess->m_steppers.AddBase(this);
717     m_active = true;
718
719     pProcess->Unlock();
720     
721     return S_OK;
722 }