public void OnBeginStepOut(DkmThread thread) { // When we're stepping out while in Python code, there are two possibilities. Either the stack looks like this: // // PythonFrame1 // PythonFrame2 // // or else it looks like this: // // PythonFrame // [Native to Python transition] // NativeFrame // // In both cases, we use native breakpoints on the return address to catch the end of step-out operation. // For Python-to-native step-out, this is the only option. For Python-to-Python, it would seem that TraceFunc // can detect it via PyTrace_RETURN, but it doesn't actually know whether the return is to Python or to // native at the point where it's reported - and, in any case, we need to let PyEval_EvalFrameEx to return // before reporting the completion of that step-out (otherwise we will show the returning frame in call stack). // Find the destination for step-out by walking the call stack and finding either the first native frame // outside of Python and helper DLLs, or the second Python frame. var inspectionSession = DkmInspectionSession.Create(_process, null); var frameFormatOptions = new DkmFrameFormatOptions(DkmVariableInfoFlags.None, DkmFrameNameFormatOptions.None, DkmEvaluationFlags.None, 10000, 10); var stackContext = DkmStackContext.Create(inspectionSession, thread, DkmCallStackFilterOptions.None, frameFormatOptions, null, null); DkmStackFrame frame = null; for (int pyFrameCount = 0; pyFrameCount != 2;) { DkmStackFrame[] frames = null; var workList = DkmWorkList.Create(null); stackContext.GetNextFrames(workList, 1, (result) => { frames = result.Frames; }); workList.Execute(); if (frames == null || frames.Length != 1) { return; } frame = frames[0]; var frameModuleInstance = frame.ModuleInstance; if (frameModuleInstance is DkmNativeModuleInstance && frameModuleInstance != _pyrtInfo.DLLs.Python && frameModuleInstance != _pyrtInfo.DLLs.DebuggerHelper && frameModuleInstance != _pyrtInfo.DLLs.CTypes) { break; } else if (frame.RuntimeInstance != null && frame.RuntimeInstance.Id.RuntimeType == Guids.PythonRuntimeTypeGuid) { ++pyFrameCount; } } var nativeAddr = frame.InstructionAddress as DkmNativeInstructionAddress; if (nativeAddr == null) { var customAddr = frame.InstructionAddress as DkmCustomInstructionAddress; if (customAddr == null) { return; } var loc = new SourceLocation(customAddr.AdditionalData, thread.Process); nativeAddr = loc.NativeAddress; if (nativeAddr == null) { return; } } var bp = DkmRuntimeInstructionBreakpoint.Create(Guids.PythonStepTargetSourceGuid, thread, nativeAddr, false, null); bp.Enable(); _stepOutTargetBreakpoints.Add(bp); }
public void OnBeginStepOut(DkmThread thread) { // When we're stepping out while in Python code, there are two possibilities. Either the stack looks like this: // // PythonFrame1 // PythonFrame2 // // or else it looks like this: // // PythonFrame // [Native to Python transition] // NativeFrame // // In both cases, we use native breakpoints on the return address to catch the end of step-out operation. // For Python-to-native step-out, this is the only option. For Python-to-Python, it would seem that TraceFunc // can detect it via PyTrace_RETURN, but it doesn't actually know whether the return is to Python or to // native at the point where it's reported - and, in any case, we need to let PyEval_EvalFrameEx to return // before reporting the completion of that step-out (otherwise we will show the returning frame in call stack). // Find the destination for step-out by walking the call stack and finding either the first native frame // outside of Python and helper DLLs, or the second Python frame. var inspectionSession = DkmInspectionSession.Create(_process, null); var frameFormatOptions = new DkmFrameFormatOptions(DkmVariableInfoFlags.None, DkmFrameNameFormatOptions.None, DkmEvaluationFlags.None, 10000, 10); var stackContext = DkmStackContext.Create(inspectionSession, thread, DkmCallStackFilterOptions.None, frameFormatOptions, null, null); DkmStackFrame frame = null; for (int pyFrameCount = 0; pyFrameCount != 2; ) { DkmStackFrame[] frames = null; var workList = DkmWorkList.Create(null); stackContext.GetNextFrames(workList, 1, (result) => { frames = result.Frames; }); workList.Execute(); if (frames == null || frames.Length != 1) { return; } frame = frames[0]; var frameModuleInstance = frame.ModuleInstance; if (frameModuleInstance is DkmNativeModuleInstance && frameModuleInstance != _pyrtInfo.DLLs.Python && frameModuleInstance != _pyrtInfo.DLLs.DebuggerHelper && frameModuleInstance != _pyrtInfo.DLLs.CTypes) { break; } else if (frame.RuntimeInstance != null && frame.RuntimeInstance.Id.RuntimeType == Guids.PythonRuntimeTypeGuid) { ++pyFrameCount; } } var nativeAddr = frame.InstructionAddress as DkmNativeInstructionAddress; if (nativeAddr == null) { var customAddr = frame.InstructionAddress as DkmCustomInstructionAddress; if (customAddr == null) { return; } var loc = new SourceLocation(customAddr.AdditionalData, thread.Process); nativeAddr = loc.NativeAddress; if (nativeAddr == null) { return; } } var bp = DkmRuntimeInstructionBreakpoint.Create(Guids.PythonStepTargetSourceGuid, thread, nativeAddr, false, null); bp.Enable(); _stepOutTargetBreakpoints.Add(bp); }