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

Metadata/TypeDef view increadibly slow #2151

Closed
tamlin-mike opened this issue Sep 7, 2020 · 18 comments
Closed

Metadata/TypeDef view increadibly slow #2151

tamlin-mike opened this issue Sep 7, 2020 · 18 comments
Labels

Comments

@tamlin-mike
Copy link

tamlin-mike commented Sep 7, 2020

Steps to reproduce

  1. Open an assembly with a fair amount of typedefs. In this scenerio they were a hair under 600.
  2. Navigate to Metadata/TypeDef view.
  3. Scroll the list, either by simply holding the scrollbar "arrow" down, or by clicking in the scrollbar to move the "thumb" towards the click-position.

ILSpy becomes unresponsive for extended periods of time, upwards of 20-30 seconds experienced. The strange thing is, NO noticable CPU or disk activity or is taking place during this waiting time - it just "hangs".

Bouncing back and forth between two pagefuls of data is faster, maybe as "low" as "only" a second, but still orders of magnitude slower than what would be expected, and again without any CPU load matching this slowness.

Trying to find the culprit, debugging displays one single WAP/WEP/WPF/WPA (whatever they are called) event, a single GC a few hundred ms after the view has scrolled. So no help there.

While in the debugger, there are a bunch of threads belonging Microsoft.VisualStudio.DesignTools.WpfTap.dll, but I suspect those are only Miscrosofts usual way of polluting debugging attempts with tons and tons of junk, since it seems to resolve to
c:\program files (x86)\microsoft visual studio...\common7\ide\commonextensions\microsoft\xamldiagnostics\Framework\x64\Microsoft.VisualStudio.DesignTools.WpfTap.dll
(try typing that path manually :-) )

