А.В. Ахо, М.С. Лам, Р. Сети, Дж. Д. Ульман - Компиляторы - принципы, технологии и инструментарий (1114947), страница 11
Текст из файла (страница 11)
В отличие от компиляторов для языков программирования, такие инструменты зачастую выполняют оптимизацию очень длительное время — часами. Существуют также методы трансляции проектов на более высоких уровнях, таких как, например, функциональный. 1.5. Применения технологий компиляторов Интерпретаторы запросов к базам данных Языки полезны не только в программном и аппаратном обеспечении, но и во многих приложениях, например языки запросов, в особенности В( Н (В1гцсщгед Опегу Ьапяцаке — язык структурированных запросов), использующиеся для выполнения поиска в базах данных. Запросы к базам данных состоят из предикатов, содержащих реляционные и булевы операторы.
Они могут интерпретироваться или компилироваться в команды для поиска в базе данных записей, удовлетворяющих указанному предикату. Компилируемое моделирование Имитационное моделирование (з1пш!а11оп) представляет собой метод, используемый во многих научных и инженерных дисциплинах, например, для лучшего понимания явлений или проверки конструкций. Входными данными для имитатора обычно включают описание проекта и конкретные входные параметры для запуска моделирования определенной ситуации.
Имитация может быть очень дорогой. Обычно требуется имитировать множество возможных альтернативных проектов для разных входных данных, причем каждый эксперимент может занимать дни вычислений на высокопроизводительных машинах. Вместо написания имитатора, интерпретирующего проект, стоит разработать компилятор, который даст на выходе машинный код, что приведет к выигрышу времени.
Такое компилируемое моделирование может ускорить работу на порядки по сравнению с интерпретатором. Этот метод используется во многих реальных инструментах, имитирующих проекты, написанные на ЧеП1оя или ЧНР) . 1.5.5 Инструментарий для повышения производительности программного обеспечения Программы, вероятно, являются наиболее сложным продуктом, производимым человеком. Они состоят из множества фрагментов, каждый из которых должен быть совершенно безошибочным, чтобы программа работала корректно. Ошибки в программах — настоящий бич; они могут приводить к краху системы, получению неверных результатов, делать систему уязвимой для внешних атак и даже приводить к катастрофическим последствиям, таким как выход из строя техники и даже гибель людей. Главный способ выявления ошибок в программах— тестирование.
Интересным и многообещающим дополнительным подходом является использование анализа потока данных для статического (до запуска программы) обнаружения ошибок. В ходе анализа потоков данных можно обнаружить ошибки во всех возможных путях выполнения, а не только в тех, которые реально выполняются для данного входного набора данных, как в случае тестирования программы. Множество методов анализа потоков данных, изначально разработанных Глава 1. Введение в компиляцию для оптимизации, могут использоваться для создания инструментов, помогающих программисту при разработке программного обеспечения. Задача поиска всех ошибок в программе неразрешима.
Анализ потоков данных может предупредить программиста обо всех возможных инструкциях, которые могут приводить к ошибкам определенной категории. Однако, если большинство таких предупреждений окажутся ложной тревогой, программист не будет пользоваться подобным инструментом. Поэтому на практике такие инструменты обнаружения ошибок оказываются не надежными и не всеохватывающим, т.е, они не обнаруживают все ошибки в программе и не все ошибки, которые они обнаруживают, на самом деле являются ошибками.
Тем не менее различные методы статического анализа продемонстрировали свою эффективность в поиске ошибок, таких как разыменование нулевого и освобожденного указателя, в реальных программах. Факт ненадежности таких детекторов ошибок существенно отличает их от оптимизации компилятором. Оптимизация обязана быть консервативной и не может изменять семантику программы ни при каких условиях.
В оставшейся части данного подраздела мы упомянем несколько путей, которыми методы анализа программ (основанные на технологиях, изначально предназначенных для оптимизации кода компиляторами) могут повысить производительность программного обеспечения. Особо важны методы, позволяющие статически выявить уязвимые места в системе безопасности программ.
Проверка типов Проверка типов — эффективная и хорошо обоснованная технология поиска несогласованностей в программе. Она может использоваться для поиска ошибок, например, когда операция применяется к объекту неверного типа или когда фактические параметры процедуры не соответствуют ее сигнатуре. Анализ программы может выполняться после поиска ошибок типов путем анализа потока данных в программе. Например, если указателю присваивается значение пц11 и тут же выполняется его разыменование, очевидно, что программа содержит ошибку. Тот же метод может использоваться для поиска различных "дыр" в системе безопасности, когда программе может быть передана строка или иные данные, которые неосторожно используются программой. Передаваемая пользователем строка может быть помечена как имеющая тип "опасный", и если не выполнена проверка корректности формата такой строки, она остается "опасной".
Если строка с таким типом может влиять на поток управления в некоторой точке программы, то это потенциальное нарушение безопасности программы, Проверка диапазона Оказывается, гораздо легче допустить ошибку при программировании на низком уровне, чем на высоком. Например, многие нарушения безопасности вызываются переполнением буфера в программе, написанной на С. Поскольку в С 57 !.6. Азы языков программирования нет средств проверки диапазона массива, программист должен сам обеспечить невозможность обращения к памяти за пределами массива. Если в программе не выполняются проверки того, что предоставляемые пользователем данные не вызывают переполнения буфера, то программа может оказаться взломанной путем размещения части данных за пределами отведенного буфера.
Может быть предпринята атака путем манипуляции входными данными таким образом, что программа начинает вести себя неверно и подвергает риску безопасность системы. Имеются методы, разработанные для поиска возможных переполнений буфера в программах, но пока что их успешность весьма ограничена. Если же программа написана на безопасном языке с автоматической проверкой лиапазона, то такая проблема никогда в ней не возникнет.
Тот же анализ потоков данных, который используется для устранения излишних проверок диапазона, может использоваться и для поиска переполнений буфера. Основное отличие, однако, заключается в том, что если ошибка при удалении проверки диапазона может привести к небольшому замедлению работы программы, то ошибка при обнаружении потенциального переполнения буфера может привести к нарушению безопасности системы.
Таким образом, в то время как для оптимизации проверки диапазона вполне адекватны простые методы, для получения высококачественных результатов в инструментах для поиска ошибок должны использоваться сложные технологии анализа, такие как отслеживание значений указателей при выполнении процедур. Инструментарий для управления памятью Сборка мусора — еще один прекрасный пример компромисса между эффективностью и сочетанием простоты программирования и надежности программного обеспечения.
Автоматическое управление памятью устраняет все ошибки работы с памятью (например, утечки памяти), которые являются основным источником проблем в программах С и С+-ь. Для помощи программисту в поисках ошибок управления памятью разработаны разнообразные инструменты. Например, широко используется такой инструмент, как Раппу, который динамически отлавливает ошибки управления памятью. Разработаны также инструменты, которые могут выявлять некоторые проблемы статически, без выполнения программы. 1.6 Азы языков программирования В этом разделе мы рассмотрим наиболее важные термины и понятия, встречающиеся при изучении языков программирования. Нашей целью не является рассмотрение всех концепций всех распространенных языков программирования.
Мы полагаем, что читатель знаком как минимум с одним из языков программирования, С, С++ или 3ача, и сталкивался с другими языками программирования. 58 Глава 1. Введение в компиляцию 1.6.1 Понятия статического и динамического Среди наиболее важных вопросов, с которыми мы встречаемся при разработке компилятора для языка программирования, является вопрос о том, какие решения может принимать компилятор. Если язык использует стратегию, позволяющую компилятору принимать решения, то мы говорим, что язык использует статическую стратегию или что вопросы могут решаться во время компиляции. С другой стороны, стратегия, которая позволяет принимать решения только в процессе выполнения программы, называется динамической или принимающей решения во время выполнения.