Skip to content

Commit e1c0c83

Browse files
Add a library mode for type hierarchy marking (#2114)
* Fix special handling of EventSource FB * special case EventSource handling to library mode * test fixes * Update src/linker/Linker/LinkContext.cs Co-authored-by: Vitek Karas <[email protected]> * FB * FB Co-authored-by: Vitek Karas <[email protected]>
1 parent c3d69fb commit e1c0c83

File tree

9 files changed

+268
-10
lines changed

9 files changed

+268
-10
lines changed

src/linker/Linker.Dataflow/DynamicallyAccessedMembersTypeHierarchy.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ public DynamicallyAccessedMembersTypeHierarchy (LinkContext context, MarkStep ma
9191

9292
Debug.Assert (!apply || annotation != DynamicallyAccessedMemberTypes.None);
9393

94+
// If OptimizeTypeHierarchyAnnotations is disabled, we will apply the annotations without seeing object.GetType()
95+
bool applyOptimizeTypeHierarchyAnnotations = (annotation != DynamicallyAccessedMemberTypes.None) && !_context.IsOptimizationEnabled (CodeOptimizations.OptimizeTypeHierarchyAnnotations, type);
96+
// Unfortunately, we cannot apply the annotation to type derived from EventSource - Revisit after https://github.com/dotnet/runtime/issues/54859
97+
// Breaking the logic to make it easier to maintain in the future since the logic is convoluted
98+
// DisableEventSourceSpecialHandling is closely tied to a type derived from EventSource and should always go together
99+
// However, logically it should be possible to use DisableEventSourceSpecialHandling to allow marking types derived from EventSource when OptimizeTypeHierarchyAnnotations is disabled
100+
apply |= applyOptimizeTypeHierarchyAnnotations && (_context.DisableEventSourceSpecialHandling || !BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context));
101+
94102
// Store the results in the cache
95103
// Don't store empty annotations for non-interface types - we can use the presence of the row
96104
// in the cache as indication of it instead.

src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ public void ApplyDynamicallyAccessedMembersToType (ref ReflectionPatternContext
124124
Debug.Assert (annotation != DynamicallyAccessedMemberTypes.None);
125125

126126
reflectionPatternContext.AnalyzingPattern ();
127+
// Handle cases where a type has no members but annotations are to be applied to derived type members
128+
reflectionPatternContext.RecordHandledPattern ();
129+
127130
MarkTypeForDynamicallyAccessedMembers (ref reflectionPatternContext, type, annotation);
128131
}
129132

src/linker/Linker.Steps/MarkStep.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,7 +1776,8 @@ protected internal virtual TypeDefinition MarkType (TypeReference reference, Dep
17761776
MarkSerializable (type);
17771777

17781778
// This marks static fields of KeyWords/OpCodes/Tasks subclasses of an EventSource type.
1779-
if (BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context)) {
1779+
// The special handling of EventSource is still needed in .NET6 in library mode
1780+
if ((!_context.DisableEventSourceSpecialHandling || _context.GetTargetRuntimeVersion () < TargetRuntimeVersion.NET6) && BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context)) {
17801781
MarkEventSourceProviders (type);
17811782
}
17821783

@@ -1911,7 +1912,8 @@ void MarkTypeSpecialCustomAttributes (TypeDefinition type)
19111912
case "DebuggerTypeProxyAttribute" when attrType.Namespace == "System.Diagnostics":
19121913
MarkTypeWithDebuggerTypeProxyAttribute (type, attribute);
19131914
break;
1914-
case "EventDataAttribute" when attrType.Namespace == "System.Diagnostics.Tracing":
1915+
// The special handling of EventSource is still needed in .NET6 in library mode
1916+
case "EventDataAttribute" when attrType.Namespace == "System.Diagnostics.Tracing" && (!_context.DisableEventSourceSpecialHandling || _context.GetTargetRuntimeVersion () < TargetRuntimeVersion.NET6):
19151917
if (MarkMethodsIf (type.Methods, MethodDefinitionExtensions.IsPublicInstancePropertyMethod, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, type)))
19161918
Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, type), marked: false);
19171919
break;
@@ -2377,6 +2379,7 @@ protected virtual bool AlwaysMarkTypeAsInstantiated (TypeDefinition td)
23772379