Breaking into the debugger while in this hung state (you might have to be quick, so ILSpy doesn't finish its processing while you try to break into it from the now topmost VS) displays something that could make a grown man cry, of shame, seeing the way Microsoft is making the craft of software development look like it's worth less than shoveling manure.

This is a **call stack from hell**:
 	[Managed to Native Transition]	
>	WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait(System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)	Unknown
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	PresentationFramework.dll!System.Windows.Documents.TextStore.GrantLock()	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextStore.GrantLockWorker(MS.Win32.UnsafeNativeMethods.LockFlags flags)	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextStore.RequestLock(MS.Win32.UnsafeNativeMethods.LockFlags flags, out int hrSession)	Unknown
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	PresentationFramework.dll!System.Windows.Documents.TextStore.OnSelectionChanged()	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextSelection.System.Windows.Documents.ITextRange.NotifyChanged(bool disableScroll, bool skipEvents)	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextRangeBase.EndChange(System.Windows.Documents.ITextRange thisRange, bool disableScroll, bool skipEvents)	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextRange.ChangeBlock.System.IDisposable.Dispose()	Unknown
 	PresentationFramework.dll!System.Windows.Controls.TextBox.OnTextPropertyChanged(string oldText, string newText)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.TextBox.OnTextPropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.TextBox.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.InvalidateProperty(System.Windows.DependencyProperty dp, bool preserveCurrentValue)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Invalidate(bool isASubPropertyChange)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.TransferValue(object newValue, bool isASubPropertyChange)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.Activate(object item)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.OnDataContextChanged(System.Windows.DependencyObject contextElement)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.HandlePropertyInvalidation(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.OnPropertyInvalidation(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.OnPropertyInvalidation(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependentList.InvalidateDependents(System.Windows.DependencyObject source, System.Windows.DependencyPropertyChangedEventArgs sourceArgs)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType)	Unknown
 	PresentationFramework.dll!System.Windows.TreeWalkHelper.OnInheritablePropertyChanged(System.Windows.DependencyObject d, System.Windows.InheritablePropertyChangeInfo info, bool visitedViaVisualTree)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>._VisitNode(System.Windows.DependencyObject d, bool visitedViaVisualTree)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>.WalkLogicalChildren(System.Windows.FrameworkElement feParent, System.Windows.FrameworkContentElement fceParent, System.Collections.IEnumerator logicalChildren)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>.WalkFrameworkElementLogicalThenVisualChildren(System.Windows.FrameworkElement feParent, bool hasLogicalChildren)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>.IterateChildren(System.Windows.DependencyObject d)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>.StartWalk(System.Windows.DependencyObject startNode, bool skipStartNode)	Unknown
 	PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, object value, System.Windows.PropertyMetadata metadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.LinkContainerToItem(System.Windows.DependencyObject container, object item)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.OnItemReplaced(object oldItem, object newItem, int index)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.OnCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.ListenerList<System.Collections.Specialized.NotifyCollectionChangedEventArgs>.DeliverEvent(object sender, System.EventArgs e, System.Type managerType)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.DeliverEvent(object sender, System.EventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.ListenerList<System.Collections.Specialized.NotifyCollectionChangedEventArgs>.DeliverEvent(object sender, System.EventArgs e, System.Type managerType)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.DeliverEvent(object sender, System.EventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.ListCollectionView.ProcessCollectionChangedWithAdjustedIndex(System.Collections.Specialized.NotifyCollectionChangedEventArgs args, int adjustedOldIndex, int adjustedNewIndex)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.MultipleCopiesCollection.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.MultipleCopiesCollection.CopiedItem.set(object value)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.Primitives.DataGridCellsPresenter.OnItemChanged(object oldItem, object newItem)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.OnItemChanged(object oldItem, object newItem)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.NotifyPropertyChanged(System.Windows.DependencyObject d, string propertyName, System.Windows.DependencyPropertyChangedEventArgs e, System.Windows.Controls.DataGridNotificationTarget target)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.OnNotifyRowPropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, object value, System.Windows.PropertyMetadata metadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.SetValue(System.Windows.DependencyProperty dp, object value)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.PrepareRow(object item, System.Windows.Controls.DataGrid owningDataGrid)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGrid.PrepareContainerForItemOverride(System.Windows.DependencyObject element, object item)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemsControl.MS.Internal.Controls.IGeneratorHost.PrepareItemContainer(System.Windows.DependencyObject container, object item)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.InsertContainer(int childIndex, System.Windows.UIElement container, bool isRecycled)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.AddContainerFromGenerator(int childIndex, System.Windows.UIElement child, bool newlyRealized, bool isBeforeViewport)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureChild(ref System.Windows.Controls.Primitives.IItemContainerGenerator generator, ref System.Windows.Controls.Primitives.IContainItemStorage itemStorageProvider, ref System.Windows.Controls.Primitives.IContainItemStorage parentItemStorageProvider, ref object parentItem, ref bool hasUniformOrAverageContainerSizeBeenSet, ref double computedUniformOrAverageContainerSize, ref double computedUniformOrAverageContainerPixelSize, ref bool computedAreContainersUniformlySized, ref bool hasAnyContainerSpanChanged, ref System.Collections.IList items, ref object item, ref System.Collections.IList children, ref int childIndex, ref bool visualOrderChanged, ref bool isHorizontal, ref System.Windows.Size childConstraint, ref System.Windows.Rect viewport, ref System.Windows.Controls.VirtualizationCacheLength cacheSize, ref System.Windows.Controls.VirtualizationCacheLengthUnit cacheUnit, ref long scrollGeneration, ref bool foundFirstItemInViewport, ref double firstItemInViewportOffset, ref System.Windows.Size stackPixelSize, ref System.Windows.Size stackPixelSizeInViewport, ref System.Windows.Size stackPixelSizeInCacheBeforeViewport, ref System.Windows.Size stackPixelSizeInCacheAfterViewport, ref System.Windows.Size stackLogicalSize, ref System.Windows.Size stackLogicalSizeInViewport, ref System.Windows.Size stackLogicalSizeInCacheBeforeViewport, ref System.Windows.Size stackLogicalSizeInCacheAfterViewport, ref bool mustDisableVirtualization, bool isBeforeFirstItem, bool isAfterFirstItem, bool isAfterLastItem, bool skipActualMeasure, bool skipGeneration, ref bool hasBringIntoViewContainerBeenMeasured, ref bool hasVirtualizingChildren)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureOverrideImpl(System.Windows.Size constraint, ref double? lastPageSafeOffset, ref System.Collections.Generic.List<double> previouslyMeasuredOffsets, ref double? lastPagePixelSize, bool remeasure)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(System.Windows.Size constraint)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.Primitives.DataGridRowsPresenter.MeasureOverride(System.Windows.Size constraint)	Unknown
 	PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize)	Unknown
 	PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize)	Unknown
 	PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout()	Unknown
 	PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayoutCallback(object arg)	Unknown
 	PresentationCore.dll!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()	Unknown
 	PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandlerCore(object resizedCompositionTarget)	Unknown
 	PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandler(object resizedCompositionTarget)	Unknown
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)	Unknown
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler)	Unknown
 	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()	Unknown
 	WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 980	C#
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 928	C#
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 917	C#
 	WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state)	Unknown
 	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()	Unknown
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()	Unknown
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)	Unknown
 	WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)	Unknown
 	WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o)	Unknown
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)	Unknown
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler)	Unknown
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)	Unknown
 	WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)	Unknown
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame)	Unknown
 	PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore)	Unknown
 	PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)	Unknown
 	ILSpy.exe!ICSharpCode.ILSpy.App.Main()	Unknown

So maybe the problem isn't ILSpy at all, but Microsofts excellent software designers and programmers attempting to provide the Genuine Windows Experience by adding implicit "Please Wait...(tm)" functionality, while leaving it up to the users of their code to display that message? :-)

If that's the case, I guess there's nothing one can do about it in this context, but it might be worth being aware of (implementing that hated "Please Wait...(tm)" dialog with an animated progress bar might be taking it a bit far, as it could be seen as validation of Microsofts ways).

@tamlin-mike tamlin-mike added the Bug label Sep 7, 2020
@siegfriedpammer
Copy link
Member

Thanks for reporting this! Would you be able to share the assembly that causes the problem? I just tried this with .NET Core 3.1 System.Private.CoreLib.dll (TypeDef has > 2000 rows in this case) and I cannot reproduce the problem. Thank you!

@tamlin-mike
Copy link
Author

tamlin-mike commented Sep 8, 2020

I tested with Core 3.1.7 System.Private.CoreLib.dll, and it still reproduces. Took approx 17 seconds from clicking an empty area in the scroll bar, to move one screenful, until the data was actually presented. CPU consumption was virtually nonexistant during the time.

I then tested swiching to the Field list (7377 entries). This was faster. Only took 6-7 seconds.
Method (22379): Same result, 6-7 seconds.
Back to TypeDef: Now down to "only" 13 seconds, but a subsequent attempt was more like 20 seconds.

Debug build of ILSpy (4b716e2 to be precise), built for 4.7.2, running on (I presume) Microsoft's 4.8 JIT+runtime.

I find it utterly baffling that it only happens in this kind of views, and with no measurable CPU consumption. I wonder what in the hell Microsoft is doing here, and with that call-stack from hell it sure won't be an easy task trying to find the bug (I'm currently leaning towards calling it "their bug", but while tempting I'll hold off on that until proof is available :-) ).

EDIT: When first entering the TypeDef view by clicking its node in the tree control, it only took 1-2 seconds, and this time it consumed CPU (I'd guess that's mainly the lookup ILSpy is doing).

EDIT2: While on a hunch looking at it using ProcMon, while it didn't display any problems with waiting for I/O, I did notice it closing+reopening the display font on every scroll operation. While that is seemingly not taking a lot of CPU time, it's definitely not what I'd expect, so that part might at least be something to look into (probably belongs in a separate issue).

EDIT3: OK, I think the fog is lifting, even if only a little bit. Running ILSpy under the VS profiler, using the "Timeline" profiling method, it displays an insane five thousand "Layout" operations, for scrolling a single screenful of data, and the call stack from that one is deep enough for your brain to shut down to protect you/itself from trauma.

The problem (what I'd call bug, but Microsoft true to their usual apathy and customer-disdain would probably call "by design") seems to emanate from PART_RowPresenter inside System.Windows.Controls.Primitives.DataGridRowsPresenter. That's the last entry in the hierarchy of idle-time-burners I can find, where a mere 50 calls to it consumed almost no CPU but 14.81 seconds wall-clock time!

I'm going to dig deeper into this cesspool, but I first need to find a thick enough hazmat suit for protection so it might take a while.

@siegfriedpammer
Copy link
Member

Thanks for following up! However, on my machine clicking the TypeDef node takes about 1 second, and scrolling one screen (39 rows) takes about the same amount of time. I am running this on a Lenovo ThinkPad E590, Intel Core i7 8565U, base speed 2.0GHz, 32 GB RAM, an SSD. One second for scrolling one screen is still slow, but it's what I have come accustomed to because, well, WPF DataGrid.

Have you tried profiling?

@tamlin-mike
Copy link
Author

PNG of the "Timeline" trace

Issue_2151_Timeline_profile

@siegfriedpammer
Copy link
Member

Hmmm... I don't see a single element that causes the long delays... what's further down in the tree?

@tamlin-mike
Copy link
Author

tamlin-mike commented Sep 8, 2020

what's further down in the tree?

Nothing useful. Just specks of milliseconds.

However, I have now tried to dig deeper into this, and I think I just enetered an episode of the Twilight Zone.

Running ILSpy under the debugger with env. var. COMPLUS_ZapDisable=1 it perform the scrolling operations FASTER than if using the ngen'd images! I'm experiencing a true "WTF?!" deer-in-headlights moment here. Did I wake up in some alternative universe this morning? Is Donald Duck president of the USA? Is the moon made of cheese?

EDIT: COMPLUS_ZapDisable was premature conclusion. It seems just running it under the debugger at all is making it run faster.

EDIT2: This includes attaching a (the VS) debugger to it. Starting ILSpy from e.g. command-prompt it runs dg-slow for this scroll operation. Attach a VS debugger to the running instance, and it starts running faster (still slow, ~2 seconds per scroll without any CPU used, but way faster than the 14-17 seconds without the debugger attached).

EDIT3: Not the debugger as-such. It's the "Enable UI Debuggin Tools fo XAML" debugger setting that makes it run faster, possibly because it introduces some delays that breaks the WPF-internal deadlock. Turning off that setting, it exhibits the same slowdown as if free-running.

Do you know/understand what those "PART_*" thingies are? They are clearly not method names, and from my understanding what I must do now is to try to find that time-hogger and disassemble the hell out of it to retain some semblance of sanity, but I haven't got a clue of where to start.
(Useless info: This experience reaffirms that my intense dislike for the bloated pig WPF is very, very well-founded)

Follow-up to EDIT2: As it currently stands, this is more and more starting to look like a bug in Microsoft's code, that may be impossible to work around in ILSpy. Do you know what of the gazillion Microsoft repo's would be the correct one for something like this?

Thanks for you interest and patience. :-)

@siegfriedpammer
Copy link
Member

Do you know/understand what those "PART_*" thingies are? They are clearly not method names, and from my understanding what I must do now is to try to find that time-hogger and disassemble the hell out of it to retain some semblance of sanity, but I haven't got a clue of where to start.

The "PART_" identifiers are used by WPF controls to make ControlTemplates work: Parts of the template that are required by the "code-behind" are marked as "PART_whatever". So it's just the Name property of the WPF control, which then can be used to access the part after the template/XAML is applied. The tree seems to show the type name of the control if no name was set. (Note: the "PART_" in front is just a convention, seems that WPF's DataGrid doesn't follow the convention itself, see, e.g., "DG_ScrollViewer").

If you want to dig down in the WPF Visual Tree, you can use Snoop. https://github.com/snoopwpf/snoopwpf

@tamlin-mike
Copy link
Author

tamlin-mike commented Sep 8, 2020

If you want to dig down in the WPF Visual Tree

I'm sure I don't want to do that. :-) But thanks for the link, it might come in handy.

Well, I have come to a brick wall. I'm leaving this issue open until I have referenced it in an issue in some Microsoft-managed repository.

With Microsoft keeping this crap closed source (whether out of spite or to protect the world from their LSD-induced madness could be up for discussion), it's practically impossible for me to debug it deeper than finding "something in a callstack-from-hell is trying to get a lock on a text, while presumably some other (non-running) thread is holding a lock on it". As such, I'll just have to find a Microsoft repo. to report this to, in an attemp to get it resolved

(only to get the issue closed as "WONTFIX", "BYDESIGN", or the wonderfully helpful and customer-oriented "Wrong repository" (basically, "piss off, we don't care") - yeah, my confidence in Microsoft is at a well-founded all-time low, so bad I don't even bother trying to be polite about it anymore).

EDIT: Well great... There is no github repo fro WPF Framework, and I sure won't jump through all the hoops and enable running scripts from umpteen untrusted websites just to file a bugreport to them... Bah. I'll just use dnSpy for metadata lookup from now on.

Sorry for the noise.

@tamlin-mike
Copy link
Author

Probably not a bug that can be fixed by ILSpy. Seems to be a bug deep down in Microsofts proprietary code.

@siegfriedpammer
Copy link
Member

I'll just use dnSpy for metadata lookup from now on.

That is very unfortunate...

EDIT: Well great... There is no github repo fro WPF Framework, and I sure won't jump through all the hoops and enable running scripts from umpteen untrusted websites just to file a bugreport to them... Bah.

Have you tried https://github.com/dotnet/wpf ? ... unfortunately it will take some time (sorry!) until we'll be able to move ILSpy to "WPF Core", which is open source and hosted in that repository...

@tamlin-mike
Copy link
Author

Unfortunately that repo only touches ".NET Core". For Framework they refer you to some other websites that forces you to run javascripts from umpteen different sites. I accepted that while it was possible to use the VS "integrated" feedback tool (so it only polluted the "system integrated" web browser that I never intentionally used), but since the VS team killed that tool off without a single word of warning, I sure am not gonna pollute my main browser with that crap if I can avoid it.

I also remember some WPF "Core" team article (or blog entry, or whatever) where they stated (in no uncertain terms) that it will be only partially open source, meaning it will only run on Windows.
What a surprise. /s

Anyway, just to have the final piece of my findings documented somewhere (might as well hang in limbo here, than in some Microsoft repo where they ignore it forever too, eh), here's...

Where it hangs without any CPU consumption At least that seems to be the place, since it always ends up there when attaching debugger and breaking into the running/hanging ILSpy during the long hang intervals. I get a headache and get sick to my stomach just looking at that call stack, needing to fight back the urge to vomit.
 	[Managed to Native Transition]	
>	WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait(System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)	Unknown
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	PresentationFramework.dll!System.Windows.Documents.TextStore.GrantLock()	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextStore.GrantLockWorker(MS.Win32.UnsafeNativeMethods.LockFlags flags)	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextStore.RequestLock(MS.Win32.UnsafeNativeMethods.LockFlags flags, out int hrSession)	Unknown
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	PresentationFramework.dll!System.Windows.Documents.TextStore.OnSelectionChanged()	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextSelection.System.Windows.Documents.ITextRange.NotifyChanged(bool disableScroll, bool skipEvents)	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextRangeBase.EndChange(System.Windows.Documents.ITextRange thisRange, bool disableScroll, bool skipEvents)	Unknown
 	PresentationFramework.dll!System.Windows.Documents.TextRange.ChangeBlock.System.IDisposable.Dispose()	Unknown
 	PresentationFramework.dll!System.Windows.Controls.TextBox.OnTextPropertyChanged(string oldText, string newText)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.TextBox.OnTextPropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.TextBox.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.InvalidateProperty(System.Windows.DependencyProperty dp, bool preserveCurrentValue)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Invalidate(bool isASubPropertyChange)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.TransferValue(object newValue, bool isASubPropertyChange)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.Activate(object item)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.OnDataContextChanged(System.Windows.DependencyObject contextElement)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.HandlePropertyInvalidation(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.OnPropertyInvalidation(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.BindingExpression.OnPropertyInvalidation(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependentList.InvalidateDependents(System.Windows.DependencyObject source, System.Windows.DependencyPropertyChangedEventArgs sourceArgs)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType)	Unknown
 	PresentationFramework.dll!System.Windows.TreeWalkHelper.OnInheritablePropertyChanged(System.Windows.DependencyObject d, System.Windows.InheritablePropertyChangeInfo info, bool visitedViaVisualTree)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>._VisitNode(System.Windows.DependencyObject d, bool visitedViaVisualTree)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>.WalkLogicalChildren(System.Windows.FrameworkElement feParent, System.Windows.FrameworkContentElement fceParent, System.Collections.IEnumerator logicalChildren)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>.WalkFrameworkElementLogicalThenVisualChildren(System.Windows.FrameworkElement feParent, bool hasLogicalChildren)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>.IterateChildren(System.Windows.DependencyObject d)	Unknown
 	PresentationFramework.dll!System.Windows.DescendentsWalker<System.Windows.InheritablePropertyChangeInfo>.StartWalk(System.Windows.DependencyObject startNode, bool skipStartNode)	Unknown
 	PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, object value, System.Windows.PropertyMetadata metadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.LinkContainerToItem(System.Windows.DependencyObject container, object item)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.OnItemReplaced(object oldItem, object newItem, int index)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.OnCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.ListenerList<System.Collections.Specialized.NotifyCollectionChangedEventArgs>.DeliverEvent(object sender, System.EventArgs e, System.Type managerType)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.DeliverEvent(object sender, System.EventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.ListenerList<System.Collections.Specialized.NotifyCollectionChangedEventArgs>.DeliverEvent(object sender, System.EventArgs e, System.Type managerType)	Unknown
 	WindowsBase.dll!System.Windows.WeakEventManager.DeliverEvent(object sender, System.EventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.CollectionView.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args)	Unknown
 	PresentationFramework.dll!System.Windows.Data.ListCollectionView.ProcessCollectionChangedWithAdjustedIndex(System.Collections.Specialized.NotifyCollectionChangedEventArgs args, int adjustedOldIndex, int adjustedNewIndex)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.MultipleCopiesCollection.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.MultipleCopiesCollection.CopiedItem.set(object value)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.Primitives.DataGridCellsPresenter.OnItemChanged(object oldItem, object newItem)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.OnItemChanged(object oldItem, object newItem)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.NotifyPropertyChanged(System.Windows.DependencyObject d, string propertyName, System.Windows.DependencyPropertyChangedEventArgs e, System.Windows.Controls.DataGridNotificationTarget target)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.OnNotifyRowPropertyChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.NotifyPropertyChange(System.Windows.DependencyPropertyChangedEventArgs args)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp, object value, System.Windows.PropertyMetadata metadata, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType, bool isInternal)	Unknown
 	WindowsBase.dll!System.Windows.DependencyObject.SetValue(System.Windows.DependencyProperty dp, object value)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGridRow.PrepareRow(object item, System.Windows.Controls.DataGrid owningDataGrid)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.DataGrid.PrepareContainerForItemOverride(System.Windows.DependencyObject element, object item)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.ItemsControl.MS.Internal.Controls.IGeneratorHost.PrepareItemContainer(System.Windows.DependencyObject container, object item)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.InsertContainer(int childIndex, System.Windows.UIElement container, bool isRecycled)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.AddContainerFromGenerator(int childIndex, System.Windows.UIElement child, bool newlyRealized, bool isBeforeViewport)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureChild(ref System.Windows.Controls.Primitives.IItemContainerGenerator generator, ref System.Windows.Controls.Primitives.IContainItemStorage itemStorageProvider, ref System.Windows.Controls.Primitives.IContainItemStorage parentItemStorageProvider, ref object parentItem, ref bool hasUniformOrAverageContainerSizeBeenSet, ref double computedUniformOrAverageContainerSize, ref double computedUniformOrAverageContainerPixelSize, ref bool computedAreContainersUniformlySized, ref bool hasAnyContainerSpanChanged, ref System.Collections.IList items, ref object item, ref System.Collections.IList children, ref int childIndex, ref bool visualOrderChanged, ref bool isHorizontal, ref System.Windows.Size childConstraint, ref System.Windows.Rect viewport, ref System.Windows.Controls.VirtualizationCacheLength cacheSize, ref System.Windows.Controls.VirtualizationCacheLengthUnit cacheUnit, ref long scrollGeneration, ref bool foundFirstItemInViewport, ref double firstItemInViewportOffset, ref System.Windows.Size stackPixelSize, ref System.Windows.Size stackPixelSizeInViewport, ref System.Windows.Size stackPixelSizeInCacheBeforeViewport, ref System.Windows.Size stackPixelSizeInCacheAfterViewport, ref System.Windows.Size stackLogicalSize, ref System.Windows.Size stackLogicalSizeInViewport, ref System.Windows.Size stackLogicalSizeInCacheBeforeViewport, ref System.Windows.Size stackLogicalSizeInCacheAfterViewport, ref bool mustDisableVirtualization, bool isBeforeFirstItem, bool isAfterFirstItem, bool isAfterLastItem, bool skipActualMeasure, bool skipGeneration, ref bool hasBringIntoViewContainerBeenMeasured, ref bool hasVirtualizingChildren)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureOverrideImpl(System.Windows.Size constraint, ref double? lastPageSafeOffset, ref System.Collections.Generic.List<double> previouslyMeasuredOffsets, ref double? lastPagePixelSize, bool remeasure)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(System.Windows.Size constraint)	Unknown
 	PresentationFramework.dll!System.Windows.Controls.Primitives.DataGridRowsPresenter.MeasureOverride(System.Windows.Size constraint)	Unknown
 	PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize)	Unknown
 	PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize)	Unknown
 	PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout()	Unknown
 	PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayoutCallback(object arg)	Unknown
 	PresentationCore.dll!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()	Unknown
 	PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandlerCore(object resizedCompositionTarget)	Unknown
 	PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandler(object resizedCompositionTarget)	Unknown
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)	Unknown
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler)	Unknown
 	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()	Unknown
 	WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj)	Unknown
 	mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 980	C#
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Line 928	C#
 	mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Line 917	C#
 	WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state)	Unknown
 	WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()	Unknown
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()	Unknown
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)	Unknown
 	WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)	Unknown
 	WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o)	Unknown
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)	Unknown
 	WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler)	Unknown
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)	Unknown
 	WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)	Unknown
 	[Native to Managed Transition]	
 	[Managed to Native Transition]	
 	WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame)	Unknown
 	PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore)	Unknown
 	PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)	Unknown
 	ILSpy.exe!ICSharpCode.ILSpy.App.Main()	Unknown

Cheers.

@dgrunwald
Copy link
Member

From some googling I think TextStore.GrantLock is related to the Text Services Framework.
This is a framework that allows other processes (e.g. speech recognition software) to access the text in WPF applications.
So I think some other process might be doing that, and ILSpy is waiting for it to the release the lock?

Do you have some other software (e.g. input method editors) running that might be using text services framework?

Maybe the problem is that ILSpy uses too many text boxes, thus overloading the TSF communication.
Try replacing the read-only TextBoxes with TextBlocks and see if that improved performance.

@tamlin-mike
Copy link
Author

Thanks Daniel, good sleuthing.

I have no IME's or text input- output- "services" of any kind intentionally running on this desktop computer. In fact, all wasteful Microsoft malware I can disable without Microsoft totally breaking Windows itself, is stopped, and disabled. I did however make the mistake of letting Microsoft "upgrade" the OS installation to (the idiotically named) "2004" in a vain hope they would have fixed some vulgarly obvious bugs (not too surprisingly, they hadn't), so it's entirely possible they included this breakage in this version as an "easter egg".

However, Microsoft have "fixed" CtfMon (or so they claimed) two years after I first reported its horrendous memory leaks, and according to them it was indeed related to text boxes (whether USER32 EDIT's or WPF's equivalent is unknown). It wouldn't surprise me the least if they broke something else in that process.

