diff options
author | Matt Ward <ward.matt@gmail.com> | 2018-02-22 14:08:59 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-22 14:08:59 +0300 |
commit | ae72d9874e58db5aa4e75d00808ff4a42a35b9ac (patch) | |
tree | 54aee6d1e857d1c2f95b4f3539f81a5e1b48dff6 /main/src | |
parent | 0e6e5f5ce3306f64fa50361288da3d14235a994e (diff) | |
parent | 8582d83924f5aade53bdff0d09c54a980b318b35 (diff) |
Merge pull request #3690 from mono/quality-telemetry-phase2
Quality telemetry - phase 2
Diffstat (limited to 'main/src')
24 files changed, 581 insertions, 85 deletions
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.csproj b/main/src/addins/CSharpBinding/CSharpBinding.csproj index 9f002f7d3d..ee7265d81a 100644 --- a/main/src/addins/CSharpBinding/CSharpBinding.csproj +++ b/main/src/addins/CSharpBinding/CSharpBinding.csproj @@ -1,4 +1,4 @@ -<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> <Import Project="..\..\..\MonoDevelop.props" /> <Import Project="$(ReferencesVSEditor)" /> <PropertyGroup> @@ -397,6 +397,7 @@ <Compile Include="MonoDevelop.CSharp.Completion\CSharpCompletionData.cs" /> <Compile Include="MonoDevelop.CSharp.Completion\CompletionProvider\ProtocolMemberCompletionProvider.cs" /> <Compile Include="MonoDevelop.CSharp.Tooltips\QuickInfoProvider.cs" /> + <Compile Include="MonoDevelop.CSharp.Navigation\Counters.cs" /> <Compile Include="MonoDevelop.CSharp.Project\CSharpLanguageVersionHelper.cs" /> </ItemGroup> <ItemGroup> diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs index 1e02c5843f..7f55dd6bea 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs @@ -59,6 +59,8 @@ using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Platform; +using Counters = MonoDevelop.Ide.Counters; + namespace MonoDevelop.CSharp.Completion { sealed class CSharpCompletionTextEditorExtension : CompletionTextEditorExtension, IDebuggerExpressionResolver diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs index c25ac90c4d..62369290bf 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.CSharp; +using MonoDevelop.CSharp.Navigation; namespace ICSharpCode.NRefactory6.CSharp.Features.GotoDefinition { @@ -78,6 +79,21 @@ namespace ICSharpCode.NRefactory6.CSharp.Features.GotoDefinition public static bool TryGoToDefinition (Document document, int position, CancellationToken cancellationToken) { + var metadata = Counters.CreateNavigateToMetadata ("Declaration"); + + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + try { + bool result = TryGoToDefinitionInternal (document, position, cancellationToken); + Counters.UpdateNavigateResult (metadata, result); + return result; + } finally { + Counters.UpdateUserCancellation (metadata, cancellationToken); + } + } + } + + static bool TryGoToDefinitionInternal (Document document, int position, CancellationToken cancellationToken) + { var symbol = FindSymbolAsync (document, position, cancellationToken).Result; if (symbol != null) { diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs new file mode 100644 index 0000000000..51dccb3cf8 --- /dev/null +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs @@ -0,0 +1,69 @@ +// +// Counters.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2018 Microsoft +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic; +using System.Threading; +using MonoDevelop.Core.Instrumentation; + +namespace MonoDevelop.CSharp.Navigation +{ + internal static class Counters + { + public static TimerCounter NavigateTo = InstrumentationService.CreateTimerCounter ("Navigate to", "Code Navigation", id: "CodeNavigation.NavigateTo"); + + public static IDictionary<string, string> CreateNavigateToMetadata (string navigationType) + { + var metadata = new Dictionary<string, string> (); + metadata ["Type"] = navigationType; + metadata ["Result"] = "Failure"; // Will be updated when navigation finishes. + return metadata; + } + + public static void UpdateUserCancellation (IDictionary<string, string> metadata, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) { + metadata ["Result"] = "UserCancel"; + } + } + + public static void UpdateNavigateResult (IDictionary<string, string> metadata, bool result) + { + metadata ["Result"] = result ? "Success" : "Failure"; + } + + /// <summary> + /// Distinguish between IDE errors and a failure due to the user selecting an invalid item. + /// Some navigation menus are enabled even if they are not valid. For example, right clicking + /// on a method and selecting Navigate - Extension Methods will find no class at the caret and + /// will not attempt to find any extension methods. Note that the CommandInfo is disabled for + /// the menu but this does not seem to have any affect presumably because the method is async. + /// </summary> + public static void UpdateUserFault (IDictionary<string, string> metadata) + { + metadata ["Result"] = "UserFault"; + } + } +} diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs index d732aca199..7dceb59c9b 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs @@ -25,28 +25,31 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; using MonoDevelop.Ide.TypeSystem; using MonoDevelop.Core; +using MonoDevelop.Core.ProgressMonitoring; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; using MonoDevelop.Components.Commands; using MonoDevelop.Refactoring; -using System.Threading.Tasks; -using System.Collections.Generic; using ICSharpCode.NRefactory6.CSharp; namespace MonoDevelop.CSharp.Navigation { class FindBaseSymbolsHandler : CommandHandler { - void FindSymbols (ISymbol sym) + Task FindSymbols (ISymbol sym, CancellationTokenSource cancellationTokenSource) { if (sym == null) - return; - Task.Run (delegate { - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + return Task.FromResult (0); + return Task.Run (delegate { + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { var foundSymbol = sym.OverriddenMember (); while (foundSymbol != null) { foreach (var loc in foundSymbol.Locations) { @@ -56,7 +59,7 @@ namespace MonoDevelop.CSharp.Navigation if (loc.SourceTree == null) continue; - monitor.ReportResult (new MemberReference (foundSymbol, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (foundSymbol, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } foundSymbol = foundSymbol.OverriddenMember (); } @@ -81,11 +84,24 @@ namespace MonoDevelop.CSharp.Navigation protected override async void Run () { - var sym = await GetSymbolAtCaret (IdeApp.Workbench.ActiveDocument); - if (sym != null) - FindSymbols (sym); - } + var metadata = Counters.CreateNavigateToMetadata ("BaseSymbols"); + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + var sym = await GetSymbolAtCaret (IdeApp.Workbench.ActiveDocument); + if (sym == null) { + Counters.UpdateUserFault (metadata); + return; + } + using (var source = new CancellationTokenSource ()) { + try { + await FindSymbols (sym, source); + Counters.UpdateNavigateResult (metadata, true); + } finally { + Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } + } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindDerivedSymbolsHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindDerivedSymbolsHandler.cs index 60dfe50938..ce81fb731d 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindDerivedSymbolsHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindDerivedSymbolsHandler.cs @@ -36,6 +36,7 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions; using MonoDevelop.Components.Commands; using MonoDevelop.Core; +using MonoDevelop.Core.ProgressMonitoring; using MonoDevelop.CSharp.Navigation; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; @@ -60,13 +61,14 @@ namespace MonoDevelop.CSharp.Refactoring return symbol.IsVirtual || symbol.IsAbstract || symbol.IsOverride; } - public static void FindDerivedSymbols (ISymbol symbol) + static Task FindDerivedSymbols (ISymbol symbol, CancellationTokenSource cancellationTokenSource) { if (symbol == null) - return; + return Task.FromResult (0); var solution = IdeApp.Workbench.ActiveDocument?.AnalysisDocument?.Project?.Solution ?? TypeSystemService.Workspace.CurrentSolution; - Task.Run (async delegate { - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + return Task.Run (async delegate { + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { IEnumerable<ISymbol> result; try { if (symbol.ContainingType != null && symbol.ContainingType.TypeKind == TypeKind.Interface) { @@ -87,7 +89,7 @@ namespace MonoDevelop.CSharp.Refactoring foreach (var foundSymbol in result) { foreach (var loc in foundSymbol.Locations) { monitor.CancellationToken.ThrowIfCancellationRequested (); - monitor.ReportResult (new MemberReference (foundSymbol, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (foundSymbol, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } } catch (OperationCanceledException) { @@ -120,9 +122,23 @@ namespace MonoDevelop.CSharp.Refactoring protected override async void Run (object dataItem) { - var sym = await FindBaseSymbolsHandler.GetSymbolAtCaret (IdeApp.Workbench.ActiveDocument); - if (sym != null) - FindDerivedSymbols (sym); + var metadata = Navigation.Counters.CreateNavigateToMetadata ("DerivedSymbols"); + using (var timer = Navigation.Counters.NavigateTo.BeginTiming (metadata)) { + var sym = await FindBaseSymbolsHandler.GetSymbolAtCaret (IdeApp.Workbench.ActiveDocument); + if (sym == null) { + Navigation.Counters.UpdateUserFault (metadata); + return; + } + + using (var source = new CancellationTokenSource ()) { + try { + await FindDerivedSymbols (sym, source); + Navigation.Counters.UpdateNavigateResult (metadata, true); + } finally { + Navigation.Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindExtensionMethodsHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindExtensionMethodsHandler.cs index c7a5f0c5a9..99a0153fc3 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindExtensionMethodsHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindExtensionMethodsHandler.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +using System.Linq; +using System.Threading; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; using Microsoft.CodeAnalysis; @@ -31,7 +33,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions; using MonoDevelop.Components.Commands; using MonoDevelop.Refactoring; -using System.Linq; using ICSharpCode.NRefactory6.CSharp; namespace MonoDevelop.CSharp.Navigation @@ -47,10 +48,24 @@ namespace MonoDevelop.CSharp.Navigation protected async override void Run () { - var doc = IdeApp.Workbench.ActiveDocument; - var sym = await GetNamedTypeAtCaret (doc); - if (sym != null) - FindExtensionMethods (await doc.GetCompilationAsync (), sym); + var metadata = Counters.CreateNavigateToMetadata ("ExtensionMethods"); + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + var doc = IdeApp.Workbench.ActiveDocument; + var sym = await GetNamedTypeAtCaret (doc); + if (sym == null) { + Counters.UpdateUserFault (metadata); + return; + } + + using (var source = new CancellationTokenSource ()) { + try { + FindExtensionMethods (await doc.GetCompilationAsync (), sym, source); + Counters.UpdateNavigateResult (metadata, true); + } finally { + Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } } internal static async System.Threading.Tasks.Task<INamedTypeSymbol> GetNamedTypeAtCaret (Ide.Gui.Document doc) @@ -62,12 +77,14 @@ namespace MonoDevelop.CSharp.Navigation return sym as INamedTypeSymbol; } - void FindExtensionMethods (Compilation compilation, ISymbol sym) + void FindExtensionMethods (Compilation compilation, ISymbol sym, CancellationTokenSource cancellationTokenSource) { var symType = sym as ITypeSymbol; if (symType == null) return; - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { foreach (var type in compilation.Assembly.GlobalNamespace.GetAllTypes (monitor.CancellationToken)) { if (!type.MightContainExtensionMethods) continue; @@ -79,7 +96,7 @@ namespace MonoDevelop.CSharp.Navigation var reducedMethod = extMethod.ReduceExtensionMethod (symType); if (reducedMethod != null) { var loc = extMethod.Locations.First (); - monitor.ReportResult (new MemberReference (extMethod, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (extMethod, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs index c37c5a4362..9bc100b784 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs @@ -24,15 +24,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; using Microsoft.CodeAnalysis; using MonoDevelop.Components.Commands; using MonoDevelop.Refactoring; -using System.Linq; using ICSharpCode.NRefactory6.CSharp; using Microsoft.CodeAnalysis.CSharp; -using System.Threading.Tasks; namespace MonoDevelop.CSharp.Navigation { @@ -47,10 +48,24 @@ namespace MonoDevelop.CSharp.Navigation protected async override void Run () { - var doc = IdeApp.Workbench.ActiveDocument; - var sym = await GetNamedTypeAtCaret (doc); - if (sym != null) - FindImplementingSymbols (await doc.GetCompilationAsync (), sym); + var metadata = Counters.CreateNavigateToMetadata ("ImplementingMembers"); + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + var doc = IdeApp.Workbench.ActiveDocument; + var sym = await GetNamedTypeAtCaret (doc); + if (sym == null) { + Counters.UpdateUserFault (metadata); + return; + } + + using (var source = new CancellationTokenSource ()) { + try { + await FindImplementingSymbols (await doc.GetCompilationAsync (), sym, source); + Counters.UpdateNavigateResult (metadata, true); + } finally { + Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } } static async Task<RefactoringSymbolInfo> GetNamedTypeAtCaret (Ide.Gui.Document doc) @@ -65,14 +80,15 @@ namespace MonoDevelop.CSharp.Navigation return info; } - void FindImplementingSymbols (Compilation compilation, RefactoringSymbolInfo info) + Task FindImplementingSymbols (Compilation compilation, RefactoringSymbolInfo info, CancellationTokenSource cancellationTokenSource) { var interfaceType = info.Symbol as ITypeSymbol; if (interfaceType == null) - return; + return Task.FromResult (0); - Task.Run (delegate { - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + return Task.Run (delegate { + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { var parentTypeNode = info.Node?.Parent?.Parent?.Parent; if (parentTypeNode == null) return; @@ -86,7 +102,7 @@ namespace MonoDevelop.CSharp.Navigation if (impl == null) continue; var loc = impl.Locations.First (); - monitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } foreach (var iFace in interfaceType.AllInterfaces) { @@ -98,7 +114,7 @@ namespace MonoDevelop.CSharp.Navigation if (impl == null) continue; var loc = impl.Locations.First (); - monitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindMemberOverloadsHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindMemberOverloadsHandler.cs index f88698e842..d02a2bc077 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindMemberOverloadsHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindMemberOverloadsHandler.cs @@ -25,6 +25,7 @@ // THE SOFTWARE. using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -53,9 +54,10 @@ namespace MonoDevelop.CSharp.Navigation } } - public static void FindOverloads (ISymbol symbol) + public static void FindOverloads (ISymbol symbol, CancellationTokenSource cancellationTokenSource) { - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { switch (symbol.Kind) { case SymbolKind.Method: foreach (var method in symbol.ContainingType.GetMembers (symbol.Name).OfType<IMethodSymbol> ()) { @@ -63,7 +65,7 @@ namespace MonoDevelop.CSharp.Navigation if (monitor.CancellationToken.IsCancellationRequested) return; - monitor.ReportResult (new MemberReference (method, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (method, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } break; @@ -73,7 +75,7 @@ namespace MonoDevelop.CSharp.Navigation if (monitor.CancellationToken.IsCancellationRequested) return; - monitor.ReportResult (new MemberReference (property, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (property, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } break; @@ -100,10 +102,26 @@ namespace MonoDevelop.CSharp.Navigation var doc = IdeApp.Workbench.ActiveDocument; if (doc == null || doc.FileName == FilePath.Null) return; - var info = await RefactoringSymbolInfo.GetSymbolInfoAsync (doc, doc.Editor); - var sym = info.Symbol ?? info.DeclaredSymbol; - if (sym != null) - FindOverloads (sym); + + var metadata = Counters.CreateNavigateToMetadata ("MemberOverloads"); + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + + var info = await RefactoringSymbolInfo.GetSymbolInfoAsync (doc, doc.Editor); + var sym = info.Symbol ?? info.DeclaredSymbol; + if (sym == null) { + Counters.UpdateUserFault (metadata); + return; + } + + using (var source = new CancellationTokenSource ()) { + try { + FindOverloads (sym, source); + Counters.UpdateNavigateResult (metadata, true); + } finally { + Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } } } }
\ No newline at end of file diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/FindReferencesHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/FindReferencesHandler.cs index 7d8044f33c..d8bb848bbd 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/FindReferencesHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/FindReferencesHandler.cs @@ -32,6 +32,7 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Shared.Extensions; using MonoDevelop.Components.Commands; using MonoDevelop.Core; +using MonoDevelop.Core.Instrumentation; using MonoDevelop.CSharp.Highlighting; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; @@ -49,7 +50,12 @@ namespace MonoDevelop.CSharp.Refactoring if (workspace == null) return; Task.Run (async delegate { + ITimeTracker timer = null; + var metadata = MonoDevelop.Refactoring.Counters.CreateFindReferencesMetadata (); + try { + timer = MonoDevelop.Refactoring.Counters.FindReferences.BeginTiming (metadata); + var antiDuplicatesSet = new HashSet<SearchResult> (new SearchResultComparer ()); foreach (var loc in symbol.Locations) { if (monitor.CancellationToken.IsCancellationRequested) @@ -97,6 +103,7 @@ namespace MonoDevelop.CSharp.Refactoring } } catch (OperationCanceledException) { } catch (Exception ex) { + MonoDevelop.Refactoring.Counters.SetFailure (metadata); if (monitor != null) monitor.ReportError ("Error finding references", ex); else @@ -104,6 +111,10 @@ namespace MonoDevelop.CSharp.Refactoring } finally { if (monitor != null) monitor.Dispose (); + if (monitor.CancellationToken.IsCancellationRequested) + MonoDevelop.Refactoring.Counters.SetUserCancel (metadata); + if (timer != null) + timer.Dispose (); } }); } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/GotoBaseDeclarationHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/GotoBaseDeclarationHandler.cs index fef916d350..c8f6651e22 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/GotoBaseDeclarationHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/GotoBaseDeclarationHandler.cs @@ -24,10 +24,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; -using Microsoft.CodeAnalysis; -using MonoDevelop.Ide; using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using MonoDevelop.Core; +using MonoDevelop.Ide; using MonoDevelop.Refactoring; namespace MonoDevelop.CSharp.Refactoring @@ -74,37 +75,46 @@ namespace MonoDevelop.CSharp.Refactoring return false; } - public static void GotoBase (MonoDevelop.Ide.Gui.Document doc, ISymbol symbol) + public static async Task GotoBase (MonoDevelop.Ide.Gui.Document doc, ISymbol symbol) { if (doc == null) throw new ArgumentNullException ("doc"); if (symbol == null) throw new ArgumentNullException ("symbol"); + + var metadata = Navigation.Counters.CreateNavigateToMetadata ("Base"); + using (var timer = Navigation.Counters.NavigateTo.BeginTiming (metadata)) { + await GotoBaseInternal (doc, symbol); + Navigation.Counters.UpdateNavigateResult (metadata, true); + } + } + + static Task GotoBaseInternal (MonoDevelop.Ide.Gui.Document doc, ISymbol symbol) + { switch (symbol.Kind) { case SymbolKind.NamedType: - RefactoringService.RoslynJumpToDeclaration (((ITypeSymbol)symbol).BaseType, doc.Project); - break; + return RefactoringService.RoslynJumpToDeclaration (((ITypeSymbol)symbol).BaseType, doc.Project); case SymbolKind.Property: var property = (IPropertySymbol)symbol; if (property.OverriddenProperty != null) - RefactoringService.RoslynJumpToDeclaration (property.OverriddenProperty, doc.Project); + return RefactoringService.RoslynJumpToDeclaration (property.OverriddenProperty, doc.Project); else - RefactoringService.RoslynJumpToDeclaration (property.ExplicitInterfaceImplementations.First (), doc.Project); - break; + return RefactoringService.RoslynJumpToDeclaration (property.ExplicitInterfaceImplementations.First (), doc.Project); case SymbolKind.Event: var evt = (IEventSymbol)symbol; if (evt.OverriddenEvent != null) - RefactoringService.RoslynJumpToDeclaration (evt.OverriddenEvent, doc.Project); + return RefactoringService.RoslynJumpToDeclaration (evt.OverriddenEvent, doc.Project); else - RefactoringService.RoslynJumpToDeclaration (evt.ExplicitInterfaceImplementations.First (), doc.Project); - break; + return RefactoringService.RoslynJumpToDeclaration (evt.ExplicitInterfaceImplementations.First (), doc.Project); case SymbolKind.Method: var method = (IMethodSymbol)symbol; if (method.OverriddenMethod != null) - RefactoringService.RoslynJumpToDeclaration (method.OverriddenMethod, doc.Project); + return RefactoringService.RoslynJumpToDeclaration (method.OverriddenMethod, doc.Project); else - RefactoringService.RoslynJumpToDeclaration (method.ExplicitInterfaceImplementations.First (), doc.Project); - break; + return RefactoringService.RoslynJumpToDeclaration (method.ExplicitInterfaceImplementations.First (), doc.Project); + default: + // CanGotoBase should prevent this from happening. + throw new ArgumentException (string.Format ("Invalid symbol.Kind {0}", symbol.Kind)); } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs index dade41b30a..29136372b0 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs @@ -111,7 +111,7 @@ namespace MonoDevelop.CSharp.Refactoring if (info.DeclaredSymbol != null && GotoBaseDeclarationHandler.CanGotoBase (info.DeclaredSymbol)) { - ainfo.Add (GotoBaseDeclarationHandler.GetDescription (info.DeclaredSymbol), new Action (() => GotoBaseDeclarationHandler.GotoBase (doc, info.DeclaredSymbol))); + ainfo.Add (GotoBaseDeclarationHandler.GetDescription (info.DeclaredSymbol), new Action (() => GotoBaseDeclarationHandler.GotoBase (doc, info.DeclaredSymbol).Ignore ())); added = true; } diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs index 326f85209b..ecbfd73d9b 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs @@ -344,6 +344,12 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol break; } break; + case "module": + var moduleEvent = (ModuleEvent)obj.Body; + if (moduleEvent.Reason == ModuleEvent.ReasonValue.New) { + OnAssemblyLoaded (moduleEvent.Module.Path); + } + break; } }); } diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs index cde6228e62..d89f406cea 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs @@ -44,9 +44,12 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol List<ObjectValue> results = new List<ObjectValue> (); var scopeBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new ScopesRequest (frames [frameIndex].Id)); foreach (var variablesGroup in scopeBody.Scopes) { - var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference)); - foreach (var variable in varibles.Variables) { - results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, variable.Name, variable.EvaluateName, variable.Type, variable.Value, variable.VariablesReference, variablesGroup.VariablesReference, frames [frameIndex].Id)); + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference)); + foreach (var variable in varibles.Variables) { + results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, variable.Name, variable.EvaluateName, variable.Type, variable.Value, variable.VariablesReference, variablesGroup.VariablesReference, frames [frameIndex].Id)); + } + timer.Success = true; } } return results.ToArray (); @@ -66,10 +69,13 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol { var results = new List<ObjectValue> (); foreach (var expr in expressions) { - var responseBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest ( - expr, - frames [frameIndex].Id)); - results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, expr, expr, responseBody.Type, responseBody.Result, responseBody.VariablesReference, 0, frames [frameIndex].Id)); + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + var responseBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest ( + expr, + frames [frameIndex].Id)); + results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, expr, expr, responseBody.Type, responseBody.Result, responseBody.VariablesReference, 0, frames [frameIndex].Id)); + timer.Success = true; + } } return results.ToArray (); } @@ -84,9 +90,12 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol List<ObjectValue> results = new List<ObjectValue> (); var scopeBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new ScopesRequest (frames [frameIndex].Id)); foreach (var variablesGroup in scopeBody.Scopes) { - var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference)); - foreach (var variable in varibles.Variables) { - results.Add (ObjectValue.CreatePrimitive (null, new ObjectPath (variable.Name), variable.Type ?? "<unknown>", new EvaluationResult (variable.Value), ObjectValueFlags.None)); + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference)); + foreach (var variable in varibles.Variables) { + results.Add (ObjectValue.CreatePrimitive (null, new ObjectPath (variable.Name), variable.Type ?? "<unknown>", new EvaluationResult (variable.Value), ObjectValueFlags.None)); + } + timer.Success = true; } } return results.ToArray (); diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs index b6cfc55f76..02fd9fc481 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs @@ -41,10 +41,13 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol if (variablesReference <= 0) { objValChildren = new ObjectValue [0]; } else { - var children = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest ( - variablesReference - )).Variables; - objValChildren = children.Select (c => VSCodeDebuggerBacktrace.VsCodeVariableToObjectValue (vsCodeDebuggerSession, c.Name, c.EvaluateName, c.Type, c.Value, c.VariablesReference, variablesReference, frameId)).ToArray (); + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + var children = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest ( + variablesReference + )).Variables; + objValChildren = children.Select (c => VSCodeDebuggerBacktrace.VsCodeVariableToObjectValue (vsCodeDebuggerSession, c.Name, c.EvaluateName, c.Type, c.Value, c.VariablesReference, variablesReference, frameId)).ToArray (); + timer.Success = true; + } } } return objValChildren; @@ -79,7 +82,11 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol public object GetRawValue (ObjectPath path, EvaluationOptions options) { - var val = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest (evalName, frameId)).Result; + string val = null; + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + val = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest (evalName, frameId)).Result; + timer.Success = true; + } if (val.StartsWith ("\"", StringComparison.Ordinal)) if (options.ChunkRawStrings) return new RawValueString (new RawString (val)); diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj index d4bb66be72..bdfe3f4405 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj @@ -210,6 +210,7 @@ <Compile Include="MonoDevelop.Debugger\NoSourceView.cs" /> <Compile Include="MonoDevelop.Debugger\DebugSourceFilesOptionsPanel.cs" /> <Compile Include="MonoDevelop.Debugger\SolutionHasDebugSourceFilesCondition.cs" /> + <Compile Include="MonoDevelop.Debugger\Counters.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="MonoDevelop.Debugger.addin.xml"> diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Counters.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Counters.cs new file mode 100644 index 0000000000..83c1330cc0 --- /dev/null +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Counters.cs @@ -0,0 +1,36 @@ +// +// Counters.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2018 Microsoft +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using MonoDevelop.Core.Instrumentation; + +namespace MonoDevelop.Debugger +{ + static class Counters + { + public static Counter DebugSession = InstrumentationService.CreateCounter ("Debug Session", "Debugger", id: "Debugger.DebugSession"); + public static Counter EvaluationStats = InstrumentationService.CreateCounter ("Evaluation Statistics", "Debugger", id: "Debugger.EvaluationStatistics"); + } +} diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs index 1d3692426e..17f5b10fb9 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs @@ -593,7 +593,6 @@ namespace MonoDevelop.Debugger var session = debugger.CreateSession (); var monitor = IdeApp.Workbench.ProgressMonitors.GetRunProgressMonitor (proc.Name); var sessionManager = new SessionManager (session, monitor.Console, debugger); - session.ExceptionHandler = ExceptionHandler; SetupSession (sessionManager); session.TargetExited += delegate { monitor.Dispose (); @@ -676,7 +675,6 @@ namespace MonoDevelop.Debugger startInfo.CloseExternalConsoleOnExit = ((ExternalConsole)c).CloseOnDispose; var session = factory.CreateSession (); - session.ExceptionHandler = ExceptionHandler; SessionManager sessionManager; // When using an external console, create a new internal console which will be used @@ -690,8 +688,10 @@ namespace MonoDevelop.Debugger SetDebugLayout (); try { + sessionManager.PrepareForRun (); session.Run (startInfo, GetUserOptions ()); } catch { + sessionManager.SessionError = true; Cleanup (sessionManager); throw; } @@ -713,6 +713,8 @@ namespace MonoDevelop.Debugger { OperationConsole console; IDisposable cancelRegistration; + System.Diagnostics.Stopwatch firstAssemblyLoadTimer; + public readonly DebuggerSession Session; public readonly DebugAsyncOperation debugOperation; public readonly DebuggerEngine Engine; @@ -721,6 +723,8 @@ namespace MonoDevelop.Debugger { Engine = engine; Session = session; + session.ExceptionHandler = ExceptionHandler; + session.AssemblyLoaded += OnAssemblyLoaded; this.console = console; cancelRegistration = console.CancellationToken.Register (Cancel); debugOperation = new DebugAsyncOperation (session); @@ -761,13 +765,80 @@ namespace MonoDevelop.Debugger public void Dispose () { + UpdateDebugSessionCounter (); + UpdateEvaluationStatsCounter (); + console?.Dispose (); console = null; + Session.AssemblyLoaded -= OnAssemblyLoaded; Session.Dispose (); debugOperation.Cleanup (); cancelRegistration?.Dispose (); cancelRegistration = null; } + + /// <summary> + /// Indicates whether the debug session failed to an exception or any debugger + /// operation failed and was reported to the user. + /// </summary> + public bool SessionError { get; set; } + + void UpdateDebugSessionCounter () + { + var metadata = new Dictionary<string, string> (); + metadata ["Success"] = (!SessionError).ToString (); + metadata ["DebuggerType"] = Engine.Id; + + if (firstAssemblyLoadTimer != null) { + if (firstAssemblyLoadTimer.IsRunning) { + // No first assembly load event. + firstAssemblyLoadTimer.Stop (); + } else { + metadata ["AssemblyFirstLoadDuration"] = firstAssemblyLoadTimer.ElapsedMilliseconds.ToString (); + } + } + + Counters.DebugSession.Inc (metadata); + } + + void UpdateEvaluationStatsCounter () + { + if (Session.EvaluationStats.TimingsCount == 0 && Session.EvaluationStats.FailureCount == 0) { + // No timings or failures recorded. + return; + } + + var metadata = new Dictionary<string, string> (); + metadata ["DebuggerType"] = Engine.Id; + metadata ["AverageDuration"] = Session.EvaluationStats.AverageTime.ToString (); + metadata ["MaximumDuration"] = Session.EvaluationStats.MaxTime.ToString (); + metadata ["MinimumDuration"] = Session.EvaluationStats.MinTime.ToString (); + metadata ["FailureCount"] = Session.EvaluationStats.FailureCount.ToString (); + metadata ["SuccessCount"] = Session.EvaluationStats.TimingsCount.ToString (); + + Counters.EvaluationStats.Inc (metadata); + } + + bool ExceptionHandler (Exception ex) + { + SessionError = true; + return DebuggingService.ExceptionHandler (ex); + } + + /// <summary> + /// Called just before DebugSession.Run is called. + /// </summary> + public void PrepareForRun () + { + firstAssemblyLoadTimer = new System.Diagnostics.Stopwatch (); + firstAssemblyLoadTimer.Start (); + } + + void OnAssemblyLoaded (object sender, AssemblyEventArgs e) + { + DebuggerSession.AssemblyLoaded -= OnAssemblyLoaded; + firstAssemblyLoadTimer?.Stop (); + } } static async void OnBusyStateChanged (object s, BusyStateEventArgs args) diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringService.cs index ae9f1ec7c8..348d67093a 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringService.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringService.cs @@ -242,16 +242,21 @@ namespace MonoDevelop.Refactoring { if (hintProject == null) hintProject = IdeApp.Workbench.ActiveDocument?.Project; + ITimeTracker timer = null; var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); try { + var metadata = Counters.CreateFindReferencesMetadata (); + timer = Counters.FindReferences.BeginTiming (metadata); foreach (var provider in findReferencesProvider) { try { foreach (var result in await provider.FindReferences (documentIdString, hintProject, monitor.CancellationToken)) { monitor.ReportResult (result); } } catch (OperationCanceledException) { + Counters.SetUserCancel (metadata); return; } catch (Exception ex) { + Counters.SetFailure (metadata); if (monitor != null) monitor.ReportError ("Error finding references", ex); LoggingService.LogError ("Error finding references", ex); @@ -261,6 +266,8 @@ namespace MonoDevelop.Refactoring } finally { if (monitor != null) monitor.Dispose (); + if (timer != null) + timer.Dispose (); } } @@ -268,16 +275,21 @@ namespace MonoDevelop.Refactoring { if (hintProject == null) hintProject = IdeApp.Workbench.ActiveDocument?.Project; + ITimeTracker timer = null; var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); try { + var metadata = Counters.CreateFindReferencesMetadata (); + timer = Counters.FindReferences.BeginTiming (metadata); foreach (var provider in findReferencesProvider) { try { foreach (var result in await provider.FindAllReferences (documentIdString, hintProject, monitor.CancellationToken)) { monitor.ReportResult (result); } } catch (OperationCanceledException) { + Counters.SetUserCancel (metadata); return; } catch (Exception ex) { + Counters.SetFailure (metadata); if (monitor != null) monitor.ReportError ("Error finding references", ex); LoggingService.LogError ("Error finding references", ex); @@ -287,6 +299,8 @@ namespace MonoDevelop.Refactoring } finally { if (monitor != null) monitor.Dispose (); + if (timer != null) + timer.Dispose (); } } @@ -308,4 +322,26 @@ namespace MonoDevelop.Refactoring return false; } } + + internal static class Counters + { + public static TimerCounter FindReferences = InstrumentationService.CreateTimerCounter ("Find references", "Code Navigation", id: "CodeNavigation.FindReferences"); + + public static IDictionary<string, string> CreateFindReferencesMetadata () + { + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; + return metadata; + } + + public static void SetFailure (IDictionary<string, string> metadata) + { + metadata ["Result"] = "Failure"; + } + + public static void SetUserCancel (IDictionary<string, string> metadata) + { + metadata ["Result"] = "UserCancel"; + } + } } diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj index 4a411f46ec..b42d4007d5 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj @@ -286,6 +286,7 @@ <Compile Include="MonoDevelop.SourceEditor\TextMarker\LineSeparatorMarker.cs" /> <Compile Include="MonoDevelop.SourceEditor.QuickTasks\QuickTaskOverviewMode.IndicatorDrawingCache.cs" /> <Compile Include="MonoDevelop.SourceEditor.QuickTasks\QuickTaskOverviewMode.IdleUpdater.cs" /> + <Compile Include="MonoDevelop.SourceEditor\TextEditorKeyPressTimings.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="gtk-gui\gui.stetic"> diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/Counters.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/Counters.cs index e55ef141ff..50d6f44d4b 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/Counters.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/Counters.cs @@ -34,5 +34,6 @@ namespace MonoDevelop.SourceEditor public static Counter EditorsInMemory = InstrumentationService.CreateCounter ("Editors in Memory", "Text Editor"); public static Counter SourceViewsInMemory = InstrumentationService.CreateCounter ("Source Views in Memory", "Text Editor"); public static Counter LoadedEditors = InstrumentationService.CreateCounter ("Loaded Editors", "Text Editor"); + public static Counter Typing = InstrumentationService.CreateCounter ("Typing", "Text Editor", id: "TextEditor.Typing"); } } diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ExtensibleTextEditor.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ExtensibleTextEditor.cs index b7c8198f08..e65d854542 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ExtensibleTextEditor.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ExtensibleTextEditor.cs @@ -56,6 +56,7 @@ namespace MonoDevelop.SourceEditor class ExtensibleTextEditor : Mono.TextEditor.MonoTextEditor { internal object MemoryProbe = Counters.EditorsInMemory.CreateMemoryProbe (); + TextEditorKeyPressTimings keyPressTimings = new TextEditorKeyPressTimings (); SourceEditorView view; Adjustment cachedHAdjustment, cachedVAdjustment; @@ -209,6 +210,12 @@ namespace MonoDevelop.SourceEditor { IsDestroyed = true; UnregisterAdjustments (); + + if (keyPressTimings != null) { + keyPressTimings.ReportTimings (view); + keyPressTimings = null; + } + view = null;
Document.SyntaxMode = null; base.OnDestroyed (); @@ -334,9 +341,18 @@ namespace MonoDevelop.SourceEditor } } - protected internal override bool OnIMProcessedKeyPressEvent (Gdk.Key key, uint ch, Gdk.ModifierType state) { + try { + keyPressTimings.StartTimer (); + return OnIMProcessedKeyPressEventInternal (key, ch, state); + } finally { + keyPressTimings.EndTimer (); + } + } + + bool OnIMProcessedKeyPressEventInternal (Gdk.Key key, uint ch, Gdk.ModifierType state) + { bool result = true; if (key == Gdk.Key.Escape) { bool b = EditorExtension != null ? ExtensionKeyPress (key, ch, state) : base.OnIMProcessedKeyPressEvent (key, ch, state); diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextEditorKeyPressTimings.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextEditorKeyPressTimings.cs new file mode 100644 index 0000000000..8dc879a2d4 --- /dev/null +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextEditorKeyPressTimings.cs @@ -0,0 +1,96 @@ +// +// TextEditorKeyPressTimings.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System;
+using System.Diagnostics; +using System.Collections.Generic; + +namespace MonoDevelop.SourceEditor +{ + class TextEditorKeyPressTimings + { + Stopwatch stopwatch = new Stopwatch (); + + TimeSpan maxTime; + TimeSpan totalTime; + TimeSpan? firstTime; + int count; + + public void StartTimer () + { + stopwatch.Start (); + } + + /// <summary> + /// Overhead to a key press of using StartTimer and EndTimer is normally less + /// than 0.001ms. + /// + /// Note that the first ever key press in the text editor this can add up to + /// ~0.1ms but this is small compared with the text editor key press itself + /// which can take ~800ms for the first ever key press in the text editor for + /// the current IDE session. + /// </summary> + public void EndTimer () + { + stopwatch.Stop (); + + var duration = stopwatch.Elapsed; + if (duration > maxTime) { + maxTime = duration; + } + + if (!firstTime.HasValue) { + firstTime = duration; + } + + totalTime += duration; + count++; + + stopwatch.Reset (); + } + + public void ReportTimings (SourceEditorView sourceEditorView) + { + if (count == 0) { + // No timings recorded. + return; + } + + string extension = sourceEditorView.Document.FileName.Extension; + + var metadata = new Dictionary<string, string> (); + if (!string.IsNullOrEmpty (extension)) + metadata ["Extension"] = extension; + + var average = totalTime.TotalMilliseconds / count; + metadata ["Average"] = average.ToString (); + metadata ["First"] = firstTime.Value.TotalMilliseconds.ToString (); + metadata ["Maximum"] = maxTime.TotalMilliseconds.ToString (); + + Counters.Typing.Inc (metadata); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs index 9eda1df61f..76d02a6bfa 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs @@ -25,8 +25,8 @@ // // - using System; +using System.Collections.Generic; using MonoDevelop.Projects; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.Components.Commands; @@ -191,8 +191,11 @@ namespace MonoDevelop.Ide.Editor.Extension completionTokenSrc = new CancellationTokenSource (); var caretOffset = Editor.CaretOffset; var token = completionTokenSrc.Token; + + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (); + Counters.ProcessCodeCompletion.BeginTiming (metadata); var task = DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (CompletionTriggerReason.CharTyped, descriptor.KeyChar), token); if (task != null) { // Show the completion window in two steps. The call to PrepareShowWindow creates the window but @@ -225,6 +228,9 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = null; } } finally { + if (token.IsCancellationRequested) { + metadata ["Result"] = "UserCancel"; + } Counters.ProcessCodeCompletion.EndTiming (); } }, Runtime.MainTaskScheduler); @@ -233,10 +239,13 @@ namespace MonoDevelop.Ide.Editor.Extension Counters.ProcessCodeCompletion.EndTiming (); } } catch (TaskCanceledException) { + metadata ["Result"] = "UserCancel"; Counters.ProcessCodeCompletion.EndTiming (); } catch (AggregateException) { + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); } catch { + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); throw; } @@ -260,8 +269,11 @@ namespace MonoDevelop.Ide.Editor.Extension completionTokenSrc = new CancellationTokenSource (); var caretOffset = Editor.CaretOffset; var token = completionTokenSrc.Token; + + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (); + Counters.ProcessCodeCompletion.BeginTiming (metadata); var task = DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (CompletionTriggerReason.BackspaceOrDeleteCommand, deleteOrBackspaceTriggerChar), token); if (task != null) { // Show the completion window in two steps. The call to PrepareShowWindow creates the window but @@ -299,6 +311,9 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = null; } } finally { + if (token.IsCancellationRequested) { + metadata ["Result"] = "UserCancel"; + } Counters.ProcessCodeCompletion.EndTiming (); } }, Runtime.MainTaskScheduler); @@ -307,11 +322,14 @@ namespace MonoDevelop.Ide.Editor.Extension } } catch (TaskCanceledException) { CurrentCompletionContext = null; + metadata ["Result"] = "UserCancel"; Counters.ProcessCodeCompletion.EndTiming (); } catch (AggregateException) { CurrentCompletionContext = null; + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); } catch { + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); throw; } @@ -429,8 +447,11 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = CompletionWidget.CreateCodeCompletionContext (cpos); CurrentCompletionContext.TriggerWordLength = wlen; + + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (); + Counters.ProcessCodeCompletion.BeginTiming (metadata); completionList = await DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (reason), token); if (completionList != null && completionList.TriggerWordStart >= 0) { CurrentCompletionContext.TriggerOffset = completionList.TriggerWordStart; @@ -439,6 +460,9 @@ namespace MonoDevelop.Ide.Editor.Extension if (completionList == null || !CompletionWindowManager.ShowWindow (this, (char)0, completionList, CompletionWidget, CurrentCompletionContext)) { CurrentCompletionContext = null; } + } catch (Exception) { + metadata ["Result"] = "Failure"; + throw; } finally { Counters.ProcessCodeCompletion.EndTiming (); } |