Explore C Code With GNU Tools

This post will introduce three GNU tools to help you explore your C code: ctags, cscope, and cflow. The first two can help you navigate your code as you work on it and can be used directly within Vim. Cflow on the other hand produces control charts that help you get to know the control flow in a project, which is particularly helpful if you are new to the codebase.

ctags

In short, ctags is a program that can generate a file listing C symbols in a way that can be used by Vim (ctags) or by Emacs (etags). Various versions exist. See this wiki page for some links. A current maintained version of universal ctags can be found on GitHub. Universal ctags expands on the original ctags by including support for additional languages.

The first step to using a ctags file is to generate one for your source code. Just run ctags followed by the location of your source files. If you have multipled directories, you can list them sequentially like this:

$ ctags h/* src/*

This will generate a “tags” file in the current folder. If you open Vim from the same folder, the tags file is automatically loaded. What is particularly useful about the tag file is that saved keywords are addressed by patterns, not line numbers. This way, minor edits don’t require ctags to be re-run.

Basic Usage

To find the definition of a C symbol in your source code, put your cursors on the symbol and press <Ctrl-]> to jump to that symbol’s definition. To get back to where you were, press <Ctrl-t>. If the symbol has multiple definitions and you jumped to the wrong one, try using the :tselect command to bring up a list of all matches.

Command Effect
<Ctrl-]> Jump to definition of the keyword under the cursor.
:ta[g] {ident} Jump to the definition of {ident}.
<Ctrl-t> Jump back up the tag stack.
:tags Show content of tag stack.
:po[p] Jump to older entry in tag stack.
:ta[g] Jump to newer entry in tag stack.
:ts[elect] [ident] List tags that match [ident].
:sts[elect] Same as above, but splits window for tag.

For details, see :help tags in Vim.

cscope

The cscope program has more advanced features compared to ctags. In addition to finding symbol definitions, it can gather more advanced information than ctags. Specifically, it can tell you

  • where a symbol is used in the code,
  • where the symbol was defined,
  • where a variable got its value from,
  • what other functions call this function,
  • what functions are called by a specific function,
  • and more.

Similar to ctags, a database file is created by the csope program. You can run it like this:

$ cscope -b h/* src/*

This will generate a cscope.out file that can be used with Vim. To make the cscope database available, you need to add it during your Vim session by using :cs[cope] add {file|dir}. By adding the following to your .vimrc you can automate this:

if has("cscope")
    " add any database in current directory
    if filereadable("cscope.out")
        cs add cscope.out
        " else add database pointed to by environment
    elseif $CSCOPE_DB != ""
        cs add $CSCOPE_DB
    endif
endif

Basic Usage

The basic command used is :cs find {querynum|querytype} {name}, with the following main query types:

querynum querytype Effect
0 s Find this C symbol
1 g Find this definition
2 d Find functions called by this function
3 c Find functions calling this function
4 t Find this text string
6 e Find this egrep pattern
7 f Find this file
8 i Find files #including this file
9 a Find places where this symbol is assigned a value

For details, see :help cscope in Vim. For some suggested options and keymappings that make using cscope more convenient, see :help cscope-suggestions. You can also use the querynum to perform a single search using the cscope cli interface, e.g.: cscope -L{querynum} {name} [-d] where [-d] suppresses updating the cscope database.

cflow

GNU cflow is a tool that creates charts showing control flow within your program. It has a lot of options and settings, so you’ll definitely want to check out its documentation.

The most basic usage is cflow {file[s]} which creates an indented listing of function calls starting from main(). Two important command line options are --main which allows you to set a different starting function, and --target which allows you to set a target function below which you don’t want to investigate. If you want to include functions that aren’t directly reachable from main() or --main in your chart, use the --all flag.

A particularly nifty feature is that cflow can generate valid dot files using cflow -f dot {file[s]}. These can be piped to graphviz to produce visual charts of your function calls, e.g.:

$ cflow -f dot example.c | dot -Tpng -o flow-example.png
D. Michael Senter
D. Michael Senter
Research Statistician Developer

My research interests include data analytics and missing data.