private PythonProcess(int pid, PythonDebugOptions debugOptions) { _pid = pid; _process = Process.GetProcessById(pid); _process.EnableRaisingEvents = true; _process.Exited += new EventHandler(_process_Exited); ListenForConnection(); using (var result = DebugAttach.AttachAD7(pid, DebugConnectionListener.ListenerPort, _processGuid, debugOptions.ToString())) { if (result.Error != ConnErrorMessages.None) { throw new ConnectionException(result.Error); } _langVersion = (PythonLanguageVersion)result.LanguageVersion; if (!result.AttachDone.WaitOne(20000)) { throw new ConnectionException(ConnErrorMessages.TimeOut); } } }
internal static PythonProcess DebugProcess(this PythonDebugger debugger, PythonVersion version, string filename, Action<PythonProcess, PythonThread> onLoaded = null, bool resumeOnProcessLoaded = true, string interpreterOptions = null, PythonDebugOptions debugOptions = PythonDebugOptions.RedirectOutput, string cwd = null, string arguments = "") { string fullPath = Path.GetFullPath(filename); string dir = cwd ?? Path.GetFullPath(Path.GetDirectoryName(filename)); if (!String.IsNullOrEmpty(arguments)) { arguments = "\"" + fullPath + "\" " + arguments; } else { arguments = "\"" + fullPath + "\""; } var process = debugger.CreateProcess(version.Version, version.InterpreterPath, arguments, dir, "", interpreterOptions, debugOptions); process.DebuggerOutput += (sender, args) => { Console.WriteLine("{0}: {1}", args.Thread.Id, args.Output); }; process.ProcessLoaded += (sender, args) => { if (onLoaded != null) { onLoaded(process, args.Thread); } if (resumeOnProcessLoaded) { process.Resume(); } }; return process; }
// TODO: turn PythonDebugOptions into a class that encapsulates all options (not just flags), including the "not set" // state for all of them, and that knows how to stringify and parse itself, and how to merge isntances, and refactor // this entire codepath, including the bits in DefaultPythonLauncher and in CustomDebuggerEventHandler, to use that. private void ParseOptions(string options) { foreach (var optionSetting in SplitOptions(options)) { var setting = optionSetting.Split(new[] { '=' }, 2); if (setting.Length == 2) { switch (setting[0]) { case VersionSetting: _languageVersion = GetLanguageVersion(setting[1]); break; case WaitOnAbnormalExitSetting: bool value; if (Boolean.TryParse(setting[1], out value) && value) { _debugOptions |= PythonDebugOptions.WaitOnAbnormalExit; } break; case WaitOnNormalExitSetting: if (Boolean.TryParse(setting[1], out value) && value) { _debugOptions |= PythonDebugOptions.WaitOnNormalExit; } break; case RedirectOutputSetting: if (Boolean.TryParse(setting[1], out value) && value) { _debugOptions |= PythonDebugOptions.RedirectOutput; } break; case BreakSystemExitZero: if (Boolean.TryParse(setting[1], out value) && value) { _debugOptions |= PythonDebugOptions.BreakOnSystemExitZero; } break; case DebugStdLib: if (Boolean.TryParse(setting[1], out value) && value) { _debugOptions |= PythonDebugOptions.DebugStdLib; } break; case IsWindowsApplication: if (Boolean.TryParse(setting[1], out value) && value) { _debugOptions |= PythonDebugOptions.IsWindowsApplication; } break; case DirMappingSetting: string[] dirs = setting[1].Split('|'); if (dirs.Length == 2) { if (_dirMapping == null) { _dirMapping = new List<string[]>(); } Debug.WriteLine(String.Format("Mapping dir {0} to {1}", dirs[0], dirs[1])); _dirMapping.Add(dirs); } break; case InterpreterOptions: _interpreterOptions = setting[1]; break; case AttachRunning: if (Boolean.TryParse(setting[1], out value) && value) { _debugOptions |= PythonDebugOptions.AttachRunning; } break; case WebBrowserUrl: _webBrowserUrl = HttpUtility.UrlDecode(setting[1]); break; case EnableDjangoDebugging: if (Boolean.TryParse(setting[1], out value) && value) { _debugOptions |= PythonDebugOptions.DjangoDebugging; } break; } } } }
private new PythonProcess DebugProcess(PythonDebugger debugger, string filename, Action<PythonProcess, PythonThread> onLoaded = null, bool resumeOnProcessLoaded = true, string interpreterOptions = null, PythonDebugOptions debugOptions = PythonDebugOptions.RedirectOutput, string cwd = null, string pythonExe = null) { string fullPath = Path.GetFullPath(filename); string dir = cwd ?? Path.GetFullPath(Path.GetDirectoryName(filename)); var process = debugger.CreateProcess(Version.Version, pythonExe ?? Version.InterpreterPath, "\"" + fullPath + "\"", dir, "", interpreterOptions, debugOptions); process.ProcessLoaded += (sender, args) => { if (onLoaded != null) { onLoaded(process, args.Thread); } if (resumeOnProcessLoaded) { process.Resume(); } }; return process; }
private void TestException( PythonDebugger debugger, string filename, bool resumeProcess, ExceptionMode defaultExceptionMode, ICollection<KeyValuePair<string, ExceptionMode>> exceptionModes, PythonDebugOptions debugOptions, params ExceptionInfo[] exceptions ) { Console.WriteLine(); Console.WriteLine("Testing {0}", filename); bool loaded = false; var process = DebugProcess(debugger, filename, (processObj, threadObj) => { loaded = true; processObj.SetExceptionInfo( (int)defaultExceptionMode, exceptionModes == null ? Enumerable.Empty<KeyValuePair<string, int>>() : exceptionModes.Select(m => new KeyValuePair<string, int>(m.Key, (int)m.Value)) ); }, debugOptions: debugOptions); var raised = new List<Tuple<string, string>>(); process.ExceptionRaised += (sender, args) => { if (loaded) { raised.Add(Tuple.Create(args.Exception.TypeName, TryGetStack(args.Thread))); } if (resumeProcess) { process.Resume(); } else { args.Thread.Resume(); } }; StartAndWaitForExit(process); if (Version.Version == PythonLanguageVersion.V30 && raised.Count > exceptions.Length) { // Python 3.0 raises an exception as the process shuts down. raised.RemoveAt(raised.Count - 1); } if (GetType() == typeof(DebuggerTestsIpy) && raised.Count == exceptions.Length + 1) { // IronPython over-reports exceptions raised.RemoveAt(raised.Count - 1); } foreach (var t in raised) { Console.WriteLine("Received {0} at{1}{2}", t.Item1, Environment.NewLine, t.Item2); } AssertUtil.AreEqual( raised.Select(t => t.Item1), exceptions.Select(e => e.TypeName).ToArray() ); }
private PythonProcess(Stream stream, int pid, PythonLanguageVersion version, PythonDebugOptions debugOptions) { _pid = pid; _process = Process.GetProcessById(pid); _process.EnableRaisingEvents = true; _process.Exited += new EventHandler(_process_Exited); _delayUnregister = true; ListenForConnection(); stream.WriteInt32(DebugConnectionListener.ListenerPort); stream.WriteString(_processGuid.ToString()); stream.WriteString(debugOptions.ToString()); }
public static PythonProcess AttachRepl(Stream stream, int pid, PythonLanguageVersion version, PythonDebugOptions debugOptions = PythonDebugOptions.None) { return new PythonProcess(stream, pid, version, debugOptions); }
/// <summary> /// Creates a new PythonProcess object for debugging. The process does not start until Start is called /// on the returned PythonProcess object. /// </summary> public PythonProcess CreateProcess(PythonLanguageVersion langVersion, string exe, string args, string dir, string env, string interpreterOptions = null, PythonDebugOptions debugOptions = PythonDebugOptions.None, TextWriter debugLog = null) { return(new PythonProcess(langVersion, exe, args, dir, env, interpreterOptions, debugOptions, debugLog)); }
public PythonProcess(PythonLanguageVersion languageVersion, string exe, string args, string dir, string env, string interpreterOptions, PythonDebugOptions options = PythonDebugOptions.None, List<string[]> dirMapping = null) : this(0, languageVersion) { ListenForConnection(); if (dir.EndsWith("\\")) { dir = dir.Substring(0, dir.Length - 1); } _dirMapping = dirMapping; var processInfo = new ProcessStartInfo(exe); processInfo.CreateNoWindow = (options & PythonDebugOptions.CreateNoWindow) != 0; processInfo.UseShellExecute = false; processInfo.RedirectStandardOutput = false; processInfo.RedirectStandardInput = (options & PythonDebugOptions.RedirectInput) != 0; processInfo.Arguments = (String.IsNullOrWhiteSpace(interpreterOptions) ? "" : (interpreterOptions + " ")) + "\"" + PythonToolsInstallPath.GetFile("visualstudio_py_launcher.py") + "\" " + "\"" + dir + "\" " + " " + DebugConnectionListener.ListenerPort + " " + " " + _processGuid + " " + "\"" + options + "\" " + args; if (env != null) { string[] envValues = env.Split('\0'); foreach (var curValue in envValues) { string[] nameValue = curValue.Split(new[] { '=' }, 2); if (nameValue.Length == 2 && !String.IsNullOrWhiteSpace(nameValue[0])) { processInfo.EnvironmentVariables[nameValue[0]] = nameValue[1]; } } } Debug.WriteLine(String.Format("Launching: {0} {1}", processInfo.FileName, processInfo.Arguments)); _process = new Process(); _process.StartInfo = processInfo; _process.EnableRaisingEvents = true; _process.Exited += new EventHandler(_process_Exited); }
internal async Task StepTestAsync(string filename, string breakFile, string arguments, int[] breakLines, Action <PythonProcess>[] breakAction, Action processLoaded, PythonDebugOptions options = PythonDebugOptions.RedirectOutput, bool waitForExit = true, params ExpectedStep[] kinds) { Console.WriteLine("--- Begin Step Test ---"); var debugger = new PythonDebugger(); if (breakFile == null) { breakFile = filename; } string fullPath = Path.GetFullPath(filename); string dir = Path.GetDirectoryName(filename); var process = debugger.CreateProcess(Version.Version, Version.InterpreterPath, "\"" + fullPath + "\" " + (arguments ?? ""), dir, "", null, options, DebugLog); try { PythonThread thread = null; process.ThreadCreated += (sender, args) => { thread = args.Thread; }; AutoResetEvent processEvent = new AutoResetEvent(false); bool processLoad = false, stepComplete = false; process.ProcessLoaded += async(sender, args) => { foreach (var breakLine in breakLines) { var bp = process.AddBreakpointByFileExtension(breakLine, breakFile); await bp.AddAsync(TimeoutToken()); } processLoad = true; processEvent.Set(); processLoaded?.Invoke(); }; process.StepComplete += (sender, args) => { stepComplete = true; processEvent.Set(); }; int breakHits = 0; ExceptionDispatchInfo edi = null; process.BreakpointHit += (sender, args) => { try { Console.WriteLine("Breakpoint hit"); if (breakAction != null) { if (breakHits >= breakAction.Length) { Assert.Fail("Unexpected breakpoint hit at {0}:{1}", args.Breakpoint.Filename, args.Breakpoint.LineNo); } breakAction[breakHits++](process); } stepComplete = true; processEvent.Set(); } catch (Exception ex) { edi = ExceptionDispatchInfo.Capture(ex); try { processEvent.Set(); } catch { } } }; await process.StartAsync(); for (int curStep = 0; curStep < kinds.Length; curStep++) { Console.WriteLine("Step {0} {1}", curStep, kinds[curStep].Kind); // process the stepping events as they occur, we cannot callback during the // event because the notificaiton happens on the debugger thread and we // need to callback to get the frames. AssertWaited(processEvent); edi?.Throw(); // first time through we hit process load, each additional time we should hit step complete. Debug.Assert((processLoad == true && stepComplete == false && curStep == 0) || (stepComplete == true && processLoad == false && curStep != 0)); processLoad = stepComplete = false; var frames = thread.Frames; var stepInfo = kinds[curStep]; Assert.AreEqual(stepInfo.StartLine, frames[0].LineNo, String.Format("{0} != {1} on {2} step", stepInfo.StartLine, frames[0].LineNo, curStep)); switch (stepInfo.Kind) { case StepKind.Into: await thread.StepIntoAsync(TimeoutToken()); break; case StepKind.Out: await thread.StepOutAsync(TimeoutToken()); break; case StepKind.Over: await thread.StepOverAsync(TimeoutToken()); break; case StepKind.Resume: await process.ResumeAsync(TimeoutToken()); break; } } if (waitForExit) { WaitForExit(process); } } finally { process.Terminate(); } }
internal async Task <PythonThread> RunAndBreakAsync(string filename, int lineNo, string breakFilename = null, string arguments = "", Action processLoaded = null, PythonDebugOptions debugOptions = PythonDebugOptions.RedirectOutput) { PythonThread thread; var debugger = new PythonDebugger(); thread = null; PythonProcess process = DebugProcess(debugger, DebuggerTestPath + filename, async(newproc, newthread) => { var breakPoint = newproc.AddBreakpointByFileExtension(lineNo, breakFilename ?? filename); await breakPoint.AddAsync(TimeoutToken()); thread = newthread; processLoaded?.Invoke(); }, arguments: arguments, debugOptions: debugOptions); AutoResetEvent brkHit = new AutoResetEvent(false); process.BreakpointHit += (sender, args) => { thread = args.Thread; brkHit.Set(); }; bool ready = false; try { await process.StartAsync(); AssertWaited(brkHit); ready = true; } finally { if (!ready) { process.Terminate(); } } return(thread); }
internal PythonProcess DebugProcess(PythonDebugger debugger, string filename, Func <PythonProcess, PythonThread, Task> onLoaded = null, bool resumeOnProcessLoaded = true, string interpreterOptions = null, PythonDebugOptions debugOptions = PythonDebugOptions.RedirectOutput, string cwd = null, string arguments = "") { return(debugger.DebugProcess(Version, filename, DebugLog, onLoaded, resumeOnProcessLoaded, interpreterOptions, debugOptions, cwd, arguments)); }
/// <summary> /// Creates a new PythonProcess object for debugging. The process does not start until Start is called /// on the returned PythonProcess object. /// </summary> public PythonProcess CreateProcess(PythonLanguageVersion langVersion, string exe, string args, string dir, string env, string interpreterOptions = null, PythonDebugOptions debugOptions = PythonDebugOptions.None) { return new PythonProcess(langVersion, exe, args, dir, env, interpreterOptions, debugOptions); }
// Launches a process by means of the debug engine. // Normally, Visual Studio launches a program using the IDebugPortEx2::LaunchSuspended method and then attaches the debugger // to the suspended program. However, there are circumstances in which the debug engine may need to launch a program // (for example, if the debug engine is part of an interpreter and the program being debugged is an interpreted language), // in which case Visual Studio uses the IDebugEngineLaunch2::LaunchSuspended method // The IDebugEngineLaunch2::ResumeProcess method is called to start the process after the process has been successfully launched in a suspended state. int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 port, string exe, string args, string dir, string env, string options, enum_LAUNCH_FLAGS launchFlags, uint hStdInput, uint hStdOutput, uint hStdError, IDebugEventCallback2 ad7Callback, out IDebugProcess2 process) { process = null; if (_mixedMode) { return VSConstants.E_NOTIMPL; } Debug.WriteLine("--------------------------------------------------------------------------------"); Debug.WriteLine("PythonEngine LaunchSuspended Begin " + launchFlags + " " + GetHashCode()); AssertMainThread(); Debug.Assert(_events == null); Debug.Assert(_process == null); Debug.Assert(_ad7ProgramId == Guid.Empty); _events = ad7Callback; _engineCreated = _programCreated = false; _loadComplete.Reset(); if (options != null) { ParseOptions(options); } Send(new AD7CustomEvent(VsPackageMessage.SetDebugOptions, this), AD7CustomEvent.IID, null, null); // If this is a windowed application, there's no console to wait on, so disable those flags if they were set. if (_debugOptions.HasFlag(PythonDebugOptions.IsWindowsApplication)) { _debugOptions &= ~(PythonDebugOptions.WaitOnNormalExit | PythonDebugOptions.WaitOnAbnormalExit); } Guid processId; if (_debugOptions.HasFlag(PythonDebugOptions.AttachRunning)) { if (!Guid.TryParse(exe, out processId)) { Debug.Fail("When PythonDebugOptions.AttachRunning is used, the 'exe' parameter must be a debug session GUID."); return VSConstants.E_INVALIDARG; } _process = DebugConnectionListener.GetProcess(processId); _attached = true; _pseudoAttach = true; } else { _process = new PythonProcess(_languageVersion, exe, args, dir, env, _interpreterOptions, _debugOptions, _dirMapping); } if (!_debugOptions.HasFlag(PythonDebugOptions.AttachRunning)) { _process.Start(false); } AttachEvents(_process); AD_PROCESS_ID adProcessId = new AD_PROCESS_ID(); adProcessId.ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_SYSTEM; adProcessId.dwProcessId = (uint)_process.Id; EngineUtils.RequireOk(port.GetProcess(adProcessId, out process)); Debug.WriteLine("PythonEngine LaunchSuspended returning S_OK"); Debug.Assert(process != null); Debug.Assert(!_process.HasExited); return VSConstants.S_OK; }
public static PythonProcess Attach(int pid, PythonDebugOptions debugOptions = PythonDebugOptions.None) { return new PythonProcess(pid, debugOptions); }
internal static PythonProcess DebugProcess(this PythonDebugger debugger, PythonVersion version, string filename, Func <PythonProcess, PythonThread, Task> onLoaded = null, bool resumeOnProcessLoaded = true, string interpreterOptions = null, PythonDebugOptions debugOptions = PythonDebugOptions.RedirectOutput, string cwd = null, string arguments = "") { string fullPath = Path.GetFullPath(filename); string dir = cwd ?? Path.GetFullPath(Path.GetDirectoryName(filename)); if (!String.IsNullOrEmpty(arguments)) { arguments = "\"" + fullPath + "\" " + arguments; } else { arguments = "\"" + fullPath + "\""; } var process = debugger.CreateProcess(version.Version, version.InterpreterPath, arguments, dir, "", interpreterOptions, debugOptions); process.DebuggerOutput += (sender, args) => { Console.WriteLine("{0}: {1}", args.Thread?.Id, args.Output); }; process.ProcessLoaded += async(sender, args) => { if (onLoaded != null) { await onLoaded(process, args.Thread); } if (resumeOnProcessLoaded) { await process.ResumeAsync(default(CancellationToken)); } }; return(process); }
public AD7Engine() { _breakpointManager = new BreakpointManager(this); _defaultBreakOnExceptionMode = (int)enum_EXCEPTION_STATE.EXCEPTION_STOP_USER_UNCAUGHT; _debugOptions = PythonDebugOptions.RichExceptions; Debug.WriteLine("Python Engine Created " + GetHashCode()); _engines.Add(new WeakReference(this)); }