Using Cscope and SilentBob to analyze source code


Author: Aleksey 'LXj' Alekseyev

When you start learning the source code of an unfamiliar project, you don’t have the knowledge of its structure or the meaning of specific functions, classes, and units in the project. You can use tags to browse for definitions, but it’s hard to get an overall picture by just looking through every definition one by one. Cscope and SilentBob are two tools that can help you analyze unfamiliar source code. They help you find symbol definitions, determine where specific functions are used, determine which functions are called by other given functions, and search for strings and patterns throughout the code base. With them, you can save time by doing fast, targeted searches instead of grepping through source files by hand.

Using Cscope

Cscope is a popular utility, and most modern distributions include it. Although Cscope was originally intended only for use with C code, it actually works well with languages like C++ and Java. Cscope comes with an ncurses-based GUI, but it also supports a command-line interface to communicate with other application that can be used as front ends, including major editors such as Emacs and Vim.

When you invoke Cscope, it scans source files in the current directory and stores the information it collects in its internal database. You can use the -R option for Cscope to scan subdirectories recursively. If you don’t want to use Cscope’s GUI, but want to query its database from another application instead (as described below), use the -b option. If you’re using Cscope on a large or system-related project, consult this guide for additional instructions on how to optimise Cscope to work faster with big sets of files.

By default the GUI front end is activated automatically after you generate the database (or you can use the -d option to tell Cscope to use a database that has already been generated). It has a two-panel interface; you enter search queries in the bottom panel, and results are displayed in the top. You can press Tab to switch between the panels and Ctrl-D to exit the program.

In the bottom panel, use the arrow keys to move between search fields. Using this panel, you can:

  • find occurrences of a specified symbol;
  • find a global definition (if the search is successful, the editor will be launched at once)
  • find functions called by a specified function
  • find functions calling a specified function
  • search for a text string in all source files
  • replace a string
  • search for an egrep pattern
  • open a specified file in an editor
  • find files that #include a specified file

Every time you perform a search, Cscope displays each result with a number, the file name, the name of the function (when applicable), the line number, and the line of code itself. If you select one of the results with the arrow keys and press Enter or the appropriate number key, Cscope will launch the default system editor (set by the EDITOR environment variable) for this file with the cursor positioned on the appropriate line (this may not work for unsupported editors, but Emacs and Vim behave properly).

Using (X)Emacs as the front end

