c - GDB поврежденный стек кадров-Как отлаживать?




recursion (5)

Предполагая, что указатель стека действителен ...

Возможно, невозможно точно узнать, где находится SEGV от обратной линии - я думаю, что первые два кадра стека полностью перезаписаны. 0xbffff284 кажется действительным адресом, но следующие два не являются. Для более пристального просмотра стека вы можете попробовать следующее:

gdb $ x / 32ga $ rsp

или вариант (замените 32 другим номером). Это напечатает некоторое количество слов (32), начиная с указателя стека гигантского (g) размера, отформатированного как адреса (a). Введите «help x» для получения дополнительной информации о формате.

Прибор вашего кода с некоторыми отпечатками «printf» может быть плохой идеей, в данном случае.

У меня есть следующая трассировка стека. Можно ли извлечь из этого что-нибудь полезное для отладки?

Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0  0x00000002 in ?? ()
#1  0x00000001 in ?? ()
#2  0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) 

Где начать искать код, когда мы получаем Segmentation fault , и трассировка стека не так полезна?

ПРИМЕЧАНИЕ. Если я отправлю код, эксперты SO дадут мне ответ. Я хочу взять руководство от SO и найти ответ сам, поэтому я не размещаю код здесь. Извиняюсь.


Если это перезапись стека, значения могут хорошо соответствовать чему-то распознаваемому из программы.

Например, я просто обнаружил, что смотрю на стек

(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x000000000000342d in ?? ()
#2  0x0000000000000000 in ?? ()

и 0x342d - 13357, который оказался идентификатором узла, когда я скопировал журналы приложений для него. Это немедленно помогло сузить сайты кандидатов, где могла произойти перезапись стека.


Если ситуация довольно проста, ответ Криса Додда является лучшим. Похоже, что он прыгнул через указатель NULL.

Однако, возможно, программа выстрелила себе в ногу, колено, шею и глаза перед сбоем - переписала стопку, испортила указатель кадра и другие зла. Если это так, то разгадать хэш вряд ли покажет вам картофель и мясо.

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

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

  • Возможно, установка точки наблюдения отладчика или вставка диагностического printf на критические переменные приведет к необходимости A ha!
  • Возможно, изменение условий тестирования с различными входами обеспечит более глубокое понимание, чем отладка.
  • Возможно, вторая пара глаз заставит вас проверить ваши предположения или собрать недооцененные доказательства.
  • Иногда все, что нужно, идет на обед и думает о собранных доказательствах.

Удачи!


Эти фиктивные адреса (0x00000002 и т. П.) Являются фактически значениями ПК, а не значениями SP. Теперь, когда вы получаете этот вид SEGV, с фиктивным (очень маленьким) адресом ПК, 99% времени он должен вызвать через указатель фиктивной функции. Обратите внимание, что виртуальные вызовы на C ++ реализуются с помощью указателей функций, поэтому любая проблема с виртуальным вызовом может проявляться одинаково.

Команда косвенного вызова просто подталкивает ПК после вызова в стек, а затем устанавливает ПК в целевое значение (фиктивный в этом случае), поэтому, если это так, вы можете легко отменить его, вручную выталкивая ПК из стека , В 32-битном коде x86 вы просто выполните:

(gdb) set $pc = *(void **)$esp
(gdb) set $esp = $esp + 4

С 64-битным кодом x86 вам нужно

(gdb) set $pc = *(void **)$rsp
(gdb) set $rsp = $rsp + 8

Тогда вы должны быть в состоянии сделать bt и выяснить, где код на самом деле.

Другие 1% времени, ошибка будет связана с перезаписью стека, обычно путем переполнения массива, хранящегося в стеке. В этом случае вы можете получить более ясную информацию о ситуации, используя инструмент, например valgrind


Предположим, вы хотите построить: u (n + 1) = f (u (n)) с u (0) = u0

Одним из решений является определение простой рекурсивной функции:

u0 = ...

def f(x):
  ...

def u(n):
  if n==0: return u0
  return f(u(n-1))

К сожалению, если вы хотите вычислить высокие значения u, вы столкнетесь с ошибкой переполнения стека.

Другое решение - простой цикл:

def u(n):
  ux = u0
  for i in xrange(n):
    ux=f(ux)
  return ux

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

def u(n):
  ux = u0
  for i in xrange(n):
    ux=f(ux)
  yield ux

for val in u(1000):
  print val

Есть много других вариантов, но я думаю, что это основные.





c recursion gdb