public DbgEngDataReader(int pid, bool invasive, uint msecTimeout) { IntPtr client = CreateIDebugClient(); CreateClient(client); DebugAttach attach = invasive ? DebugAttach.Default : DebugAttach.NonInvasive; _control.AddEngineOptions(DebugControl.INITIAL_BREAK); int hr = _client.AttachProcess((uint)pid, attach); if (hr == 0) hr = _control.WaitForEvent(msecTimeout) ? 0 : -1; if (hr == 1) { throw new TimeoutException("Break in did not occur within the allotted timeout."); } if (hr != 0) { if ((uint)hr == 0xd00000bb) throw new InvalidOperationException("Mismatched architecture between this process and the target process."); throw new ClrDiagnosticsException($"Could not attach to process {pid:X}, HRESULT: 0x{hr:x8}", ClrDiagnosticsExceptionKind.DebuggerError, hr); } }
public HResult AttachProcess(uint pid, DebugAttach flags) { InitDelegate(ref _attachProcess, VTable.AttachProcess); HResult hr = _attachProcess(Self, 0, pid, flags); _sys.Init(); return(hr); }
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); }
/// <summary> /// Attaches debugger to the already running specified process. /// </summary> /// <param name="processId">The process identifier.</param> /// <param name="attachFlags">The attaching flags.</param> /// <param name="symbolPaths">Array of paths where debugger will look for symbols.</param> public static void AttachToProcess(uint processId, DebugAttach attachFlags = DebugAttach.Noninvasive, params string[] symbolPaths) { IDebugClient debugClient = DebugClient.DebugCreate(); IDebugSymbols5 symbols = (IDebugSymbols5)debugClient; IDebugControl7 control = (IDebugControl7)debugClient; symbols.SetSymbolPathWide(string.Join(";", symbolPaths)); debugClient.AttachProcess(0, processId, attachFlags); control.WaitForEvent(0, uint.MaxValue); InitializeDbgEng(debugClient); }
public DbgEngDataReader(int processId, bool invasive, uint msecTimeout) { DisplayName = $"{processId:x}"; IntPtr client = CreateIDebugClient(); CreateClient(client); DebugAttach attach = invasive ? DebugAttach.Default : DebugAttach.NonInvasive; _control.AddEngineOptions(DebugControl.INITIAL_BREAK); HResult hr = _client.AttachProcess((uint)processId, attach); if (hr) { hr = _control.WaitForEvent(msecTimeout); } if (hr == HResult.S_FALSE) { throw new TimeoutException("Break in did not occur within the allotted timeout."); } if (hr != 0) { if ((uint)hr.Value == 0xd00000bb) { throw new InvalidOperationException("Mismatched architecture between this process and the target process."); } if (!WindowsFunctions.IsProcessRunning(processId)) { throw new ArgumentException($"Process {processId} is not running."); } throw new ArgumentException($"Could not attach to process {processId}, HRESULT: 0x{hr:x}"); } }
public static int Main(string[] args) { return(DebugAttach.Main(args)); }
// Obtains information about programs running, filtered in a variety of ways. int IDebugProgramProvider2.GetProviderProcessData(enum_PROVIDER_FLAGS Flags, IDebugDefaultPort2 port, AD_PROCESS_ID ProcessId, CONST_GUID_ARRAY EngineFilter, PROVIDER_PROCESS_DATA[] processArray) { processArray[0] = new PROVIDER_PROCESS_DATA(); // we handle creation of the remote program provider ourselves. This is because we always load our program provider locally which keeps // attach working when developing Python Tools and running/debugging from within VS and in the experimental hive. When we are installed // we install into the GAC so these types are available to create and then remote debugging works as well. When we're running in the // experimental hive we are not in the GAC so if we're created outside of VS (e.g. in msvsmon on the local machine) then we can't get // at our program provider and debug->attach doesn't work. if (port != null && port.QueryIsLocal() == VSConstants.S_FALSE) { IDebugCoreServer3 server; if (ErrorHandler.Succeeded(port.GetServer(out server))) { IDebugCoreServer90 dbgServer = server as IDebugCoreServer90; if (dbgServer != null) { Guid g = typeof(IDebugProgramProvider2).GUID; IntPtr remoteProviderPunk; int hr = dbgServer.CreateManagedInstanceInServer(typeof(AD7ProgramProvider).FullName, typeof(AD7ProgramProvider).Assembly.FullName, 0, ref g, out remoteProviderPunk); try { if (ErrorHandler.Succeeded(hr)) { var remoteProvider = (IDebugProgramProvider2)Marshal.GetObjectForIUnknown(remoteProviderPunk); return(remoteProvider.GetProviderProcessData(Flags, null, ProcessId, EngineFilter, processArray)); } } finally { if (remoteProviderPunk != IntPtr.Zero) { Marshal.Release(remoteProviderPunk); } } } } } else if ((Flags & enum_PROVIDER_FLAGS.PFLAG_GET_PROGRAM_NODES) != 0) { // The debugger is asking the engine to return the program nodes it can debug. We check // each process if it has a python##.dll or python##_d.dll loaded and if it does // then we report the program as being a Python process. if (DebugAttach.IsPythonProcess((int)ProcessId.dwProcessId)) { IDebugProgramNode2 node = new AD7ProgramNode((int)ProcessId.dwProcessId); IntPtr[] programNodes = { Marshal.GetComInterfaceForObject(node, typeof(IDebugProgramNode2)) }; IntPtr destinationArray = Marshal.AllocCoTaskMem(IntPtr.Size * programNodes.Length); Marshal.Copy(programNodes, 0, destinationArray, programNodes.Length); processArray[0].Fields = enum_PROVIDER_FIELDS.PFIELD_PROGRAM_NODES; processArray[0].ProgramNodes.Members = destinationArray; processArray[0].ProgramNodes.dwCount = (uint)programNodes.Length; return(VSConstants.S_OK); } } return(VSConstants.S_FALSE); }