Page 1 of 1

C++ gdb displaying regions of strings and vectors

Posted: Sun Jun 25, 2017 3:12 pm
by robinwhittle
Here are some techniques which took me a long time to figure out. Hopefully they will be of interest to other people. Perhaps someone can suggest better approaches.

I am using Codelite 10.0.0, gcc 4.9.2 and gdb 7.7.1 on 64 bit Debian 8. Codelite automatically configures pretty printing of containers library (AKA STL) vectors, maps and deques etc. (Pretty printing enables gdb to output the actual values in vectors etc. in a way which enables Codelite to display them clearly. Without this, all or most of what we see is a bunch of internal pointer values which are not the values we are interested in.) I have Settings > GDB Settings > GNU gdb debugger > General > Display > Enable GDB Pretty Printing = Yes. One important settings which affects the Locals and Watch display of variables is the item Settings > GDB Settings > GNU gdb debugger > General > Display > Number of elements to display for arrays/strings. This affects vectors etc. as well.

There are at least two reasons for wanting to be able to display (in the Watch window) regions of long variables, such as long strings or vectors with numerous elements. Firstly, to conserve precious space within the Watch window. (It would be great if there were several watch windows, each able to exist in their own independent resizable window, rather than being a tab amongst many other tabs in the Debugger window (which for me is a separate window I can resize and move, separate from the main Codelite window.)

Secondly to improve response time when stepping through a program. I find gdb's containers library pretty printing system to be very slow. It can take seconds for it to work with a vector with a small (~4) number of elements, where those elements are themselves class objects with further containers library objects, further class objects etc. (This is with the python script Codelite correctly chooses and puts in ~/.codelite/gdb_printers/libstdcxx/v6/printers.py being compiled into a file there printers.pyc - so I assume that gdb is somehow using this compiled version.)

A further reason with strings is that even if the Watch window is made very wide, such as spanning several monitors, which is highly inconvenient, it is impossible to see past a few hundred characters.

The solution for strings is easy. To display characters 5000 to 5099, the Watch line's text is:

Code: Select all

ssss.substr(5000, 100)
For vectors, it is trickier. The gdb manual section 10.4 "Artificial Arrays" https://sourceware.org/gdb/current/onli ... rrays.html describes a binary operator '@' which can be used to display a subset of an array arrA , such as elements 3 to 6, with:

Code: Select all

arrA[3] @ 4
However, from the Codelite Watch windows, this does not work for vectors, or as far as I know any other containers class objects. (Codelite displays the data structure of the four elements, but not the value of the elements within.)

(One gotcha of asking gdb to display an element of a vector with operator [] is that (as far as I know) this will fail if the source program does not have code for this operator for vectors of objects of this class. One way to fix this is ensure that the program actually uses this operator somewhere. Most programs do, but I ran into this problem when I had written enough of the program to push_back() elements into the vector, but had not written anything to access them. gdb displayed - this was visible in the Value section of the Watch line - "Could not find operator[]". Further discussion of this is at: https://stackoverflow.com/questions/241 ... d-operator )

The solution I found came in part from a comment by bcumming in https://stackoverflow.com/questions/253 ... tor-in-gdb . My outer vector is "slots" and each object within it has multiple strings, ints, bools etc. and two objects of a second class, (one of them called "log") which also contain strings, bools etc. and a vector of strings.

This incantation (which works fine even if the compiled program doesn't contain code for the vector's operator [ ]) enables me to view a sub-set of the elements in the outer vector, starting at 3 and displaying four elements:

Code: Select all

*(slots._M_impl._M_start + 3) @ 4
bcummings' suggestion leads to a simpler version, which does require the operator [ ] code be compiled into the inferior:

Code: Select all

*(&slots[3]) @ 4
The Watch window shows the results initially as one line, which I can expand (by clicking the right-pointing triangle) into four lines 0 to 3. Each of these can similarly be expanded perfectly into pretty printed (intelligible values, not inner gobbledegook) displays of all the elements of each object, and the elements of the other class of objects which it contains. This works perfectly with Codelite. Below are some alternatives and elaborations.

The alternative is prompted by me not being able to find a way of using the above construct on the outer vector, whilst having gdb display only the "log" element of each of the four selected outer vector elements. Again, this is to reduce gdb effort and delay times and to reduce clutter in the Watch window. One way of doing this is rather manual and clunky. It involves four separate Watch lines, each with the appropriate code such as "slots[3].log". A better way of doing this, assuming I want a range which is easily steerable to different starting points, is to add some static variables to the program (global, outside any function) which are purely for use by gdb. Then, using the Codelite's facility for giving commands to gdb, I can set these variables as I wish, and use them in Watch lines to display a steerable range of items. This can be used just for the elements of the outer vector, but we already have a good way of doing that. This alternative technique still requires a handful of lines in the Watch window, manually entered - as many as the range I want - but it is easily steerable.

The variables are:

Code: Select all

    static int XX = 0;
    static int YY = 0;
    static int ZZ = 0;
The text for the four Watch lines is:

Code: Select all

slots[XX + 0].log
slots[XX + 1].log
slots[XX + 2].log
slots[XX + 3].log
This works fine, with commands of the following form being entered into the "Send:" line at the bottom of what I call the "gdb console". This is the "Output" tab, with a red ladybug icon, in the Debugger window.

Code: Select all

set XX = 3
While the inferior (the program running under gdb) is paused at a breakpoint I can give such commands and then switch back to the Watches tab, where I have previously expanded the four objects as I choose, and this new value of XX has caused gdb to re-evaluate the data for those lines, and Codelite shows whatever is different in red. This is an excellent outcome!

This use gdb to set variables while the inferior is paused can also be applied to make the main vector-sub-range technique easily steerable and resizable, without actually changing the text of the watch line:

Code: Select all

*(&slots[YY]) @ ZZ
Thanks for Codelite!

- Robin