An analysis of the hang up of. Net WPF linen management system

Time:2021-6-30

1: Background

1. Tell a story

I’ve seen a lot of dumps these days. It’s a bit nerve racking. I dream about dumps at night. When I got to the company this morning, I heard my colleagues complain that the WPF program he was in charge of was dead, and then the little girl who tested it also complained… Hi, I don’t know which iteration caused the problem. Anyway, it’s not a big problem if the customer doesn’t revolt.

But when I heard that the program didn’t respond, I was really in a hurry… Instinctively, I sent him a procdump. In the past, I generated two dumps and sent them to him.

In other words, the hang up problem of WPF with UI interface is actually very easy to analyzeUI threadLost the response, as for why lost the response, must be to do something invisible, such as playing smartTask.ResultOne more thing to note is that the UI is uniqueSynchronizationContext, such as WinForm’s:WindowsFormsSynchronizationContext, WPF’sDispatcherSynchronizationContextLater, I’m going to open an article to deeply analyze the causes of this deadlock with WinDbg. OK, so much has been said and dump has arrived. Let’s go to WinDbg for analysis.

2: WinDbg analysis

1. Review UI thread

It’s very simple. First pass~0sSwitch to No. 0, which is the UI thread, and then go through the!dumpstackCall out the managed and unmanaged stacks of UI threads. In order to protect privacy, I will simplify them a little.

0:000> ~0s
eax=00000000 ebx=01855bf8 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=776a171c esp=014fe3b8 ebp=014fe410 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
ntdll!NtWaitForSingleObject+0xc:
776a171c c20c00          ret     0Ch
0:000> !dumpstack
OS Thread Id: 0x4ee0 (0)
Current frame: ntdll!NtWaitForSingleObject+0xc
ChildEBP RetAddr  Caller, Callee
014fe3b4 7468a9c5 mswsock!SockWaitForSingleObject+0x125, calling ntdll!NtWaitForSingleObject
014fe410 7469932c mswsock!SockDoConnectReal+0x36b, calling mswsock!SockWaitForSingleObject
014fe4b4 74698df7 mswsock!SockDoConnect+0x482, calling mswsock!SockDoConnectReal
014fe544 74699861 mswsock!WSPConnect+0x61, calling mswsock!SockDoConnect
014fe564 77316cf7 ws2_32!WSAConnect+0x77
014fe5a0 6422aeea (MethodDesc 64088970 +0x5a DomainBoundILStubClass.IL_STUB_PInvoke(IntPtr, Byte[], Int32, IntPtr, IntPtr, IntPtr, IntPtr))
014fe5d4 6422aeea (MethodDesc 64088970 +0x5a DomainBoundILStubClass.IL_STUB_PInvoke(IntPtr, Byte[], Int32, IntPtr, IntPtr, IntPtr, IntPtr))
014fe5f4 641c72eb (MethodDesc 63ff4310 +0x4b System.Net.Sockets.Socket.DoConnect(System.Net.EndPoint, System.Net.SocketAddress)), calling 1d4d538c
014fe628 642160c5 (MethodDesc 640847c4 +0x7d System.Net.Sockets.Socket.Connect(System.Net.EndPoint)), calling (MethodDesc 63ff4310 +0 System.Net.Sockets.Socket.DoConnect(System.Net.EndPoint, System.Net.SocketAddress))
014fe644 1d4d5bd3 (MethodDesc 1c93d404 +0x33 xxx.SocketHelper.xxxSocket.Connect(System.Net.IPEndPoint)), calling (MethodDesc 640847c4 +0 System.Net.Sockets.Socket.Connect(System.Net.EndPoint))
014fe660 1d4d5834 (MethodDesc 1c01df50 +0x114 xxx.MainWindow.Connect()), calling (MethodDesc 1c93d404 +0 xxx.Utilities.SocketHelper.xxxSocket.Connect(System.Net.IPEndPoint))
014fe714 1d4d8d84 (MethodDesc 1c01e094 +0x9c xxx.MainWindow.b__18_0(System.Object, System.EventArgs)), calling (MethodDesc 1c01df50 +0 xxx.MainWindow.Connect())

