Xtrace fakes an X server and forwards all connections to a real X server, displaying the communication between the clients and the server in an (well, thoretically) human readable form.
For further reference take a look at the manpage (current version).


xtrace -D:9 -d:0 -k
starts xtrace as server :9, forwarding to server :0 and not terminating after a client exited.
Then connect with some client (in some other terminal) to the new server, for example with
xclock -display :9
and see the trace of the connection showing up. Let's take an easier example first, the output for
xlsfonts -display :9 -fn fixed
Got connection from unknown(local)
000:<: am lsb-first want 11:0 authorising with 'MIT-MAGIC-COOKIE-1' of length 16
000:>: Success, version is 11:0-60897.
First xtrace tells it got a connection via a local UNIX socket. The client wants the protocoll to be little endian, an X11 server and authorized with a MIT-MAGIC-COOKIE-1 token. The server sucessfully replies. (It actually sends a lot more of information but that is not printed here). Then it is time for the the client to send requests:
000:<:0001: 20: Request(55): CreateGC cid=0x01600000 drawable=0x00000131  values={background=0x00ffffff}
000:<:0002: 20: Request(98): QueryExtension  name='BIG-REQUESTS'
000:<:0003: 24: Request(20): GetProperty delete=false(0x00) window=0x00000131 property=0x17("RESOURCE_MANAGER") type=0x1f("STRING") long-offset=0x00000000 long-length=0x05f5e100
Note that the reply for request two only comes after request 3 was sent. This is because the X protocoll is asynchronous and typical Xlibs do not even sent out requests before enough piled up or the application asks to flush them out. Also note that request 1 did not get any reply, only some requests get replies, and the type of the request specified whether it will get a reply:
000:>:0x0002:32: Reply to QueryExtension: present=true(0x01) major-opcode=131 first-event=0 first-error=0
000:>:0x0003:1528: Reply to GetProperty:  type=0x1f("STRING") bytes-after=0x00000000   data='*Keyboard.keyboard:\tPC105DE\n*XYZ*Foreground:\tyellow\n*XYZ*Tek4014.Translations:\t#override <Key>F1: string("bla")\n*customization:\t-color\n*reverseVideo:\ton\n*scrollKey:\tTrue\n*scrollTtyOutput:\tFalse\nScrollbar.JumpCursor:\tTrue\nXBuffy*Background:\tdarkblue\nXBuffy*Bordercolor:\tgreen\nXBuffy*Foreground:\tyellow\nXBuffy*background:\tdarkblue\nXBuffy*bordercolor:\tdarkgreen\nXDvi.urlBase:\tfile://localhost/\nXDvi.wwwBrowser:\tmozilla-firefox\nXLess*Background:\tlightgray\nXLess*Foreground:\tblack\nXLock*dpmsstandby:\t-1\nXLock*dpmssuspend:\t-1\nXLock*logoutButton:\t15\nXLock*mode:\troll\nXLock*modelist:\troll\nXTerm*Background:\tdarkblue\nXTerm*Bordercolor:\tyellow\nXTerm*Foreground:\tyellow\nXTerm*Tek4014.Translations:\t#override <Key>F1: string("bla") string(0x0d)\nXTerm*VT100*colorBD:\twhite\nXTerm*VT100*colorBDMode:\ton\nXTerm*VT100*colorUL:\tmagenta\nXTerm*highlightSelection:\ttrue\nXTerm*saveLines:\t5000\nXTerm*scrollBar:\ttrue\nXTerm.VT100*colorULMode:\ton\nXTerm.VT100*dynamicColors:\ton\nXTerm.VT100*underLine:\toff\nXTerm.VT100.titeInhibit:\ttrue\nXclock*Hands:\tyellow\nXfm*Background:\tdarkgray\nXfm*Foreground:\tblack\nXfm*SimpleMenu.Translations:\t#override <Btn2Down>: notify() unhighlight() popdown() \nXfm*defaultEditor:\tnedit\nXpdf*reverseVideo:\toff\nemacs*Background:\tDarkSlateGray\nemacs*Foreground:\tWheat\nemacs*bitmapIcon:\ton\nemacs*cursorColor:\tOrchid\nemacs*font:\tfixed\nemacs*pointerColor:\tOrchid\nemacs.geometry:\t80x25\nmaxima*Foreground:\tblack\nxdvi*Background:\twhite\nxdvi*Foreground:\tblack\nxpdf*reverseVideo:\toff\nxterm*reverseWrap:\ttrue\n'
000:<:0004:  4: BIG-REQUESTSRequest(131): BigReqEnable 
Now the client sent a special request, that is not in the basic set. For that it asked before if that extension exists and what number it has, which xtrace also parsed so it now knows that it is a request to allow big requests, whcih the server answers with the new maximum size of requests allowed:
000:>:0x0004:32: Reply to BigReqEnable: maximum-request-length=1048575
000:<:0005: 20: Request(98): QueryExtension  name='XKEYBOARD'
000:>:0x0005:32: Reply to QueryExtension: present=true(0x01) major-opcode=152 first-event=111 first-error=178
000:<:0006:  8: Request(152): unknown
As xtrace does not yet know the XKEYBOARD extension, it does not know which request this is, so it calls it "unknown" and cannot show what fields it contains. (xtrace has a --denyextensions option to modify the answer to every query for a extension, so the client thinks those do not exist and thus hopefully uses replacements within the known set.) It also does not know if that request will get a reply. But as it does not know what the reply means, it assused it has no reply and thus complains when it gets one:
000:>:0006:32: unexpected reply
000:<:0007: 16: Request(49): ListFonts max-names=0x03e8  pattern='fixed'
000:>:0x0007:104: Reply to ListFonts:  names={ s='fixed'},{ s='-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso8859-1'};
Finally the actual request (everything else is just a artefact of xlib being used, to only list the known fonts none of them would have been needed) is sent out and got a reply. (If you are using a version of xtrace up to 0.5 you wll only see the number of replies with this reply instead of the list, as that was overlooked before).

Getting xtrace

Further Reading

The best description for the X protocol easily found are the X Specifications, for example found as /usr/share/doc/xspecs/proto.html on your Debian host if xspecs is installed.

Why the name is stupid

xtrace is actually a stupid name for this program, as so much other things call itself xtrace. But as none of those other things have anything todo with X, I think I will keep that name. (So it better fit into the scheme started by strace and ltrace). Or perhaps I will rename it to x11trace...