// Binds this pending breakpoint to one or more code locations. int IDebugPendingBreakpoint2.Bind() { if (CanBind()) { IDebugDocumentPosition2 docPosition = (IDebugDocumentPosition2)(Marshal.GetObjectForIUnknown(_bpRequestInfo.bpLocation.unionmember2)); // Get the name of the document that the breakpoint was put in string documentName; EngineUtils.CheckOk(docPosition.GetFileName(out documentName)); // Get the location in the document that the breakpoint is in. TEXT_POSITION[] startPosition = new TEXT_POSITION[1]; TEXT_POSITION[] endPosition = new TEXT_POSITION[1]; EngineUtils.CheckOk(docPosition.GetRange(startPosition, endPosition)); lock (_boundBreakpoints) { var bp = _engine.Process.AddBreakPoint(documentName, (int)(startPosition[0].dwLine + 1), _bpRequestInfo.bpCondition.bstrCondition, _bpRequestInfo.bpCondition.styleCondition == enum_BP_COND_STYLE.BP_COND_WHEN_TRUE ? false : true); AD7BreakpointResolution breakpointResolution = new AD7BreakpointResolution(_engine, bp, GetDocumentContext(bp)); AD7BoundBreakpoint boundBreakpoint = new AD7BoundBreakpoint(_engine, bp, this, breakpointResolution); _boundBreakpoints.Add(boundBreakpoint); _bpManager.AddBoundBreakpoint(bp, boundBreakpoint); if (_enabled) { bp.Add(); } } return(VSConstants.S_OK); } else { // The breakpoint could not be bound. This may occur for many reasons such as an invalid location, an invalid expression, etc... // The sample engine does not support this, but a real world engine will want to send an instance of IDebugBreakpointErrorEvent2 to the // UI and return a valid instance of IDebugErrorBreakpoint2 from IDebugPendingBreakpoint2::EnumErrorBreakpoints. The debugger will then // display information about why the breakpoint did not bind to the user. return(VSConstants.S_FALSE); } }
// Binds this pending breakpoint to one or more code locations. int IDebugPendingBreakpoint2.Bind() { if (CanBind()) { IDebugDocumentPosition2 docPosition = (IDebugDocumentPosition2)(Marshal.GetObjectForIUnknown(_bpRequestInfo.bpLocation.unionmember2)); // Get the name of the document that the breakpoint was put in string documentName; EngineUtils.CheckOk(docPosition.GetFileName(out documentName)); // Get the location in the document that the breakpoint is in. TEXT_POSITION[] startPosition = new TEXT_POSITION[1]; TEXT_POSITION[] endPosition = new TEXT_POSITION[1]; EngineUtils.CheckOk(docPosition.GetRange(startPosition, endPosition)); lock (_boundBreakpoints) { if (_bpRequestInfo.guidLanguage == DebuggerConstants.guidLanguagePython) { var bp = _engine.Process.AddBreakPoint( documentName, (int)(startPosition[0].dwLine + 1), _bpRequestInfo.bpCondition.styleCondition.ToPython(), _bpRequestInfo.bpCondition.bstrCondition, _bpRequestInfo.bpPassCount.stylePassCount.ToPython(), (int)_bpRequestInfo.bpPassCount.dwPassCount); AD7BreakpointResolution breakpointResolution = new AD7BreakpointResolution(_engine, bp, GetDocumentContext(bp)); AD7BoundBreakpoint boundBreakpoint = new AD7BoundBreakpoint(_engine, bp, this, breakpointResolution, _enabled); _boundBreakpoints.Add(boundBreakpoint); _bpManager.AddBoundBreakpoint(bp, boundBreakpoint); if (_enabled) { bp.Add(); } return(VSConstants.S_OK); } else if (_bpRequestInfo.guidLanguage == DebuggerConstants.guidLanguageDjangoTemplate) { // bind a Django template var bp = _engine.Process.AddDjangoBreakPoint( documentName, (int)(startPosition[0].dwLine + 1) ); AD7BreakpointResolution breakpointResolution = new AD7BreakpointResolution(_engine, bp, GetDocumentContext(bp)); AD7BoundBreakpoint boundBreakpoint = new AD7BoundBreakpoint(_engine, bp, this, breakpointResolution, _enabled); _boundBreakpoints.Add(boundBreakpoint); _bpManager.AddBoundBreakpoint(bp, boundBreakpoint); if (_enabled) { bp.Add(); } return(VSConstants.S_OK); } } } // The breakpoint could not be bound. This may occur for many reasons such as an invalid location, an invalid expression, etc... // The Python engine does not support this. // TODO: send an instance of IDebugBreakpointErrorEvent2 to the UI and return a valid instance of IDebugErrorBreakpoint2 from // IDebugPendingBreakpoint2::EnumErrorBreakpoints. The debugger will then display information about why the breakpoint did not // bind to the user. return(VSConstants.S_FALSE); }
// Resume a process launched by IDebugEngineLaunch2.LaunchSuspended int IDebugEngineLaunch2.ResumeProcess(IDebugProcess2 process) { Debug.WriteLine("Python Debugger ResumeProcess Begin"); AssertMainThread(); if (_events == null) { // process failed to start Debug.WriteLine("ResumeProcess fails, no events"); return(VSConstants.E_FAIL); } Debug.Assert(_events != null); Debug.Assert(_process != null); Debug.Assert(_process != null); Debug.Assert(_ad7ProgramId == Guid.Empty); int processId = EngineUtils.GetProcessId(process); if (processId != _process.Id) { Debug.WriteLine("ResumeProcess fails, wrong process"); return(VSConstants.S_FALSE); } // Send a program node to the SDM. This will cause the SDM to turn around and call IDebugEngine2.Attach // which will complete the hookup with AD7 IDebugPort2 port; EngineUtils.RequireOk(process.GetPort(out port)); IDebugDefaultPort2 defaultPort = (IDebugDefaultPort2)port; IDebugPortNotify2 portNotify; EngineUtils.RequireOk(defaultPort.GetPortNotify(out portNotify)); EngineUtils.RequireOk(portNotify.AddProgramNode(new AD7ProgramNode(_process.Id))); if (_ad7ProgramId == Guid.Empty) { Debug.WriteLine("ResumeProcess fails, empty program guid"); Debug.Fail("Unexpected problem -- IDebugEngine2.Attach wasn't called"); return(VSConstants.E_FAIL); } // wait for the load event to complete, and pump messages while (!_process.HasExited && !_loadComplete.WaitOne(100)) { Debug.WriteLine("ResumeProcess waiting for load complete"); } // Resume the threads in the debuggee process if (_process.HasExited) { Debug.WriteLine("ResumeProcess resume all"); _process.Resume(); } else { // return failure? Debug.WriteLine("Process exited"); } Debug.WriteLine("ResumeProcess return S_OK"); return(VSConstants.S_OK); }
// 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) { Debug.WriteLine("PythonEngine LaunchSuspended Begin " + launchFlags + " " + GetHashCode()); AssertMainThread(); Debug.Assert(_events == null); Debug.Assert(_process == null); Debug.Assert(_ad7ProgramId == Guid.Empty); process = null; _events = ad7Callback; PythonLanguageVersion version = DefaultVersion; bool waitOnAbnormalExit = false; bool redirectOutput = false; List <string[]> dirMapping = null; if (options != null) { var splitOptions = options.Split(new[] { ';' }, 2); foreach (var optionSetting in splitOptions) { var setting = optionSetting.Split(new[] { '=' }, 2); if (setting.Length == 2) { switch (setting[0]) { case VersionSetting: version = GetLanguageVersion(setting[1]); break; case WaitOnAbnormalExitSetting: bool value; if (Boolean.TryParse(setting[1], out value)) { waitOnAbnormalExit = value; } break; case RedirectOutputSetting: if (Boolean.TryParse(setting[1], out value)) { redirectOutput = value; } 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; } } } } _process = new PythonProcess(version, exe, args, dir, env, waitOnAbnormalExit, redirectOutput, dirMapping); AttachEvents(_process); _programCreated = false; _loadComplete.Reset(); _process.Start(); 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); }
// Attach the debug engine to a program. int IDebugEngine2.Attach(IDebugProgram2[] rgpPrograms, IDebugProgramNode2[] rgpProgramNodes, uint celtPrograms, IDebugEventCallback2 ad7Callback, enum_ATTACH_REASON dwReason) { Debug.WriteLine("PythonEngine Attach Begin " + GetHashCode()); AssertMainThread(); Debug.Assert(_ad7ProgramId == Guid.Empty); if (celtPrograms != 1) { Debug.Fail("Python debugging only supports one program in a process"); throw new ArgumentException(); } int processId = EngineUtils.GetProcessId(rgpPrograms[0]); if (processId == 0) { // engine only supports system processes Debug.WriteLine("PythonEngine failed to get process id during attach"); return(VSConstants.E_NOTIMPL); } EngineUtils.RequireOk(rgpPrograms[0].GetProgramId(out _ad7ProgramId)); // Attach can either be called to attach to a new process, or to complete an attach // to a launched process if (_process == null) { // TODO: Where do we get the language version from? _events = ad7Callback; var attachRes = PythonProcess.TryAttach(processId, out _process); if (attachRes != ConnErrorMessages.None) { string msg; switch (attachRes) { case ConnErrorMessages.CannotInjectThread: msg = "Cannot create thread in debuggee process"; break; case ConnErrorMessages.CannotOpenProcess: msg = "Cannot open process for debugging"; break; case ConnErrorMessages.InterpreterNotInitialized: msg = "Python interpreter has not been initialized in this process"; break; case ConnErrorMessages.LoadDebuggerBadDebugger: msg = "Failed to load debugging script (incorrect version of script?)"; break; case ConnErrorMessages.LoadDebuggerFailed: msg = "Failed to compile debugging script"; break; case ConnErrorMessages.OutOfMemory: msg = "Out of memory"; break; case ConnErrorMessages.PythonNotFound: msg = "Python interpreter not found"; break; case ConnErrorMessages.TimeOut: msg = "Timeout while attaching"; break; case ConnErrorMessages.UnknownVersion: msg = "Unknown Python version loaded in process"; break; case ConnErrorMessages.SysNotFound: msg = "sys module not found"; break; case ConnErrorMessages.SysSetTraceNotFound: msg = "settrace not found in sys module"; break; case ConnErrorMessages.PyDebugAttachNotFound: msg = "Cannot find PyDebugAttach.dll at " + attachRes; break; default: msg = "Unknown error"; break; } MessageBox.Show("Failed to attach debugger: " + msg); return(VSConstants.E_FAIL); } AttachEvents(_process); _attached = true; } else { if (processId != _process.Id) { Debug.Fail("Asked to attach to a process while we are debugging"); return(VSConstants.E_FAIL); } _attached = false; } AD7EngineCreateEvent.Send(this); lock (_syncLock) { _programCreated = true; AD7ProgramCreateEvent.Send(this); if (_processLoadedThread != null) { SendLoadComplete(_processLoadedThread); } } Debug.WriteLine("PythonEngine Attach returning S_OK"); return(VSConstants.S_OK); }