As you can see from the call stack above, aSocket.ConnectConnection, and finally stuck in the unmanagedmswsock!SockDoConnectRealIn terms of method, it should be caused by the socket’s failure to connect. Since it’s a socket, it’s better to take out its IP and port and Telnet, right? It can be used!dsoFind out all the managed objects in the current thread stack.

0:000> !dso
OS Thread Id: 0x4ee0 (0)
ESP/REG  Object   Name
014FE4D8 03a47588 System.Net.SafeCloseSocket+InnerSafeCloseSocket
014FE598 03a476bc System.Net.EndpointPermission
014FE5E4 03a4762c System.Byte[]
014FF068 03681374 System.AppDomain
014FF4D8 03681238 System.SharedStatics
014FE6B4 036a4dfc System.String    9901
014FE6C4 036a4ba0 System.String    192.168.1.79

Haha, we can see from the last two lines that the socket address is:192.168.1.79:9901Telnet doesn’t work. After asking, it turns out that the tester has recently been rebooted, and the socket server has not been started. It seems that the problem has been found…

Is there something wrong with it? Yes, that’s why we do it in the main threadConnectWhat about it? If the socket can’t connect, it’s just like putting yourself in the wrong position. I asked about the implementation and said that WPF and socketserver are deployed together. It’s said that they occasionally encounter it on site. Maybe they have stepped on too many holes and found out by themselves. They can restart the sockerserver and get it done. However, they may not be able to see it this time, It’s a real scandal…

2. Check the problem code

The problem still needs to be solved. First, export the problem code and use the!name2ee + !savemoduleThat’s it.

0:000> !name2ee *!xxx.MainWindow.Connect
Module:      01754044
Assembly:    xxx.exe
Token:       06000af5
MethodDesc:  1c01df50
Name:        xxx.MainWindow.Connect()
JITTED Code Address: 1d4d5720
0:000> !savemodule 01754044  E:\dumps.dll
3 sections in file
section 0 - VA=2000, VASize=3835b4, FileAddr=200, FileSize=383600
section 1 - VA=386000, VASize=3520, FileAddr=383800, FileSize=3600
section 2 - VA=38a000, VASize=c, FileAddr=386e00, FileSize=200

Then open it with ilspy3.dll, see the simplified code as follows:

private void Window_Loaded(object sender, RoutedEventArgs e)
	{
		Connect();
	}

        private bool Connect()
	{
		string ipString = ConfigurationManager.AppSettings["ServerSocketIp"];
		IPAddress address = IPAddress.Parse(ipString);
		IPEndPoint iPEndPoint = new IPEndPoint(address, Convert.ToInt32(ConfigurationManager.AppSettings["ServerPort"]));
		sockClient = (xxxSocket)(object)new xxxSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
		try
		{
			sockClient.Connect(iPEndPoint);
			((Socket)(object)sockClient).IOControl(IOControlCode.KeepAliveValues, KeepAlive(1, 1000, 1000), (byte[])null);
			sockClient.add_RecievedMessage((EventHandler)sockClient_RecievedMessage);
		}
		catch (SocketException ex)
		{
		
			return false;
		}
		return true;
	}

It’s very clear that the main thread didConnectOperation, this is taboo… Maybe this socket code was also found on the Internet. I didn’t pay much attention to it…

3: Summary

After knowing the cause and effect, the optimization method is relatively simple.

  • Put connect into task. Run and release the main thread. It’s simple and crude,
private async void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Task.Run(()=> { Connect() });
        }
  • Using async, await

In this1+1In the era of asynchronous writing, it’s really out of date… I don’t have to think about how to use it hereXXXAsyncThe family’s gone.

private async void Window_Loaded(object sender, RoutedEventArgs e)
        {
            string address = "192.168.1.79";
            int port = 9901;

            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.SendTimeout = 1000 * 10;
            socket.ReceiveTimeout = 1000 * 10;

            await socket.ConnectAsync(address, port);

           //....
        }

This real case is very simple, difficulty level 0, do not know you learned it? In fact, I sometimes sigh that this kind of case will be solved by WinDbg in three minutes, instead of the first morning.

More high quality dry goods: see my GitHub:dotnetfly

图片名称