From c993852f9803b8c5d855819c480fdff899b1715e Mon Sep 17 00:00:00 2001 From: MikhailArkhipov Date: Fri, 31 May 2019 14:52:35 -0700 Subject: [PATCH 1/5] First cut --- .../Ast/Impl/Analyzer/ActivityTracker.cs | 90 +++++++++++++++++++ .../Definitions/AnalysisCompleteEventArgs.cs | 28 ++++++ .../Analyzer/Definitions/IPythonAnalyzer.cs | 6 ++ .../Ast/Impl/Analyzer/PythonAnalyzer.cs | 15 +++- .../Impl/Analyzer/PythonAnalyzerSession.cs | 12 ++- .../Impl/Dependencies/DependencyResolver.cs | 1 + .../Dependencies/IDependencyChainWalker.cs | 2 +- src/LanguageServer/Impl/Program.cs | 2 +- 8 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs create mode 100644 src/Analysis/Ast/Impl/Analyzer/Definitions/AnalysisCompleteEventArgs.cs diff --git a/src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs b/src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs new file mode 100644 index 000000000..1b6afed86 --- /dev/null +++ b/src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs @@ -0,0 +1,90 @@ +// Copyright(c) Microsoft Corporation +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the License); you may not use +// this file except in compliance with the License. You may obtain a copy of the +// License at http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS +// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY +// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +// MERCHANTABILITY OR NON-INFRINGEMENT. +// +// See the Apache Version 2.0 License for specific language governing +// permissions and limitations under the License. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Microsoft.Python.Analysis.Analyzer { + internal static class ActivityTracker { + private static readonly Dictionary _modules = new Dictionary(); + private static readonly object _lock = new object(); + private static bool _complete; + private static Stopwatch _sw; + + private struct AnalysisState { + public int Count; + public bool IsComplete; + } + + public static void OnEnqueueModule(string path) { + if (string.IsNullOrEmpty(path)) { + return; + } + + lock (_lock) { + if (!_modules.TryGetValue(path, out var st)) { + _modules[path] = default; + } else { + st.IsComplete = false; + } + } + } + + public static void OnModuleAnalysisComplete(string path) { + lock (_lock) { + if (_modules.TryGetValue(path, out var st)) { + st.Count++; + st.IsComplete = true; + } + } + } + + public static bool IsAnalysisComplete { + get { + lock (_lock) { + return _modules.All(m => m.Value.IsComplete); + } + } + } + + + public static void StartTracking() { + lock (_lock) { + if (_complete) { + _modules.Clear(); + _complete = false; + _sw = Stopwatch.StartNew(); + } + } + } + + public static void EndTracking() { + lock (_lock) { + _complete = true; + _sw?.Stop(); + } + } + + public static int ModuleCount { + get { + lock (_lock) { + return _modules.Count; + } + } + } + public static double MillisecondsElapsed => _sw?.Elapsed.TotalMilliseconds ?? 0; + } +} diff --git a/src/Analysis/Ast/Impl/Analyzer/Definitions/AnalysisCompleteEventArgs.cs b/src/Analysis/Ast/Impl/Analyzer/Definitions/AnalysisCompleteEventArgs.cs new file mode 100644 index 000000000..27e6c5ba0 --- /dev/null +++ b/src/Analysis/Ast/Impl/Analyzer/Definitions/AnalysisCompleteEventArgs.cs @@ -0,0 +1,28 @@ +// Copyright(c) Microsoft Corporation +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the License); you may not use +// this file except in compliance with the License. You may obtain a copy of the +// License at http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS +// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY +// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +// MERCHANTABILITY OR NON-INFRINGEMENT. +// +// See the Apache Version 2.0 License for specific language governing +// permissions and limitations under the License. + +using System; + +namespace Microsoft.Python.Analysis.Analyzer { + public sealed class AnalysisCompleteEventArgs : EventArgs { + public int ModuleCount { get; } + public double MillisecondsElapsed { get; } + + public AnalysisCompleteEventArgs(int moduleCount, double msElapsed) { + ModuleCount = moduleCount; + MillisecondsElapsed = msElapsed; + } + } +} diff --git a/src/Analysis/Ast/Impl/Analyzer/Definitions/IPythonAnalyzer.cs b/src/Analysis/Ast/Impl/Analyzer/Definitions/IPythonAnalyzer.cs index 1922eb08e..970c4bb3f 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Definitions/IPythonAnalyzer.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Definitions/IPythonAnalyzer.cs @@ -13,6 +13,7 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -64,5 +65,10 @@ public interface IPythonAnalyzer { /// Returns list of currently loaded modules. /// IReadOnlyList LoadedModules { get; } + + /// + /// Fires when analysis is complete. + /// + event EventHandler AnalysisComplete; } } diff --git a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs index 888a6a945..32fd71262 100644 --- a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs +++ b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs @@ -192,11 +192,22 @@ public void ResetAnalyzer() { } } - public IReadOnlyList LoadedModules - => _analysisEntries.Values.ExcludeDefault().Select(v => v.Module).ExcludeDefault().ToArray(); + public IReadOnlyList LoadedModules { + get { + lock (_syncObj) { + return _analysisEntries.Values.ExcludeDefault().Select(v => v.Module).ExcludeDefault().ToArray(); + } + } + } + + public event EventHandler AnalysisComplete; + + internal void RaiseAnalysisComplete(int moduleCount, double msElapsed) + => AnalysisComplete?.Invoke(this, new AnalysisCompleteEventArgs(moduleCount, msElapsed)); private void AnalyzeDocument(AnalysisModuleKey key, PythonAnalyzerEntry entry, ImmutableArray dependencies) { _analysisCompleteEvent.Reset(); + ActivityTracker.StartTracking(); _log?.Log(TraceEventType.Verbose, $"Analysis of {entry.Module.Name}({entry.Module.ModuleType}) queued"); var graphVersion = _dependencyResolver.ChangeValue(key, entry, entry.IsUserModule, dependencies); diff --git a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs index ca2a0a7df..5f870c09e 100644 --- a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs +++ b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs @@ -137,14 +137,21 @@ private async Task StartAsync() { stopWatch.Stop(); bool isCanceled; + bool isFinal; lock (_syncObj) { isCanceled = _isCanceled; _state = State.Completed; + isFinal = _walker.MissingKeys.Count == 0 && !isCanceled && remaining == 0; _walker = null; } if (!isCanceled) { _progress.ReportRemaining(remaining); + if(isFinal) { + ActivityTracker.EndTracking(); + (_analyzer as PythonAnalyzer)?.RaiseAnalysisComplete(ActivityTracker.ModuleCount, ActivityTracker.MillisecondsElapsed); + _log?.Log(TraceEventType.Information, $"Analysis complete: {ActivityTracker.ModuleCount} modules in { ActivityTracker.MillisecondsElapsed} ms."); + } } } @@ -226,6 +233,8 @@ private async Task AnalyzeAffectedEntriesAsync(Stopwatch stopWatch) { continue; } + ActivityTracker.OnEnqueueModule(node.Value.Module.FilePath); + if (Interlocked.Increment(ref _runningTasks) >= _maxTaskRunning || _walker.Remaining == 1) { Analyze(node, null, stopWatch); } else { @@ -241,7 +250,7 @@ private async Task AnalyzeAffectedEntriesAsync(Stopwatch stopWatch) { if (_walker.MissingKeys.Count == 0 || _walker.MissingKeys.All(k => k.IsTypeshed)) { Interlocked.Exchange(ref _runningTasks, 0); - + if (!isCanceled) { _analysisCompleteEvent.Set(); } @@ -279,6 +288,7 @@ private void Analyze(IDependencyChainNode node, AsyncCountd var startTime = stopWatch.Elapsed; AnalyzeEntry(entry, module, ast, _walker.Version); node.Commit(); + ActivityTracker.OnModuleAnalysisComplete(node.Value.Module.FilePath); _log?.Log(TraceEventType.Verbose, $"Analysis of {module.Name}({module.ModuleType}) completed in {(stopWatch.Elapsed - startTime).TotalMilliseconds} ms."); } catch (OperationCanceledException oce) { diff --git a/src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs b/src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs index 7e41ae874..a78ae5616 100644 --- a/src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs +++ b/src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs @@ -546,6 +546,7 @@ private sealed class DependencyChainWalker : IDependencyChainWalker MissingKeys { get; } public ImmutableArray AffectedValues { get; } public int Version { get; } + public ImmutableArray> Vertices => _startingVertices; public int Remaining { get { diff --git a/src/Analysis/Ast/Impl/Dependencies/IDependencyChainWalker.cs b/src/Analysis/Ast/Impl/Dependencies/IDependencyChainWalker.cs index 19904b1d6..a78479e4f 100644 --- a/src/Analysis/Ast/Impl/Dependencies/IDependencyChainWalker.cs +++ b/src/Analysis/Ast/Impl/Dependencies/IDependencyChainWalker.cs @@ -23,7 +23,7 @@ internal interface IDependencyChainWalker { ImmutableArray AffectedValues { get; } int Version { get; } int Remaining { get; } - + ImmutableArray> Vertices { get; } Task> GetNextAsync(CancellationToken cancellationToken); } } diff --git a/src/LanguageServer/Impl/Program.cs b/src/LanguageServer/Impl/Program.cs index 024f9af18..24adfa924 100644 --- a/src/LanguageServer/Impl/Program.cs +++ b/src/LanguageServer/Impl/Program.cs @@ -13,7 +13,7 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. -// #define WAIT_FOR_DEBUGGER +#define WAIT_FOR_DEBUGGER using System; using System.Diagnostics; From 933118e3681cd4285d4ba437bc6e58801d79b8cf Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Fri, 31 May 2019 17:51:20 -0700 Subject: [PATCH 2/5] Move telemetry to server --- .../Impl/Analyzer/PythonAnalyzerSession.cs | 34 ------------ .../Impl/Implementation/Server.Telemetry.cs | 55 +++++++++++++++++++ .../Impl/Implementation/Server.cs | 1 + src/LanguageServer/Impl/Program.cs | 2 +- 4 files changed, 57 insertions(+), 35 deletions(-) create mode 100644 src/LanguageServer/Impl/Implementation/Server.Telemetry.cs diff --git a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs index 5f870c09e..da3315e1e 100644 --- a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs +++ b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs @@ -156,8 +156,6 @@ private async Task StartAsync() { } var elapsed = stopWatch.Elapsed.TotalMilliseconds; - - SendTelemetry(_telemetry, elapsed, originalRemaining, remaining, Version); LogResults(_log, elapsed, originalRemaining, remaining, Version); ForceGCIfNeeded(originalRemaining, remaining); } @@ -169,38 +167,6 @@ private static void ForceGCIfNeeded(int originalRemaining, int remaining) { } } - private static void SendTelemetry(ITelemetryService telemetry, double elapsed, int originalRemaining, int remaining, int version) { - if (telemetry == null) { - return; - } - - if (remaining != 0 || originalRemaining < 100) { - return; - } - - double privateMB; - double peakPagedMB; - double workingMB; - - using (var proc = Process.GetCurrentProcess()) { - privateMB = proc.PrivateMemorySize64 / 1e+6; - peakPagedMB = proc.PeakPagedMemorySize64 / 1e+6; - workingMB = proc.WorkingSet64 / 1e+6; - } - - var e = new TelemetryEvent { - EventName = "python_language_server/analysis_complete", // TODO: Move this common prefix into Core. - }; - - e.Measurements["privateMB"] = privateMB; - e.Measurements["peakPagedMB"] = peakPagedMB; - e.Measurements["workingMB"] = workingMB; - e.Measurements["elapsedMs"] = elapsed; - e.Measurements["entries"] = originalRemaining; - e.Measurements["version"] = version; - - telemetry.SendTelemetryAsync(e).DoNotWait(); - } private static void LogResults(ILogger logger, double elapsed, int originalRemaining, int remaining, int version) { if (logger == null) { diff --git a/src/LanguageServer/Impl/Implementation/Server.Telemetry.cs b/src/LanguageServer/Impl/Implementation/Server.Telemetry.cs new file mode 100644 index 000000000..d8c762946 --- /dev/null +++ b/src/LanguageServer/Impl/Implementation/Server.Telemetry.cs @@ -0,0 +1,55 @@ +// Copyright(c) Microsoft Corporation +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the License); you may not use +// this file except in compliance with the License. You may obtain a copy of the +// License at http://www.apache.org/licenses/LICENSE-2.0 +// +// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS +// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY +// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +// MERCHANTABILITY OR NON-INFRINGEMENT. +// +// See the Apache Version 2.0 License for specific language governing +// permissions and limitations under the License. + +using System.Diagnostics; +using Microsoft.Python.Analysis.Analyzer; +using Microsoft.Python.Core; +using Microsoft.Python.Core.Services; + +namespace Microsoft.Python.LanguageServer.Implementation { + public sealed partial class Server { + private void OnAnalysisComplete(object sender, AnalysisCompleteEventArgs e) { + if (e.MillisecondsElapsed < 500) { + return; + } + var telemetry = _services.GetService(); + if (telemetry == null) { + return; + } + + double privateMB; + double peakPagedMB; + double workingMB; + + using (var proc = Process.GetCurrentProcess()) { + privateMB = proc.PrivateMemorySize64 / 1e+6; + peakPagedMB = proc.PeakPagedMemorySize64 / 1e+6; + workingMB = proc.WorkingSet64 / 1e+6; + } + + var te = new TelemetryEvent { + EventName = "python_language_server/analysis_complete", // TODO: Move this common prefix into Core. + }; + + te.Measurements["privateMB"] = privateMB; + te.Measurements["peakPagedMB"] = peakPagedMB; + te.Measurements["workingMB"] = workingMB; + te.Measurements["elapsedMs"] = e.MillisecondsElapsed; + te.Measurements["entries"] = e.ModuleCount; + + telemetry.SendTelemetryAsync(te).DoNotWait(); + } + } +} diff --git a/src/LanguageServer/Impl/Implementation/Server.cs b/src/LanguageServer/Impl/Implementation/Server.cs index a21c1c1ea..ae346f20c 100644 --- a/src/LanguageServer/Impl/Implementation/Server.cs +++ b/src/LanguageServer/Impl/Implementation/Server.cs @@ -98,6 +98,7 @@ public async Task InitializeAsync(InitializeParams @params, Ca _services.AddService(new DiagnosticsService(_services)); var analyzer = new PythonAnalyzer(_services); + analyzer.AnalysisComplete += OnAnalysisComplete; _services.AddService(analyzer); _services.AddService(new RunningDocumentTable(_services)); diff --git a/src/LanguageServer/Impl/Program.cs b/src/LanguageServer/Impl/Program.cs index 24adfa924..024f9af18 100644 --- a/src/LanguageServer/Impl/Program.cs +++ b/src/LanguageServer/Impl/Program.cs @@ -13,7 +13,7 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. -#define WAIT_FOR_DEBUGGER +// #define WAIT_FOR_DEBUGGER using System; using System.Diagnostics; From 4615580d9d2680631bef15b45d3878a605273a6b Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Fri, 31 May 2019 18:00:33 -0700 Subject: [PATCH 3/5] Cleanup --- src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs | 1 - src/Analysis/Ast/Impl/Dependencies/IDependencyChainWalker.cs | 1 - src/LanguageServer/Impl/Implementation/Server.cs | 4 +++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs b/src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs index a78ae5616..7e41ae874 100644 --- a/src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs +++ b/src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs @@ -546,7 +546,6 @@ private sealed class DependencyChainWalker : IDependencyChainWalker MissingKeys { get; } public ImmutableArray AffectedValues { get; } public int Version { get; } - public ImmutableArray> Vertices => _startingVertices; public int Remaining { get { diff --git a/src/Analysis/Ast/Impl/Dependencies/IDependencyChainWalker.cs b/src/Analysis/Ast/Impl/Dependencies/IDependencyChainWalker.cs index a78479e4f..87697bd27 100644 --- a/src/Analysis/Ast/Impl/Dependencies/IDependencyChainWalker.cs +++ b/src/Analysis/Ast/Impl/Dependencies/IDependencyChainWalker.cs @@ -23,7 +23,6 @@ internal interface IDependencyChainWalker { ImmutableArray AffectedValues { get; } int Version { get; } int Remaining { get; } - ImmutableArray> Vertices { get; } Task> GetNextAsync(CancellationToken cancellationToken); } } diff --git a/src/LanguageServer/Impl/Implementation/Server.cs b/src/LanguageServer/Impl/Implementation/Server.cs index ae346f20c..2f19b50ab 100644 --- a/src/LanguageServer/Impl/Implementation/Server.cs +++ b/src/LanguageServer/Impl/Implementation/Server.cs @@ -98,9 +98,11 @@ public async Task InitializeAsync(InitializeParams @params, Ca _services.AddService(new DiagnosticsService(_services)); var analyzer = new PythonAnalyzer(_services); - analyzer.AnalysisComplete += OnAnalysisComplete; _services.AddService(analyzer); + analyzer.AnalysisComplete += OnAnalysisComplete; + _disposableBag.Add(() => analyzer.AnalysisComplete -= OnAnalysisComplete); + _services.AddService(new RunningDocumentTable(_services)); _rdt = _services.GetService(); From 38f927e040ebddbe39cfd86425e87b04b6fc109f Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Sat, 1 Jun 2019 14:06:22 -0700 Subject: [PATCH 4/5] Better track time spent --- src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs | 12 +++++++----- .../Ast/Impl/Analyzer/PythonAnalyzerSession.cs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs b/src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs index 1b6afed86..2b5b22c83 100644 --- a/src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs +++ b/src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs @@ -21,7 +21,7 @@ namespace Microsoft.Python.Analysis.Analyzer { internal static class ActivityTracker { private static readonly Dictionary _modules = new Dictionary(); private static readonly object _lock = new object(); - private static bool _complete; + private static bool _tracking; private static Stopwatch _sw; private struct AnalysisState { @@ -63,9 +63,9 @@ public static bool IsAnalysisComplete { public static void StartTracking() { lock (_lock) { - if (_complete) { + if (!_tracking) { + _tracking = true; _modules.Clear(); - _complete = false; _sw = Stopwatch.StartNew(); } } @@ -73,8 +73,10 @@ public static void StartTracking() { public static void EndTracking() { lock (_lock) { - _complete = true; - _sw?.Stop(); + if (_tracking) { + _sw?.Stop(); + _tracking = false; + } } } diff --git a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs index e46f7b6f9..8ea38f6ea 100644 --- a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs +++ b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzerSession.cs @@ -150,7 +150,7 @@ private async Task StartAsync() { if(isFinal) { ActivityTracker.EndTracking(); (_analyzer as PythonAnalyzer)?.RaiseAnalysisComplete(ActivityTracker.ModuleCount, ActivityTracker.MillisecondsElapsed); - _log?.Log(TraceEventType.Information, $"Analysis complete: {ActivityTracker.ModuleCount} modules in { ActivityTracker.MillisecondsElapsed} ms."); + _log?.Log(TraceEventType.Verbose, $"Analysis complete: {ActivityTracker.ModuleCount} modules in { ActivityTracker.MillisecondsElapsed} ms."); } } } From 15b8c2567beebcb74630d64890d8c8edb2152410 Mon Sep 17 00:00:00 2001 From: MikhailArkhipov Date: Mon, 3 Jun 2019 12:21:12 -0700 Subject: [PATCH 5/5] Rename field --- src/LanguageServer/Impl/Implementation/Server.Telemetry.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LanguageServer/Impl/Implementation/Server.Telemetry.cs b/src/LanguageServer/Impl/Implementation/Server.Telemetry.cs index d8c762946..97a954490 100644 --- a/src/LanguageServer/Impl/Implementation/Server.Telemetry.cs +++ b/src/LanguageServer/Impl/Implementation/Server.Telemetry.cs @@ -47,7 +47,7 @@ private void OnAnalysisComplete(object sender, AnalysisCompleteEventArgs e) { te.Measurements["peakPagedMB"] = peakPagedMB; te.Measurements["workingMB"] = workingMB; te.Measurements["elapsedMs"] = e.MillisecondsElapsed; - te.Measurements["entries"] = e.ModuleCount; + te.Measurements["moduleCount"] = e.ModuleCount; telemetry.SendTelemetryAsync(te).DoNotWait(); }