OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 namespace NativeClientVSAddIn | 5 namespace NativeClientVSAddIn |
6 { | 6 { |
7 using System; | 7 using System; |
8 using System.Collections.Generic; | 8 using System.Collections.Generic; |
9 using System.IO; | 9 using System.IO; |
10 using System.Linq; | 10 using System.Linq; |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 private string gdbInitFileName_; | 84 private string gdbInitFileName_; |
85 | 85 |
86 /// <summary> | 86 /// <summary> |
87 /// The platform that the start-up project is currently configured with (NaC
l or PPAPI). | 87 /// The platform that the start-up project is currently configured with (NaC
l or PPAPI). |
88 /// </summary> | 88 /// </summary> |
89 private ProjectPlatformType projectPlatformType_; | 89 private ProjectPlatformType projectPlatformType_; |
90 | 90 |
91 /// <summary> | 91 /// <summary> |
92 /// When debugging is started this is the web server process object. | 92 /// When debugging is started this is the web server process object. |
93 /// </summary> | 93 /// </summary> |
94 private System.Diagnostics.Process webServer_ = null; | 94 private System.Diagnostics.Process webServer_; |
95 | 95 |
96 /// <summary> | 96 /// <summary> |
97 /// Visual Studio output window pane that captures output from the web serve
r. | 97 /// Visual Studio output window pane that captures output from the web serve
r. |
98 /// </summary> | 98 /// </summary> |
99 private OutputWindowPane webServerOutputPane_ = null; | 99 private OutputWindowPane webServerOutputPane_; |
100 | 100 |
101 /// <summary> | 101 /// <summary> |
102 /// Path to the web server executable. | 102 /// Path to the web server executable. |
103 /// </summary> | 103 /// </summary> |
104 private string webServerExecutable_; | 104 private string webServerExecutable_; |
105 | 105 |
106 /// <summary> | 106 /// <summary> |
107 /// Arguments to be passed to the web server executable to start it. | 107 /// Arguments to be passed to the web server executable to start it. |
108 /// </summary> | 108 /// </summary> |
109 private string webServerArguments_; | 109 private string webServerArguments_; |
110 | 110 |
111 /// <summary> | 111 /// <summary> |
112 /// Timer object that periodically calls a function to look for the plug-in
process to debug. | 112 /// Timer object that periodically calls a function to look for the plug-in
process to debug. |
113 /// </summary> | 113 /// </summary> |
114 private Timer pluginFinderTimer_; | 114 private Timer pluginFinderTimer_; |
115 | 115 |
116 /// <summary> | 116 /// <summary> |
117 /// List of process IDs which we should not attempt to attach the debugger t
o. Mainly this | 117 /// List of process IDs which we should not attempt to attach the debugger t
o. Mainly this |
118 /// list contains process IDs of processes we have already attached to. | 118 /// list contains process IDs of processes we have already attached to. |
119 /// </summary> | 119 /// </summary> |
120 private List<uint> pluginFinderForbiddenPids_; | 120 private List<uint> pluginFinderForbiddenPids_; |
121 | 121 |
122 /// <summary> | 122 /// <summary> |
123 /// Process searcher class which allows us to query the system for running p
rocesses. | 123 /// Process searcher class which allows us to query the system for running p
rocesses. |
124 /// </summary> | 124 /// </summary> |
125 private ProcessSearcher processSearcher_; | 125 private ProcessSearcher processSearcher_; |
126 | 126 |
127 /// <summary> | 127 /// <summary> |
| 128 /// The main process of chrome that was started by Visual Studio during debu
gging. |
| 129 /// </summary> |
| 130 private System.Diagnostics.Process debuggedChromeMainProcess_; |
| 131 |
| 132 /// <summary> |
128 /// Constructs the PluginDebuggerHelper. | 133 /// Constructs the PluginDebuggerHelper. |
129 /// Object is not usable until LoadProjectSettings() is called. | 134 /// Object is not usable until LoadProjectSettings() is called. |
130 /// </summary> | 135 /// </summary> |
131 /// <param name="dte">Automation object from Visual Studio.</param> | 136 /// <param name="dte">Automation object from Visual Studio.</param> |
132 public PluginDebuggerHelper(DTE2 dte) | 137 public PluginDebuggerHelper(DTE2 dte) |
133 { | 138 { |
134 if (dte == null) | 139 if (dte == null) |
135 { | 140 { |
136 throw new ArgumentNullException("dte"); | 141 throw new ArgumentNullException("dte"); |
137 } | 142 } |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 { | 223 { |
219 projectPlatformType_ = ProjectPlatformType.Other; | 224 projectPlatformType_ = ProjectPlatformType.Other; |
220 return false; | 225 return false; |
221 } | 226 } |
222 | 227 |
223 // We only support certain project types (e.g. C/C++ projects). Otherwise
we fail. | 228 // We only support certain project types (e.g. C/C++ projects). Otherwise
we fail. |
224 // If supported, extract necessary information from specific project type. | 229 // If supported, extract necessary information from specific project type. |
225 if (Utility.IsVisualCProject(startProject)) | 230 if (Utility.IsVisualCProject(startProject)) |
226 { | 231 { |
227 VCConfiguration config = Utility.GetActiveVCConfiguration(startProject); | 232 VCConfiguration config = Utility.GetActiveVCConfiguration(startProject); |
| 233 IVCRulePropertyStorage general = config.Rules.Item("ConfigurationGeneral
"); |
| 234 VCLinkerTool linker = config.Tools.Item("VCLinkerTool"); |
228 VCProject vcproj = (VCProject)startProject.Object; | 235 VCProject vcproj = (VCProject)startProject.Object; |
229 VCLinkerTool linker = config.Tools.Item("VCLinkerTool"); | 236 sdkRootDirectory_ = general.GetEvaluatedPropertyValue("VSNaClSDKRoot"); |
| 237 pluginOutputDirectory_ = config.Evaluate(config.OutputDirectory); |
| 238 pluginAssembly_ = config.Evaluate(linker.OutputFile); |
230 pluginProjectDirectory_ = vcproj.ProjectDirectory; // Macros not allowe
d here. | 239 pluginProjectDirectory_ = vcproj.ProjectDirectory; // Macros not allowe
d here. |
231 pluginAssembly_ = config.Evaluate(linker.OutputFile); | |
232 pluginOutputDirectory_ = config.Evaluate(config.OutputDirectory); | |
233 } | 240 } |
234 else | 241 else |
235 { | 242 { |
236 return false; | 243 return false; |
237 } | 244 } |
238 | 245 |
239 // TODO(tysand): Add user option to specify this. | 246 if (string.IsNullOrEmpty(sdkRootDirectory_)) |
240 int webServerPort = 5103; | |
241 sdkRootDirectory_ = Environment.GetEnvironmentVariable(Strings.SDKPathEnvi
ronmentVariable); | |
242 if (sdkRootDirectory_ == null) | |
243 { | 247 { |
244 MessageBox.Show( | 248 MessageBox.Show( |
245 string.Format(Strings.SDKPathNotSetFormat, Strings.SDKPathEnvironmen
tVariable)); | 249 string.Format(Strings.SDKPathNotSetFormat, Strings.SDKPathEnvironmen
tVariable)); |
246 return false; | 250 return false; |
247 } | 251 } |
248 | 252 |
249 sdkRootDirectory_ = sdkRootDirectory_.TrimEnd("/\\".ToArray<char>()); | 253 sdkRootDirectory_ = sdkRootDirectory_.TrimEnd("/\\".ToArray<char>()); |
250 | 254 |
| 255 // TODO(tysand): Add user option to specify this. |
| 256 int webServerPort = 5103; |
251 webServerExecutable_ = "python.exe"; | 257 webServerExecutable_ = "python.exe"; |
252 webServerArguments_ = string.Format( | 258 webServerArguments_ = string.Format( |
253 "{0}\\examples\\httpd.py --no_dir_check {1}", | 259 "{0}\\examples\\httpd.py --no_dir_check {1}", |
254 sdkRootDirectory_, | 260 sdkRootDirectory_, |
255 webServerPort); | 261 webServerPort); |
256 | 262 |
257 // TODO(tysand): Update this to nacl-gdb when it is ready. Should be able
to remove irtPath_. | 263 // TODO(tysand): Update this to nacl-gdb when it is ready. Should be able
to remove irtPath_. |
258 gdbPath_ = sdkRootDirectory_ + @"\customGDB\gdb.exe"; | 264 gdbPath_ = sdkRootDirectory_ + @"\gdb-remote-x86-64\gdb.exe"; |
259 irtPath_ = sdkRootDirectory_ + @"\tools\irt_x86_64.nexe"; | 265 irtPath_ = sdkRootDirectory_ + @"\tools\irt_x86_64.nexe"; |
260 | 266 |
| 267 debuggedChromeMainProcess_ = null; |
| 268 |
261 isProperlyInitialized_ = true; | 269 isProperlyInitialized_ = true; |
262 return true; | 270 return true; |
263 } | 271 } |
264 | 272 |
265 /// <summary> | 273 /// <summary> |
266 /// This function should be called to start the PluginDebuggerHelper functio
nality. | 274 /// This function should be called to start the PluginDebuggerHelper functio
nality. |
267 /// </summary> | 275 /// </summary> |
268 public void StartDebugging() | 276 public void StartDebugging() |
269 { | 277 { |
270 if (!isProperlyInitialized_) | 278 if (!isProperlyInitialized_) |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
322 { | 330 { |
323 File.Delete(gdbInitFileName_); | 331 File.Delete(gdbInitFileName_); |
324 } | 332 } |
325 | 333 |
326 gdbProcess_ = null; | 334 gdbProcess_ = null; |
327 } | 335 } |
328 } | 336 } |
329 | 337 |
330 /// <summary> | 338 /// <summary> |
331 /// This is called periodically by the Visual Studio UI thread to look for o
ur plug-in process | 339 /// This is called periodically by the Visual Studio UI thread to look for o
ur plug-in process |
332 /// and attach the debugger to it. The call is triggered by the pluginFinde
rTimer_ object | 340 /// and attach the debugger to it. The call is triggered by the pluginFinde
rTimer_ object. |
333 /// </summary> | 341 /// </summary> |
334 /// <param name="unused">The parameter is not used.</param> | 342 /// <param name="unused">The parameter is not used.</param> |
335 /// <param name="unused1">The parameter is not used.</param> | 343 /// <param name="unused1">The parameter is not used.</param> |
336 private void FindAndAttachToPlugin(object unused, EventArgs unused1) | 344 private void FindAndAttachToPlugin(object unused, EventArgs unused1) |
337 { | 345 { |
338 string processNameTarget; | 346 StringComparison ignoreCase = StringComparison.InvariantCultureIgnoreCase; |
339 string typeFlagTarget; | 347 |
340 string identifierFlagTarget; | 348 // Set the main chrome process that was started by visual studio. If it's
not chrome |
| 349 // or not found then we have no business attaching to any plug-ins so retu
rn. |
| 350 if (debuggedChromeMainProcess_ == null) |
| 351 { |
| 352 foreach (Process proc in dte_.Debugger.DebuggedProcesses) |
| 353 { |
| 354 if (proc.Name.EndsWith(Strings.ChromeProcessName, ignoreCase)) |
| 355 { |
| 356 debuggedChromeMainProcess_ = System.Diagnostics.Process.GetProcessBy
Id(proc.ProcessID); |
| 357 break; |
| 358 } |
| 359 } |
| 360 |
| 361 return; |
| 362 } |
| 363 |
| 364 // Get the list of all descendants of the main chrome process. |
| 365 uint mainChromeProcId = (uint)debuggedChromeMainProcess_.Id; |
| 366 List<ProcessInfo> chromeDescendants = processSearcher_.GetDescendants(main
ChromeProcId); |
| 367 |
| 368 // From the list of descendants, find the plug-in by it's command line arg
uments and |
| 369 // process name as well as not being attached to already. |
| 370 List<ProcessInfo> plugins; |
341 switch (projectPlatformType_) | 371 switch (projectPlatformType_) |
342 { | 372 { |
343 case ProjectPlatformType.Pepper: | 373 case ProjectPlatformType.Pepper: |
344 processNameTarget = Strings.PepperProcessName; | 374 string identifierFlagTarget = |
345 typeFlagTarget = Strings.PepperProcessTypeFlag; | |
346 identifierFlagTarget = | |
347 string.Format(Strings.PepperProcessPluginFlagFormat, pluginAssembl
y_); | 375 string.Format(Strings.PepperProcessPluginFlagFormat, pluginAssembl
y_); |
| 376 plugins = chromeDescendants.FindAll(p => |
| 377 p.Name.Equals(Strings.ChromeProcessName, ignoreCase) && |
| 378 p.CommandLine.Contains(Strings.ChromeRendererFlag, ignoreCase) && |
| 379 p.CommandLine.Contains(identifierFlagTarget, ignoreCase) && |
| 380 !pluginFinderForbiddenPids_.Contains(p.ID)); |
348 break; | 381 break; |
349 case ProjectPlatformType.NaCl: | 382 case ProjectPlatformType.NaCl: |
350 processNameTarget = Strings.NaClProcessName; | 383 plugins = chromeDescendants.FindAll(p => |
351 typeFlagTarget = Strings.NaClProcessTypeFlag; | 384 p.Name.Equals(Strings.NaClProcessName, ignoreCase) && |
352 identifierFlagTarget = Strings.NaClDebugFlag; | 385 p.CommandLine.Contains(Strings.NaClLoaderFlag, ignoreCase) && |
| 386 !pluginFinderForbiddenPids_.Contains(p.ID)); |
353 break; | 387 break; |
354 default: | 388 default: |
355 return; | 389 return; |
356 } | 390 } |
357 | 391 |
358 // To identify our target plug-in we look for: its process name (e.g. chro
me.exe), | 392 // Attach to all plug-ins that we found. |
359 // identifying command line arguments (e.g. --type=renderer), not already
attached | 393 foreach (ProcessInfo process in plugins) |
360 // to by us, and must be a descendant process of this instance of Visual S
tudio. | |
361 List<ProcessInfo> results = processSearcher_.GetResultsByName(processNameT
arget); | |
362 uint currentProcessId = (uint)System.Diagnostics.Process.GetCurrentProcess
().Id; | |
363 StringComparison ignoreCase = StringComparison.InvariantCultureIgnoreCase; | |
364 foreach (ProcessInfo process in results) | |
365 { | 394 { |
366 if (!pluginFinderForbiddenPids_.Contains(process.ID) && | 395 // If we are attaching to a plug-in, add it to the forbidden list to ens
ure we |
367 !string.IsNullOrEmpty(process.CommandLine) && | 396 // don't try to attach again later. |
368 process.CommandLine.Contains(typeFlagTarget, ignoreCase) && | 397 pluginFinderForbiddenPids_.Add(process.ID); |
369 process.CommandLine.Contains(identifierFlagTarget, ignoreCase) && | 398 PluginFoundEvent.Invoke(this, new PluginFoundEventArgs(process.ID)); |
370 Utility.IsDescendantOfProcess(processSearcher_, process.ID, currentP
rocessId)) | |
371 { | |
372 // If we are attaching to a plug-in, add it to the forbidden list to e
nsure we | |
373 // don't try to attach again later. | |
374 pluginFinderForbiddenPids_.Add(process.ID); | |
375 PluginFoundEvent.Invoke(this, new PluginFoundEventArgs(process.ID)); | |
376 | 399 |
377 // Slow down the frequency of checks for new plugins. | 400 // Slow down the frequency of checks for new plugins. |
378 pluginFinderTimer_.Interval = RelaxedPluginCheckFrequency; | 401 pluginFinderTimer_.Interval = RelaxedPluginCheckFrequency; |
379 } | |
380 } | 402 } |
381 } | 403 } |
382 | 404 |
383 /// <summary> | 405 /// <summary> |
384 /// Attaches the visual studio debugger to a given process ID. | 406 /// Attaches the visual studio debugger to a given process ID. |
385 /// </summary> | 407 /// </summary> |
386 /// <param name="src">The parameter is not used.</param> | 408 /// <param name="src">The parameter is not used.</param> |
387 /// <param name="args">Contains the process ID to attach to.</param> | 409 /// <param name="args">Contains the process ID to attach to.</param> |
388 private void AttachVSDebugger(object src, PluginFoundEventArgs args) | 410 private void AttachVSDebugger(object src, PluginFoundEventArgs args) |
389 { | 411 { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
425 contents.AppendFormat("target remote localhost:{0}", 4014); | 447 contents.AppendFormat("target remote localhost:{0}", 4014); |
426 contents.AppendLine(); | 448 contents.AppendLine(); |
427 | 449 |
428 // TODO(tysand): Nacl-gdb should detect this automatically making this cal
l unnecessary. | 450 // TODO(tysand): Nacl-gdb should detect this automatically making this cal
l unnecessary. |
429 contents.Append("set architecture i386:x86-64"); | 451 contents.Append("set architecture i386:x86-64"); |
430 contents.AppendLine(); | 452 contents.AppendLine(); |
431 contents.AppendFormat("cd {0}", projectDir); | 453 contents.AppendFormat("cd {0}", projectDir); |
432 contents.AppendLine(); | 454 contents.AppendLine(); |
433 | 455 |
434 // TODO(tysand): Nacl-gdb should handle the offset automatically. Remove 0
xC00020080. | 456 // TODO(tysand): Nacl-gdb should handle the offset automatically. Remove 0
xC00020080. |
435 contents.AppendFormat("add-symbol-file {0} 0xC00020080", pluginAssemblyEsc
aped); | 457 contents.AppendFormat("add-symbol-file \"{0}\" 0xC00020080", pluginAssembl
yEscaped); |
436 contents.AppendLine(); | 458 contents.AppendLine(); |
437 | 459 |
438 // TODO(tysand): Nacl-gdb should handle loading the irt automatically. Rem
ove this line. | 460 // TODO(tysand): Nacl-gdb should handle loading the irt automatically. Rem
ove this line. |
439 contents.AppendFormat("add-symbol-file {0} 0xC0fc00080", irtPathEscaped); | 461 contents.AppendFormat("add-symbol-file \"{0}\" 0xC0fc00080", irtPathEscape
d); |
440 contents.AppendLine(); | 462 contents.AppendLine(); |
441 | 463 |
442 // Insert breakpoints from Visual Studio project. | 464 // Insert breakpoints from Visual Studio project. |
443 foreach (Breakpoint bp in dte_.Debugger.Breakpoints) | 465 foreach (Breakpoint bp in dte_.Debugger.Breakpoints) |
444 { | 466 { |
445 if (bp.Enabled && | 467 if (bp.Enabled && |
446 bp.LocationType == dbgBreakpointLocationType.dbgBreakpointLocationTy
peFile) | 468 bp.LocationType == dbgBreakpointLocationType.dbgBreakpointLocationTy
peFile) |
447 { | 469 { |
448 contents.AppendFormat("b {0}:{1}", Path.GetFileName(bp.File), bp.FileL
ine); | 470 contents.AppendFormat("b {0}:{1}", Path.GetFileName(bp.File), bp.FileL
ine); |
449 contents.AppendLine(); | 471 contents.AppendLine(); |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
530 WebServerWriteLine(e.Data); | 552 WebServerWriteLine(e.Data); |
531 } | 553 } |
532 | 554 |
533 /// <summary> | 555 /// <summary> |
534 /// Helper function to write data to the Web Server Output Pane. | 556 /// Helper function to write data to the Web Server Output Pane. |
535 /// </summary> | 557 /// </summary> |
536 /// <param name="message">Message to write.</param> | 558 /// <param name="message">Message to write.</param> |
537 private void WebServerWriteLine(string message) | 559 private void WebServerWriteLine(string message) |
538 { | 560 { |
539 webServerOutputPane_.OutputString(message + "\n"); | 561 webServerOutputPane_.OutputString(message + "\n"); |
540 webServerOutputPane_.Activate(); | |
541 } | 562 } |
542 | 563 |
543 /// <summary> | 564 /// <summary> |
544 /// The event arguments when a plug-in is found. | 565 /// The event arguments when a plug-in is found. |
545 /// </summary> | 566 /// </summary> |
546 public class PluginFoundEventArgs : EventArgs | 567 public class PluginFoundEventArgs : EventArgs |
547 { | 568 { |
548 /// <summary> | 569 /// <summary> |
549 /// Construct the PluginFoundEventArgs. | 570 /// Construct the PluginFoundEventArgs. |
550 /// </summary> | 571 /// </summary> |
551 /// <param name="pid">Process ID of the found plug-in.</param> | 572 /// <param name="pid">Process ID of the found plug-in.</param> |
552 public PluginFoundEventArgs(uint pid) | 573 public PluginFoundEventArgs(uint pid) |
553 { | 574 { |
554 this.ProcessID = pid; | 575 this.ProcessID = pid; |
555 } | 576 } |
556 | 577 |
557 /// <summary> | 578 /// <summary> |
558 /// Gets or sets process ID of the found plug-in. | 579 /// Gets or sets process ID of the found plug-in. |
559 /// </summary> | 580 /// </summary> |
560 public uint ProcessID { get; set; } | 581 public uint ProcessID { get; set; } |
561 } | 582 } |
562 } | 583 } |
563 } | 584 } |
OLD | NEW |