Статьи

Шаблоны отладки для утечек ресурсов

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

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

На практике, однако, история сильно отличается. В некоторых крупных компаниях, особенно в области корпоративного программного обеспечения, есть отдельные группы, которые специально работают над устранением ошибок в поставляемых продуктах. Они называются непрерывной разработкой продукта (CPD), или командой по развитию / поддержке. Как только продукт поставляется, команда разработчиков в основном передает собственность этой команде, которая решает любые проблемы, замеченные клиентом.

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

Drudgerous Искусство отладки

Независимо от качества работ, выполняемых на этапах проектирования и разработки программного обеспечения, ошибки неизбежны.

На этапе тестирования инженеры Quality Assurance (QA) находят и регистрируют ошибки, и разработчики должны устранить эти ошибки до того, как продукт будет поставлен. Это время, затрачиваемое на отладку, которое обычно задерживает выпуск продукта, поэтому разработчики испытывают огромное давление, чтобы исправить эти ошибки как можно скорее.

Команда разработчиков обычно может позволить себе роскошь установки QA, где ошибка воспроизводима на этом этапе, что делает этот процесс немного менее болезненным. С помощью этой настройки разработчик может внести изменения в код продукта, чтобы добавить диагностическую информацию или другие изменения, которые могут помочь в поиске основной причины дефекта. Патчи, содержащие такие изменения, которые служат исключительно для диагностики обнаруженного дефекта, называются «отладочные патчи».

Отладочные патчи допускают такие свободы, как побочные эффекты, которые могут изменить нормальное приемлемое поведение продукта. Во внутренних / внутренних средах выпуском исправления отладки и привлечением инженера QA для его тестирования там, где обнаружена проблема, является обычным явлением. Такие исправления, которые будут запускаться только на системах / машинах, предназначенных для использования инженерами по обеспечению качества, могут изменить поведение продукта, и даже если изменения, внесенные разработчиком, будут иметь неблагоприятные побочные эффекты для настройки обеспечения качества, это всегда можно воссоздать. Излишне говорить, что ни один клиент (особенно корпоративный клиент) никогда не согласится запускать сборки, которые не проверены и не одобрены командой QA и сопровождаются предупреждениями.

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

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

Бремя ошибок не заканчивается доставкой товара клиенту. Некоторые ошибки могут даже подсунуться команде QA и оказаться в выпущенном продукте, который используется клиентом, который заплатил за программное обеспечение. Есть много причин, по которым ошибка, замеченная клиентом, могла быть пропущена командой QA, например:

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

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

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

Обнаруженные заказчиком ошибки обычно называются эскалациями. Устранение ошибок на этом этапе более важно, чем раньше (потому что теперь клиент должен ждать). Если своевременное решение не предусмотрено, они могут повлиять на конкурента вашего продукта, и это повлияет на будущее продукта и, в конечном счете, на саму компанию. Кроме того, любое время, затрачиваемое на эскалации, является временем, отведенным для работы над текущим / будущим выпуском.

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

Страшные утечки ресурсов

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

С точки зрения кода ресурсы распределяются / получаются с помощью системного вызова (malloc / open) и высвобождаются обратно в систему с помощью другого (free / close). Утечки возникают, когда выделенный ресурс не освобождается, когда ресурс выполнил свою задачу.

Помимо замораживания процесса, вызывающего утечку, утечки ресурсов могут привести к полной остановке всей системы, в которой выполняется процесс, если она не обнаружена и не устранена на ранней стадии. Эти последствия явно ужасны. Например, рассмотрим процесс с утечкой сокетов, медленным использованием всех доступных сокетов и их закрытием. В какой-то момент система, в которой выполняется процесс, выйдет из сокетов, и другие процессы не смогут получить их при необходимости. Очень скоро никто не сможет получить доступ к системе из Secure Shell и т. Д.

Эти ошибки могут легко проскочить цикл QA, поскольку для их проявления требуется период времени, даже при наличии надежного графика испытаний на выносливость. Есть инструменты, которые очень эффективны против них, но они не могут быть использованы во всех средах; например, не многие клиенты захотят установить такой инструмент, как valgrind (очень мощный инструмент обнаружения утечек памяти) в своей производственной среде.

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

Шаблоны для отладки утечек ресурсов

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

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

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

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

1. Где проблема?

Первым шагом в отладке утечки ресурсов является определение уровня, на котором существует проблема. Цель здесь — выяснить, есть ли ошибка в самом процессе или в платформе.

С точки зрения кода, вопрос, который нужно задать: была ли вызвана функция release? Например, если функция free () действительно была вызвана для определенного фрагмента памяти, но все еще лежит, это означает, что проблема в уровне ОС, а не в приложении.

