2010-09-06 2 views
15

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

И что означает команда «break» без аргумента?

+0

Вы изучили rbr? – Jack

ответ

8

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

+3

Точка останова задана текущей инструкцией, а не следующей. Выполнение уже прекращено, если вы вообще выполняете команду. Любая точка останова в вызывающей функции не будет возникать, если текущая функция активна, если она не является рекурсией, и в этом случае такая отладка становится запутанной. – Potatoswatter

+0

Как вы «выбираете следующий внешний фрейм и ломаете там»? Вы можете уточнить? (Обратите внимание, что цель состоит в том, чтобы иметь функцию останова * внутри * функции (например, иметь возможность смотреть на своих локалей), но как раз перед ее возвратом.) – ShreevatsaR

2

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

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

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

15

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

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

Согласно этой странице для этого требуется Linux-x86, и, вероятно, она имеет грозное снижение производительности. +1 в любом случае, так как это так здорово. – Potatoswatter

+2

Инструмент 'rr' на http://rr-project.org/ позволяет осуществлять обратную отладку через воспроизведение в Linux, хотя и имеет место только с замедлением в 1,2 раза (по крайней мере, по его веб-сайту). Это делает прохладную вещь еще более прохладной. :) – pnkfelix

+0

@Potatoswatter и, кроме того, он полностью распадается в 7.11, если вы выполняете большинство вызовов библиотеки из-за отсутствия поддержки AVX ... https://stackoverflow.com/questions/2528918/gdb-reverse-debugging-fails -with-process-record-does-not-support-instruction-0x/46113472 # 46113472 –

15

Вопреки ответов до сих пор, большинство компиляторов будет создать единую команду возврата сборки, независимо от того, сколько 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 
+0

. Я голосую за это, потому что это полезный лакомый кусочек, но может ли OP сказать, какой «возврат» в код был вызван. – dmckee

+1

Это, вместе с обратным шагом от @ ks1322, неоценимо. Вам нужно сделать два обратных шага, и именно поэтому. – falstro

+1

Интересно! Я сделал команду Python, которая находит 'retq' и автоматически устанавливает точку останова: http://stackoverflow.com/a/31264709/895245 –

4

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

Эта команда 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

Просто для удовольствия, другой реализации, который останавливается, когда 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() 

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

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

версии, которая останавливается на данный опкод можно найти по адресу: https://stackoverflow.com/a/31249378/895245

+0

Как бы то ни было, с рекурсивной функцией, которая называется несколько раз, кажется, haywire, и каждая точка останова, которая должна работать при возврате, называется несколько раз. (На самом деле не попробовал это с более простой функцией ...) (С другой стороны, это действительно работает, даже если точка останова вызывается несколько раз, поэтому спасибо.) – ShreevatsaR

+0

@ShreevatsaR weird. Если возможно, ссылайтесь на минимально воспроизводимый пример. –

1

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

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 (благодаря , -оператора).

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

1

rr обратной отладки

Подобно GDB record упомянутого в https://stackoverflow.com/a/3649698/895245, но гораздо более функциональным, как из 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; 

Мы теперь на правильной обратной линии.