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