PageRenderTime 47ms CodeModel.GetById 34ms app.highlight 10ms RepoModel.GetById 0ms app.codeStats 1ms

/Python/Product/PythonTools/PythonTools/Commands/DiagnosticsCommand.cs

https://gitlab.com/SplatoonModdingHub/PTVS
C# | 319 lines | 263 code | 32 blank | 24 comment | 13 complexity | bae710100bb580d2934730b9d9369e3e MD5 | raw file
  1// Python Tools for Visual Studio
  2// Copyright(c) Microsoft Corporation
  3// All rights reserved.
  4//
  5// Licensed under the Apache License, Version 2.0 (the License); you may not use
  6// this file except in compliance with the License. You may obtain a copy of the
  7// License at http://www.apache.org/licenses/LICENSE-2.0
  8//
  9// THIS CODE IS PROVIDED ON AN  *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
 10// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
 11// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
 12// MERCHANTABLITY OR NON-INFRINGEMENT.
 13//
 14// See the Apache Version 2.0 License for specific language governing
 15// permissions and limitations under the License.
 16
 17using System;
 18using System.Collections.Generic;
 19using System.Diagnostics;
 20using System.Globalization;
 21using System.IO;
 22using System.Linq;
 23using System.Reflection;
 24using System.Text;
 25using System.Text.RegularExpressions;
 26using System.Threading;
 27using System.Threading.Tasks;
 28using Microsoft.PythonTools.Infrastructure;
 29using Microsoft.PythonTools.Interpreter;
 30using Microsoft.PythonTools.Logging;
 31using Microsoft.PythonTools.Project;
 32using Microsoft.PythonTools.Project.Web;
 33using Microsoft.VisualStudioTools;
 34using Microsoft.VisualStudioTools.Project;
 35
 36namespace Microsoft.PythonTools.Commands {
 37    /// <summary>
 38    /// Provides the command for starting a file or the start item of a project in the REPL window.
 39    /// </summary>
 40    internal sealed class DiagnosticsCommand : Command {
 41        private readonly IServiceProvider _serviceProvider;
 42
 43        private static readonly IEnumerable<string> InterestingDteProperties = new[] {
 44            "InterpreterId",
 45            "InterpreterVersion",
 46            "StartupFile",
 47            "WorkingDirectory",
 48            "PublishUrl",
 49            "SearchPath",
 50            "CommandLineArguments",
 51            "InterpreterPath"
 52        };
 53        
 54        private static readonly IEnumerable<string> InterestingProjectProperties = new[] {
 55            "ClusterRunEnvironment",
 56            "ClusterPublishBeforeRun",
 57            "ClusterWorkingDir",
 58            "ClusterMpiExecCommand",
 59            "ClusterAppCommand",
 60            "ClusterAppArguments",
 61            "ClusterDeploymentDir",
 62            "ClusterTargetPlatform",
 63            PythonWebLauncher.DebugWebServerTargetProperty,
 64            PythonWebLauncher.DebugWebServerTargetTypeProperty,
 65            PythonWebLauncher.DebugWebServerArgumentsProperty,
 66            PythonWebLauncher.DebugWebServerEnvironmentProperty,
 67            PythonWebLauncher.RunWebServerTargetProperty,
 68            PythonWebLauncher.RunWebServerTargetTypeProperty,
 69            PythonWebLauncher.RunWebServerArgumentsProperty,
 70            PythonWebLauncher.RunWebServerEnvironmentProperty,
 71            PythonWebPropertyPage.StaticUriPatternSetting,
 72            PythonWebPropertyPage.StaticUriRewriteSetting,
 73            PythonWebPropertyPage.WsgiHandlerSetting
 74        };
 75
 76        private static readonly Regex InterestingApplicationLogEntries = new Regex(
 77            @"^Application: (devenv\.exe|.+?Python.+?\.exe|ipy(64)?\.exe)",
 78            RegexOptions.IgnoreCase | RegexOptions.CultureInvariant
 79        );
 80
 81        public DiagnosticsCommand(IServiceProvider serviceProvider) {
 82            _serviceProvider = serviceProvider;
 83        }
 84
 85        public override void DoCommand(object sender, EventArgs args) {
 86            var dlg = new DiagnosticsForm(_serviceProvider, "Gathering data...");
 87
 88            ThreadPool.QueueUserWorkItem(x => {
 89                string data;
 90                try {
 91                    data = GetData(dlg);
 92                } catch (Exception ex) when (!ex.IsCriticalException()) {
 93                    data = ex.ToUnhandledExceptionMessage(GetType());
 94                }
 95                try {
 96                    dlg.BeginInvoke((Action)(() => {
 97                        dlg.Ready(data);
 98                    }));
 99                } catch (InvalidOperationException) {
100                    // Window has been closed already
101                }
102            });
103            dlg.ShowDialog();
104        }
105
106        private string GetData(System.Windows.Forms.Control ui) {
107            StringBuilder res = new StringBuilder();
108
109            if (PythonToolsPackage.IsIpyToolsInstalled()) {
110                res.AppendLine("WARNING: IpyTools is installed on this machine.  Having both IpyTools and Python Tools for Visual Studio installed will break Python editing.");
111            }
112
113            string pythonPathIsMasked = "";
114            EnvDTE.DTE dte = null;
115            IPythonInterpreterFactoryProvider[] knownProviders = null;
116            IPythonLauncherProvider[] launchProviders = null;
117            InMemoryLogger inMemLogger = null;
118            ui.Invoke((Action)(() => {
119                pythonPathIsMasked = _serviceProvider.GetPythonToolsService().GeneralOptions.ClearGlobalPythonPath
120                    ? " (masked)"
121                    : "";
122                dte = (EnvDTE.DTE)_serviceProvider.GetService(typeof(EnvDTE.DTE));
123                var model = _serviceProvider.GetComponentModel();
124                knownProviders = model.GetExtensions<IPythonInterpreterFactoryProvider>().ToArray();
125                launchProviders = model.GetExtensions<IPythonLauncherProvider>().ToArray();
126                inMemLogger = model.GetService<InMemoryLogger>();
127            }));
128
129            res.AppendLine("Projects: ");
130
131            var projects = dte.Solution.Projects;
132
133            foreach (EnvDTE.Project project in projects) {
134                string name;
135                try {
136                    // Some projects will throw rather than give us a unique
137                    // name. They are not ours, so we will ignore them.
138                    name = project.UniqueName;
139                } catch (Exception ex) when (!ex.IsCriticalException()) {
140                    bool isPythonProject = false;
141                    try {
142                        isPythonProject = Utilities.GuidEquals(PythonConstants.ProjectFactoryGuid, project.Kind);
143                    } catch (Exception ex2) when (!ex2.IsCriticalException()) {
144                    }
145
146                    if (isPythonProject) {
147                        // Actually, it was one of our projects, so we do care
148                        // about the exception. We'll add it to the output,
149                        // rather than crashing.
150                        res.AppendLine("    Project: " + ex.Message);
151                        res.AppendLine("        Kind: Python");
152                    }
153                    continue;
154                }
155                res.AppendLine("    Project: " + name);
156
157                if (Utilities.GuidEquals(PythonConstants.ProjectFactoryGuid, project.Kind)) {
158                    res.AppendLine("        Kind: Python");
159
160                    foreach (var prop in InterestingDteProperties) {
161                        res.AppendLine("        " + prop + ": " + GetProjectProperty(project, prop));
162                    }
163
164                    var pyProj = project.GetPythonProject();
165                    if (pyProj != null) {
166                        ui.Invoke((Action)(() => {
167                            foreach (var prop in InterestingProjectProperties) {
168                                var propValue = pyProj.GetProjectProperty(prop);
169                                if (propValue != null) {
170                                    res.AppendLine("        " + prop + ": " + propValue);
171                                }
172                            }
173                        }));
174
175                        foreach (var factory in pyProj.InterpreterFactories) {
176                            res.AppendLine();
177                            res.AppendLine("        Interpreter: " + factory.Configuration.FullDescription);
178                            res.AppendLine("            Id: " + factory.Configuration.Id);
179                            res.AppendLine("            Version: " + factory.Configuration.Version);
180                            res.AppendLine("            Arch: " + factory.Configuration.Architecture);
181                            res.AppendLine("            Prefix Path: " + factory.Configuration.PrefixPath ?? "(null)");
182                            res.AppendLine("            Path: " + factory.Configuration.InterpreterPath ?? "(null)");
183                            res.AppendLine("            Windows Path: " + factory.Configuration.WindowsInterpreterPath ?? "(null)");
184                            res.AppendLine("            Lib Path: " + factory.Configuration.LibraryPath ?? "(null)");
185                            res.AppendLine(string.Format("            Path Env: {0}={1}{2}",
186                                factory.Configuration.PathEnvironmentVariable ?? "(null)",
187                                Environment.GetEnvironmentVariable(factory.Configuration.PathEnvironmentVariable ?? ""),
188                                pythonPathIsMasked
189                            ));
190                        }
191                    }
192                } else {
193                    res.AppendLine("        Kind: " + project.Kind);
194                }
195
196                res.AppendLine();
197            }
198
199            res.AppendLine("Environments: ");
200            foreach (var provider in knownProviders.MaybeEnumerate()) {
201                res.AppendLine("    " + provider.GetType().FullName);
202                foreach (var config in provider.GetInterpreterConfigurations()) {
203                    res.AppendLine("        Id: " + config.Id);
204                    res.AppendLine("        Factory: " + config.FullDescription);
205                    res.AppendLine("        Version: " + config.Version);
206                    res.AppendLine("        Arch: " + config.Architecture);
207                    res.AppendLine("        Prefix Path: " + config.PrefixPath ?? "(null)");
208                    res.AppendLine("        Path: " + config.InterpreterPath ?? "(null)");
209                    res.AppendLine("        Windows Path: " + config.WindowsInterpreterPath ?? "(null)");
210                    res.AppendLine("        Lib Path: " + config.LibraryPath ?? "(null)");
211                    res.AppendLine("        Path Env: " + config.PathEnvironmentVariable ?? "(null)");
212                    res.AppendLine();
213                }
214            }
215
216            res.AppendLine("Launchers:");
217            foreach (var launcher in launchProviders.MaybeEnumerate()) {
218                res.AppendLine("    Launcher: " + launcher.GetType().FullName);
219                res.AppendLine("        " + launcher.Description);
220                res.AppendLine("        " + launcher.Name);
221                res.AppendLine();
222            }
223
224            try {
225                res.AppendLine("Logged events/stats:");
226                res.AppendLine(inMemLogger.ToString());
227                res.AppendLine();
228            } catch (Exception ex) when (!ex.IsCriticalException()) {
229                res.AppendLine("  Failed to access event log.");
230                res.AppendLine(ex.ToString());
231                res.AppendLine();
232            }
233
234            try {
235                res.AppendLine("System events:");
236
237                var application = new EventLog("Application");
238                var lastWeek = DateTime.Now.Subtract(TimeSpan.FromDays(7));
239                foreach (var entry in application.Entries.Cast<EventLogEntry>()
240                    .Where(e => e.InstanceId == 1026L)  // .NET Runtime
241                    .Where(e => e.TimeGenerated >= lastWeek)
242                    .Where(e => InterestingApplicationLogEntries.IsMatch(e.Message))
243                    .OrderByDescending(e => e.TimeGenerated)
244                ) {
245                    res.AppendLine(string.Format("Time: {0:s}", entry.TimeGenerated));
246                    using (var reader = new StringReader(entry.Message.TrimEnd())) {
247                        for (var line = reader.ReadLine(); line != null; line = reader.ReadLine()) {
248                            res.AppendLine(line);
249                        }
250                    }
251                    res.AppendLine();
252                }
253
254            } catch (Exception ex) when (!ex.IsCriticalException()) {
255                res.AppendLine("  Failed to access event log.");
256                res.AppendLine(ex.ToString());
257                res.AppendLine();
258            }
259
260            res.AppendLine("Loaded assemblies:");
261            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().OrderBy(assem => assem.FullName)) {
262                AssemblyFileVersionAttribute assemFileVersion;
263                var error = "(null)";
264                try {
265                    assemFileVersion = assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false)
266                        .OfType<AssemblyFileVersionAttribute>()
267                        .FirstOrDefault();
268                } catch (Exception e) when (!e.IsCriticalException()) {
269                    assemFileVersion = null;
270                    error = string.Format("{0}: {1}", e.GetType().Name, e.Message);
271                }
272
273                res.AppendLine(string.Format("  {0}, FileVersion={1}",
274                    assembly.FullName,
275                    assemFileVersion?.Version ?? error
276                ));
277            }
278            res.AppendLine();
279
280            string globalAnalysisLog = PythonTypeDatabase.GlobalLogFilename;
281            if (File.Exists(globalAnalysisLog)) {
282                res.AppendLine("Global Analysis:");
283                try {
284                    res.AppendLine(File.ReadAllText(globalAnalysisLog));
285                } catch (Exception ex) when (!ex.IsCriticalException()) {
286                    res.AppendLine("Error reading the global analysis log.");
287                    res.AppendLine("Please wait for analysis to complete and try again.");
288                    res.AppendLine(ex.ToString());
289                }
290            }
291            res.AppendLine();
292
293            res.AppendLine("Environment Analysis Logs: ");
294            foreach (var provider in knownProviders) {
295                foreach (var factory in provider.GetInterpreterFactories().OfType<IPythonInterpreterFactoryWithDatabase>()) {
296                    res.AppendLine(factory.Configuration.FullDescription);
297                    string analysisLog = factory.GetAnalysisLogContent(CultureInfo.InvariantCulture);
298                    if (!string.IsNullOrEmpty(analysisLog)) {
299                        res.AppendLine(analysisLog);
300                    }
301                    res.AppendLine();
302                }
303            }
304            return res.ToString();
305        }
306
307        private static string GetProjectProperty(EnvDTE.Project project, string name) {
308            try {
309                return project.Properties.Item(name).Value.ToString();
310            } catch {
311                return "<undefined>";
312            }
313        }
314
315        public override int CommandId {
316            get { return (int)PkgCmdIDList.cmdidDiagnostics; }
317        }
318    }
319}