public override void Handle(DkmProcess process) { var pythonRuntime = process.GetPythonRuntimeInstance(); if (pythonRuntime == null) { return; } string moduleName; if (ModuleId == Guids.UnknownPythonModuleGuid) { moduleName = "<unknown>"; } else { try { moduleName = Path.GetFileName(FileName); } catch (ArgumentException) { moduleName = FileName; } } var module = DkmModule.Create(new DkmModuleId(ModuleId, Guids.PythonSymbolProviderGuid), FileName, new DkmCompilerId(Guids.MicrosoftVendorGuid, Guids.PythonLanguageGuid), process.Connection, null); var moduleInstance = DkmCustomModuleInstance.Create(moduleName, FileName, 0, pythonRuntime, null, null, DkmModuleFlags.None, DkmModuleMemoryLayout.Unknown, 0, 0, 0, "Python", false, null, null, null); moduleInstance.SetModule(module, true); }
public override void Handle(DkmProcess process) { var thread = process.GetThreads().Single(t => t.UniqueId == ThreadId); var excInfo = DkmCustomExceptionInformation.Create( process.GetPythonRuntimeInstance(), Guids.PythonExceptionCategoryGuid, thread, null, Name, 0, DkmExceptionProcessingStage.Thrown | DkmExceptionProcessingStage.UserVisible | DkmExceptionProcessingStage.UserCodeSearch, null, new ReadOnlyCollection <byte>(AdditionalInformation)); excInfo.OnDebugMonitorException(); }
private static void InjectHelperDll(DkmProcess process) { var injectionData = process.GetDataItem <HelperDllInjectionDataHolder>(); if (injectionData != null) { // Injection is already in progress. return; } injectionData = new HelperDllInjectionDataHolder(); process.SetDataItem(DkmDataCreationDisposition.CreateNew, injectionData); var pyrtInfo = process.GetPythonRuntimeInfo(); // Loading the helper is done via CreateRemoteThread(LoadLibrary), which is inherently asynchronous. // On the other hand, we will not handle breakpoints until it is loaded - they won't even be bound. // If any Python code is running in the meantime, this may cause us to skip breakpoints, which is // very surprising in the run (F5) scenario, as the user expects all preset breakpoints to be hit. // To fix that, we need block the Python interpreter loop until the helper is fully loaded. // // Pausing all threads is not a good way to do this, because one of the threads may be holding the // loader lock, which will prevent the helper from loading and result in a deadlock. So instead, // block at a known location at the beginning of PyInitialize_Ex, and only freeze the thread that // calls it - this is sufficient to prevent execution of Python code in run scenario before helper // is loaded. // // For attach-to-running-process scenario, we do nothing because the attach itself is inherently // asynchronous, and so there's no user expectation that breakpoints light up instantly. // If Python is already initialized, this is attach-to-running-process - don't block. var initialized = pyrtInfo.DLLs.Python.GetStaticVariable <Int32Proxy>( "initialized", GetPyInitializeObjectFile(pyrtInfo.LanguageVersion) ); if (initialized.Read() == 0) { // When Py_InitializeEx is hit, suspend the thread. DkmRuntimeBreakpoint makePendingCallsBP = null; makePendingCallsBP = CreateRuntimeDllExportedFunctionBreakpoint(pyrtInfo.DLLs.Python, "Py_InitializeEx", (thread, frameBase, vFrame) => { makePendingCallsBP.Close(); if (process.GetPythonRuntimeInstance() == null) { thread.Suspend(true); injectionData.SuspendedThread = thread; } }); makePendingCallsBP.Enable(); } // Inject the helper DLL; OnHelperDllInitialized will resume the thread once the DLL is loaded and initialized. DebugAttach.AttachDkm(process.LivePart.Id); }
public override void Handle(DkmProcess process) { if (process.LivePart == null) { // When debugging dumps, there's no stepping or live expression evaluation. Hence, we don't // need the helper DLL nor _ctypes.pyd for anything, and even if they are loaded in the dump, // we don't care about them at all. return; } var pyrtInfo = process.GetPythonRuntimeInfo(); var moduleInstance = process.GetNativeRuntimeInstance().GetNativeModuleInstances().Single(mi => mi.UniqueId == ModuleInstanceId); if (pyrtInfo.DLLs.CTypes == null && PythonDLLs.CTypesNames.Contains(moduleInstance.Name)) { moduleInstance.TryLoadSymbols(); if (moduleInstance.HasSymbols()) { pyrtInfo.DLLs.CTypes = moduleInstance; var traceHelper = process.GetDataItem <TraceManagerLocalHelper>(); if (traceHelper != null) { traceHelper.OnCTypesLoaded(moduleInstance); } } } if (process.GetPythonRuntimeInstance() != null) { return; } if (PythonDLLs.GetPythonLanguageVersion(moduleInstance) != PythonLanguageVersion.None) { pyrtInfo.DLLs.Python = moduleInstance; for (int i = 0; i < 2; ++i) { if (moduleInstance.HasSymbols()) { if (IsModuleCompiledWithPGO(moduleInstance)) { pyrtInfo.DLLs.Python = null; var pgoWarnMsg = DkmCustomMessage.Create(process.Connection, process, Guid.Empty, (int)VsPackageMessage.WarnAboutPGO, moduleInstance.Name, null); pgoWarnMsg.SendToVsService(Guids.CustomDebuggerEventHandlerGuid, IsBlocking: true); return; } if (process.LivePart == null) { // If debugging crash dumps, runtime can be created as soon as Python symbols are resolved. CreatePythonRuntimeInstance(process); } else { // If not, we need to check for debugger helper DLL as well, and inject it if it isn't there yet. if (pyrtInfo.DLLs.DebuggerHelper != null) { CreatePythonRuntimeInstance(process); } else { InjectHelperDll(process); } } return; } moduleInstance.TryLoadSymbols(); } var symWarnMsg = DkmCustomMessage.Create(process.Connection, process, Guid.Empty, (int)VsPackageMessage.WarnAboutPythonSymbols, moduleInstance.Name, null); symWarnMsg.SendToVsService(Guids.CustomDebuggerEventHandlerGuid, IsBlocking: true); } else if (PythonDLLs.DebuggerHelperNames.Contains(moduleInstance.Name)) { moduleInstance.TryLoadSymbols(); // When the module is reported is loaded, it is not necessarily fully initialized yet - it is possible to get into a state // where its import table is not processed yet. If we register TraceFunc and it gets called by Python when in that state, // we'll get a crash as soon as any imported WinAPI function is called. So check whether DllMain has already run - if it // is, we're good to go, and if not, set a breakpoint on a hook that will be called once it is run, and defer runtime // creation until that breakpoint is hit. bool isInitialized = moduleInstance.GetExportedStaticVariable <ByteProxy>("isInitialized").Read() != 0; if (isInitialized) { OnHelperDllInitialized(moduleInstance); } else { DkmRuntimeBreakpoint initBP = null; initBP = CreateRuntimeDllExportedFunctionBreakpoint(moduleInstance, "OnInitialized", (thread, frameBase, vFrame) => { initBP.Close(); OnHelperDllInitialized(moduleInstance); }); initBP.Enable(); } } }
public DkmStackWalkFrame[] FilterNextFrame(DkmStackContext stackContext, DkmStackWalkFrame nativeFrame) { PyFrameObject pythonFrame = null; var nativeModuleInstance = nativeFrame.ModuleInstance; if (nativeModuleInstance == _pyrtInfo.DLLs.DebuggerHelper) { if (_pyrtInfo.LanguageVersion < PythonLanguageVersion.V36 || (pythonFrame = PyFrameObject.TryCreate(nativeFrame)) == null) { return(DebuggerOptions.ShowNativePythonFrames ? new[] { nativeFrame } : new DkmStackWalkFrame[0]); } } var result = new List <DkmStackWalkFrame>(); if (pythonFrame == null) { var stackWalkData = stackContext.GetDataItem <StackWalkContextData>(); if (stackWalkData == null) { stackWalkData = new StackWalkContextData(); stackContext.SetDataItem(DkmDataCreationDisposition.CreateNew, stackWalkData); } bool?wasLastFrameNative = stackWalkData.IsLastFrameNative; if (nativeModuleInstance != _pyrtInfo.DLLs.Python && nativeModuleInstance != _pyrtInfo.DLLs.CTypes) { stackWalkData.IsLastFrameNative = true; if (wasLastFrameNative == false) { result.Add(DkmStackWalkFrame.Create(nativeFrame.Thread, null, nativeFrame.FrameBase, nativeFrame.FrameSize, DkmStackWalkFrameFlags.NonuserCode, Strings.DebugCallStackNativeToPythonTransition, null, null)); } else { stackWalkData.IsLastFrameNative = true; } result.Add(nativeFrame); return(result.ToArray()); } else { stackWalkData.IsLastFrameNative = false; if (wasLastFrameNative == true) { result.Add(DkmStackWalkFrame.Create(nativeFrame.Thread, null, nativeFrame.FrameBase, nativeFrame.FrameSize, DkmStackWalkFrameFlags.NonuserCode, Strings.DebugCallStackPythonToNativeTransition, null, null)); } } pythonFrame = PyFrameObject.TryCreate(nativeFrame); } if (pythonFrame == null) { if (DebuggerOptions.ShowNativePythonFrames) { result.Add(nativeFrame); } return(result.ToArray()); } PyCodeObject code = pythonFrame.f_code.Read(); var loc = new SourceLocation( code.co_filename.Read().ToStringOrNull(), pythonFrame.f_lineno.Read(), code.co_name.Read().ToStringOrNull(), nativeFrame.InstructionAddress as DkmNativeInstructionAddress); var pythonRuntime = _process.GetPythonRuntimeInstance(); var pythonModuleInstances = pythonRuntime.GetModuleInstances().OfType <DkmCustomModuleInstance>(); var pyModuleInstance = pythonModuleInstances.Where(m => m.FullName == loc.FileName).FirstOrDefault(); if (pyModuleInstance == null) { pyModuleInstance = pythonModuleInstances.Single(m => m.Module.Id.Mvid == Guids.UnknownPythonModuleGuid); } var encodedLocation = loc.Encode(); var instrAddr = DkmCustomInstructionAddress.Create(pythonRuntime, pyModuleInstance, encodedLocation, 0, encodedLocation, null); var frame = DkmStackWalkFrame.Create( nativeFrame.Thread, instrAddr, nativeFrame.FrameBase, nativeFrame.FrameSize, DkmStackWalkFrameFlags.None, null, nativeFrame.Registers, nativeFrame.Annotations); result.Add(frame); if (DebuggerOptions.ShowNativePythonFrames) { result.Add(nativeFrame); } return(result.ToArray()); }
private void OnStepTargetBreakpoint(DkmThread thread) { if (_stepper == null) { Debug.Fail("OnStepTargetBreakpoint called but no step operation is in progress."); throw new InvalidOperationException(); } if (_stepper.StepKind == DkmStepKind.Into) { StepDone(thread).OnStepArbitration(DkmStepArbitrationReason.ExitRuntime, _process.GetPythonRuntimeInstance()); } else { // Just because we hit the return breakpoint doesn't mean that we've actually returned - it could be // a recursive call. Check stack depth to distinguish this from an actual return. var beginState = _stepper.GetDataItem <StepBeginState>(); if (beginState != null) { thread.GetCurrentFrameInfo(out global::System.UInt64 retAddr, out global::System.UInt64 frameBase, out global::System.UInt64 vframe); if (frameBase > beginState.FrameBase) { OnStepComplete(thread); } } } }