简体   繁体   中英

Indexing c++ vector in python gdb script

I would like to write a python script to analyze the debugger state of an algorithm, but I can't figure out how to get the basic example of indexing a vector to work.

for example, when debugging the following file (gdb_test.cpp):

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::cout << v.size() << std::endl;
    for (const auto& element : v)
    {
        std::cout << element << std::endl;
    }
}

With gdb and sourcing the following script (print_vector.py) which is intended to print out the first three elements of an input vector provided by name:

import gdb
class print_vector(gdb.Command):
    def __init__(self):
        super(print_vector, self).__init__('print_vector',
                                           gdb.COMMAND_SUPPORT,
                                           gdb.COMPLETE_FILENAME)

    def invoke(self, arg, from_tty):
        # Access the variable from gdb.
        frame = gdb.selected_frame()

        args = arg.split('; ')
        val = frame.read_var(args[0])

        for i in range(3):
            print(val[0])
print_vector()

With commands:

g++ gdb_test.cpp -std=c++11 -g
gdb ./a.out
(gdb) b gdb_test.cpp:10
(gdb) r
(gdb) source print_vector.py
(gdb) print_vector v

I get the error

Python Exception <class 'gdb.error'> Cannot subscript requested type.: 
Error occurred in Python command: Cannot subscript requested type.

Which is referring to the print(val[0]) -line. The problem is that gdb.Value objects can't be indexed as lists. Is there some other way to access the elements?

I have also tried things like val['at'][0] and val['at(0)'] , neither of which works. Loading the elements one-by-one with one call to gdb.parse_and_eval each works but is too slow.

The problem is that the C++ objects exposed to Python are not "first class" Python objects - so, they don't feature a Python __getitem__ method that maps to the corresponding mechanism to get an item in the "C++" class of the object.

On the other hand, the API does expose a parse_and_eval method that will handle a string as if it was typed in the C++ source - therefore allowing one to use v[i] from Python in an easy way -

For the example above, you can change your Python source to:

import gdb
class print_vector(gdb.Command):
    def __init__(self):
        super(print_vector, self).__init__('print_vector',
                                           gdb.COMMAND_SUPPORT,
                                           gdb.COMPLETE_FILENAME)

    def invoke(self, arg, from_tty):
        # Access the variable from gdb.
        frame = gdb.selected_frame()

        args = arg.split('; ')
        valname = args[0]

        for i in range(3):
            content_value = gdb.parse_and_eval(f"{valname}[{i}]")
            print(int(content_value))
print_vector()

To see this working. For more complex code, it may be worth to create a wrapper class in Python for gdb.Value objects that will implement a __getitem__ that does this automatically.

I ended up using the following function in c++11:

def vector_to_list(std_vector):
    out_list = []
    value_reference = std_vector['_M_impl']['_M_start']
    while value_reference != std_vector['_M_impl']['_M_finish']:
        out_list.append(value_reference.dereference())
        value_reference += 1

    return out_list

If std_vector is a gdb.Value -object holding a std::vector<T> this function returns a python list of gdb.Value -objects of type T . Based on this blog-post: https://hgad.net/posts/object-inspection-in-gdb/

I found this to be significantly faster than calling gdb.parse_and_eval['v[i]'] for all the i 's

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM