Valgrind-mmt is a Valgrind modification which allows tracing application accesses to mmaped memory (which is how userspace parts of graphics drivers communicate with hardware). It was created by Dave Airlie and then extended/fixed by others.

Installation:

git clone https://github.com/envytools/valgrind.git
git clone https://github.com/envytools/VEX.git valgrind/VEX
cd valgrind/VEX
git checkout -f VEX_3_10_BRANCH
cd ..
git checkout -f mmt-3.10
./autogen.sh
./configure --prefix=...
make
make install

Update:

cd valgrind
git pull
cd VEX
git pull
cd ..

Use:

valgrind --tool=mmt --mmt-trace-file=/dev/nvidia0 --mmt-trace-file=/dev/nvidiactl --mmt-trace-nvidia-ioctls --log-file=file-bin.log glxgears

Options:

--mmt-trace-file=path        trace loads and stores to memory mapped for this file (e.g. /dev/nvidia0) (you can pass this option multiple times)
--mmt-trace-all-files        trace loads and stores to memory mapped for all files
--mmt-trace-nvidia-ioctls    trace nvidia ioctls on /dev/nvidiactl
--mmt-trace-nouveau-ioctls   trace nouveau ioctls on /dev/dri/cardX
--mmt-trace-all-opens        trace all 'open' syscalls
--mmt-trace-marks            send mmiotrace marks before and after ioctls
--mmt-trace-stdout-stderr    trace writes to stdout and stderr
--mmt-ioctl-create-fuzzer=   0-disabled (default), 1-enabled (safe), 2-enabled (unsafe)
--mmt-object-ctr=class,cargs sets the number of u32 constructor args(dec) for specified class(hex)
--mmt-ioctl-call-fuzzer=     0-disabled (default), 1-enabled
--mmt-sync-file=path         emit synchronization markers in output stream and wait for replies from specified file

What to do with generated trace:

You can decode (binary) trace by:

./demmt -l file-bin.log

or translate it to text:

./mmt_bin2dedma < file-bin.log > file-txt.log

and feed it to dedma:

./dedma -m nvXX -v $(mapid) file-txt.log | less -SR

where $(mapid) is a buffer number. Note that dedma is a pretty basic tool and have unfixable bugs. Using demmt is recommended.

All decoding tools (demmt, mmt_bin2dedma and dedma) are from envytools repository.

Note: Valgrind-mmt used to output in purely text form (dedma-compatible). However, due to binary output being way more powerful, the text output was removed.

All demmt options:

  -l file  use "file" as input (can be compressed by: xz, bzip2, gzip)
  -q        print only the most important data (quiet); shortcut for: -d all -e pb,shader,macro,tsc,tic,cp,classes=all,buffer-usage,nvrm-class-desc
  -m chipset    set chipset version (default: auto-detect based on trace content and file name (nvXX...))
  -c 0/1    disable/enable colors (default: 1 if stdout is a terminal)
  -g 0/1    = -d/-e gpu-addr (default: 0)
  -o 0/1    = -d/-e ioctl-raw (default: 0)
  -r 0/1    = -d/-e macro-rt-verbose (default: 0)
  -p 0/1    disable/enable pager (default: 1 if stdout is a terminal)
  -i 0/1    disable/enable log indentation (default: 0)
  -a        = -d classes=all
  -s file   in response to sync markers in input file: flush the output stream and reply by writing marker id to specified file (see: scripts/mmiotrace/mmt-app-demmt-mmiotrace.sh)

  -d msg_type1[,msg_type2[,msg_type3....]] - disable messages
  -e msg_type1[,msg_type2[,msg_type3....]] - enable messages
     message types:
     - write - memory write
     - read - memory read
     - gpu-addr - gpu address
     - mem = read,write
     - pb - push buffer
     - class=[all,0x...] - class decoder
     - tsc - texture sampler control block
     - tic - texture image control block
     - vp - vertex program
     - fp - fragment program
     - gp - geometry program
     - cp - compute program
     - tep
     - tcp
     - shader = vp,fp,gp,tep,tcp
     - macro-rt-verbose - verbose macro interpreter
     - macro-rt - macro interpreter 
     - macro-dis - macro disasm
     - macro = macro-rt,macro-dis
     - sys_mmap
     - sys_mmap_details - prot & flags
     - sys_munmap
     - sys_mremap
     - sys_open
     - sys_write
     - sys = sys_mmap,sys_munmap,sys_mremap,sys_open,sys_write,ioctl-desc
     - ioctl-raw - raw ioctl data
     - ioctl-desc - decoded ioctl
     - ioctl = ioctl-raw,ioctl-desc
     - nvrm-ioctl=[all,name] name=create,call,host_map,etc...
     - nvrm-mthd=[all,0x...] - method call
     - nvrm-handle-desc - handle description
     - nvrm-class-desc - class description
     - nvrm-unk-0-fields - unk zero fields
     - nvrm-obj-tree - object tree after create and before destroy ioctls
     - nvrm = nvrm-ioctl=all,nvrm-mthd=all,nvrm-handle-desc,nvrm-class-desc
     - buffer-usage
     - msg - textual valgrind message
     - info - various informations
     - all - everything above

Some notes about tracing Xorg:

  • Valgrind can't trace suid binaries, so you have to copy Xorg, unset its suid bit, and trace that file.
  • Because of the above, you need to be root when tracing.
  • X and Xorg sometimes are not the same file. Make sure you are tracing the correct file.
  • Running plain Xorg binary won't load any window manager, so before tracing, start on another console:
    export DISPLAY=:0; while [ true ]; do xterm; sleep 1; done
  • When Valgrind crashes, Xorg leaves only black screen - you need to press Alt-SysRq-R, switch to some free console (ctrl-alt-fxx), blindly login and start normal Xorg (startx)