Using Cscope with Emacs is easy. If you don’t already have commands starting with cscope- and a Cscope menu available in Emacs, you can integrate Cscope with Emacs by installing xcscope.el. You can obtain it from the contrib/xcscope subdirectory of the Cscope source tarball. Just copy the cscope-indexer script into some convenient directory in $PATH and the xcscope.el file to some directory where Emacs can find it (consult the load-path variable — ``C-h v load-path`` in Emacs). Then add the line (require 'xcscope) to ~/.emacs or ~/.emacs.d/init.el.

The comments in the first lines of the xcscope.el file serve as the documentation.

The package adds all the Cscope search commands to the Cscope submenu on the Emacs menu and makes them available via key bindings when you’re editing source files. For example, if you want to look for a symbol, either select Cscope -> Symbol in the menu, or type M-x cscope-find-this-symbol, or press C-c s s, then enter the symbol name (the word that is under the cursor will be used if you enter nothing).

Search results will be presented in the *cscope* buffer, grouped by file name and shown as <function name>[<line number>] <line>. Moving the cursor to one of the search results and pressing Space will open the appropriate file in another buffer and move the cursor to the appropriate line. If you press Enter instead of Space, you will also switch to this buffer from the *cscope* buffer (or you can mouse-click on the relevant search result). You can press n or p to select the next or previous search result (or C-c s n and C-c s p when the *cscope* buffer is not active). N or P will select the next or previous file (or C-C s N and C-c s P respectively).

Using Cscope from Vim

If you prefer Vim to Emacs, Cscope still has you covered. First of all, your version of Vim should be compiled with the --enable-cscope option. Most binary Linux distributions do so. Gentoo users should enable the cscope USE flag. I’ll assume that you have Vim 6.x or 7.x. The Vim reference manual contains an article on using its Cscope interface; you might find it at /usr/share/vim/vim&version/doc/if_cscop.txt. You can also read a brief tutorial on using Cscope with Vim.

In Vim, you invoke Cscope search commands in this fashion: :cscope find search typesearch string (you can type :cs f instead of cscope find if you want), where search type can be

  • symbol or s — find all references to a symbol;
  • global or g — find a global definition
  • calls or c — find calls of the specified function
  • called or d — find functions that the specified function calls
  • text or t — find some text
  • file or f — open a file
  • include or i — find files that #include the specified file

Search results will be displayed as a menu at the bottom of your Vim window. You can type the number of the search result you want to jump to and press Enter. If you use the :scscope or :scs command instead of :cscope, your Vim window will split in two horizontally and the Cscope search result you choose will be put in the new window.

In Vim, jumping to a result from a Cscope query is just like jumping to any tag; you can use Ctrl-T to pop back to where you were before the search and use :tnext and :tprevious to cycle through results.

If you want to invoke search commands for the words under the cursor, you should install the cscope_maps.vim plugin (just stick this file in the $HOME/.vim/plugin directory). Documentation for the plugin is contained in its file as comments. When using this plugin you type Ctrl- instead of :cscope and Ctrl-spacebar instead of :scscope, and the search will be processed for the word that is under the cursor (e.g. if you move the cursor to the word “initialize” and type Ctrl- s you will invoke the search for references to the symbol “initialize”).

Next: SilentBob


Why using SilentBob for generating tags?

SilentBob uses syntax analysis to parse source files, which makes it faster than a utility like Exuberant Ctags, which uses regular expressions to locate the appropriate line in a file. In a test run on the sources for the Linux kernel (version 2.6.19) Exuberant Ctags generated the tags table in 90 seconds, while SilentBob did its job in 10 seconds (on a 2.6MHz Celeron). SilentBob also supports multithreading optimizations.

Another difference is the format of tags tables: In tables created by Exuberant Ctags, regular expressions are used to locate the appropriate line in a file. This means that if you edit the file, and the position of some definition is changed, you don’t need to rebuild the tags table. This is good for Exuberant Ctags because it means you don’t need to regenerate the table often, but it also means that if you are viewing huge files, jumping to a tag definition will be much slower than if line numbers were used. That’s why SilentBob uses line number in its tags tables; however, that means you have to update the tags table after every major edit.

SilentBob is another new tool for analyzing source code. It currently supports C/C++, Perl, and Python, but its plugin framework (which is not documented at the moment) enables users to add support for new languages and other features easily.

You can get source tarball and deb packages from the downloads section of the software’s site. After installation, invoke SilentBob three times in the directory with source code:

  bob --make-ctags
  bob --cfiles
  bob -L cfiles --call-tags

This will generate three files: tags, the common tags table; cfiles, a list of C/C++ files; and call_tags, the call tags table. The call tags table contains tags for function calls, so if you want to find all the calls of some function, you can point Vim to use the call tags table (:set tags=./call_tags) and use the same commands as for searching tags (:tag function-name and :tnext and :tprevious to cycle through results). These index files can be used by SilentBob to build call trees and backward call trees.

For Perl and Python, only tags tables and file lists are supported:

  bob <--perl | --python> --make-ctags
  bob <--perl | --python> --files

The second command will generate a perl_files or python_files file.

Once you have a tags table, SilentBob can show you a call tree:

  bob [--depth N] function

--depth option allows you to limit the depth of the tree. If the only thing you need to know is which functions are called by function, specify --depth 0. Otherwise, you will be shown which functions are called by functions called by functions (…) called by function.

Note that you can use this option with the tags table generated by SilentBob for Python and Perl files. Tables generated by Exuberant Ctags are not supported.

The call tags table is used to generate a backward call tree:

  bob [--depth N] -u function

This will show functions which call functions (…) which call function. In this case specify --depth 1 to see only functions which call function

SilentBob can also use the created cfiles file to search for text within C/C++ code. It checks operators; strings and comments are ignored.

  bob list of files --cgrep pieces of text, separated by comma

You can specify -L ./cfiles to use generated file list. Pieces of text should be from one operator, so if you are looking for testing of T variable, use:

  bob -L ./cfiles --cgrep if,T

SilentBob also includes a tags utility that lets you to view the tag definitions in a C/C++ file in a console. Invoke tags tag1 tag2 ... tagN to strip the fragments of code you are interested in from code — function definitions, global variable declarations, etc. — and you will see the fragments of code you need.