Skip to content
Fangrui Song edited this page Jun 17, 2018 · 14 revisions

First of all, use a --variant=debug Build. It is compiled with -O0 -g while the default --variant=release is -O3 -g.

Logs

--log-file=/tmp/cq.log

Dump LSP requests/responses

Alternatively, enable logs and pass the --log-all-to-stderr option to the ccls executable (bin/ccls --log-file=/tmp/cq.log --log-all-to-stderr). You can find stderr output in:

  • LanguageClient-neovim: /tmp/LanguageServer.log (default)
  • Emacs lsp-mode: *lsp-ccls stderr* buffer. They will also go to *message* buffer if (setq lsp-print-io t)
  • VSCode: TODO

You can also use sysdig on Linux:

sudo sysdig -As999 --unbuffered -p '%evt.type %proc.name %evt.buffer' "proc.exe contains ccls and fd.type=pipe" | egrep -v '^Content|^$'

Stopping at the start to debug early issues

To debug individual LSP requests, you can attach your debugger after ccls has done indexing. However, for many other issues, such as project file loading (project.cc) and C/C++ parsing and indexing clang_indexer.cc, you need to set an early breakpoint to be able to trace the code.

It is recommended to use LanguageClient-neovim for debugging (even if you use Emacs or VSCode) because it can be started with simple shell command.

# vimrc
nn ,al :LanguageClientStart<cr>
rm -r /tmp/ccls && CQUERY_TRACEME=1 nvim a.cc +'normal ,al'

The Neovim buffer will hang there because CCLS_TRACEME=1 causes the ccls process to SIGTSTP itself. In another shell, gdb -p $(pgrep -fn debug/ccls)

Poor man's breakpoint

Insert an infinite loop volatile static int z=0;while(!z); somewhere and ccls will stop there. Attach to the ccls process with gdb -p $(pgrep -fn ccls). Set some breakpoints, use print commands, and execute p z=1 for continuing.

When setting breakpoints, if several threads may stop on the same breakpoint (e.g. concurrent indexer threads), execute set scheduler-locking on.

Using a debugger

Cache files are deleted to avoid possible issues related to stale cache. CCLS_TRACEME=1 causes the ccls process to stop at the start of main(). You may attach to the process with:

  • gdb -p $(pgrep -fn ccls). Invoke signal SIGCONT if you want ccls to continue running after detaching of gdb.
  • lldb -p $(pgrep -fn ccls). Invoke pro sig SIGCONT when the process resumes (with a c command) if you want ccls to continue running after detaching.

libclang or indexer callback issues

export LIBCLANG_DISABLE_CRASH_RECOVERY=1 disables libclang crash recovery. In case of libclang or indexer callback issues, you will not see libclang: crash detected in the log file but get a process crash. And if you attach a debugger before it crashes, you can get the stack trace.

Developing

Diagnose whether requests are handled correctly

Set breakpoints in src/messages/* files. They are inbound message handlers.

Clone this wiki locally