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 ? true : false; |
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 | } |
725 | |