Как установить точку останова в GDB, где функция возвращает?

У меня есть функция C++, которая имеет много операторов возврата в разных местах. Как установить точку останова в операторе возврата, куда функция фактически возвращается?

А что значит команда "break" без аргумента?

7 ответов

Решение

break без аргументов останавливает выполнение следующей инструкции в текущем выбранном кадре стека. Вы выбираете рамку с помощью frame или же up а также down команды. Если вы хотите отладить точку, в которой вы фактически покидаете текущую функцию, выберите следующий внешний кадр и разбейте его там.

Вопреки ответам до сих пор, большинство компиляторов создадут одну инструкцию по сборке возврата независимо от того, сколько return операторы находятся в функции (это удобно для компилятора, поэтому есть только одно место для выполнения очистки всего стека).

Если вы хотите остановиться на этой инструкции, все, что вам нужно сделать, это disas и искать retq (или какой-либо другой инструкцией возврата для вашего процессора) и установите для нее точку останова. Например:

int foo(int x)
{
  switch(x) {
   case 1: return 2;
   case 2: return 3;
   default: return 42;
  }
}

int main()
{
  return foo(0);
}


(gdb) disas foo
Dump of assembler code for function foo:
   0x0000000000400448 <+0>: push   %rbp
   0x0000000000400449 <+1>: mov    %rsp,%rbp
   0x000000000040044c <+4>: mov    %edi,-0x4(%rbp)
   0x000000000040044f <+7>: mov    -0x4(%rbp),%eax
   0x0000000000400452 <+10>:    mov    %eax,-0xc(%rbp)
   0x0000000000400455 <+13>:    cmpl   $0x1,-0xc(%rbp)
   0x0000000000400459 <+17>:    je     0x400463 <foo+27>
   0x000000000040045b <+19>:    cmpl   $0x2,-0xc(%rbp)
   0x000000000040045f <+23>:    je     0x40046c <foo+36>
   0x0000000000400461 <+25>:    jmp    0x400475 <foo+45>
   0x0000000000400463 <+27>:    movl   $0x2,-0x8(%rbp)
   0x000000000040046a <+34>:    jmp    0x40047c <foo+52>
   0x000000000040046c <+36>:    movl   $0x3,-0x8(%rbp)
   0x0000000000400473 <+43>:    jmp    0x40047c <foo+52>
   0x0000000000400475 <+45>:    movl   $0x2a,-0x8(%rbp)
   0x000000000040047c <+52>:    mov    -0x8(%rbp),%eax
   0x000000000040047f <+55>:    leaveq 
   0x0000000000400480 <+56>:    retq   
End of assembler dump.
(gdb) b *0x0000000000400480
Breakpoint 1 at 0x400480
(gdb) r

Breakpoint 1, 0x0000000000400480 in foo ()
(gdb) p $rax
$1 = 42

Вы можете использовать обратную отладку, чтобы узнать, где функция действительно возвращается. Завершите выполнение текущего кадра, сделайте обратный шаг, и затем вы должны остановиться на только что возвращенном утверждении.

(gdb) record
(gdb) fin
(gdb) reverse-step

Перерыв на все функции текущей функции

Эта команда Python ставит точку останова на каждом retq инструкция текущей функции:

class BreakReturn(gdb.Command):
    def __init__(self):
        super().__init__(
            'break-return',
            gdb.COMMAND_RUNNING,
            gdb.COMPLETE_NONE,
            False
        )
    def invoke(self, arg, from_tty):
        frame = gdb.selected_frame()
        # TODO make this work if there is no debugging information, where .block() fails.
        block = frame.block()
        # Find the function block in case we are in an inner block.
        while block:
            if block.function:
                break
            block = block.superblock
        start = block.start
        end = block.end
        arch = frame.architecture()
        pc = gdb.selected_frame().pc()
        instructions = arch.disassemble(start, end - 1)
        for instruction in instructions:
            if instruction['asm'].startswith('retq '):
                gdb.Breakpoint('*{}'.format(instruction['addr']))
BreakReturn()

Источник это с:

source gdb.py

и используйте команду как:

break-return
continue

Теперь вы должны быть в retq,

Шаг до ретк

Просто для удовольствия, другая реализация, которая останавливается, когда retq найдено (менее эффективно из-за отсутствия аппаратной поддержки):

