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; } |
+ } |
+ } |
+} |