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