Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add marking of types used in .config files #712

Closed
sbomer opened this issue Aug 15, 2019 · 10 comments
Closed

Add marking of types used in .config files #712

sbomer opened this issue Aug 15, 2019 · 10 comments
Assignees
Labels

Comments

@sbomer
Copy link
Member

sbomer commented Aug 15, 2019

@onovotny commented in https://github.com/dotnet/coreclr/issues/26196#issue-481363865:

With the latest code of NuGet Package Explorer in master, I added true to the main exe csproj and then published as self-contained x64. The app failed to launch. When I set that published exe as the startup object in the debugger, I got a lot of exceptions (attached).

There appear to be two main categories:

  1. Things attached to ConfigurationManager that has a wide impact
  2. Issues with ETW sources (look for the EventSource ERROR in the log)

NPE debug log with trimming.txt

@jeffschwMSFT commented in https://github.com/dotnet/coreclr/issues/26196#issuecomment-521819777:

@onovotny the mono/linker is not enlighted about most reflection based discovery. In these cases we can help you craft a roots file to ensure the right details are rooted (leading to your application working).

cc @sbomer @tlakollo

@sbomer sbomer self-assigned this Aug 15, 2019
@sbomer
Copy link
Member Author

sbomer commented Aug 15, 2019

@onovotny, you should be able to get past these errors with the following:

<ItemGroup>
  <TrimmerRootAssembly Include="System" />
  <TrimmerRootAssembly Include="System.Diagnostics.Tracing" />
</ItemGroup>

See https://aka.ms/dotnet-illink for more info. Happy to help if you run into further issues.

@clairernovotny
Copy link
Member

Two things -- that got through part of it. I'd ask how a library author can annotate their code so a user doesn't need to do that. Like can EventSource be decorated and ConfigurationManager, etc?

Second, after including those, I get more errors from WPF internals -- and I thought WPF was supposed to be working by default

System.Reflection.TargetInvocationException
  HResult=0x80131604
  Message=Exception has been thrown by the target of an invocation.
  Source=System.Private.CoreLib
  StackTrace:
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean wrapExceptions, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& hasNoDefaultCtor)
   at System.RuntimeType.CreateInstanceDefaultCtorSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean fillCache)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type)
   at MS.Internal.AssemblyHelper.LoadExtensionFor(String name)
   at MS.Internal.AssemblyHelper.ExtensionsForSystemXmlLinq(Boolean force)
   at MS.Internal.SystemXmlLinqHelper.IsXElement(Object item)
   at System.Windows.Controls.ContentPresenter.DataTypeForItem(Object item, DependencyObject target, Type& type)
   at System.Windows.FrameworkElement.FindTemplateResourceInternal(DependencyObject target, Object item, Type templateType)
   at System.Windows.Controls.ContentPresenter.DefaultSelector.SelectTemplate(Object item, DependencyObject container)
   at System.Windows.Controls.ContentPresenter.ChooseTemplate()
   at System.Windows.Controls.ContentPresenter.EnsureTemplate()
   at System.Windows.Controls.ContentPresenter.OnPreApplyTemplate()
   at System.Windows.FrameworkElement.ApplyTemplate()
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.DockPanel.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Control.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.WrapPanel.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
   at System.Windows.Controls.ItemsPresenter.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Border.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Control.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.DockPanel.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
   at System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Decorator.MeasureOverride(Size constraint)
   at System.Windows.Documents.AdornerDecorator.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Border.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Window.MeasureOverrideHelper(Size constraint)
   at System.Windows.Window.MeasureOverride(Size availableSize)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Interop.HwndSource.SetLayoutSize()
   at System.Windows.Interop.HwndSource.set_RootVisualInternal(Visual value)
   at System.Windows.Interop.HwndSource.set_RootVisual(Visual value)
   at System.Windows.Window.SourceWindowHelper.set_RootVisual(Visual value)
   at System.Windows.Window.SetRootVisual()
   at System.Windows.Window.SetRootVisualAndUpdateSTC()
   at System.Windows.Window.SetupInitialState(Double requestedTop, Double requestedLeft, Double requestedWidth, Double requestedHeight)
   at System.Windows.Window.CreateSourceWindow(Boolean duringShow)
   at System.Windows.Window.CreateSourceWindowDuringShow()
   at System.Windows.Window.SafeCreateWindowDuringShow()
   at System.Windows.Window.ShowHelper(Object booleanBox)
   at System.Windows.Window.Show()
   at PackageExplorer.App.<Application_Startup>d__4.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_0(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at PackageExplorer.App.Main()

Inner Exception 1:
TypeInitializationException: The type initializer for 'MS.Internal.SystemXmlLinqExtension' threw an exception.

Inner Exception 2:
NullReferenceException: Object reference not set to an instance of an object.

@sbomer
Copy link
Member Author

sbomer commented Aug 16, 2019

Currently we don't support a way to annotate libraries in such a way like .NET Native did - this gets complex in the general case where libraries might reflect over types depending on inputs from user code. We're taking a different approach and trying to be very clear up-front about what kinds of patterns are supported with linking - currently the answer is most reflection-based apps will require developer intervention, and if that's not feasible you might not want to link your app at this point.

We did pick defaults that were conservative enough to make the most basic WPF apps (templates) work, but we don't catch cases when reflection is used internally and so real-life WPF apps will need extra roots. If you take the approach of rooting whole assemblies (using TrimmerRootAssembly for example), hopefully just a few extra roots will pull in enough to make your app work.

@clairernovotny
Copy link
Member

Seems like whatever assembly SystemXmlLinqHelper lives in should be in the defaults as it’s internal wpf machinery?

@clairernovotny
Copy link
Member

I don’t expect anyone to know what AssebmlyHelper loads as that’s all private impl details.

@sbomer
Copy link
Member Author

sbomer commented Aug 17, 2019

It looks like the assembly is PresentationFramework-SystemXmlLinq: https://github.com/dotnet/wpf/blob/ac9d1b7a6b0ee7c44fd2875a1174b820b3940619/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/AssemblyHelper.cs#L193. The challenge is that we don't want to include it in the defaults for those WPF apps that don't use it - it's a tradeoff between smaller apps and making the linker more usable.

In the longer term we would like to have a way for frameworks to communicate optional dependencies like these to the linker (so that users don't have to dig into implementation details like you mention...), but we don't want to go the same path that .NET Native went down - it's something we're thinking about. Hopefully the doc I linked sets the expectations appropriately for this version.

@vitek-karas
Copy link
Member

The first problem hit here - that is the System.Diagnostics.Tracing missing, is caused by #737.

@vitek-karas
Copy link
Member

The first problem with the System.Diagnostics.Tracing is fixed in latest linker (I verified with the NuGetPackageExplorer that it gets pass that point).

The second problem with the missing System.dll is actually caused by the app.config. Linker doesn't understand configuration files, so any type references in them are ignored. In this app's case the app.config contains this piece of XML:

<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">

Which contains a fully qualified type name System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 which points to System.dll. The linker trims System.dll since it's a pure facade, but then the app fails because if calls Type.GetType on this type name.

This is something the linker should solve in some way. That said I tried this in a new WPF .NET Core app and VS doesn't generate this XML anymore, it now generates a type reference like System.Configuration.UserSettingsGroup, System.Configuration.ConfigurationManager, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 which points to System.Configuration.ConfigurationManager assembly, which is the implementation assembly and linker will keep it (as it contains all of the config reading code).

Given that NuGetPackageExplorer is a .NET Core only app, it would be safe to change the beginning of app.config to look like this:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System.Configuration.ConfigurationManager, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51">
      <section name="PackageExplorer.Properties.Settings" type="System.Configuration.ClientSettingsSection, System.Configuration.ConfigurationManager, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
    </sectionGroup>
  </configSections>

I verified that with this change the app runs just fine in non-trimmed mode. When trimmed with linker (using the latest build of linker) it now gets all the way to the above mentioned problem with SystemXmlLinqExtension.

@marek-safar marek-safar changed the title Trimming defaults cause WPF app to crash on launch Add marking of types used in .config files Oct 3, 2019
@zlatanov
Copy link

zlatanov commented Feb 3, 2020

@onovotny I've hit the same error recently when trying to publish NET Core WPF app. System.TypeInitializationException: The type initializer for 'MS.Internal.SystemXmlLinqExtension' threw an exception.

I started digging to find the root cause of this and I think I've found it. The issue is not that the WPF uses reflection for XElement, it's how it uses it.

If you look at the source code you will find that XElement is decorated as such:

[System.ComponentModel.TypeDescriptionProvider("MS.Internal.Xml.Linq.ComponentModel.XTypeDescriptionProvider`1[[System.Xml.Linq.XElement, System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],System.ComponentModel.TypeConverter")]
public class XElement : XContainer, IXmlSerializable
{  
}

This type descriptor is important for WPF and depends on it. However there is an issue here that I think the .NET Core team missed - the XElement is referred from System.Xml.Linq assembly System.Xml.Linq.XElement, System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 but that assembly no longer really exists - it only has [TypeForwardedTo] attributes inside it which point to System.Private.Xml.Linq assembly.

The linker correctly notices the use of XElement and includes System.Private.Xml.Linq in the trimmed output, but omits System.Xml.Linq as it has no executable code in it.

The way to fix at the moment it is to explicitly tell the linker that we want System.Xml.Linq assembly as is, by using

<ItemGroup>
    <TrimmerRootAssembly Include="System.Xml.Linq" />
</ItemGroup>

So in the end if we use any type reflection such as Type.GetType( string name ) and the name includes the assembly which has been changed with only type forwarding, the linker will omit it even if it knows which type we're reflecting on because it really lives somewhere else.

I hope this helps someone.

@vitek-karas
Copy link
Member

Trimming WPF apps is currently not supported: dotnet/wpf#3811
The config issue is currently not solved (the runtime libraries are annotated, but it will probably produce a warning). Solving that would probably require something like a source generator for the config reading code (which is effectively a deserializer). Currently we don't have plans to support plugins or other means to customize the trimming tools to understand additional file formats.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants