Recently I got an opportunity to work with a customer who was observing some performance issue with their VS2012 IDE intermittently while editing some UML diagrams. More information about the problem assessment is as below:
While trying to edit any UML model in Model Explorer, e.g. editing a sequence diagram/activity diagram/component diagram etc., the Visual Studio IDE stops responding for several minutes.
A hang dump of the Visual Studio process (devenv.exe), captured when it was unresponsive, revealed that the main UI thread was trying to auto-save the backup files onto some remote location (why a remote location? refer to the section ‘More Information’ below). As saving files onto a remote location was susceptible to some network latencies, the VS IDE just used to freeze up.
Configure Visual Studio to save these auto-backup files somewhere onto the local computer itself (using the registry value – HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0\VisualStudioLocation).
To understand what really was going on when the VS IDE was just unresponsive, we started digging into the memory dump of the devenv.exe process. We saw that the main UI thread of the IDE was stuck in a call to "CloseHandle()" as seen on the below managed call stack:
OS Thread Id: 0xcf8 (0)
Child SP IP Call Site
0039de34 6a70d5f0 DomainNeutralILStubClass.IL_STUB_PInvoke(IntPtr)
0039de38 6a6de3bb [InlinedCallFrame: 0039de38] Microsoft.Win32.Win32Native.CloseHandle()
0039de68 6a6de3bb Microsoft.Win32.SafeHandles.SafeFileHandle.ReleaseHandle()
0039dfdc 6b3a3eb2 [HelperMethodFrame_1OBJ: 0039dfdc] System.Runtime.InteropServices.SafeHandle.InternalDispose()
0039e058 6a6de4b0 System.Runtime.InteropServices.SafeHandle.Dispose()
0039e060 69af4b32 System.IO.FileSystemWatcher.StopRaisingEvents()
0039e06c 69b05196 System.IO.FileSystemWatcher.set_EnableRaisingEvents()
0039e078 55aed108 Microsoft.VisualStudio.Services.DirectoryWatcher.set_EnableRaisingEvents()
0039e088 55aecb22 Microsoft.VisualStudio.Services.DirectoryWatcher.UpdateWatcherState()
0039e0bc 55af64e4 Microsoft.VisualStudio.Services.DirectoryWatcher.RemoveFileWatcher()
0039e0e8 55af48d9 Microsoft.VisualStudio.Services.DirectoryWatcher.TryRemoveFileWatcher()
0039e0f8 039d729d Microsoft.VisualStudio.Services.DirectoryWatcher.OnFileWatcherIgnoreStateChanged()
0039e114 0027a2a7 [MulticastFrame: 0039e114] System.EventHandler.Invoke()
0039e140 55af9be2 Microsoft.VisualStudio.Services.IgnorableObject.set_IgnoreCount()
0039e150 55af9b6f Microsoft.VisualStudio.Services.IgnorableObject.Ignore()
0039e158 55aefb69 Microsoft.VisualStudio.Services.FileChangeService.IgnoreFile()
0039e18c 039d7176 Microsoft.VisualStudio.Services.FileChangeService+<>c__DisplayClass11.<Microsoft.VisualStudio.Shell.Interop.IVsFileChangeEx.IgnoreFile>b__f()
0039e194 54602d1b Microsoft.VisualStudio.ErrorHandler+<>c__DisplayClass1.<CallWithCOMConvention>b__0()
0039e198 54618c54 Microsoft.VisualStudio.ErrorHandler.CallWithCOMConvention()
0039e1cc 54602cf5 Microsoft.VisualStudio.ErrorHandler.CallWithCOMConvention()
0039e1e0 039d7104 Microsoft.VisualStudio.Services.FileChangeService.Microsoft.VisualStudio.Shell.Interop.IVsFileChangeEx.IgnoreFile()
0039e218 039d7052 Microsoft.VisualStudio.Modeling.Shell.DocData.SuspendFileChangeHelper()
0039e234 039d6e86 Microsoft.VisualStudio.TeamArchitect.SequenceDesigner.SequenceDesignerDocDataBase.SaveSubordinateFile()
0039e274 039d6d42 Microsoft.VisualStudio.Modeling.Shell.SubordinateDocData.SaveAs()
0039e284 039d6d02 Microsoft.VisualStudio.Modeling.Shell.SubordinateDocData.Save()
0039e294 039d6c9c Microsoft.VisualStudio.Modeling.Shell.DocData.BackupFile()
0039e2a8 039d6c2a Microsoft.VisualStudio.Modeling.Shell.DocData.Microsoft.VisualStudio.TextManager.Interop.IVsFileBackup.BackupFile()
0039e2cc 5545cf05 DomainNeutralILStubClass.IL_STUB_COMtoCLR()
0039e38c 6b3a42ab [ComMethodFrame: 0039e38c]
The above managed call stack clearly indicates that Visual Studio is trying to auto-save some UML model presently open in the designer (in this case, a sequence diagram) into a backup file during its OnIdle processing. And in this process, it tries to stop/dispose its “FileSystemWatcher” object from monitoring the backup files which seem to be located at some network share as below:
OS Thread Id: 0xcf8 (0)
ESP/REG Object Name
. . .
0039DFD0 2f39d95c System.IO.FileSystemWatcher
. . .
0:000> !DumpObj 2f39d95c
Size: 92(0x5c) bytes
MT Field Offset Type VT Attr Value Name
. . .
6a76b718 4002aae 10 System.String 0 instance 2f39cf30 directory
6a76b718 4002aaf 14 System.String 0 instance 04e4ca94 filter
6a76d93c 4002ab0 18 …es.SafeFileHandle 0 instance 2f3b1418 directoryHandle
. . .
0:000> !DumpObj 2f39cf30
Size: 222(0xde) bytes
String: \\MyRemoteServer\home\AThakur\My Documents\Visual Studio 2012\Backup Files\Regenstrief.LOINCSubmission
. . .
0:000> !DumpObj 04e4ca94
Size: 20(0x14) bytes
String: *.* <== i.e. monitoring/watching for all the files in the remote directory
. . .
Drilling further into the stack, we saw the actual file being used for the backup as below:
0:000> !DumpObj 2f39c2b4
Size: 326(0x146) bytes
String: \\MyRemoteServer\home\AThakur\My Documents\Visual Studio 2012\Backup Files\Regenstrief.LOINCSubmission\~AutoRecover.EditAnswerList.sequencediagram.layout0
. . .
The above details are good enough to describe the intermittent/periodic performance issue the VS was running into; but so far, we still didn’t exactly know why the VS IDE was trying to use some network location for its backup purposes in first place. So, we thought of looking into the environment settings under which the Visual Studio was started:
. . .
. . .
VisualStudioDir=\\MyRemoteServer\home\AThakur\My Documents\Visual Studio 2012
VisualStudioEdition=Microsoft Visual Studio Ultimate 2012
. . .
The above environment variables gave us a clue that Visual Studio indeed tried to look for a local path inside the current user’s profile folder, but as the user’s profile was redirected to some remote share, VS IDE ended up using that particular remote location for its periodic auto-backup operations. This was later confirmed by our customer that they had some policies enforced by their company to automatically redirect user’s profile to a centralized network location. And, due to some network connectivity/latency issues, the call to “CloseHandle()” to their remote directory just used to get delayed causing the main UI thread to lock the IDE up.
So the resolution/workaround to this kind of issues in general is to have Visual Studio use a local path for all its temporary operations. The Visual Studio populates its internal environment variable “VisualStudioDir” from the below location value in the system registry:
The default data for this location value is “%USERPROFILE%\Documents\Visual Studio 2012”. So, if the user profile redirection is enabled on your machine, then I’m afraid there is not much option than changing this location to some local path which shouldn’t get redirected (please make sure all instances of the Visual Studio is/are shutdown before making this registry change so that it would be effective next time VS IDE is launched). That said, having VS configurations/settings onto the local machine would definitely give you the performance improvements but you’ll not enjoy the benefits of a centralized location implemented by your company via user profile redirection. It’s like a tradeoff between two benefits, so please make your choice carefully.