Рубрики
Без рубрики

Контрольный список отладчика – Часть II

В части I Контрольного списка отладчиков я представил некоторые концепции высокого уровня и рассмотрел некоторые из них… С тегами java, программирование, учебник, инструменты.

В части I контрольного списка Отладчиков я представил некоторые концепции высокого уровня и рассмотрел некоторые общие вещи, которые вы можете сделать. В этой части мы перейдем к самому процессу. Опять же, важно подчеркнуть, что это сводится к минимуму и концентрируется.

Я не хочу обсуждать трекеры проблем, модульное тестирование и правильное TDD. Я думаю, что существует множество ресурсов, охватывающих все это. В центре внимания этих постов находится сам процесс отладки, которым часто пренебрегают как “инструментами”. Существует множество приемов, которые отделяют опытного разработчика от новичка. Кроме того, есть много небольших достижений и изменений, за которыми мы, возможно, не сможем угнаться. Я хотел бы рассказать обо всех этих вещах (некоторые в будущих постах).

Покончив с этим, давайте продолжим…

Процесс

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

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

  • Сделайте простейшее предположение ты можешь
  • Проверка допущений
  • Пока ошибка не найдена:
    • Узкие допущения
    • Проверка суженных допущений

Вы, наверное, говорите: “Ну, да…”. Это довольно очевидно…

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

На этом этапе становится полезным резиновое ныряние (разговор с уткой). Это процесс разговора с кем-то (или чем-то) о вашей проблеме и предположениях. Когда мы произносим что-то вслух или даже пытаемся выразить это в уме, это помогает прояснить наши ошибочные предположения. Я постараюсь подробнее рассказать о подобных трюках в будущем посте “советы и рекомендации”.

Самые Простые Предположения

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

Недавно у меня была ошибка, с которой я случайно столкнулся. Я заметил значение переменной в трассировке стека. Он был явно испорчен. Поскольку я использовал агент отладки, я неправильно предположил, что это было источником проблемы. Но я пытался проверять снова и снова. Когда все остальное потерпело неудачу, это привело меня к серьезной ошибке в коде.

Очевидно, что мы не можем начать с проверки предположения, что 1 + . Поэтому нам нужно сузить круг поисков до применимых допущений. Этот “трюк” не является “концом всего”, но это очень полезный способ проверить множество распространенных предположений.

Отладка “Рабочего кода”

Лучший способ проверить свои предположения – это ознакомиться с рабочим кодом. Если ваш код не работает для случая X и работает успешно для Кейси, сначала попробуйте случай X. Посмотрите, почему код работает, и перешагните через блок кода. Затем попробуйте случай Y. Это должно представить вам два случая, которые вы можете легко сравнить, чтобы помочь вам сузить круг подозреваемых.

Если это неприменимо или никуда вас не приведет, вам необходимо ознакомиться со следующим:

  • Исключения
  • Государство
  • Потоки/Синхронизация
  • Тайминги и гонки

Я заказал их в соответствии со сложностью и вероятностью.

Исключения

Большинство проблем являются государственными, но исключения относительно легко обнаружить. Поэтому просто установите точку останова для всех исключений и убедитесь, что ничего “подозрительного” не происходит за кулисами в одном из методов, которые вы вызвали и т.д. Медленно отфильтровывайте “допустимые” исключения по мере прохождения процесса. Обычно исключения являются “громкими” и “очевидными”, поэтому, если кто-то молча не поймал исключение (что случается), вы должны быть в курсе.

Я бы также настоятельно рекомендовал правило линтера, которое проверяет наличие проглоченных/незарегистрированных исключений. Например. checkstyle поддерживает эту проверку, которая блокирует пустые блоки catch. Он все еще не может блокировать глупый код, который “ничего” не делает в этом блоке, но, по крайней мере, это начало.

Государство

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

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

Если это не помогает, попробуйте определить конкретные области, которые могут быть проблематичными. Большинство отладчиков позволят вам разместить точку останова в поле, чтобы отслеживать изменения в указанном поле… Я использовал эту функцию пару раз во время консультаций, и люди всегда удивлялись, что вы можете это сделать…

В IntelliJ значок точки останова поля выглядит по-другому. Но это точка останова, как и любая другая, вы можете применить к ней условие и посмотреть стек и т.д.

Теперь, если проблема не устранена и все выходит из строя… Попробуйте изменить код, чтобы вернуть жестко заданное состояние или состояние из рабочего случая. Обычно я не являюсь поклонником методов, требующих изменения кода для отладки, поскольку я рассматриваю две разные задачи. Однако, если у вас закончились все варианты, это может быть вашим единственным выходом. Естественно, вам следует использовать “Перезагрузить измененные классы” (или Применить изменения кода, Отредактировать и продолжить и т.д.). если применимо.

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

Потоки/Синхронизация

Проблемы с потоками трудно решить… На самом деле это не то, во что мы будем вдаваться. Мы сосредоточимся только на поиске и понимании ошибки, и это более простая (выполнимая) задача.

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

Вы также можете получить дамп потока во время точки останова, это функция практически любого существующего отладчика. Например. в IntelliJ/IDEA вы можете выбрать Выполнить -> Действия по отладке -> Получить дамп потока . Это не так полезно, как просмотр кадров стека, но это только начало.

В частности, в IntelliJ/IDEA я рекомендую щелкнуть правой кнопкой мыши вкладку “Отладка” и включить просмотр потоков. Затем включите группы потоков, щелкнув правой кнопкой мыши на вкладке и выбрав Настроить представление потоков вот так:

