Подтвердить что ты не робот

Вызов лямбда-функции в 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 показывает, что нужно установить точки останова в lambdas, но ничего не сказать о том, как вызывать эти lambdas из отладчика.

Для чего это стоит, это на Ubuntu 14.04 64-бит, используя g++ 4.8.2-19ubuntu1 и gdb 7.7-0ubuntu3.1

4b9b3361

Ответ 1

Я ожидал работы 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 <[email protected]>
   0x4008b2 <+23>:  mov    $0x400770,%esi
   0x4008b7 <+28>:  mov    %rax,%rdi
   0x4008ba <+31>:  callq  0x400760 <[email protected]>
   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 <[email protected]>
   0x4008da <+63>:  mov    $0x400770,%esi
   0x4008df <+68>:  mov    %rax,%rdi
   0x4008e2 <+71>:  callq  0x400760 <[email protected]>
   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, в GDB disassemble нет строки с __lambda0::operator()() const.