Мёртвый код

В теории компиляторов, мёртвым кодом (англ. dead code, также отмершим кодом, бесполезным кодом, неиспользуемым кодом) называют код, который может быть исполнен (в существующей на текущий момент версии кодовой базы), исполнялся или мог быть исполнен ранее (до внесения в код на каком-то из моментов его существования изменений, сделавших его бесполезным), но результаты его вычислений не влияют на дальнейшую программу (в частности, не используются)[1][2][3]. Другими словами, это код, определяющий только мёртвые переменные[англ.] или вообще не определяющий никакие переменные.

При рассмотрении исходного кода, часто используют другое, более общее понятие мёртвого (отмершего) кода, которое, кроме бесполезного кода, включает в себя недостижимый код[4][5].

Наличие мёртвого кода в программе увеличивает её размер, давление на ресурсы (устройства, регистры), тепловыделение и может увеличить время исполнения, не неся при этом никакой пользы. В оптимизирующих компиляторах для выявления и удаления мёртвого и недостижимого кодов на уровне промежуточного представления используются оптимизации удаления мёртвого кода и удаления недостижимого кода. Для поиска мёртвого кода в исходном коде применяются всевозможные анализаторы и детекторы мёртвого кода[4][5]. Такие анализаторы часто бывают встроены в компилятор или IDE и выдают соответствующие предупреждения о наличии в программе мёртвого кода во время её компиляции[6][7][8].

Примеры

Рассмотрим следующий пример на языке Си:

 int foo(int x, int y)
 {
   int z; /* Объявление мёртвой переменной */
   z = x/y; /* Мёртвый код */
   return x*y;
 }

Здесь, операция z = x/y является мёртвым (бесполезным) кодом, так как результат работы этой операции, переменная z, в дальнейшем в программе не используется. Сама переменная z является мёртвой в процедуре foo. Если переменная y равна нулю, то операция, выполняющая бесполезное вычисление, спровоцирует исключение, следовательно её удаление, возможно, способно изменить вывод программы. Оптимизация удаления мёртвого кода удалит операцию z = x/y, только если не будет сомнений в том, что из-за этого не изменится результат работы программы[9].

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

 int foo(void)
 {
   int x = 25;
   return x;
   x = 2*x; /* Недостижимый код */
   return 0; /* Недостижимый код */
 }

Здесь, операции x = 2*x и return 0 не могут быть исполнены ни при каких условиях, так как они происходят после безусловного возврата из процедуры и являются недостижимыми (операции, стоящие после возврата из процедуры могут и не являться недостижимым кодом, например, если на метку, стоящую после возврата ссылается оператор goto). Оптимизация удаления недостижимого кода может удалить эту операцию.

Анализ

Для выявления и удаления бесполезного кода, оптимизация удаления мёртвого кода использует результаты анализа потока данных[англ.] (например анализа активных переменных[англ.]) или осуществляет самостоятельный анализ SSA-представления программы. Оптимизация удаления недостижимого кода анализирует граф потока управления и устраняет недостижимые узлы.

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

В исходном коде больших приложений бывает сложно распознать мёртвый код (бесполезный и недостижимый). Для этого могут применяется детекторы мёртвого кода[4][5], выполняющие статический анализ кода. Многие компиляторы и IDE выдают предупреждения об объявленных, но неиспользуемых функциях, методах, классах, переменных[6][7][8].

Мёртвый код и информационная безопасность

Для сокрытия алгоритмов, используемых в программе, с целью защиты интеллектуальной собственности, мёртвый код может добавляется в программу намеренно, в качестве затеняющего преобразования. Такое преобразование призвано увеличить энтропию кода, чтобы затруднить восстановление алгоритма, реализованного в программе. Так же, в целях затенения, в программу может добавляться недостижимый некорректный код: во время работы программы такой участок кода никогда не исполняется и не вызывает ошибок, но дизассемблер или декомпилятор во время работы с этим участком кода могут повести себя непредсказуемо[10][11].

Наличие в программе мёртвого и недостижимого кодов может являться уязвимостью, так как в такие участки кода могут внедряться программные закладки[12][13].

См. также

Примечания

  1. Engineering a Compiler — С. 544.
  2. Компиляторы — принципы, технологии, инструменты — С. 713, 714.
  3. Debray, S. K., Evans, W., Muth, R., and De Sutter, B. 2000. Compiler techniques for code compaction Архивная копия от 22 мая 2003 на Wayback Machine. ACM Trans. Program. Lang. Syst. 22, 2 (Mar. 2000), 378—415. (summary Архивная копия от 11 сентября 2004 на Wayback Machine)
  4. 1 2 3 Dead code detection and removal. Aivosto. Дата обращения: 12 июля 2012. Архивировано 5 августа 2012 года.
  5. 1 2 3 Compares some free alternatives to DCD (Dead Code Detector). Java.net. Дата обращения: 12 июля 2012. Архивировано из оригинала 23 сентября 2012 года.
  6. 1 2 GCC online documentation. Options to Request or Suppress Warnings. GNU Compiler Collection. Дата обращения: 12 июля 2012. Архивировано 25 июня 2012 года.
  7. 1 2 JDT Plug-in Developer Guide. Compiling Java code. eclipse.org. Дата обращения: 22 октября 2018. Архивировано из оригинала 25 июня 2012 года.
  8. 1 2 Discover dead code in your application using Code Analysis. Habib Heydarian, Microsoft Corp.. Дата обращения: 12 июля 2012. Архивировано 23 сентября 2012 года.
  9. 1 2 Appel, A. W. Modern Compiler Implementation in Java. — Cambridge University Press, 2004. — С. 360. — ISBN 0-511-04286-8.
  10. И. Ю. Иванов / Киевский национальный университет им. Тараса Шевченко / О проблемах защиты интеллектуальной собственности в программных системах // Проблеми прграмування. — 2006. — № 2-3 Спеціальний випуск — С. 68-72. (текст Архивная копия от 16 января 2014 на Wayback Machine)
  11. Обфускация и её преодоление // Лаборатория взлома. — Май 2006. — С. 8-13. (текст)
  12. Торшенко Ю. А. / СПб ГУ ИТМО / Модель и метод обнаружения уязвимостей на начальных этапах промышленного проектирования программного продукта. — 2008. (текст Архивная копия от 24 января 2022 на Wayback Machine)
  13. Сакулина М. С. / Выявление и устранение «мертвого кода» с использованием технологии программирования IBM Rational Application Developer. (текст Архивировано)

Литература

  • Cooper and Torczon. Engineering a Compiler. — Morgan Kaufmann, 2011. — С. 544—550, 593. — ISBN 978-0-12-088478-0.
  • Ахо, Альфред В.; Сети, Рави; Ульман, Джеффри Д. Компиляторы — принципы, технологии, инструменты. — Вильямс, 2003. — С. 713, 714, 733, 734. — ISBN 5-8459-0189-8.
  • Muchnick, Steven S. Advanced Compiler Design and Implementation. — Morgan Kaufmann Publishers, 1997. — С. 592—597. — ISBN 1-55860-320-4.

Ссылки