Это обеспечивает гораздо более “чистое” представление потоков в виде иерархии вместо представления по умолчанию в IntelliJ, которое лучше ориентировано на отладку одного потока.

Тупики и Живые блокировки

Тупики обычно довольно очевидны. Приложение застревает, вы нажимаете паузу, и отладчик показывает вам, какой поток застрял в ожидании какого монитора. Затем вы можете просмотреть другие темы и посмотреть, кто держит монитор. Исправить это может быть сложно, но отладчик буквально “говорит нам”, что происходит.

С помощью livelock мы удерживаем один монитор и нуждаемся в другом. Другой поток держит другой монитор и нуждается в том, который мы держим. Так что на первый взгляд кажется, что оба работают и не застряли. Немного похоже на двух людей, бегущих друг против друга в коридоре и пытающихся уступить друг другу дорогу. К сожалению, блокировки могут происходить без физического “застревания” потоков, поэтому код может выглядеть нормально на поверхности без четкого монитора в трассировках стека.

Для отладки этого требуется переходить по потокам по одному в представлении потока и просматривать каждый из них, чтобы увидеть, ожидает ли он потенциально оспариваемого ресурса. Технически это несложно но это очень утомительно. Вот почему я рекомендовал включить группы потоков в представлении потоков выше. Типичное приложение имеет МНОЖЕСТВО потоков (и больше, связанных с проектом Ткацкий станок ). Это создает много шума, который мы можем уменьшить, сгруппировав потоки и сосредоточив внимание на важных деталях.

Производительность и нехватка ресурсов

Проблемы с производительностью, вызванные конфликтом мониторов, немного сложнее отслеживать с помощью отладчика. Обычно я рекомендую случайным образом приостанавливать код и просматривать запущенные потоки в вашем приложении. Поток X постоянно удерживает монитор? Может быть, здесь какая-то проблема.

Затем вы можете вывести предположения и доказать их, зарегистрировав точку входа/выхода для блокировки/синхронизированного блока.

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

Нехватка ресурсов – это ответвление проблем с производительностью. Часто вы рассматриваете это как чрезвычайную проблему с производительностью, которая обычно возникает только при попытке масштабирования. В этом случае ресурс, необходимый потоку, всегда занят, и мы просто не можем до него добраться. Например, У нас слишком много потоков и слишком мало подключений к базе данных. Или слишком мало потоков и слишком много входящих веб-запросов. Эта проблема часто вообще не нуждается в отладчике. Ваша среда обычно сразу указывает на то, что у нее закончился данный ресурс, поэтому проблема почти всегда проста и очевидна.

Решение не всегда такое ясное, например, вы не хотите добавлять слишком много потоков или подключений к БД для решения краткосрочной проблемы. Вам нужно понять, почему произошло голодание…

Это происходит по двум разным причинам:

  • Недостаточно быстрое высвобождение ресурсов
  • Не во всех случаях высвобождаются ресурсы

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

Вот тут-то и пригодится инкапсуляция. Все ваши ресурсы (распределение/высвобождение) должны быть инкапсулированы. Если вы сделаете это правильно, добавление ведения журнала для выделения и освобождения должно очень быстро выявить такие проблемы.

Это немного сложнее обнаружить с помощью фреймворков DI, таких как Spring, Где для вас вводятся соединения и т. Д. Вы все еще можете использовать подобные трюки, чтобы отслеживать даже введенные данные .

Тайминги и гонки

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

Это часто происходит, когда ваш код непреднамеренно зависит от производительности или времени. Например, много лет назад у меня было приложение, которое выходило из строя только тогда, когда им пользовался наш клиент. Приложение было мобильным, а нашим клиентом был местный оператор. Оказалось, что у нас была ошибка, из-за которой подключение к сети на сайте клиента было НАСТОЛЬКО БЫСТРЫМ, что он просто немедленно возвращал ответ, а все остальное не было готово. Таким образом, приложение вышло из строя. Обычно проблема заключается в низкой производительности и тайм-аутах.

Так что это был тот случай, когда мое предположение о том, что сеть работает медленнее, чем процессор, было ошибочным…

То, как я подхожу к условиям гонки в потоковом коде, таково: “Это ошибка состояния”. Это всегда ошибка штата. Когда у нас есть состояние гонки, это означает, что мы либо читаем из штата, когда он не был готов, либо писали в штат слишком поздно/рано. Точки останова на местах в данном случае являются вашими друзьями и действительно могут помочь вам получить полную картину происходящего. Вы также можете смоделировать ситуацию плохого состояния, изменив значения переменных.

Окончательно

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

Я не буду вдаваться в исправление ошибки, регистрацию проблемы, создание тестового примера и т. Д. Ты должен сделать все это но об этом много написано.

На самом деле, готовясь к этому блогу, я взял много книг по отладке на Amazon. Оказывается, большинство из них не являются “настоящими” книгами по отладке. Да, они освещают это в одной или двух главах. Остальная часть книги всегда посвящена процессу, тестовым случаям и всему, что его окружает. Я думаю, это было бы справедливо, если бы отладка не была огромной темой, которая может заполнить целую книгу. На мой взгляд, это действительно возможно, и я только начинаю.

Настройтесь на большее!

Оригинал: “https://dev.to/codenameone/the-debugger-checklist-part-ii-198e”