OLD | NEW |
| (Empty) |
1 #region | |
2 | |
3 using System; | |
4 using System.Collections; | |
5 using System.Diagnostics; | |
6 using System.IO; | |
7 using System.Net.Sockets; | |
8 using System.Windows.Forms; | |
9 using Google.NaClVsx.DebugSupport; | |
10 using Microsoft.VisualStudio; | |
11 using Microsoft.VisualStudio.Project; | |
12 using Microsoft.VisualStudio.Shell; | |
13 using Microsoft.VisualStudio.Shell.Interop; | |
14 using MSBuild = Microsoft.Build.BuildEngine; | |
15 | |
16 #endregion | |
17 | |
18 namespace Google.NaClVsx.ProjectSupport { | |
19 class NaClProjectConfig : ProjectConfig { | |
20 // Although the NaClProjectConfig class is not implemented as a singleton, | |
21 // it is what launches the debugger with a given nexe, and therefore there | |
22 // should (at least in the short term) be a current Nexe that we are | |
23 // debugging. In order to provide the most recent Nexe launched and still | |
24 // have the flexibility (in the future) to have multiple nexes, as well as | |
25 // to keep track of all the nexes we have tried to debug in a session, | |
26 // the variable |nexeList_| is an ArrayList of strings. | |
27 // Whenever the |DebugLaunch| method is invoked, it adds the current nexe | |
28 // that is being launched (though sel_ldr) to the end of the list. | |
29 | |
30 /// <summary> | |
31 /// Create the NaCl Project config, which sets default properties. | |
32 /// </summary> | |
33 /// <param name = "project"> Project that is created. </param> | |
34 /// <param name = "configuration"> Configuration string, | |
35 /// such as "Debug" or "Release". </param> | |
36 public NaClProjectConfig(ProjectNode project, string configuration) | |
37 : base(project, configuration) { | |
38 // Currently this is hardcoded, since we only support 64-bit debugging. | |
39 Debug.WriteLine("In NaClProjectConfig"); | |
40 SetConfigurationProperty("TargetArch", "x86_64"); | |
41 } | |
42 | |
43 public static string GetLastNexe() { | |
44 // Current assumption -- nexeList_ contains only 1 nexe, but just in | |
45 // case we will grab the last one added to nexeList_. | |
46 // Print an error if nexeList_ contanis more than 1. | |
47 if (nexeList_.Count != 1) { | |
48 Debug.WriteLine( | |
49 "WARNING: nexeList_ Count is " + | |
50 nexeList_.Count); | |
51 foreach (string a_nexe in nexeList_) { | |
52 Debug.WriteLine("NEXE: " + a_nexe); | |
53 } | |
54 } | |
55 if (nexeList_.Count == 0) { | |
56 Debug.WriteLine("ERROR: nexeList_.Count is 0"); | |
57 return ""; | |
58 } | |
59 return (string) nexeList_[nexeList_.Count - 1]; | |
60 } | |
61 | |
62 /// <summary> | |
63 /// Determine if the server is running by connecting to the | |
64 /// specified |hostName| and |portNum|. | |
65 /// </summary> | |
66 /// <param name = "hostName"> Name of the host, such as "localhost". </param
> | |
67 /// <param name = "portNum"> Port number, such as 5103. </param> | |
68 /// <returns> true if we could connect, false otherwise. </returns> | |
69 public static bool IsServerRunning(string hostName, int portNum) { | |
70 var didConnect = false; | |
71 try { | |
72 var socket = new TcpClient(hostName, portNum); | |
73 didConnect = true; | |
74 Debug.WriteLine("Connected to " + hostName + ":" + portNum); | |
75 socket.Close(); | |
76 } | |
77 catch (Exception e) { | |
78 Debug.WriteLine( | |
79 "Exception {" + e.Message + "} connecting to " + | |
80 hostName + ":" + portNum); | |
81 } | |
82 return didConnect; | |
83 } | |
84 | |
85 /// <summary> | |
86 /// StartServer will launch a process, which is assumed to be a server | |
87 /// such as httpd.cmd. | |
88 /// </summary> | |
89 /// <param name = "serverName"> Name of the executable (full path).</param> | |
90 /// <param name = "serverArgs"> Name of the args to the server, can be empty | |
91 /// <param name = "workingDir"> Directory where the .nexe, .html, .js, and | |
92 /// .nmf files are.</param> | |
93 /// string.</param> | |
94 /// TODO: We do not have method to kill or halt the server. I | |
95 /// looked into this, but it's non-trivial to kill the entire process | |
96 /// tree and also depends on the server itself. For example, httpd.cmd | |
97 /// has had issues with closing when you tell it to. | |
98 public static void StartServer(string serverName, | |
99 string serverArgs, | |
100 string workingDir) { | |
101 serverProcess_ = new Process(); | |
102 serverProcess_.StartInfo.FileName = serverName; | |
103 serverProcess_.StartInfo.WorkingDirectory = workingDir; | |
104 serverProcess_.StartInfo.Arguments = serverArgs; | |
105 | |
106 // try/catch will handle the exceptions that can be thrown in C# if the | |
107 // process fails to start for anyh reason (bad path, etc.). | |
108 try { | |
109 serverProcess_.Start(); | |
110 } | |
111 catch (Exception e) { | |
112 Debug.WriteLine("Caught an exception " + e); | |
113 MessageBox.Show( | |
114 "Tried to launch server process {" + serverName + | |
115 "} with arguments {" + | |
116 serverArgs + "} but encountered exception:\n " + e); | |
117 } | |
118 Debug.WriteLine("Name is " + serverProcess_.ProcessName); | |
119 } | |
120 | |
121 /// <summary> | |
122 /// DebugLaunch populates the VsDebugTargetInfo object, and then hands it | |
123 /// to VsShellUtilities to launch the debugger. | |
124 /// </summary> | |
125 /// <param name = "grfLaunch"> | |
126 /// An enumeration of launch parameters, interpreted as a | |
127 /// __VSDBGLAUNCHFLAGS Enumeration. | |
128 /// </param> | |
129 /// <returns> | |
130 /// VSConstants.S_OK if all goes well. Otherwise VSConstants.E_UNEXPECTED | |
131 /// </returns> | |
132 public override int DebugLaunch(uint grfLaunch) { | |
133 var info = new VsDebugTargetInfo(); | |
134 info.clsidCustom = new Guid(Engine.kId); | |
135 info.dlo = DEBUG_LAUNCH_OPERATION.DLO_CreateProcess; | |
136 | |
137 var host = ProjectMgr.GetProjectProperty("DebugHost", false); | |
138 if (string.IsNullOrEmpty(host)) { | |
139 throw new FileNotFoundException( | |
140 "The Debug Host property has not been specified. " + | |
141 "Debugging cannot continue.\n\n" + | |
142 "To specify a debug host, open the project properties and " + | |
143 "select Debug from the list on the left of the window."); | |
144 } else if (!File.Exists(host)) { | |
145 throw new FileNotFoundException( | |
146 "The DebugHost {" + host + "} was not found. " + | |
147 "Debugging cannot continue.\n\n" + | |
148 "To specify a debug host, open the project properties and " + | |
149 "select Debug from the list on the left of the window."); | |
150 } | |
151 info.bstrExe = host; | |
152 | |
153 if (null == ProjectMgr) { | |
154 return VSConstants.E_UNEXPECTED; | |
155 } | |
156 | |
157 var nexe = | |
158 Path.Combine( | |
159 Path.GetDirectoryName(ProjectMgr.BaseURI.Uri.LocalPath), | |
160 GetConfigurationProperty("OutputFullPath", false)); | |
161 // If a server is specified and is not running, launch it now. | |
162 var hostName = GetConfigurationProperty("LaunchHostname", false); | |
163 var portNumValue = GetConfigurationProperty("LaunchPort", false); | |
164 int portNum = 0; | |
165 if (null != portNumValue) { | |
166 try { | |
167 portNum = Convert.ToInt32(portNumValue); | |
168 } catch(System.FormatException e) { | |
169 Debug.WriteLine("Caught exception for portNumValue{" + portNumValue +
"}" + e); | |
170 } catch(System.OverflowException e) { | |
171 Debug.WriteLine("Caught exception for portNumValue{" + portNumValue +
"}" + e); | |
172 } | |
173 } | |
174 var isServerRunning = IsServerRunning(hostName, portNum); | |
175 var serverProgram = GetConfigurationProperty("WebServer", false); | |
176 var serverArgs = GetConfigurationProperty("WebServerArgs", false); | |
177 if (serverProgram != null && serverProgram.Length > 0 && | |
178 !isServerRunning) { | |
179 // We know that the server at the host is specified and | |
180 // is not currently running. We will start it now. | |
181 // Currently, we are not having the user specify args | |
182 // for the server, but if we need to later that would be the | |
183 // second argument to StartServer. | |
184 var workingDir = Path.GetDirectoryName(nexe); | |
185 StartServer(serverProgram, serverArgs, workingDir); | |
186 } | |
187 var safeNexeString = string.Format("\"{0}\"", nexe); | |
188 nexeList_.Add(nexe); | |
189 | |
190 if (host.Contains("sel_ldr")) { | |
191 // sel_ldr needs a -g to enable debugger | |
192 var irtNexe = GetConfigurationProperty("IrtNexe", false); | |
193 if (null == irtNexe || irtNexe.Length == 0) { | |
194 MessageBox.Show( | |
195 "Error: IrtNexe property is not set, but is required by sel_ldr", | |
196 "Error"); | |
197 return VSConstants.S_FALSE; | |
198 } | |
199 info.bstrArg = string.Format( | |
200 "-g -B {0} {1} {2}", | |
201 irtNexe, | |
202 GetConfigurationProperty("DebugArgs", false), | |
203 safeNexeString); | |
204 } else if (host.Contains("chrome.exe")) { | |
205 // Use for in-process debugger | |
206 var html_page = hostName + ":" + portNum + "/" + | |
207 GetConfigurationProperty("HtmlPage", false); | |
208 // Get args based on project property | |
209 var chrome_debug_args = GetConfigurationProperty("DebugArgs", false); | |
210 info.bstrArg = string.Format( | |
211 "{0} {1} {2}", | |
212 chrome_debug_args, | |
213 html_page, | |
214 GetConfigurationProperty("DebugArgs", false)); | |
215 } else { | |
216 // Used for out-of-process debugger | |
217 info.bstrArg = string.Format( | |
218 "{0} {1}", | |
219 safeNexeString, | |
220 GetConfigurationProperty("DebugArgs", false)); | |
221 } | |
222 | |
223 info.bstrCurDir = Path.GetDirectoryName(nexe); | |
224 info.fSendStdoutToOutputWindow = 1; | |
225 info.grfLaunch = grfLaunch; | |
226 info.clsidPortSupplier = typeof (NaClPortSupplier).GUID; | |
227 info.bstrPortName = "127.0.0.1:4014"; | |
228 | |
229 // If we need to set env vars, this is a way to do it | |
230 // Environment.SetEnvironmentVariable("NACL_DEBUG_ENABLE","1"); | |
231 | |
232 VsShellUtilities.LaunchDebugger(ProjectMgr.Site, info); | |
233 return VSConstants.S_OK; | |
234 } | |
235 | |
236 #region Private Implementation | |
237 | |
238 private static readonly ArrayList nexeList_ = new ArrayList(); | |
239 private static Process serverProcess_; | |
240 | |
241 #endregion | |
242 | |
243 internal string GetRawConfigurationProperty(string propertyName, | |
244 bool cacheNeedReset) { | |
245 var property = GetMsBuildProperty(propertyName, cacheNeedReset); | |
246 if (property == null) { | |
247 return null; | |
248 } | |
249 | |
250 return property.Value; | |
251 } | |
252 } | |
253 } | |
OLD | NEW |