23782380
void MarkEventSourceProviders (TypeDefinition td)
23792381
{
2382+
Debug.Assert (_context.GetTargetRuntimeVersion () < TargetRuntimeVersion.NET6 || !_context.DisableEventSourceSpecialHandling);
23802383
foreach (var nestedType in td.NestedTypes) {
23812384
if (BCL.EventTracingForWindows.IsProviderName (nestedType.Name))
23822385
MarkStaticFields (nestedType, new DependencyInfo (DependencyKind.EventSourceProviderField, td));

src/linker/Linker.Steps/RootAssemblyInputStep.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ protected override void Process ()
8181
CodeOptimizations.RemoveDescriptors |
8282
CodeOptimizations.RemoveLinkAttributes |
8383
CodeOptimizations.RemoveSubstitutions |
84-
CodeOptimizations.RemoveDynamicDependencyAttribute, assembly.Name.Name);
84+
CodeOptimizations.RemoveDynamicDependencyAttribute |
85+
CodeOptimizations.OptimizeTypeHierarchyAnnotations, assembly.Name.Name);
86+
87+
// Enable EventSource special handling
88+
Context.DisableEventSourceSpecialHandling = false;
8589

8690
// No metadata trimming
8791
Context.MetadataTrimming = MetadataTrimming.None;

src/linker/Linker/LinkContext.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ public bool IgnoreUnresolved {
125125

126126
public bool DisableOperatorDiscovery { get; set; }
127127

128+
/// <summary>
129+
/// Option to not special case EventSource.
130+
/// Currently, values are hard-coded and does not have a command line option to control
131+
/// </summary>
132+
public bool DisableEventSourceSpecialHandling { get; set; }
133+
128134
public bool IgnoreDescriptors { get; set; }
129135

130136
public bool IgnoreSubstitutions { get; set; }
@@ -246,7 +252,10 @@ public LinkContext (Pipeline pipeline, ILogger logger)
246252
CodeOptimizations.RemoveDescriptors |
247253
CodeOptimizations.RemoveLinkAttributes |
248254
CodeOptimizations.RemoveSubstitutions |
249-
CodeOptimizations.RemoveDynamicDependencyAttribute;
255+
CodeOptimizations.RemoveDynamicDependencyAttribute |
256+
CodeOptimizations.OptimizeTypeHierarchyAnnotations;
257+
258+
DisableEventSourceSpecialHandling = true;
250259

251260
Optimizations = new CodeOptimizationsSettings (defaultOptimizations);
252261
}
@@ -986,5 +995,12 @@ public enum CodeOptimizations
986995
RemoveSubstitutions = 1 << 21,
987996
RemoveLinkAttributes = 1 << 22,
988997
RemoveDynamicDependencyAttribute = 1 << 23,
998+
999+
/// <summary>
1000+
/// Option to apply annotations to type heirarchy
1001+
/// Enable type heirarchy apply in library mode to annotate derived types eagerly
1002+
/// Otherwise, type annotation will only be applied with calls to object.GetType()
1003+
/// </summary>
1004+
OptimizeTypeHierarchyAnnotations = 1 << 24,
9891005
}
9901006
}

test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/CustomEventSource.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ public class CustomEventSource
88
{
99
public static void Main ()
1010
{
11-
var b = MyCompanyEventSource.Log.IsEnabled ();
11+
// This call will trigger Object.GetType() Reflection pattern that will preserve all
12+
EventSource.GenerateManifest (typeof (MyCompanyEventSource), null);
1213
}
1314
}
1415