class ContinueReturn(gdb.Command):
    def __init__(self):
        super().__init__(
            'continue-return',
            gdb.COMMAND_RUNNING,
            gdb.COMPLETE_NONE,
            False
        )
    def invoke(self, arg, from_tty):
        thread = gdb.inferiors()[0].threads()[0]
        while thread.is_valid():
            gdb.execute('ni', to_string=True)
            frame = gdb.selected_frame()
            arch = frame.architecture()
            pc = gdb.selected_frame().pc()
            instruction = arch.disassemble(pc)[0]['asm']
            if instruction.startswith('retq '):
                break
ContinueReturn()

Это будет игнорировать ваши другие точки останова. ТОДО: можно избежать?

Не уверен, что это быстрее или медленнее, чем reverse-step,

Версия, которая останавливается на указанном коде операции, может быть найдена по адресу: /questions/43932753/kak-slomat-instruktsiyu-s-opredelennyim-kodom-operatsii-v-gdb/43932758#43932758

rrобратная отладка

Похож на GDB record упоминается по адресу /questions/13115347/kak-ustanovit-tochku-ostanova-v-gdb-gde-funktsiya-vozvraschaet/13115354#13115354, но гораздо более функциональный, чем GDB 7.11, по сравнению с rr 4.1.0 в Ubuntu 16.04.

Примечательно, что он правильно работает с AVX:

что мешает ему работать со стандартными вызовами библиотеки по умолчанию.

Установите Ubuntu 16.04.

sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance

Но также подумайте о компиляции из исходного кода, чтобы получить последние обновления, это было не сложно.

Тестовая программа:

int where_return(int i) {
    if (i)
        return 1;
    else
        return 0;
}

int main(void) {
    where_return(0);
    where_return(1);
}

скомпилируйте и запустите:

gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay

Теперь вы остались внутри сеанса GDB, и вы можете правильно отменить отладку:

(rr) break main
Breakpoint 1 at 0x56057c458619: file a.c, line 9.
(rr) continue
Continuing.

Breakpoint 1, main () at a.c:9
9           where_return(0);
(rr) step
where_return (i=0) at a.c:2
2           if (i)
(rr) finish
Run till exit from #0  where_return (i=0) at a.c:2
main () at a.c:10
10          where_return(1);
Value returned is $1 = 0
(rr) reverse-step
where_return (i=0) at a.c:6
6       }
(rr) reverse-step
5               return 0;

Сейчас мы на правильной обратной линии.

Если вы можете изменить исходный код, вы можете использовать некоторые хитрости с препроцессором:

void on_return() {

}

#define return return on_return(), /* If the function has a return value != void */
#define return return on_return()  /* If the function has a return value == void */

/* <<<-- Insert your function here -->>> */

#undef return

Затем установите точку останова на on_return и идти один кадр up,

Внимание: это не будет работать, если функция не возвращается через return заявление. Поэтому убедитесь, что это последняя строка return,

Пример (беспардонно скопирован из кода C, но будет работать и в C++):

#include <stdio.h>

/* Dummy function to place the breakpoint */
void on_return(void) {

}

#define return return on_return()
void myfun1(int a) {
    if (a > 10) return;
    printf("<10\n");
    return;   
}
#undef return

#define return return on_return(),
int myfun2(int a) {
    if (a < 0) return -1;
    if (a > 0) return 1;
    return 0;
}
#undef return


int main(void)
{
    myfun1(1);
    myfun2(2);
}

Первый макрос изменится

return;

в

return on_return();

Который действителен, так как on_return также возвращает void,

Второй макрос изменится

return -1;

в

return on_return(), -1;

Который позвонит on_return() а затем вернуть -1 (благодаря ,-оператором).

Это очень грязный трюк, но, несмотря на использование пошагового перехода назад, он будет работать в многопоточных средах и встроенных функциях.

Break без аргумента устанавливает точку останова на текущей строке.

Для одной точки останова невозможно отловить все пути возврата. Либо установите точку останова на вызывающем абоненте сразу после его возвращения, либо прервите его вообще return заявления.

Поскольку это C++, я полагаю, вы могли бы создать локальный сторожевой объект и, тем не менее, разбить его деструктор.

Другие вопросы по тегам