Debugging X11 with tcpdump
X11 is designed as client-server mode. The communication between the X client and server complies with TCP protocol. Recently I have a Windows X server VcXsrv installed on my Windows 10 and I debug an OpenGL demo glxgears on the WSL2 with tcpdump.
Environment
WSL2 is equipped with its own networking interface like a virtual machine.
1 | $ ifconfig |
1 | $ /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -C "ipconfig" |
After starting up the X server VcXsrv, you need to export the environmnet variable DISPLAY on the WSL. In case of the vEthernet configuration changed after rebooting you’d better do it like this:
1 | export DISPLAY=$(grep 'nameserver' /etc/resolv.conf | awk '{print $2}'):0 |
NOTE: The Firewall between the host and WSL2 must be disabled or your X client can not connect VcXsrv.
Debugging
I trace the demo glxgears using gdb and tcpdump at the same time.
- gdb
1 | gdb -q -tui glxgears |
- tcpdump
1 | sudo tcpdump -vvX not icmp and not arp and not udp and portrange 37900-37999 -w x110224.pcap |
- vv: verboser than -v
- X: show the packet’s content
- not icmp: filter out icmp packets
- not arp: filter out arp packets
- not udp: filter out udp packets
- portrange 37900-37999: listening on the ports from 37900 to 37999
- w x110224.pcap: save the packet captures into the file
1 | tcpdump -X -r x110224.pcap |
1 | reading from file /home/luc/github/x110224.pcap, link-type EN10MB (Ethernet) |
what codes sends and receives these packets? The first two twenty-byted segments are IP header (20 bytes without option) and TCP header (20 bytes without option) separately in these packets.
The source code snippet:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24Bool
XQueryExtension(
register Display *dpy,
_Xconst char *name,
int *major_opcode, /* RETURN */
int *first_event, /* RETURN */
int *first_error) /* RETURN */
{
xQueryExtensionReply rep;
register xQueryExtensionReq *req;
LockDisplay(dpy);
GetReq(QueryExtension, req);
req->nbytes = name ? strlen(name) : 0;
req->length += (req->nbytes+(unsigned)3)>>2;
_XSend(dpy, name, (long)req->nbytes);
(void) _XReply (dpy, (xReply *)&rep, 0, xTrue);
*major_opcode = rep.major_opcode;
*first_event = rep.first_event;
*first_error = rep.first_error;
UnlockDisplay(dpy);
SyncHandle();
return (rep.present);
}
The gdb log:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35Starting program: /mnt/c/Users/lulu/Documents/github/demos/src/xdemos/glxgears
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, XextAddDisplay (extinfo=0x7ffff7e77380 <_dri2Info_data>, dpy=0x55555555d2a0,
ext_name=0x7ffff7e76780 <dri2ExtensionName> "DRI2", hooks=0x7ffff7e767a0 <dri2ExtensionHooks>, nevents=0, data=0x0)
at extutil.c:103
XInitExtension (dpy=dpy@entry=0x55555555d2a0, name=name@entry=0x7ffff7e76780 <dri2ExtensionName> "DRI2")
at InitExt.c:44
XQueryExtension (dpy=dpy@entry=0x55555555d2a0, name=name@entry=0x7ffff7e76780 <dri2ExtensionName> "DRI2",
major_opcode=major_opcode@entry=0x7fffffffd984, first_event=first_event@entry=0x7fffffffd988,
first_error=first_error@entry=0x7fffffffd98c) at QuExt.c:39
$2 = {
reqType = 0x62,
pad = 0x0,
length = 0x3,
nbytes = 0x4,
pad1 = 0x0,
pad2 = 0x0
}
$3 = {
type = 0x1,
pad1 = 0x0,
sequenceNumber = 0xa,
length = 0x0,
present = 0x0,
major_opcode = 0x0,
first_event = 0x0,
first_error = 0x0,
pad3 = 0x0,
pad4 = 0x0,
pad5 = 0x0,
pad6 = 0x0,
pad7 = 0x0
}
$2 is Request packet content to VcXsrv, $3 is Reply packet content from VcXsrv. Even that we can notice the three-way handshake of TCP from the zero-lengthed packet in x110224.pcap.