| Index: visual_studio/NativeClientVSAddIn/NativeClientVSAddIn/PluginDebuggerBase.cs | 
| diff --git a/visual_studio/NativeClientVSAddIn/NativeClientVSAddIn/PluginDebuggerBase.cs b/visual_studio/NativeClientVSAddIn/NativeClientVSAddIn/PluginDebuggerBase.cs | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..595c2fa5eee3352e3e0a134148b9fa1baef55216 | 
| --- /dev/null | 
| +++ b/visual_studio/NativeClientVSAddIn/NativeClientVSAddIn/PluginDebuggerBase.cs | 
| @@ -0,0 +1,220 @@ | 
| +// 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.Windows.Forms; | 
| + | 
| +  using EnvDTE; | 
| +  using EnvDTE80; | 
| + | 
| +  /// <summary> | 
| +  /// This is a base class for encapsulating functionality related to attaching a debugger | 
| +  /// to a nacl/pepper plug-in.  This base class mostly contains functionality related to finding | 
| +  /// the plug-in. | 
| +  /// </summary> | 
| +  public class PluginDebuggerBase : IDisposable | 
| +  { | 
| +    /// <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> | 
| +    /// 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. | 
| +    /// </summary> | 
| +    /// <param name="dte">Automation object from Visual Studio.</param> | 
| +    /// <param name="properties">PropertyManager set to a valid project/platform.</param> | 
| +    protected PluginDebuggerBase(DTE2 dte, PropertyManager properties) | 
| +    { | 
| +      if (dte == null) | 
| +      { | 
| +        throw new ArgumentNullException("dte"); | 
| +      } | 
| + | 
| +      if (properties == null) | 
| +      { | 
| +        throw new ArgumentNullException("properties"); | 
| +      } | 
| + | 
| +      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(); | 
| + | 
| +      pluginFinderTimer_.Interval = InitialPluginCheckFrequency; | 
| +      pluginFinderTimer_.Start(); | 
| +    } | 
| + | 
| +    /// <summary> | 
| +    /// Finalizer. Should clean up unmanaged resources. Should not be overriden in derived classes. | 
| +    /// </summary> | 
| +    ~PluginDebuggerBase() | 
| +    { | 
| +      Dispose(false); | 
| +    } | 
| + | 
| +    /// <summary> | 
| +    /// An event indicating a target plug-in was found on the system. | 
| +    /// </summary> | 
| +    public event EventHandler<PluginFoundEventArgs> PluginFoundEvent; | 
| + | 
| +    /// <summary> | 
| +    /// Gets or sets a value indicating whether this object has been disposed of already. | 
| +    /// </summary> | 
| +    protected bool Disposed { get; set; } | 
| + | 
| +    /// <summary> | 
| +    /// Gets or sets the main visual studio object. | 
| +    /// </summary> | 
| +    protected DTE2 Dte { get; set; } | 
| + | 
| +    /// <summary> | 
| +    /// Disposes the object when called by user code (not directly by garbage collector). | 
| +    /// </summary> | 
| +    public void Dispose() | 
| +    { | 
| +      Dispose(true); | 
| +      GC.SuppressFinalize(this); | 
| +    } | 
| + | 
| +    /// <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> | 
| +    public void FindAndAttachToPlugin(object unused, EventArgs unused1) | 
| +    { | 
| +      // This function is called by the main Visual Studio event loop and we may have put the event | 
| +      // on the queue just before disposing it meaning this could be called after we've disposed. | 
| +      if (Disposed) | 
| +      { | 
| +        return; | 
| +      } | 
| + | 
| +      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); | 
| +      string mainChromeFlags = chromeDescendants.Find(p => p.ID == mainChromeProcId).CommandLine; | 
| + | 
| +      // 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 = chromeDescendants.FindAll(p => | 
| +          IsPluginProcess(p, mainChromeFlags) && !pluginFinderForbiddenPids_.Contains(p.ID)); | 
| + | 
| +      // 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> | 
| +    /// Disposes the object. If disposing is false then this has been called by garbage collection, | 
| +    /// and we shouldn't reference managed objects. | 
| +    /// </summary> | 
| +    /// <param name="disposing">True if user call to Dispose, false if garbase collection.</param> | 
| +    protected virtual void Dispose(bool disposing) | 
| +    { | 
| +      if (!Disposed && disposing) | 
| +      { | 
| +        pluginFinderTimer_.Stop(); | 
| +      } | 
| + | 
| +      Disposed = true; | 
| +    } | 
| + | 
| +    /// <summary> | 
| +    /// Called to check if a process is a valid plugin to attach to. | 
| +    /// </summary> | 
| +    /// <param name="proc">Contains information about the process in question.</param> | 
| +    /// <param name="mainChromeFlags">Flags on the main Chrome process.</param> | 
| +    /// <returns>True if we should attach to the process.</returns> | 
| +    protected virtual bool IsPluginProcess(ProcessInfo proc, string mainChromeFlags) | 
| +    { | 
| +      throw new InvalidOperationException(); | 
| +    } | 
| + | 
| +    /// <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; } | 
| +    } | 
| +  } | 
| +} | 
|  |