Maybe the problem is that ILSpy uses too many text boxes, thus overloading the TSF communication.

That is certainly plausible, since that's not the way to do it. Imagine how something like Excel would work (or rather, not work) if every cell was an EDIT box. :-) a An EDIT should only be instantiated on top of an editable cell once a user actually invokes an edit operation. During all other time it should only be rendered text. In this case I don't even think ILSpy allows editing anything in those tables?

I'll give that suggestion a shot and report back.

Thanks for the input.

@tamlin-mike
Copy link
Author

Oh yeah. HELL yeah!

@dgrunwald You came as a guardian angel and hit the nail on the head.

Not only is it now a few thousand times faster (with the bug I experience, but likely also a bit faster in general), it's consuming less memory.

As I don't know diddle-squat about the XML-wrapped XAML-this and WPF-that, please see this only as an example. It seems to work for me, for this scenario, but I can take no responsibility for it working for all uses.

For your consideration:

diff --git a/ILSpy/Metadata/DataGridCustomTextColumn.cs b/ILSpy/Metadata/DataGridCustomTextColumn.cs
index 238f85e2b..1dc92140f 100644
--- a/ILSpy/Metadata/DataGridCustomTextColumn.cs
+++ b/ILSpy/Metadata/DataGridCustomTextColumn.cs
@@ -24,35 +24,38 @@

 namespace ICSharpCode.ILSpy.Metadata
 {
+       //using TextCellType = TextBox;
+       using TextCellType = TextBlock;
+
        class DataGridCustomTextColumn : DataGridTextColumn
        {
                public BindingBase ToolTipBinding { get; set; }

                protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
                {
-                       TextBox textBox = new TextBox() { Style = (Style)MetadataTableViews.Instance["DataGridCustomTextColumnTextBoxStyle"] };
-                       BindingOperations.SetBinding(textBox, TextBox.TextProperty, Binding);
+                       var textCell = new TextCellType() { Style = (Style)MetadataTableViews.Instance["DataGridCustomTextColumnTextBoxStyle"] };
+                       BindingOperations.SetBinding(textCell, TextCellType.TextProperty, Binding);
                        if (ToolTipBinding != null) {
-                               textBox.MouseMove += TextBox_MouseMove;
+                               textCell.MouseMove += Cell_MouseMove;
                        }
-                       textBox.GotFocus += TextBox_GotFocus;
-                       return textBox;
+                       textCell.GotFocus += Cell_GotFocus;
+                       return textCell;
                }

-               private void TextBox_GotFocus(object sender, RoutedEventArgs e)
+               private void Cell_GotFocus(object sender, RoutedEventArgs e)
                {
-                       TextBox tb = (TextBox)sender;
+                       var tb = (TextCellType)sender;
                        var cell = tb.GetParent<DataGridCell>();
                        var row = cell.GetParent<DataGridRow>();
                        row.IsSelected = cell.IsSelected = true;
                }

-               private void TextBox_MouseMove(object sender, MouseEventArgs e)
+               private void Cell_MouseMove(object sender, MouseEventArgs e)
                {
                        e.Handled = true;
-                       var textBox = (TextBox)sender;
-                       BindingOperations.SetBinding(textBox, TextBox.ToolTipProperty, ToolTipBinding);
-                       textBox.MouseMove -= TextBox_MouseMove;
+                       var textCell = (TextCellType)sender;
+                       BindingOperations.SetBinding(textCell, TextCellType.ToolTipProperty, ToolTipBinding);
+                       textCell.MouseMove -= Cell_MouseMove;
                }
        }
 }
