From 1b3a73d9e9f70675c2c6363c8eb164e7f5f81b66 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Tue, 12 Oct 2021 11:24:48 +0200 Subject: [PATCH 1/4] Add ETW for event + property decompilation as PoC --- .../CSharp/CSharpDecompiler.cs | 12 ++++++++++ .../ICSharpCode.Decompiler.csproj | 1 + .../Instrumentation/DecompilerEventSource.cs | 24 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 42144c5e6..1cffa39ea 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1744,6 +1744,7 @@ namespace ICSharpCode.Decompiler.CSharp EntityDeclaration DoDecompile(IProperty property, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentMember == property); + var watch = System.Diagnostics.Stopwatch.StartNew(); try { var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); @@ -1798,11 +1799,17 @@ namespace ICSharpCode.Decompiler.CSharp { throw new DecompilerException(module, property, innerException); } + finally + { + watch.Stop(); + Instrumentation.DecompilerEventSource.Log.DoDecompileProperty(property.Name, watch.ElapsedMilliseconds); + } } EntityDeclaration DoDecompile(IEvent ev, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentMember == ev); + var watch = System.Diagnostics.Stopwatch.StartNew(); try { var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); @@ -1838,6 +1845,11 @@ namespace ICSharpCode.Decompiler.CSharp { throw new DecompilerException(module, ev, innerException); } + finally + { + watch.Stop(); + Instrumentation.DecompilerEventSource.Log.DoDecompileEvent(ev.Name, watch.ElapsedMilliseconds); + } } #region Sequence Points diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 7ebd56f7c..2c31e5b87 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -100,6 +100,7 @@ + diff --git a/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs b/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs new file mode 100644 index 000000000..902dcc4d0 --- /dev/null +++ b/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs @@ -0,0 +1,24 @@ +using System; +using System.Diagnostics.Tracing; + +namespace ICSharpCode.Decompiler.Instrumentation +{ + [EventSource(Name = "ICSharpCode.Decompiler")] + public sealed class DecompilerEventSource : EventSource + { + [Event(1, Level = EventLevel.Informational)] + public void DoDecompileEvent(string eventName, long elapsedMilliseconds) + { + WriteEvent(1, eventName, elapsedMilliseconds); + } + + [Event(2, Level = EventLevel.Informational)] + public void DoDecompileProperty(string propertyName, long elapsedMilliseconds) + { + WriteEvent(2, propertyName, elapsedMilliseconds); + } + + public static DecompilerEventSource Log = new DecompilerEventSource(); + } + +} From 6e5c474dfc93673f51ee18b0e15d22b28e633cdf Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Tue, 12 Oct 2021 15:50:22 +0200 Subject: [PATCH 2/4] Instrument more DoDecompile methods --- .../CSharp/CSharpDecompiler.cs | 16 ++++++++++++++-- .../Instrumentation/DecompilerEventSource.cs | 12 ++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 1cffa39ea..d7e06ab89 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1209,6 +1209,7 @@ namespace ICSharpCode.Decompiler.CSharp EntityDeclaration DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentTypeDefinition == typeDef); + var watch = System.Diagnostics.Stopwatch.StartNew(); try { var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); @@ -1337,6 +1338,11 @@ namespace ICSharpCode.Decompiler.CSharp { throw new DecompilerException(module, typeDef, innerException); } + finally + { + watch.Stop(); + Instrumentation.DecompilerEventSource.Log.DoDecompileTypeDefinition(typeDef.FullName, watch.ElapsedMilliseconds); + } } enum EnumValueDisplayMode @@ -1660,6 +1666,7 @@ namespace ICSharpCode.Decompiler.CSharp EntityDeclaration DoDecompile(IField field, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentMember == field); + var watch = System.Diagnostics.Stopwatch.StartNew(); try { var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); @@ -1722,6 +1729,11 @@ namespace ICSharpCode.Decompiler.CSharp { throw new DecompilerException(module, field, innerException); } + finally + { + watch.Stop(); + Instrumentation.DecompilerEventSource.Log.DoDecompileField(field.FullName, watch.ElapsedMilliseconds); + } } internal static bool IsFixedField(IField field, out IType type, out int elementCount) @@ -1802,7 +1814,7 @@ namespace ICSharpCode.Decompiler.CSharp finally { watch.Stop(); - Instrumentation.DecompilerEventSource.Log.DoDecompileProperty(property.Name, watch.ElapsedMilliseconds); + Instrumentation.DecompilerEventSource.Log.DoDecompileProperty(property.FullName, watch.ElapsedMilliseconds); } } @@ -1848,7 +1860,7 @@ namespace ICSharpCode.Decompiler.CSharp finally { watch.Stop(); - Instrumentation.DecompilerEventSource.Log.DoDecompileEvent(ev.Name, watch.ElapsedMilliseconds); + Instrumentation.DecompilerEventSource.Log.DoDecompileEvent(ev.FullName, watch.ElapsedMilliseconds); } } diff --git a/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs b/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs index 902dcc4d0..97b775033 100644 --- a/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs +++ b/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs @@ -18,6 +18,18 @@ namespace ICSharpCode.Decompiler.Instrumentation WriteEvent(2, propertyName, elapsedMilliseconds); } + [Event(3, Level = EventLevel.Informational)] + public void DoDecompileField(string fieldName, long elapsedMilliseconds) + { + WriteEvent(3, fieldName, elapsedMilliseconds); + } + + [Event(4, Level = EventLevel.Informational)] + public void DoDecompileTypeDefinition(string typeDefName, long elapsedMilliseconds) + { + WriteEvent(4, typeDefName, elapsedMilliseconds); + } + public static DecompilerEventSource Log = new DecompilerEventSource(); } From c68bf60b1beb19b1acedeae4c6824cbe76bd5133 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sat, 16 Oct 2021 15:25:01 +0200 Subject: [PATCH 3/4] Wrap DoDecompileEvent(IMethod...) --- .../CSharp/CSharpDecompiler.cs | 75 +++++++++++-------- .../Instrumentation/DecompilerEventSource.cs | 9 ++- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index d7e06ab89..ee18e5ec5 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1429,43 +1429,52 @@ namespace ICSharpCode.Decompiler.CSharp EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext.CurrentMember == method); - var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); - var methodDecl = typeSystemAstBuilder.ConvertEntity(method); - int lastDot = method.Name.LastIndexOf('.'); - if (method.IsExplicitInterfaceImplementation && lastDot >= 0) - { - methodDecl.Name = method.Name.Substring(lastDot + 1); - } - FixParameterNames(methodDecl); - var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); - if (!settings.LocalFunctions && LocalFunctionDecompiler.LocalFunctionNeedsAccessibilityChange(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken)) - { - // if local functions are not active and we're dealing with a local function, - // reduce the visibility of the method to private, - // otherwise this leads to compile errors because the display classes have lesser accessibility. - // Note: removing and then adding the static modifier again is necessary to set the private modifier before all other modifiers. - methodDecl.Modifiers &= ~(Modifiers.Internal | Modifiers.Static); - methodDecl.Modifiers |= Modifiers.Private | (method.IsStatic ? Modifiers.Static : 0); - } - if (methodDefinition.HasBody()) - { - DecompileBody(method, methodDecl, decompileRun, decompilationContext); - } - else if (!method.IsAbstract && method.DeclaringType.Kind != TypeKind.Interface) - { - methodDecl.Modifiers |= Modifiers.Extern; - } - if (method.SymbolKind == SymbolKind.Method && !method.IsExplicitInterfaceImplementation && methodDefinition.HasFlag(System.Reflection.MethodAttributes.Virtual) == methodDefinition.HasFlag(System.Reflection.MethodAttributes.NewSlot)) + var watch = System.Diagnostics.Stopwatch.StartNew(); + try { - SetNewModifier(methodDecl); + var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings); + var methodDecl = typeSystemAstBuilder.ConvertEntity(method); + int lastDot = method.Name.LastIndexOf('.'); + if (method.IsExplicitInterfaceImplementation && lastDot >= 0) + { + methodDecl.Name = method.Name.Substring(lastDot + 1); + } + FixParameterNames(methodDecl); + var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); + if (!settings.LocalFunctions && LocalFunctionDecompiler.LocalFunctionNeedsAccessibilityChange(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken)) + { + // if local functions are not active and we're dealing with a local function, + // reduce the visibility of the method to private, + // otherwise this leads to compile errors because the display classes have lesser accessibility. + // Note: removing and then adding the static modifier again is necessary to set the private modifier before all other modifiers. + methodDecl.Modifiers &= ~(Modifiers.Internal | Modifiers.Static); + methodDecl.Modifiers |= Modifiers.Private | (method.IsStatic ? Modifiers.Static : 0); + } + if (methodDefinition.HasBody()) + { + DecompileBody(method, methodDecl, decompileRun, decompilationContext); + } + else if (!method.IsAbstract && method.DeclaringType.Kind != TypeKind.Interface) + { + methodDecl.Modifiers |= Modifiers.Extern; + } + if (method.SymbolKind == SymbolKind.Method && !method.IsExplicitInterfaceImplementation && methodDefinition.HasFlag(System.Reflection.MethodAttributes.Virtual) == methodDefinition.HasFlag(System.Reflection.MethodAttributes.NewSlot)) + { + SetNewModifier(methodDecl); + } + if (IsCovariantReturnOverride(method)) + { + RemoveAttribute(methodDecl, KnownAttribute.PreserveBaseOverrides); + methodDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual); + methodDecl.Modifiers |= Modifiers.Override; + } + return methodDecl; } - if (IsCovariantReturnOverride(method)) + finally { - RemoveAttribute(methodDecl, KnownAttribute.PreserveBaseOverrides); - methodDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual); - methodDecl.Modifiers |= Modifiers.Override; + watch.Stop(); + Instrumentation.DecompilerEventSource.Log.DoDecompileMethod(method.FullName, watch.ElapsedMilliseconds); } - return methodDecl; } private bool IsCovariantReturnOverride(IEntity entity) diff --git a/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs b/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs index 97b775033..def7ec5bb 100644 --- a/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs +++ b/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs @@ -1,5 +1,4 @@ -using System; -using System.Diagnostics.Tracing; +using System.Diagnostics.Tracing; namespace ICSharpCode.Decompiler.Instrumentation { @@ -30,6 +29,12 @@ namespace ICSharpCode.Decompiler.Instrumentation WriteEvent(4, typeDefName, elapsedMilliseconds); } + [Event(5, Level = EventLevel.Informational)] + public void DoDecompileMethod(string methodName, long elapsedMilliseconds) + { + WriteEvent(5, methodName, elapsedMilliseconds); + } + public static DecompilerEventSource Log = new DecompilerEventSource(); } From ff739c4cac94c642731b3de9ed4ad3cb3d5d155c Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sat, 23 Oct 2021 10:48:41 +0200 Subject: [PATCH 4/4] Add header --- .../Instrumentation/DecompilerEventSource.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs b/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs index def7ec5bb..aaacec5f9 100644 --- a/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs +++ b/ICSharpCode.Decompiler/Instrumentation/DecompilerEventSource.cs @@ -1,4 +1,22 @@ -using System.Diagnostics.Tracing; +// Copyright (c) 2021 Christoph Wille +// +// 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.Diagnostics.Tracing; namespace ICSharpCode.Decompiler.Instrumentation {