@@ -21,29 +22,41 @@ public static void Main ()
2122
[EventSource (Name = "MyCompany")]
2223
class MyCompanyEventSource : EventSource
2324
{
25+
[KeptMember (".ctor()")]
2426
[Kept]
2527
public class Keywords
2628
{
2729
[Kept]
2830
public const EventKeywords Page = (EventKeywords) 1;
2931

32+
[Kept]
3033
public int Unused;
3134
}
3235

36+
[KeptMember (".ctor()")]
3337
[Kept]
3438
public class Tasks
3539
{
3640
[Kept]
3741
public const EventTask Page = (EventTask) 1;
3842

43+
[Kept]
3944
public int Unused;
4045
}
4146

47+
[KeptMember (".ctor()")]
48+
[Kept]
4249
class NotMatching
4350
{
4451
}
4552

4653
[Kept]
4754
public static MyCompanyEventSource Log = new MyCompanyEventSource ();
55+
56+
[Kept]
57+
int private_member;
58+
59+
[Kept]
60+
void PrivateMethod () { }
4861
}
4962
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System;
2+
using System.Diagnostics.Tracing;
3+
using Mono.Linker.Tests.Cases.Expectations.Assertions;
4+
using Mono.Linker.Tests.Cases.Expectations.Metadata;
5+
6+
namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW
7+
{
8+
[SetupLinkerArgument ("-a", "test.exe", "library")]
9+
[KeptMember (".ctor()")]
10+
public class CustomLibraryEventSource
11+
{
12+
public static void Main ()
13+
{
14+
// Reference to a derived EventSource but does not trigger Object.GetType()
15+
var b = CustomEventSourceInLibraryMode.Log.IsEnabled ();
16+
}
17+
}
18+
19+
[Kept]
20+
[KeptBaseType (typeof (EventSource))]
21+
[KeptAttributeAttribute (typeof (EventSourceAttribute))]
22+
[KeptMember (".ctor()")]
23+
[KeptMember (".cctor()")]
24+
25+
[EventSource (Name = "MyLibraryCompany")]
26+
class CustomEventSourceInLibraryMode : EventSource
27+
{
28+
// In library mode, we special case nested types
29+
[Kept]
30+
public class Keywords
31+
{
32+
[Kept]
33+
public const EventKeywords Page = (EventKeywords) 1;
34+
35+
public int Unused;
36+
}
37+
38+
[Kept]
39+
public class Tasks
40+
{
41+
[Kept]
42+
public const EventTask Page = (EventTask) 1;
43+
44+
public int Unused;
45+
}
46+
47+
class NotMatching
48+
{
49+
}
50+
51+
[Kept]
52+
public static CustomEventSourceInLibraryMode Log = new CustomEventSourceInLibraryMode ();
53+
54+
int private_member;
55+
56+
void PrivateMethod () { }
57+
}
58+
}

test/Mono.Linker.Tests.Cases/Reflection/MembersUsedViaReflection.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ private int PrivateProperty {
194194
[Kept]
195195
public static class PublicNestedType
196196
{
197-
// PublicNestedType should be kept but linker won't mark anything besides the declaration
198197
[Kept]
199198
public static int _nestedPublicField;
200199
[Kept]
@@ -261,7 +260,6 @@ private int PrivateProperty {
261260
[Kept]
262261
public static class PublicNestedType
263262
{
264-
// PublicNestedType should be kept but linker won't mark anything besides the declaration
265263
[Kept]
266264
public static int _nestedPublicField;
267265
[Kept]
@@ -318,7 +316,6 @@ private int PrivateProperty {
318316
[Kept]
319317
public static class PublicNestedType
320318
{
321-
// PublicNestedType should be kept but linker won't mark anything besides the declaration
322319
[Kept]
323320
public static int _nestedPublicField;
324321
[Kept]
@@ -375,7 +372,6 @@ private int PrivateProperty {
375372
[Kept]
376373
public static class PublicNestedType
377374
{
378-
// PublicNestedType should be kept but linker won't mark anything besides the declaration
379375
[Kept]
380376
public static int _nestedPublicField;
381377
[Kept]
@@ -432,7 +428,6 @@ private int PrivateProperty {
432428
[Kept]
433429
public static class PublicNestedType
434430
{
435-
// PublicNestedType should be kept but linker won't mark anything besides the declaration
436431
[Kept]
437432
public static int _nestedPublicField;
438433
[Kept]

0 commit comments

Comments
 (0)