diff --git a/ILSpy/Metadata/MetadataTableViews.xaml b/ILSpy/Metadata/MetadataTableViews.xaml
index a0b52ecf0..75aefd465 100644
--- a/ILSpy/Metadata/MetadataTableViews.xaml
+++ b/ILSpy/Metadata/MetadataTableViews.xaml
@@ -8,9 +8,10 @@
                         xmlns:srm="clr-namespace:System.Reflection;assembly=System.Reflection.Metadata"
                         xmlns:dgx="urn:tom-englert.de/DataGridExtensions">

-       <Style x:Key="DataGridCustomTextColumnTextBoxStyle" TargetType="TextBox">
-               <Setter Property="IsReadOnly" Value="True" />
-               <Setter Property="IsReadOnlyCaretVisible" Value="True" />
+      <!-- <Style x:Key="DataGridCustomTextColumnTextBoxStyle" TargetType="TextBox"> -->
+      <Style x:Key="DataGridCustomTextColumnTextBoxStyle" TargetType="TextBlock">
+               <!-- <Setter Property="IsReadOnly" Value="True" />
+               <Setter Property="IsReadOnlyCaretVisible" Value="True" /> -->
                <Setter Property="Padding" Value="2" />
                <Style.Triggers>
                        <MultiTrigger>
@@ -18,7 +19,7 @@
                                        <Condition Property="IsMouseOver" Value="False" />
                                        <Condition Property="IsFocused" Value="False" />
                                </MultiTrigger.Conditions>
-                               <Setter Property="BorderBrush" Value="Transparent" />
+                               <!-- <Setter Property="BorderBrush" Value="Transparent" /> -->
                                <Setter Property="Background" Value="Transparent" />
                        </MultiTrigger>
                        <MultiDataTrigger>

@siegfriedpammer
Copy link
Member

I have refactored the Metadata DataGridCell template... Please note that copying values from cells is now available through a context menu entry. Thank you for your understanding!

@tamlin-mike
Copy link
Author

Thanks Siegfried.

I'd still like to really understand where Microsoft introduced this, since it was 100% reproducible for me, but you didn't experience it at all.

I hit the problem on Windows 10 x64 10.0.19041.450 (a.k.a. "2004"), with
%windir%\Microsoft.NET\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll

@siegfriedpammer
Copy link
Member

but you didn't experience it at all.

With the debugger attached, it did lag for me only a little bit. However, without the debugger attached it was very noticeable.
So, thanks for reporting this! I usually use ILSpy with the debugger attached, so I never experienced this.

@tamlin-mike
Copy link
Author

without the debugger attached it was very noticeable.

Aha. I got the impression you didn't see the problem at all. Now all is clear. Thanks.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants