Я хотел бы написать скрипт python для анализа состояния отладчика алгоритма, но я не могу понять, как заставить базовый пример индексации вектора работать.
например, при отладке следующего файла (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;
}
}
С помощью gdb и получения следующего скрипта (print_vector.py), который предназначен для печати первых трех элементов входного вектора, указанного по имени:
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()
С командами:
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
я получаю ошибку
Python Exception <class 'gdb.error'> Cannot subscript requested type.:
Error occurred in Python command: Cannot subscript requested type.
Что относится к print(val[0])-линии. Проблема в том, что gdb.Value объекты не могут быть проиндексированы как списки. Есть ли другой способ получить доступ к элементам?
Я также пробовал такие вещи, как val['at'][0] и val['at(0)'], ни один из них не работает. Загрузка элементов один за другим с одним вызовом gdb.parse_and_eval работает, но слишком медленно.






Проблема в том, что объекты C++, открытые для Python, не являются объектами Python «первого класса», поэтому в них нет метода Python __getitem__, который сопоставляется с соответствующим механизмом для получения элемента в классе «C++» объекта.
С другой стороны, API предоставляет метод parse_and_eval, который будет обрабатывать строку, как если бы она была напечатана в исходном коде C++, что позволяет легко использовать v[i] из Python —
В приведенном выше примере вы можете изменить исходный код Python на:
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()
Чтобы увидеть, как это работает. Для более сложного кода, возможно, стоит создать класс-оболочку в Python для объектов gdb.Value, который будет реализовывать __getitem__, который делает это автоматически.
В итоге я использовал следующую функцию в С++ 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
Если std_vector является gdb.Value-объектом, содержащим std::vector<T>, эта функция возвращает список Python из gdb.Value-объектов типа T. На основе этого сообщения в блоге: https://hgad.net/posts/object-inspection-in-gdb/
Я обнаружил, что это значительно быстрее, чем вызов gdb.parse_and_eval['v[i]'] для всех i
Я пробовал это, но это очень медленно для больших векторов структур, предположительно из-за накладных расходов в функции
parse_and_eval. В идеале должен быть способ получить доступ к элементам непосредственно из полногоValue-объекта. Технически это должно быть возможно, так как добавлениеprint(val)к приведенному выше коду дает строку со всеми элементами, что означает, что все они на самом деле загружены в python. В худшем случае, я полагаю, я мог бы разобрать эту строку...