Справка
Телеграм чат начинающих программистов. Общаемся и помогаем друг другу
Если ссылка не открывается, можно найти нас в поиске по чатам @rubyrush
или
пойти другим путем
До сих пор, если вы все внимательно повторяли за нами, вы скорее всего избежали серьезных проблем и ошибок. Но это только потому, что мы готовились к каждому уроку и тщательно проверяли каждую нашу программу.
В реальной работе никогда не бывает все так гладко. Пора уже научиться находить и уничтожать баги. Это неотъемлемая часть работы программиста, и для этого существуют специальные инструменты.
И вновь, вот уже в который раз вспомним нашу метафору с дорогой и машинкой.
Если наша машина вдруг заглохла, есть два варианта: либо проблему видно сразу, например, камень на дороге или уровень бензина на нуле, либо машина просто заглохла и всё. Жми педали, крути руль — не едет и всё тут. Приходится думать и искать поломку, залезать под капот.
Программы в компьютере обычно выполняются очень быстро, процессор старается выполнить код программы с максимальной скоростью, чтобы освободить ресурсы для следующей программы.
Эта скорость иногда мешает понять, в чём проблема и в какой момент случилась загвоздка. Тогда хочется программу остановить или замедлить.
Подобно тому, как спортивные машины на испытаниях обвешивают специальным оборудованием, которое в режиме реального времени снимает все возможные показания (температуру мотора, нагрузку на карданный вал, давление жидкостей и так далее), нам тоже хочется запустить нашу программу и в любой момент иметь возможность остановить её и посмотреть значения каких-то переменных и выражений.
Это можно сделать с помощью дебаггера — специальной программы, которая позволяет погрузиться внутрь работающей программы, наблюдать за ее выполнением и даже вмешиваться в ее выполнение.
Главная задача дебаггера — помочь найти баг (ошибку), поэтому он так и называется.
Если вы обнаружили ошибку, но не знаете ее причину, ваша задача — сделать какие-то предположения. И запустить программу с помощью дебаггера для проверки этих предположений.
Для дебага программа запускается в специальном режиме, в котором вы можете поставить в любой точке «брейкпоинт», инструкцию для дебаггера остановить выполнение программы на каком-то конкретном шаге и передать управление вам.
Как только программа дошла до брейкпойнта, она останавливается. Дальше вы можете рулить машиной «step-by-step», шаг за шагом выполняя программу строчку за строчкой и анализируя все ее переменные.
Давайте попробуем отдебажить что-нибудь из написанного нами ранее.
В RubyMine довольно просто запустить программу в режиме отладки, для этого рядом с кнопкой Run всегда есть кнопка с зеленым жучком:
При первом запуске дебаггера RubyMine может попросить вас разрешения установить дополнительные гемы:
Гемы лучше установить предварительно самостоятельно. Давайте установим основной гем, нужный для дебага:
gem install ruby-debug-ide
(если это не получится в обычной консоли, запустите Dev-Kit-овскую, c:\dev\msys.bat
, см. как мы это делаем на видео)
Теперь давайте напишем программу, которую мы будем дебажить.
Пишем программу, которая решает квадратные уравнения.
equation.rb
:
puts 'Solve equation: A * x^2 + B * x + C = 0'
puts 'Enter A:'
a = gets.to_f
puts 'Enter B:'
b = gets.to_f
puts 'Enter C:'
c = gets.to_f
# считаем дискриминант
discr = b*b - 4*a*c;
# первый корень
x1 = (-b + Math::sqrt(discr))/(2*a)
# второй корень
x2 = (-b - Math::sqrt(discr))/(2*a)
puts 'Solution 1:'
puts x1
puts 'Solution 2:'
puts x2
Если мы попробуем запустить эту программу с параметрами a=0, b=4, c=2
или a=3, b=2, c=1
, то программа выдаст либо какую-то фигню, либо вообще упадет с ошибкой.
Давайте разбираться. Поставим брейкоинт вот на этой строчке:
discr = b*b - 4*a*c;
И запустим программу ещё раз:
Когда программа остановится, давайте попробуем вычислить дискриминант отдельно: нажмите Alt+F8
(или иконку с калькулятором), у вас появилось окно Evaluate expression.
Это окно, которое позволяет выполнять любой код (и видеть результат), как если бы он был написан перед той строчкой, на которой остановилось ваше приложение.
Если мы вычислим дискриминант, то увидим, что всё хорошо, дискриминант равен 4-м. Идём дальше (нажимаем F8
). Это называется Step Over, означает «выполнить следующую строчку и остановиться». Так можно строчка за строчкой пройти всю программу в любом нужном вам темпе.
Давайте на следующем шаге вычислим первый корень, снова нажмите Alt+F8
и вычислите значение первого корня:
Оп-па! Результат выходит не числом. Тут уже легко разобраться, что раз у нас a=0
, то делить на эту переменную другие нельзя. Это исключение надо обработать отдельно.
Давайте разберёмся со вторым случаем. Снова запускаем нашу программу в режиме отладки, но уже с новыми значениями a=3, b=2, c=1
и снова останавливаем:
Мы попытались взять квадратный корень из отрицательного числа. В этом случае корней в действительных числах нет.
Можно написать в программе, что в таком случае корней нет и выйти. А можно сделать умней — добавить в программу поддержку комплексных чисел, тогда корень из отрицательного числа обретет новый смысл.
Вот так будет выглядеть наша исправленная программа:
# Метод, выводящий решение на экран
def print_root(x_real, x_complex)
puts 'Solution:'
print x_real
print ' +' if x_complex > 0
print " #{x_complex} * i" if x_complex != 0
puts
end
puts 'Solve equation: A * x^2 + B * x + C = 0'
puts 'Enter A:'
a = gets.to_f
puts 'Enter B:'
b = gets.to_f
puts 'Enter C:'
c = gets.to_f
if a == 0
abort "It is linear equation! x = #{-c/b}"
end
# считаем дискриминант
discr = b*b - 4*a*c;
if discr < 0 # комплексные числа пошли
x1_real = -b/(2*a)
x1_complex = Math::sqrt(-discr)/(2*a)
x2_real = -b/(2*a)
x2_complex = -Math::sqrt(-discr)/(2*a)
else
# первый корень
x1_real = (-b + Math::sqrt(discr))/(2*a)
x1_complex = 0
# второй корень
x2_real = (-b - Math::sqrt(discr))/(2*a)
x2_complex = 0
end
print_root(x1_real, x1_complex)
print_root(x2_real, x2_complex)
Если наша программа работает с несколькими потоками, то при остановке одного из потоков, другие могут продолжить выполняться или остановиться в непредсказуемых местах.
Также во время остановки могут протухнуть внешние ресурсы (удаляться какие-нибудь временные файлы, пропадёт сеть и так далее), так что очень сложная программа в режиме отладки может вести себя не совсем так, как она ведёт себя в обычном режиме.
Ещё один нюанс: во время отладки может так выйти, что вы забредёте в код сторонних библиотек или языка руби. Не стоит бояться заглянуть и изучить чужой код, особенно если он хорош.
Если дебаг в RubyMine по каким-то причинам не для вас, то есть другие способы. Например с помощью специальной библиотеки byebug
:
gem install byebug
Чтобы вашу программу можно было отдебажить с помощью «байбага», вам нужно добавить его в список подключённых библиотек в коде программы:
require byebug
А вместо брейкпоинта вам просто нужно написать в интересном вам месте волшебное слово
byebug
Дойдя до этой строчки, программа остановится и передаст вам управление прямо в консоли. Вы сможете выполнять там команды, также как в окне Evaluate Expression рубимайна, и смотреть значение выражений:
Когда пользоваться дебагером, а когда нет, решать вам. В конечном итоге ошибку всё равно ищете вы, дебаггер лишь помогает проверять предположения.
Но вот несколько советов, когда вам стоит вспомнить про дебагер:
Мы очень надеемся, что теперь вы перестанете бояться ошибок в ваших приложениях, какими бы сложными они ни были.
Дальше научимся делать так, чтобы сложные ошибки появлялись в наших программах как можно реже!