Beispiel #1
0
            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);
            }
Beispiel #2
0
            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();
            }
Beispiel #3
0
        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);
        }
Beispiel #4
0
            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();
                    }
                }
            }
Beispiel #5
0
        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());
        }
Beispiel #6
0
        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);
                    }
                }
            }
        }