Ниже приведены некоторые способы выяснить, пропускаем ли мы вызовы на закрытие / освобождение:

  1. Если утечка воспроизводима в настройках, доступных в компании, мы можем регистрировать / подсчитывать каждую функцию выделения и выпуска. В противном случае мы должны полагаться на пакет журналов и искать в журнале выбросы вокруг функции получения и выпуска в качестве указания. Например, рассмотрим следующий псевдокод:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    function foo()
       ….
       log.message(“ABCD”)
       ...
       variablea = acquire_function()
      ...
    function bar()
       ….
       log.message(“WXYZ”)
       ...
       release_function(variablea)

    Если в журналах мы видим ABCD, но не WXYZ, скорее всего, переменная a просочилась, так как не была вызвана функция release_function.

  2. Используйте средства обнаружения утечек, которые можно применять в различных средах. Например:; Valgrind , WinDbg и т. Д. К сожалению, это относится только к внутренней среде. Даже здесь некоторые из мощных инструментов значительно замедляют процессы и могут сделать процесс проверки бесполезным. Ни один клиент никогда бы не позволил установить такой инструмент на производственной установке.
  3. Ищите признаки того, что операционная система может предоставить. Например, некоторые операционные системы предоставляют информацию о запущенных процессах, которые включают количество открытых сокетов и их состояния, которые сообщат вам, был ли вызван метод close.
  4. Разработайте свой собственный инструмент / технику для подсчета / аудита функций получения и выпуска. Наилучшим способом решения этой проблемы может быть разработка инструмента, включение его в состав продукта и возможность запуска продукта в режиме отладки, который выдает необходимую информацию. Некоторые платформы допускают подобные конструкции с использованием LD_PRELOAD, _malloc_hook_ и т. Д. Они позволяют вам обернуть вокруг выделения, выпустить функции и внедрить код, который отслеживает то, что нужно.
  5. Если ничего не помогает, вам придется выпустить исправление отладки даже для клиента. Было бы идеально, если бы в патче было только дополнительное ведение журнала, но это все равно нужно сделать, чтобы пройти базовую проверку качества, чтобы гарантировать, что патч не будет иметь неблагоприятных побочных эффектов в среде заказчика.

2. Какой ресурс подтекает?

Следующий шаг — выяснить, какой экземпляр ресурса вызывает утечку. С точки зрения кода, вопрос, который нужно задать: является ли причиной утечки памяти объект X или Y? Если вы использовали часть A, B, D или E для ответа на вопрос 1, ответ на этот вопрос следует непосредственно.

Например, 1A прямо говорит вам, что проблема в приложении, а не в платформе, и что переменная A протекает. Однако, если вы использовали часть C, чтобы ответить на вопрос 1, или если каким-то образом вы уже знаете, что в платформа / ОС, этот ответ не ясен, поэтому вам придется использовать другие шаги, чтобы найти свой ответ.

3. Где используется ресурс?

Учитывая, что мы знаем, что объект X протекает, он может использоваться в ряде рабочих процессов / сценариев использования в коде. Например, в фрагменте псевдокода в 1A, foo может вызываться в нескольких местах, поэтому нам нужно выяснить, какой из этих вызовов оставляет объект позади, а какой вариант использования продукта вызывает это. На уровне кода нам нужно знать трассировку вызовов кода, которые приводят к получению этого конкретного объекта.

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

Если это окажется непрактичным в течение доступного времени, создайте дерево вызовов, сопоставляя журналы и код, и переходите оттуда. Возвращаясь к нашему примеру в 1А, мы знаем, что вызовы foo получают, и нам нужно увидеть, где весь foo вызывается прямо с точки входа в процесс (например, основная функция в C / C ++ / Java).

Виновником является ветвь в дереве вызовов foo, у которой нет соответствующей ветки в столбце bar.

4. Почему ресурс просачивается?

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

Например, в 1A нам нужно выяснить, почему не вызывается код, который вызывает bar, или, если мы знаем, что вызывается bar, почему не вызывается release_function (переменная A). Это делается путем сопоставления того, что видно в журналах и коде.

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

Последнее слово совета

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

Говорят, что «лучший удар — тот, который вам не нужно наносить» (или что-то в этом роде). Точно так же, лучший способ устранить ошибку — это сначала ее избежать или поймать в настройке QA перед ее отправкой.

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

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

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

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

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

Я успешно использовал последовательность шагов, описанную не раз, и надеюсь, что вы тоже.

Опубликовано на Java Code Geeks с разрешения Сиддхарта Самбамурти, партнера нашей программы JCG. Посмотрите оригинальную статью здесь: Отладочные шаблоны для утечек ресурсов

Мнения, высказанные участниками Java Code Geeks, являются их собственными.