Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1203)

Unified Diff: visual_studio/NativeClientVSAddIn/NativeClientVSAddIn/PluginDebuggerHelper.cs

Issue 10836143: Refactored the VS add-in (Closed) Base URL: https://nativeclient-sdk.googlecode.com/svn/trunk/src
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: visual_studio/NativeClientVSAddIn/NativeClientVSAddIn/PluginDebuggerHelper.cs
diff --git a/visual_studio/NativeClientVSAddIn/NativeClientVSAddIn/PluginDebuggerHelper.cs b/visual_studio/NativeClientVSAddIn/NativeClientVSAddIn/PluginDebuggerHelper.cs
deleted file mode 100644
index a8be9a92b96090a0d7928e8f32165cf4cf4c23f7..0000000000000000000000000000000000000000
--- a/visual_studio/NativeClientVSAddIn/NativeClientVSAddIn/PluginDebuggerHelper.cs
+++ /dev/null
@@ -1,592 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-namespace NativeClientVSAddIn
-{
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Windows.Forms;
-
- using EnvDTE;
- using EnvDTE80;
- using Microsoft.VisualStudio.VCProjectEngine;
-
- /// <summary>
- /// This class contains functions and utilities which are run when the user
- /// presses F5 or otherwise starts debugging from within Visual Studio.
- /// </summary>
- public class PluginDebuggerHelper
- {
- /// <summary>
- /// This is the initial number of milliseconds to wait between
- /// checking for plug-in processes to attach the debugger to.
- /// </summary>
- private const int InitialPluginCheckFrequency = 1000;
-
- /// <summary>
- /// After a plug-in has been found, we slow the frequency of checking
- /// for new ones. This value is in milliseconds.
- /// </summary>
- private const int RelaxedPluginCheckFrequency = 5000;
-
- /// <summary>
- /// The web server port to default to if the user does not specify one.
- /// </summary>
- private const int DefaultWebServerPort = 5103;
-
- /// <summary>
- /// The main visual studio object through which all Visual Studio functions are executed.
- /// </summary>
- private DTE2 dte_;
-
- /// <summary>
- /// Indicates the PluginDebuggerHelper is configured properly to run.
- /// </summary>
- private bool isProperlyInitialized_ = false;
-
- /// <summary>
- /// Directory of the plug-in project we are debugging.
- /// </summary>
- private string pluginProjectDirectory_;
-
- /// <summary>
- /// Directory where the plug-in assembly is placed.
- /// </summary>
- private string pluginOutputDirectory_;
-
- /// <summary>
- /// Path to the actual plug-in assembly.
- /// </summary>
- private string pluginAssembly_;
-
- /// <summary>
- /// Path to the NaCl IRT.
- /// </summary>
- private string irtPath_;
-
- /// <summary>
- /// Path to the project's nmf file.
- /// </summary>
- private string manifestPath_;
-
- /// <summary>
- /// Root directory of the installed NaCl SDK.
- /// </summary>
- private string sdkRootDirectory_;
-
- /// <summary>
- /// If debugging a .nexe this is the nacl-gdb process object.
- /// </summary>
- private System.Diagnostics.Process gdbProcess_;
-
- /// <summary>
- /// Path to NaCl-GDB executable.
- /// </summary>
- private string gdbPath_;
-
- /// <summary>
- /// Path to the gdb initialization file that we auto-generate from the VS project.
- /// </summary>
- private string gdbInitFileName_;
-
- /// <summary>
- /// The platform that the start-up project is currently configured with (NaCl or PPAPI).
- /// </summary>
- private ProjectPlatformType projectPlatformType_;
-
- /// <summary>
- /// When debugging is started this is the web server process object.
- /// </summary>
- private System.Diagnostics.Process webServer_;
-
- /// <summary>
- /// Visual Studio output window pane that captures output from the web server.
- /// </summary>
- private OutputWindowPane webServerOutputPane_;
-
- /// <summary>
- /// Path to the web server executable.
- /// </summary>
- private string webServerExecutable_;
-
- /// <summary>
- /// Arguments to be passed to the web server executable to start it.
- /// </summary>
- private string webServerArguments_;
-
- /// <summary>
- /// Timer object that periodically calls a function to look for the plug-in process to debug.
- /// </summary>
- private Timer pluginFinderTimer_;
-
- /// <summary>
- /// List of process IDs which we should not attempt to attach the debugger to. Mainly this
- /// list contains process IDs of processes we have already attached to.
- /// </summary>
- private List<uint> pluginFinderForbiddenPids_;
-
- /// <summary>
- /// Process searcher class which allows us to query the system for running processes.
- /// </summary>
- private ProcessSearcher processSearcher_;
-
- /// <summary>
- /// The main process of chrome that was started by Visual Studio during debugging.
- /// </summary>
- private System.Diagnostics.Process debuggedChromeMainProcess_;
-
- /// <summary>
- /// Constructs the PluginDebuggerHelper.
- /// Object is not usable until LoadProjectSettings() is called.
- /// </summary>
- /// <param name="dte">Automation object from Visual Studio.</param>
- public PluginDebuggerHelper(DTE2 dte)
- {
- if (dte == null)
- {
- throw new ArgumentNullException("dte");
- }
-
- dte_ = dte;
-
- // Every second, check for a new instance of the plug-in to attach to.
- // Note that although the timer itself runs on a separate thread, the event
- // is fired from the main UI thread during message processing, thus we do not
- // need to worry about threading issues.
- pluginFinderTimer_ = new Timer();
- pluginFinderTimer_.Tick += new EventHandler(FindAndAttachToPlugin);
- pluginFinderForbiddenPids_ = new List<uint>();
- processSearcher_ = new ProcessSearcher();
- }
-
- /// <summary>
- /// An event indicating a target plug-in was found on the system.
- /// </summary>
- public event EventHandler<PluginFoundEventArgs> PluginFoundEvent;
-
- /// <summary>
- /// Specifies the type of plug-in being run in this debug session.
- /// </summary>
- private enum ProjectPlatformType
- {
- /// <summary>
- /// Represents all non-pepper/non-nacl platform types.
- /// </summary>
- Other,
-
- /// <summary>
- /// Indicates project platform is a trusted plug-in (nexe).
- /// </summary>
- NaCl,
-
- /// <summary>
- /// Indicates project platform is an untrusted plug-in.
- /// </summary>
- Pepper
- }
-
- /// <summary>
- /// Initializes the PluginDebuggerHelper with the current project settings
- /// If project settings are unsupported for NaCl/Pepper debugging then
- /// the object is not initialized and we return false.
- /// </summary>
- /// <returns>True if the object is successfully initialized, false otherwise.</returns>
- public bool LoadProjectSettings()
- {
- isProperlyInitialized_ = false;
-
- string platformToolset;
-
- // We require that there is only a single start-up project.
- // If multiple start-up projects are specified then we use the first and
- // leave a warning message in the Web Server output pane.
- Array startupProjects = dte_.Solution.SolutionBuild.StartupProjects as Array;
- if (startupProjects == null || startupProjects.Length == 0)
- {
- throw new ArgumentOutOfRangeException("startupProjects.Length");
- }
- else if (startupProjects.Length > 1)
- {
- WebServerWriteLine(Strings.WebServerMultiStartProjectWarning);
- }
-
- // Get the first start-up project object.
- List<Project> projList = dte_.Solution.Projects.OfType<Project>().ToList();
- string startProjectName = startupProjects.GetValue(0) as string;
- Project startProject = projList.Find(proj => proj.UniqueName == startProjectName);
-
- // Get the current platform type. If not nacl/pepper then fail.
- string activePlatform = startProject.ConfigurationManager.ActiveConfiguration.PlatformName;
- if (string.Compare(activePlatform, Strings.PepperPlatformName, true) == 0)
- {
- projectPlatformType_ = ProjectPlatformType.Pepper;
- PluginFoundEvent += new EventHandler<PluginFoundEventArgs>(AttachVSDebugger);
- }
- else if (string.Compare(activePlatform, Strings.NaClPlatformName, true) == 0)
- {
- projectPlatformType_ = ProjectPlatformType.NaCl;
- PluginFoundEvent += new EventHandler<PluginFoundEventArgs>(AttachNaClGDB);
- }
- else
- {
- projectPlatformType_ = ProjectPlatformType.Other;
- return false;
- }
-
- // We only support certain project types (e.g. C/C++ projects). Otherwise we fail.
- if (!Utility.IsVisualCProject(startProject))
- {
- return false;
- }
-
- // Extract necessary information from specific project type.
- VCConfiguration config = Utility.GetActiveVCConfiguration(startProject);
- IVCRulePropertyStorage general = config.Rules.Item("ConfigurationGeneral");
- VCLinkerTool linker = config.Tools.Item("VCLinkerTool");
- VCProject vcproj = (VCProject)startProject.Object;
-
- sdkRootDirectory_ = general.GetEvaluatedPropertyValue("VSNaClSDKRoot");
- platformToolset = general.GetEvaluatedPropertyValue("PlatformToolset");
- pluginOutputDirectory_ = config.Evaluate(config.OutputDirectory);
- pluginAssembly_ = config.Evaluate(linker.OutputFile);
- pluginProjectDirectory_ = vcproj.ProjectDirectory; // Macros not allowed here.
-
- if (projectPlatformType_ == ProjectPlatformType.NaCl)
- {
- irtPath_ = general.GetEvaluatedPropertyValue("NaClIrtPath");
- manifestPath_ = general.GetEvaluatedPropertyValue("NaClManifestPath");
- }
-
- if (string.IsNullOrEmpty(sdkRootDirectory_))
- {
- MessageBox.Show(Strings.SDKPathNotSetError);
- return false;
- }
-
- sdkRootDirectory_ = sdkRootDirectory_.TrimEnd("/\\".ToArray<char>());
-
- // TODO(tysand): Move this code getting port to where the web server is started.
- int webServerPort;
- if (!int.TryParse(general.GetEvaluatedPropertyValue("NaClWebServerPort"), out webServerPort))
- {
- webServerPort = DefaultWebServerPort;
- }
-
- webServerExecutable_ = "python.exe";
- webServerArguments_ = string.Format(
- "{0}\\examples\\httpd.py --no_dir_check {1}", sdkRootDirectory_, webServerPort);
-
- gdbPath_ = Path.Combine(
- sdkRootDirectory_, "toolchain", platformToolset, @"bin\x86_64-nacl-gdb.exe");
-
- debuggedChromeMainProcess_ = null;
-
- isProperlyInitialized_ = true;
- return true;
- }
-
- /// <summary>
- /// This function should be called to start the PluginDebuggerHelper functionality.
- /// </summary>
- public void StartDebugging()
- {
- if (!isProperlyInitialized_)
- {
- throw new Exception(Strings.NotInitializedMessage);
- }
-
- StartWebServer();
- pluginFinderTimer_.Interval = InitialPluginCheckFrequency;
- pluginFinderTimer_.Start();
- }
-
- /// <summary>
- /// This function should be called to stop the PluginDebuggerHelper functionality.
- /// </summary>
- public void StopDebugging()
- {
- isProperlyInitialized_ = false;
- pluginFinderTimer_.Stop();
- pluginFinderForbiddenPids_.Clear();
-
- // Remove all event handlers from the plug-in found event.
- if (PluginFoundEvent != null)
- {
- foreach (Delegate del in PluginFoundEvent.GetInvocationList())
- {
- PluginFoundEvent -= (EventHandler<PluginFoundEventArgs>)del;
- }
- }
-
- Utility.EnsureProcessKill(ref webServer_);
- WebServerWriteLine(Strings.WebServerStopMessage);
- CleanUpGDBProcess();
- }
-
- /// <summary>
- /// This function cleans up the started GDB process.
- /// </summary>
- private void CleanUpGDBProcess()
- {
- Utility.EnsureProcessKill(ref gdbProcess_);
- if (!string.IsNullOrEmpty(gdbInitFileName_) && File.Exists(gdbInitFileName_))
- {
- File.Delete(gdbInitFileName_);
- gdbInitFileName_ = null;
- }
- }
-
- /// <summary>
- /// This is called periodically by the Visual Studio UI thread to look for our plug-in process
- /// and attach the debugger to it. The call is triggered by the pluginFinderTimer_ object.
- /// </summary>
- /// <param name="unused">The parameter is not used.</param>
- /// <param name="unused1">The parameter is not used.</param>
- private void FindAndAttachToPlugin(object unused, EventArgs unused1)
- {
- StringComparison ignoreCase = StringComparison.InvariantCultureIgnoreCase;
-
- // Set the main chrome process that was started by visual studio. If it's not chrome
- // or not found then we have no business attaching to any plug-ins so return.
- if (debuggedChromeMainProcess_ == null)
- {
- foreach (Process proc in dte_.Debugger.DebuggedProcesses)
- {
- if (proc.Name.EndsWith(Strings.ChromeProcessName, ignoreCase))
- {
- debuggedChromeMainProcess_ = System.Diagnostics.Process.GetProcessById(proc.ProcessID);
- break;
- }
- }
-
- return;
- }
-
- // Get the list of all descendants of the main chrome process.
- uint mainChromeProcId = (uint)debuggedChromeMainProcess_.Id;
- List<ProcessInfo> chromeDescendants = processSearcher_.GetDescendants(mainChromeProcId);
-
- // If we didn't start with debug flags then we should not attach.
- string mainChromeFlags = chromeDescendants.Find(p => p.ID == mainChromeProcId).CommandLine;
- if (projectPlatformType_ == ProjectPlatformType.NaCl &&
- !mainChromeFlags.Contains(Strings.NaClDebugFlag))
- {
- return;
- }
-
- // From the list of descendants, find the plug-in by it's command line arguments and
- // process name as well as not being attached to already.
- List<ProcessInfo> plugins;
- switch (projectPlatformType_)
- {
- case ProjectPlatformType.Pepper:
- string identifierFlagTarget =
- string.Format(Strings.PepperProcessPluginFlagFormat, pluginAssembly_);
- plugins = chromeDescendants.FindAll(p =>
- p.Name.Equals(Strings.ChromeProcessName, ignoreCase) &&
- p.CommandLine.Contains(Strings.ChromeRendererFlag, ignoreCase) &&
- p.CommandLine.Contains(identifierFlagTarget, ignoreCase) &&
- !pluginFinderForbiddenPids_.Contains(p.ID));
- break;
- case ProjectPlatformType.NaCl:
- plugins = chromeDescendants.FindAll(p =>
- p.Name.Equals(Strings.NaClProcessName, ignoreCase) &&
- p.CommandLine.Contains(Strings.NaClLoaderFlag, ignoreCase) &&
- !pluginFinderForbiddenPids_.Contains(p.ID));
- break;
- default:
- return;
- }
-
- // Attach to all plug-ins that we found.
- foreach (ProcessInfo process in plugins)
- {
- // If we are attaching to a plug-in, add it to the forbidden list to ensure we
- // don't try to attach again later.
- pluginFinderForbiddenPids_.Add(process.ID);
- PluginFoundEvent.Invoke(this, new PluginFoundEventArgs(process.ID));
-
- // Slow down the frequency of checks for new plugins.
- pluginFinderTimer_.Interval = RelaxedPluginCheckFrequency;
- }
- }
-
- /// <summary>
- /// Attaches the visual studio debugger to a given process ID.
- /// </summary>
- /// <param name="src">The parameter is not used.</param>
- /// <param name="args">Contains the process ID to attach to.</param>
- private void AttachVSDebugger(object src, PluginFoundEventArgs args)
- {
- foreach (EnvDTE.Process proc in dte_.Debugger.LocalProcesses)
- {
- if (proc.ProcessID == args.ProcessID)
- {
- proc.Attach();
- break;
- }
- }
- }
-
- /// <summary>
- /// Attaches the NaCl GDB debugger to the NaCl plug-in process. Handles loading symbols
- /// and breakpoints from Visual Studio.
- /// </summary>
- /// <param name="src">The parameter is not used.</param>
- /// <param name="args">
- /// Contains the process ID to attach to, unused since debug stub is already attached.
- /// </param>
- private void AttachNaClGDB(object src, PluginFoundEventArgs args)
- {
- // Clean up any pre-existing GDB process (can happen if user reloads page).
- CleanUpGDBProcess();
-
- gdbInitFileName_ = Path.GetTempFileName();
- string pluginAssemblyEscaped = pluginAssembly_.Replace("\\", "\\\\");
- string irtPathEscaped = irtPath_.Replace("\\", "\\\\");
-
- // Create the initialization file to read in on GDB start.
- StringBuilder contents = new StringBuilder();
-
- if (!string.IsNullOrEmpty(manifestPath_))
- {
- string manifestEscaped = manifestPath_.Replace("\\", "\\\\");
- contents.AppendFormat("nacl-manifest {0}\n", manifestEscaped);
- }
- else
- {
- contents.AppendFormat("file \"{0}\"\n", pluginAssemblyEscaped);
- }
-
- contents.AppendFormat("nacl-irt {0}\n", irtPathEscaped);
- contents.AppendFormat("target remote localhost:{0}\n", 4014);
-
- // Insert breakpoints from Visual Studio project.
- foreach (Breakpoint bp in dte_.Debugger.Breakpoints)
- {
- if (!bp.Enabled)
- {
- continue;
- }
-
- if (bp.LocationType == dbgBreakpointLocationType.dbgBreakpointLocationTypeFile)
- {
- contents.AppendFormat("b {0}:{1}\n", Path.GetFileName(bp.File), bp.FileLine);
- }
- else if (bp.LocationType == dbgBreakpointLocationType.dbgBreakpointLocationTypeFunction)
- {
- contents.AppendFormat("b {0}\n", bp.FunctionName);
- }
- else
- {
- WebServerWriteLine(
- string.Format(Strings.UnsupportedBreakpointTypeFormat, bp.LocationType.ToString()));
- }
- }
-
- contents.AppendLine("continue");
- File.WriteAllText(gdbInitFileName_, contents.ToString());
-
- // Start NaCl-GDB.
- try
- {
- gdbProcess_ = new System.Diagnostics.Process();
- gdbProcess_.StartInfo.UseShellExecute = true;
- gdbProcess_.StartInfo.FileName = gdbPath_;
- gdbProcess_.StartInfo.Arguments = string.Format("-x {0}", gdbInitFileName_);
- gdbProcess_.StartInfo.WorkingDirectory = pluginProjectDirectory_;
- gdbProcess_.Start();
- }
- catch (Exception e)
- {
- MessageBox.Show(
- string.Format("NaCl-GDB Start Failed. {0}. Path: {1}", e.Message, gdbPath_));
- }
- }
-
- /// <summary>
- /// Spins up the web server process to host our plug-in.
- /// </summary>
- private void StartWebServer()
- {
- // Add a panel to the output window which is used to capture output
- // from the web server hosting the plugin.
- if (webServerOutputPane_ == null)
- {
- webServerOutputPane_ = dte_.ToolWindows.OutputWindow.OutputWindowPanes.Add(
- Strings.WebServerOutputWindowTitle);
- }
-
- webServerOutputPane_.Clear();
- WebServerWriteLine(Strings.WebServerStartMessage);
-
- try
- {
- webServer_ = new System.Diagnostics.Process();
- webServer_.StartInfo.CreateNoWindow = true;
- webServer_.StartInfo.UseShellExecute = false;
- webServer_.StartInfo.RedirectStandardOutput = true;
- webServer_.StartInfo.RedirectStandardError = true;
- webServer_.StartInfo.FileName = webServerExecutable_;
- webServer_.StartInfo.Arguments = webServerArguments_;
- webServer_.StartInfo.WorkingDirectory = pluginProjectDirectory_;
- webServer_.OutputDataReceived += WebServerMessageReceive;
- webServer_.ErrorDataReceived += WebServerMessageReceive;
- webServer_.Start();
- webServer_.BeginOutputReadLine();
- webServer_.BeginErrorReadLine();
- }
- catch (Exception e)
- {
- WebServerWriteLine(Strings.WebServerStartFail);
- WebServerWriteLine("Exception: " + e.Message);
- }
- }
-
- /// <summary>
- /// Receives output from the web server process to display in the Visual Studio UI.
- /// </summary>
- /// <param name="sender">The parameter is not used.</param>
- /// <param name="e">Contains the data to display.</param>
- private void WebServerMessageReceive(object sender, System.Diagnostics.DataReceivedEventArgs e)
- {
- WebServerWriteLine(e.Data);
- }
-
- /// <summary>
- /// Helper function to write data to the Web Server Output Pane.
- /// </summary>
- /// <param name="message">Message to write.</param>
- private void WebServerWriteLine(string message)
- {
- if (webServerOutputPane_ != null)
- {
- webServerOutputPane_.OutputString(message + "\n");
- }
- }
-
- /// <summary>
- /// The event arguments when a plug-in is found.
- /// </summary>
- public class PluginFoundEventArgs : EventArgs
- {
- /// <summary>
- /// Construct the PluginFoundEventArgs.
- /// </summary>
- /// <param name="pid">Process ID of the found plug-in.</param>
- public PluginFoundEventArgs(uint pid)
- {
- this.ProcessID = pid;
- }
-
- /// <summary>
- /// Gets or sets process ID of the found plug-in.
- /// </summary>
- public uint ProcessID { get; set; }
- }
- }
-}

Powered by Google App Engine
This is Rietveld 408576698