Вызов лямбда-функции в GDB
#include <iostream>
void do_something(void) {
std::cout << "blah blah" << std::endl;
auto lambda_func = [](void){
std::cout << "in lambda" << std::endl;
return;
};
lambda_func();
std::cout << "..." << std::endl;
return;
}
int main(int argc, char **argv) {
do_something();
return 0;
}
В этом примере программы, если вы компилируете (g++ gdb-call-lambda.cpp --std=c++11 -g
), а затем запустить его в GDB (gdb ./a.out
), вы можете заставить GDB вызывать любую "нормальную" функцию. Пример:
(gdb) break main
Breakpoint 1 at 0x4008e7: file gdb-call-lambda.cpp, line 20.
(gdb) r
Starting program: /home/keithb/dev/mytest/gdb-call-lambda/a.out
Breakpoint 1, main (argc=1, argv=0x7fffffffdfb8) at gdb-call-lambda.cpp:20
20 do_something();
(gdb) call do_something()
blah blah
in lambda
...
Однако, если вы затем попытаетесь вызвать лямбду:
(gdb) break do_something
Breakpoint 2 at 0x400891: file gdb-call-lambda.cpp, line 5.
(gdb) c
Continuing.
Breakpoint 2, do_something () at gdb-call-lambda.cpp:5
5 std::cout << "blah blah" << std::endl;
(gdb) n
blah blah
12 lambda_func();
(gdb) n
in lambda
14 std::cout << "..." << std::endl;
(gdb) call lambda_func()
Invalid data type for function to be called
GDB вроде как сходит с ума. Таким образом, мой вопрос таков: как вы называете лямбду в GDB? Запрос GDB о том, что он ожидает, не дает ничего интересного по сравнению с обычной функцией:
(gdb) whatis lambda_func
type = __lambda0
(gdb) whatis do_something
type = void (void)
Я посмотрел, есть ли в lambda_func какие-либо специальные члены, например, указатель функции для вызова, похожий на std:: function и / или std:: bind:
(gdb) print lambda_func
$1 = {<No data fields>}
Нет специальных членов? Хорошо, может быть, это просто прославленный указатель на функцию?
(gdb) call ((void (void)) lambda_func)()
Program received signal SIGSEGV, Segmentation fault.
0x00007fffffffdeaf in ?? ()
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on".
Evaluation of the expression containing the function
(at 0x0x7fffffffdeaf) will be abandoned.
When the function is done executing, GDB will silently stop.
Так что я даже не уверен на 100%, в каком порядке передавать аргументы или особенно захваченные типы.
Я попробовал дополнительно call lambda_func.operator()()
, call lambda_func::operator()
, call lambda_func::operator()()
, call __lambda0
, call __lambda0()
, call __lambda0::operator()
, call __lambda0::operator()()
все безрезультатно.
Поиск в Google показывает вещи об установке точек останова в лямбдах, но ничего не говорит о том, как вызывать эти лямбды из отладчика.
Что бы это ни стоило, это на 64-битной Ubuntu 14.04 с использованием g++ 4.8.2-19ubuntu1 и gdb 7.7-0ubuntu3.1
3 ответа
Я ожидал call __lambdaX::operator()()
работает, но это не так. Я думаю, что это связано с реализацией GCC. Я не уверен, что есть лучший способ, но это мое обходное решение, когда мне нужно вызвать лямбду в GDB.
Вкратце, GDB имеет disassemble
команда, и это дает __lambda0::operator()() const
в качестве отладочной информации на call
инструкция строка. Затем преобразуйте этот адрес в указатель на функцию и вызовите его.
Пример объясняет лучше.
$ g++ -g -std=c++0x lambda.cpp
$ ./a.out
blah blah
in lambda
...
GDB:
$ gdb ./a.out
GNU gdb (GDB) Fedora 7.7.1-13.fc20
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...done.
(gdb) b do_something()
Breakpoint 1 at 0x4008a3: file lambda.cpp, line 4.
(gdb) run
Starting program: /home/alper/cplusplus/a.out
Breakpoint 1, do_something () at lambda.cpp:4
4 std::cout << "blah blah" << std::endl;
Missing separate debuginfos, use:
(gdb) n
blah blah
11 lambda_func();
разбирать do_something
(gdb) disassemble do_something
Dump of assembler code for function do_something():
0x40089b <+0>: push %rbp
0x40089c <+1>: mov %rsp,%rbp
0x40089f <+4>: sub $0x10,%rsp
=> 0x4008a3 <+8>: mov $0x4009fb,%esi
0x4008a8 <+13>: mov $0x601060,%edi
0x4008ad <+18>: callq 0x400750 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x4008b2 <+23>: mov $0x400770,%esi
0x4008b7 <+28>: mov %rax,%rdi
0x4008ba <+31>: callq 0x400760 <_ZNSolsEPFRSoS_E@plt>
0x4008bf <+36>: lea -0x1(%rbp),%rax
0x4008c3 <+40>: mov %rax,%rdi
0x4008c6 <+43>: callq 0x400870 <__lambda0::operator()() const>
0x4008cb <+48>: mov $0x400a05,%esi
0x4008d0 <+53>: mov $0x601060,%edi
0x4008d5 <+58>: callq 0x400750 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x4008da <+63>: mov $0x400770,%esi
0x4008df <+68>: mov %rax,%rdi
0x4008e2 <+71>: callq 0x400760 <_ZNSolsEPFRSoS_E@plt>
0x4008e7 <+76>: nop
0x4008e8 <+77>: leaveq
0x4008e9 <+78>: retq
Линия выходов GDB callq 0x400870 <__lambda0::operator()() const>
так что конвертировать 0x400870
в указатель на функцию и вызвать его.
(gdb) call ((void (*)()) 0x400870)()
in lambda
(gdb) call ((void (*)()) 0x400870)()
in lambda
(gdb) call ((void (*)()) 0x400870)()
in lambda
Примечание: если GCC вставляет лямбду, звонить нечего. Например, если приведенный выше пример скомпилирован с переключателем оптимизации -O3
нет строки с __lambda0::operator()() const
в ГБД disassemble
выход.
В менее синтетическом варианте использования мне удалось вызвать лямбду, назвав ее как
call myLambda.operator()(param1,param2)
Но в тестовом примере в OP GDB, похоже, думает, что функция встроена, хотя это не так. Я сообщил об этом как об ошибке 28137.
Примечание: не гарантируется, что эти методы будут работать с универсальной лямбдой C++14.
Случай 1. Когда модификация исходного кода разрешена
Требуется изменение исходного кода для каждой отдельной лямбды.
Способ 1.1.
Заверните лямбду в
std::function
и явно создайте его экземпляр. (у него есть дополнительный удар по производительности из-за динамического выделения памяти, но это не имеет значения, потому что это только отладочная сборка)
Если сама лямбда не принимает какие-либо локальные типы данных в качестве входных или возвращаемых, можно явно создать экземпляр всего класса.
В противном случае,
operator()
может быть создан с помощью его в программе.
#include<iostream>
#include<functional>
template class std::function<int(int)>; // explicit instantiation
int main(){
auto a=[](int x){
std::cout<<x<<'\n';
return x+1;
};
std::function a_function{a};
a_function(1); // implicit instantiation
__builtin_trap();
}
Вызвать gdb как
a_function.operator()(1)
.
Ссылка:
- C++, STL, GDB: невозможно оценить функцию, возможно, встроенную – 2 Ответа
- c++ - функции gdb stl по-прежнему отображаются как встроенные после отключения оптимизации - Qaru
Способ 1.2: сохранить адрес лямбды
Программа:
#include<iostream>
int main(){
auto a=[](int x){
std::cout<<x<<'\n';
return x+1;
};
auto a_operator_call=&decltype(a)::operator();
__builtin_trap();
}
Вызовите так: (требуется соответствующая опция компиляции, чтобы значение
a_operator_call
не оптимизировано)
(gdb) print (a.*a_operator_call)(1)
1
$1 = 2
Ссылка:
Случай 2. Когда изменение исходного кода не допускается
Случай 2.1. Глобальная лямбда
#include<iostream>
auto a=[](int x){
std::cout<<x<<'\n';
return x+1;
};
int main(){
__builtin_trap();
}
Вам нужно отключить xmethod (по крайней мере, в моей текущей версии gdb. Я думаю, что это ошибка) и вызвать с синтаксисом
a.operator()(...)
:
(gdb) print a
$1 = {<No data fields>}
(gdb) print a(1)
Invalid data type for function to be called.
(gdb) print a.operator()(1)
Python Exception <class 'TypeError'> expected string or bytes-like object:
Error while looking for matching xmethod workers defined in Python.
(gdb) disable xmethod
(gdb) print a.operator()(1)
1
$2 = 2
Ссылка:
- #836820 — gdb: Python Exception &lt;class 'TypeError'&gt; ожидаемая строка или байтовый объект: — Журналы отчетов об ошибках Debian
- Gentoo Forums :: Просмотр темы - [решено] gdb больше не может печатать типы
Случай 2.2. Локальная лямбда
Примечание : для этого метода может потребоваться компиляция с расширением .
Рассмотрим простую программу
#include<iostream>
int main(){
auto a=[](int x){
std::cout<<x<<'\n';
return x+1;
};
__builtin_trap();
}
И посмотрите, какие символы генерируются в исполняемом файле:
(gdb) shell nm --demangle ./a.out |grep lambda
000000000001fe4c t main::{lambda(int)#1}::operator()(int) const
000000000001ff5c t main::{lambda(int)#1}::operator int (*)(int)() const
000000000001ff40 t main::{lambda(int)#1}::_FUN(int)
(gdb) print 'main::{lambda(int)#1}::operator()(int) const'
$1 = {int (const struct {...} * const, int)} 0x555555573e4c <operator()(int) const>
(gdb) print 'main::{lambda(int)#1}::operator()(int) const' (a, 1)
No symbol "(null)" in current context.
(gdb) set $c='main::{lambda(int)#1}::operator()(int) const'
(gdb) print (a.*$c)(1)
Non-pointer-to-member value used in pointer-to-member construct
(gdb) print $c(&a, 1)
1
$2 = 2
Вы можете видеть, что символ имеет тип указателя на функцию , а не указателя на функцию-член , поэтому он должен вызываться с нормальным синтаксисом (хотя, как видно из метода 1.2 выше, gdb поддерживает функцию-член). разыменование)
Можно (например, с помощью программы Python)
автоматически анализировать символы с помощью
lambda
в его запутанном имениполучить его тип с помощью команды
при вызове лямбда: получить фактический тип объекта лямбда с помощью
maintenance print type
команда(
ptype
недостаточно, так как некоторые разные типы имеют идентичное строковое представление и рассматриваются gdb как равные, см. пример ниже)затем сопоставьте типы друг с другом, чтобы определить правильный символ.
Неудачные попытки
Используйте gdb:
(gdb) print a $1 = {<No data fields>} (gdb) ptype a type = struct { } (gdb) ptype $c type = int (*)(const struct {...} * const, int)
Используйте API Python:
К сожалению, почему-то он считает разные типы с одинаковым определением равными.
Например в этой программе:
#include<iostream> int main(){ auto a=[](int x){ std::cout<<x<<'\n'; return x+1; }; auto b=[](int x){ std::cout<<x<<'\n'; return x+2; }; struct{ int x; } c; struct{ int x; } d; struct{ int y; } e; __builtin_trap(); }
в интерактивной оболочке IPython, встроенной в gdb:
In [1]: gdb.parse_and_eval("a").type == gdb.parse_and_eval("b").type Out[38]: True In [39]: gdb.parse_and_eval("c").type == gdb.parse_and_eval("d").type Out[39]: True In [40]: gdb.parse_and_eval("c").type == gdb.parse_and_eval("e").type Out[40]: False
Вещи, которые можно попробовать (хотя и не обязательно в данном конкретном случае)
-
-fno-inline-functions
-
-fkeep-inline-functions
-
-gdwarf-5
-
-g3
-
__attribute__((used))
-
-fno-eliminate-unused-debug-types
-
-fkeep-static-functions
-
-fkeep-static-consts
Ссылка:
- c++ - Невозможно оценить функцию - может быть встроено - Qaru QaruSite
- c++ - Функция для искажения / демонтажа функций - Qaru QaruSite
- c++ - Получение искаженного имени из измененного имени - Qaru
- конструкция компилятора. Что такое утилита Linux для изменения имени символа С++? - Переполнение стека
- Параметры отладки (с использованием коллекции компиляторов GNU (GCC))
- Как заставить gdb показывать оригинальное имя функции без искажений на модели дизассемблирования? - Переполнение стека
- https://dwarfstd.org/doc/Debugging%20using%20DWARF.pdf