When we talk about projects in C/C++, or any other programming languages, it is pretty common that as the project advances, the lines of code increases, and hence increases the number of source files in the project. Although, as a developer, it is always intended to make the sources more and more organized, predictable and simple at the same time. Still, we all would agree, when it comes to browsing the code, debugging while moving through the definitions and function calls in different sources, it gives a hard time.
Think of a scenario, where person A developed the project with around hundred of source files, and person B has to add to a feature to it, or maybe just has to fix a bug. Person B would have to understand the basic flow of the sources, and browse through it.Well it is definitely not the first time person B will be developing something on someone else’s sources. Hence, there are many solutions and even alternatives available.
One of the convenient one is to use an IDE. There are many IDE’s available on Linux supporting the various Linux flavors. To name a few of the examples, we have all eclipse with the debugging capability as well, Code::Blocks which is an open source IDE for Linux.
However, generally problem with IDE is you need to create a project, use its compiling tools and do some extra effort to get the project running using that particular IDE. However, many open source projects come up with makefiles which are not so helpful in creating IDE projects. Therefore, it is not so easy always and may not be a practical solution at times.
Another workaround solution could be, just using routine text editor to view and edit the sources, command shell to build the sources using ‘make’ and then use common command line tools like ‘find’ or ‘grep’ to browse through the sources and function definitions. It needs no initial effort, and one can start understanding the sources as soon as they are on our system. However, to determine, where a source is, we use ‘find’ command line tool. As an example, if we need to find where do we have the main.c, we use
$find /home/user01/sources/ -name main.c
And many times, we have to see the definition of a function known through a function call, we use ‘grep’ command line tool. As for example, we want to determine where the function definition ‘displayDeviceCommand()’ lies, we would use
$grep -rs “displayDeviceCommand” /home/user01/sources
The output would include all the function calls, definitions and the declarations. However, it may not be that difficult to determine from the output. But, if we have to do this every time we encounter a function call, it may be a pain.
Another solution is, on which this whole article is dedicated, is CSCOPE.
Introduction & Capabilities
Cscope is a Linux Tool for browsing and exploring symbols, symbol definitions, directory structures and much more. It is a tool which comes with working support along with the Linux editors like Vim and Emac. It was initially built for working with C code, but now the support has been extended for C++ and Java too.
However, cscope is pretty similar to, in fact it adds to the set of features of ctags . Although, the readers need not bother even a bit if they don’t know ctag.
To state the capabilities of cscope tool:
- Look at the function definitions straightway from the function call
- Look out for all the places from where a particular function is being called.
- Searching strings and patterns
- Determining sources which include a specific header
- Besides, it does editor tasks like viewing the source file, replacing strings, etc
Being a Linux tool, let us go by the popular way of installing cscope. The Linux way is to download the complete source, build and then install it.
To begin with, download the complete source of cscope from here. It would be downloaded as a tar.gz file which includes the complete source code.
In my case I have:
To extract the source code out of this tar.gz, use following command:
tar zxvf <cscope_tar.gz_filename>
Hence in my case I use,
tar zxvf cscope-15.8a.tar.gz
I get all the sources extracted and so cscope is ready to be build. Generally, with the sources, we have a text file named ‘INSTALL’, which includes the steps and information to build and install.
To build the downloaded and extracted sources, it uses ‘make’. Therefore use the following sequence of ‘make’ to build and install cscope
$./configure $make $make install
In the end of the sequence, I get
Making install in src make: Entering directory `/home/rupali/cscope/cscope-15.8a/src' make: Entering directory `/home/rupali/cscope/cscope-15.8a/src' test -z "/usr/local/bin" || /bin/mkdir -p "/usr/local/bin" /usr/bin/install -c cscope '/usr/local/bin' make: Nothing to be done for `install-data-am'. make: Leaving directory `/home/rupali/cscope/cscope-15.8a/src' make: Leaving directory `/home/rupali/cscope/cscope-15.8a/src' Making install in contrib make: Entering directory `/home/rupali/cscope/cscope-15.8a/contrib' make: Entering directory `/home/rupali/cscope/cscope-15.8a/contrib' test -z "/usr/local/bin" || /bin/mkdir -p "/usr/local/bin" /usr/bin/install -c ocs '/usr/local/bin' make: Nothing to be done for `install-data-am'. make: Leaving directory `/home/rupali/cscope/cscope-15.8a/contrib' make: Leaving directory `/home/rupali/cscope/cscope-15.8a/contrib' make: Entering directory `/home/rupali/cscope/cscope-15.8a' make: Entering directory `/home/rupali/cscope/cscope-15.8a' make: Nothing to be done for `install-exec-am'. make: Nothing to be done for `install-data-am'. make: Leaving directory `/home/rupali/cscope/cscope-15.8a' make: Leaving directory `/home/rupali/cscope/cscope-15.8a'
which confirms everything completed without error.
Now we have cscope installed with us. The next step is to make it working with our editor. As I’ve a;ready mentioned, cscope comes up with support to the vim editor as well as the emac. However, in this article, we shall pick up vim and from now onwards, all the discussions and examples use vim editor.
cscope with vim
The first and foremost, vim should be built with the option ‘–enable-scope’. Generally it has cscope enabled, however if it doesn’t, one needs to re-configure, re-build and re-install vim.
To check, if cscope was enabled while building vim, run following command.
vim --version | grep "cscope"
One should get a line of the output including
As in, in my case I got
+conceal +cryptv +cscope +cursorbind +cursorshape +dialog_con_gui +diff
Hence, in case you have vim 6.x, just place the downloaded file to
or else, just copy paste the contents of the file to the end of file
This would basically create a database file and add it, so that one can start with browsing of the symbols, etc and some mappings for all kinds of finds.
And now you are all set to use cscope with vim.
Before we jump onto the extensive working hands on, let us first have a look at the cscope usage and be familiar with its available options.
This is what we get as an output to its help.
$ cscope -help
Usage: cscope [-bcCdehklLqRTuUvV] [-f file] [-F file] [-i file] [-I dir] [-s dir]
[-p number] [-P path] [-[0-8] pattern] [source files]
-b Build the cross-reference only.
-C Ignore letter case when searching.
-c Use only ASCII characters in the cross-ref file (don’t compress).
-d Do not update the cross-reference.
-e Suppress the -e command prompt between files.
-F symfile Read symbol reference lines from symfile.
-f reffile Use reffile as cross-ref file name instead of cscope.out.
-h This help screen.
-I incdir Look in incdir for any #include files.
-i namefile Browse through files listed in namefile, instead of cscope.files
-k Kernel Mode – don’t use /usr/include for #include files.
-L Do a single search with line-oriented output.
-l Line-oriented interface.
-num pattern Go to input field num (counting from 0) and find pattern.
-P path Prepend path to relative file names in pre-built cross-ref file.
-p n Display the last n file path components.
-q Build an inverted index for quick symbol searching.
-R Recurse directories for files.
-s dir Look in dir for additional source files.
-T Use only the first eight characters to match against C symbols.
-U Check file time stamps.
-u Unconditionally build the cross-reference file.
-v Be more verbose in line mode.
-V Print the version number.
To get a hands on experience, lets have code base with us. As the purpose is to understand and explore cscope usage, let us take a small code base which is convenient in terms of comprehending the bigger picture scenario as well as browsing through the sources using cscope. Hence, we take the sources of the 7zip utility. We can download the complete Linux source code from here.
For me, I get the source code compressed into a single file:
Extracting this ‘.tar.bz2’ file as
$tar xvjf 7z922.tar.bz2 -C 7zipsources/
We get the complete source code on our system. Please note, the sources downloaded is just for the sake of an example. One can take up any other open source or even better a personal project as well.
Coming back to what I have, my source directory structure from root looks like
$ls 7zipsources/ Asm C CPP DOC
Well, now we have the cscope installed, a set of source files for a particular project to work on. Before we proceed further, let us list down some assumptions in our case.
- The OS is Ubuntu 12.04
- The editor is Vim 7.3
- Working on sources of 7zip utility to browse and understand them through cscope in our own directory ‘7zipsources’.
Next step is to scan all the source files and give it to cscope utility, so that it know what and where all we have. To do so, we first need to scan all files and make a list of all the relevant sources like .c, .cpp, etc. For simplicity of understanding, we are not including the assembly i.e. the asm sources.
$find . -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" > cscope.files
Then we provide this list of source files to cscope. Here is how we do this:
$ cscope -q -R -b -i cscope.files
To go back and revive the cscope options, here is a yet again brief explanation.
- q option is to build an inverted index for faster symbol retrieval.
- R is for recursive traversal through files in a directory
- b is to build a database only and
- i suggests the next argument is a file which includes the list of scanned sources.
And we get an output, a cscope.out as a result of this command.
Next to get into the real browsing of symbols, use command:
The option ‘-d’ is just to let it know not to update the database.
Once you press enter after the above command, you should see following screen.
So as a initial screen of scope, we have lots of options to do. We can find a symbol in the whole source code, a global definition, etc. All the options are self explanatory. However, important is to know how to navigate on this screen. One can use Up and Down Arrows to navigate between choices to do. The cursor moves along with these arrows to indicate where you are. One can type at the current cursor position.
Also, note the blank space above the choices is for the results to be shown once an option is selected.
Since this is an application, there has to be a main() method. And since, the execution starts from the main, we can begin with understanding the code flow with main(). So, to get hold of the main() method, we find the symbol ‘main’. Therefore, we type ‘main’ in from of first choice and press enter.
Find this C symbol: main
The results what we get looks like
So you see, in the results for symbol ‘main’ we see 5 results, with the file name, function, Line number in the corresponding file and the function protocol. With the help of this exercise, we came to know that the the entire source code is not for just one application. It has a GUI version, a command line version of 7zip, another maybe an installer, etc.
The moment we get the results, the cursor now moves to the results section of the screen, and we can navigate through the results using Up and Down Arrows. Press enter to select a particular result. On pressing enter, the corresponding file of the selected result is opened in Vim, and its positioned at the relevant line number. Hence, we directly get to the precise symbol result what we looked at.
With the vim opening the file, one can work on the file as needed and then get back to the cscope choices by exiting the vim editor. To go back to the command prompt, just use key combinations CTRL + d.
From the 5 results, we shall select result number 1 i.e. the one in file 7zmain.c. The choice is absolutely random as the aim is to understand using cscope. Press one arrow down to select result#1 and then press enter. You’ll see the 7zMain.c opened in the Vim editor.
We see an extensive main() method. A screenshot of it is here
Checking the code in main function, we see usage printf’s. From the usage printf’s and argument checks, it seems like the command line 7zip utility.
Go to the function call
To see what this function does, we need to go to its definition. Note, option ‘g’ is for going on to the definition of the input symbol.
At the vim command prompt, we type
:cs find g InFile_Open
And you it takes you directly to the definition of the method InFile_Open(). Following are the screenshots illustrating vim command and function definition.
Now, on pressing enter, it opens the file 7zFile.c which holds the only definition of InFile_Open symbol.
To go back to the previous source file, we came from, use key combination CTRL + t. However, if we have more than one definition for the same symbol name, it lists them down and asks for the choice which one we would like to go.
As for example, if we want to go to the definition of
In the VIM command prompt we type
:cs find g PrintError
And in my case I get a list of 4 definitions of PrintError() in 4 different sources. And it asks me the number, corresponding to which I would like to go
Cscope tag: PrintError # line filename / context / line 1 261 C/Util/7z/7zMain.c <<PrintError>> void PrintError(char *sz) 2 33 C/Util/Lzma/LzmaUtil.c <<PrintError>> int PrintError(char *buffer, const char *message) 3 86 CPP/7zip/UI/Client7z/Client7z.cpp <<PrintError>> static void PrintError(const char *message, const FString &name) 4 94 CPP/7zip/UI/Client7z/Client7z.cpp <<PrintError>> static void PrintError(const AString &s) Type number and <Enter> (empty cancels):
When I type ‘1’ at the input prompt and press enter, there I go to the first listed definition. Check out the following screenshot with the list of 4 definitions and an input prompt.
To have a look at other available cscope commands and their options, we type at the vim command prompt:
And what we get is:
cscope commands: add : Add a new database (Usage: add file|dir [pre-path] [flags]) find : Query for a pattern (Usage: find c|d|e|f|g|i|s|t name) c: Find functions calling this function d: Find functions called by this function e: Find this egrep pattern f: Find this file g: Find this definition i: Find files #including this file s: Find this C symbol t: Find this text string help : Show this message (Usage: help) kill : Kill a connection (Usage: kill #) reset: Reinit all connections (Usage: reset) show : Show connections (Usage: show)
Some time we might get, no cscope connection which implies that there is no database connected. To reset the connections in such a case, we use
A screenshot illustrating reset command
If one checks the cscope vim help, it offers an extensive operations within the editor to browse and understand the source code.
Besides the cscope vim commands, we can also go to a symbol reference through a key combination CTRL + ]
By reference, we mean that anywhere the symbol is used. So, move your cursor to a particular symbol, whose definition/reference one wishes to view. As for example, in our case let us place the cursor in the vim editor to symbol ‘SzArEx_Init’ as in following function call. (Note, we are still in the source file 7zMain.c)
Place your cursor at any of the 11 characters of the symbol name ‘SzArEx_Init’ and press the key combination ‘Ctrl + ]’. It will direction move to the file where it is being defined/referred, irrespective of it being in the same or different source file. In case there are more than one references/definitions of a particular symbol, it lists them down and asks for the input choice for the one the user is interested in.
Modifications to Sources
In case the sources are modified after viewing them through cscope or anytime after the database is created, cscope gets out of sync. As a result, one may see incorrect behaviours of cscope. To set it right, just create the database again update it.
$find . -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" > cscope.files $ cscope -q -R -b -i cscope.files
In this article, we learned about another way to browse through or debug our C/C++ programs in Linux. With the impressive capabilities cscope support, it will help a programmer to understand someone else’s sources. Moreover, it will help to understand the control flow of the source without even the need to build and run the application first.
To think about its usefulness, cscope will be a strength to the programmers working on huge frameworks, or supporting libraries without much relying on the test applications.