2
// RemoteSoftDebuggerSession.cs
5
// Michael Hutchinson <mhutchinson@novell.com>
7
// Copyright (c) 2009 Novell, Inc.
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
// of this software and associated documentation files (the "Software"), to deal
11
// in the Software without restriction, including without limitation the rights
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
// copies of the Software, and to permit persons to whom the Software is
14
// furnished to do so, subject to the following conditions:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
using Mono.Debugger.Soft;
30
using Mono.Debugging.Client;
31
using System.Threading;
32
using System.Diagnostics;
34
using System.Net.Sockets;
36
using System.Collections.Generic;
37
using System.Reflection;
38
using MonoDevelop.Core;
39
using Mono.Debugging.Soft;
41
namespace MonoDevelop.Debugger.Soft
44
public abstract class RemoteSoftDebuggerSession : SoftDebuggerSession
49
Func<bool> retryConnection;
51
/// <summary>Subclasses must implement this to start the session </summary>
52
protected override void OnRun (DebuggerStartInfo startInfo)
54
throw new NotImplementedException ();
57
/// <summary>Starts the debugger listening for a connection over TCP/IP</summary>
58
protected void StartListening (RemoteDebuggerStartInfo dsi)
60
IPEndPoint dbgEP, conEP;
61
PreConnectionInit (dsi, out dbgEP, out conEP);
63
var callback = HandleConnectionCallbackErrors (ListenCallback);
64
OnConnecting (VirtualMachineManager.BeginListen (dbgEP, conEP, callback));
65
ShowConnectingDialog (dsi);
68
/// <summary>Starts the debugger connecting to a remote IP</summary>
69
protected void StartConnecting (RemoteDebuggerStartInfo dsi, int maxAttempts, int timeBetweenAttempts)
71
if (timeBetweenAttempts < 0 || timeBetweenAttempts > 10000)
72
throw new ArgumentException ("timeBetweenAttempts");
74
IPEndPoint dbgEP, conEP;
75
PreConnectionInit (dsi, out dbgEP, out conEP);
77
var callback = HandleConnectionCallbackErrors (ConnectCallback);
79
retryConnection = () => {
80
if (maxAttempts == 1 || Exited) {
86
if (timeBetweenAttempts > 0)
87
System.Threading.Thread.Sleep (timeBetweenAttempts);
89
OnConnecting (VirtualMachineManager.BeginConnect (dbgEP, conEP, callback));
90
} catch (Exception ex2) {
91
retryConnection = null;
92
OnConnectionError (ex2);
98
ShowConnectingDialog (dsi);
100
OnConnecting (VirtualMachineManager.BeginConnect (dbgEP, conEP, callback));
103
void PreConnectionInit (RemoteDebuggerStartInfo dsi, out IPEndPoint dbgEP, out IPEndPoint conEP)
106
throw new InvalidOperationException ("Cannot initialize connection more than once");
108
appName = dsi.AppName;
109
RegisterUserAssemblies (dsi.UserAssemblyNames);
111
dbgEP = new IPEndPoint (dsi.Address, dsi.DebugPort);
112
conEP = dsi.RedirectOutput? new IPEndPoint (dsi.Address, dsi.OutputPort) : null;
114
if (!String.IsNullOrEmpty (dsi.LogMessage))
115
LogWriter (false, dsi.LogMessage + "\n");
118
protected override void OnConnectionError (Exception ex)
120
if (retryConnection != null) {
121
var sx = ex as SocketException;
123
bool retry = sx.ErrorCode == 10061; //connection refused
124
if (retry && retryConnection ())
127
retryConnection = null;
130
base.OnConnectionError (ex);
135
Gtk.Application.Invoke (delegate {
137
dialog.Respond (Gtk.ResponseType.Ok);
141
//get rid of the dialog if there's an exception while connecting
142
protected override bool HandleException (Exception ex)
144
if (dialog != null) {
145
Gtk.Application.Invoke (delegate {
147
dialog.Respond (Gtk.ResponseType.Ok);
150
return base.HandleException (ex);
153
void ListenCallback (IAsyncResult ar)
155
HandleConnection (VirtualMachineManager.EndListen (ar));
159
void ConnectCallback (IAsyncResult ar)
161
HandleConnection (VirtualMachineManager.EndConnect (ar));
162
retryConnection = null;
167
protected virtual string GetListenMessage (RemoteDebuggerStartInfo dsi)
169
return GettextCatalog.GetString ("Waiting for debugger to connect...");
172
protected virtual string GetConnectingMessage (RemoteDebuggerStartInfo dsi)
174
//ignore the Obsolete warning
175
#pragma warning disable 0612
176
return GetListenMessage (dsi);
177
#pragma warning restore 0612
180
void ShowConnectingDialog (RemoteDebuggerStartInfo dsi)
182
string message = GetConnectingMessage (dsi);
184
Gtk.Application.Invoke (delegate {
185
if (VirtualMachine != null || Exited)
188
dialog = new Gtk.Dialog () {
189
Title = "Waiting for debugger"
192
var label = new Gtk.Alignment (0.5f, 0.5f, 1f, 1f) {
193
Child = new Gtk.Label (message),
196
dialog.VBox.PackStart (label);
199
dialog.AddButton ("Cancel", Gtk.ResponseType.Cancel);
201
int response = MonoDevelop.Ide.MessageService.ShowCustomDialog (dialog);
205
if (response != (int) Gtk.ResponseType.Ok) {
211
protected override void EndSession ()
213
if (dialog != null) {
214
Gtk.Application.Invoke (delegate {
216
dialog.Respond (Gtk.ResponseType.Cancel);
222
protected override void OnResumed ()
228
protected override ProcessInfo[] OnGetProcesses ()
231
procs = new ProcessInfo[] { new ProcessInfo (0, appName) };
235
protected override void EnsureExited ()
237
//no-op, we can't kill remote processes
241
public class RemoteDebuggerStartInfo : DebuggerStartInfo
243
public IPAddress Address { get; private set; }
244
public int DebugPort { get; private set; }
245
public int OutputPort { get; private set; }
246
public bool RedirectOutput { get; private set; }
247
public string AppName { get; set; }
248
public List<AssemblyName> UserAssemblyNames { get; set; }
250
public RemoteDebuggerStartInfo (string appName, IPAddress address, int debugPort)
251
: this (appName, address, debugPort, false, 0) {}
253
public RemoteDebuggerStartInfo (string appName, IPAddress address, int debugPort, int outputPort)
254
: this (appName, address, debugPort, outputPort > 0, outputPort) {}
256
RemoteDebuggerStartInfo (string appName, IPAddress address, int debugPort, bool redirectOutput, int outputPort)
258
SoftDebuggerEngine.EnsureSdbLoggingService ();
259
this.AppName = appName;
260
this.Address = address;
261
this.DebugPort = debugPort;
262
this.OutputPort = outputPort;
263
this.RedirectOutput = redirectOutput;
266
internal string LogMessage { get; private set; }
268
public void SetUserAssemblies (IList<string> files)
271
UserAssemblyNames = SoftDebuggerEngine.GetAssemblyNames (files, out error);