Детерминированная производительность компиляторов C (или любых других)

StackOverflow https://stackoverflow.com/questions/60547

Вопрос

Во время работы над недавним проектом меня посетил представитель отдела контроля качества клиентов и задал мне вопрос, о котором я раньше не задумывался:

Откуда вы знаете, что используемый вами компилятор генерирует машинный код, который точно соответствует функциональности кода C, и что компилятор полностью детерминирован?

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

Я понимаю, что, возможно, это не проблема для всех, и на самом деле ответ может быть просто...«Вы уже в тупике и смиритесь с этим».Однако при работе во встроенной среде вы безоговорочно доверяете своему компилятору.Как я могу доказать себе и QA, что я прав?

Это было полезно?

Решение

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

Еще один подход заключается в том, чтобы вообще НЕ доверять выводам компилятора.Любые недостатки компилятора и даже зависящие от языка (приложение G стандарта C-90, кто-нибудь?) должны быть покрыты строгим набором статического анализа, модульного тестирования и тестирования покрытия в дополнение к более позднему функциональному тестированию.

Стандарт вроде МИСРА-С может помочь ограничить входные данные компилятору «безопасным» подмножеством языка C.Другой подход состоит в том, чтобы ограничить входные данные компилятора подмножеством языка и проверить, каковы выходные данные для всего подмножества.Если наше приложение построено только из компонентов из подмножества, предполагается, что известно, какими будут выходные данные компилятора.Обычно это происходит по «квалификации компилятора».

Цель всего этого - иметь возможность ответить на вопрос представителя QA: «Мы не просто полагаемся на детерминизм компилятора, но и доказываем это таким образом...».

Другие советы

Вы можете применить этот аргумент на любом уровне:доверяете ли вы сторонним библиотекам?ты доверяешь ОС?ты доверяешь процессору?

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

Аналогичные вопросы были подняты и об алгоритмах шифрования: откуда нам знать, что в DES нет лазейки, через которую АНБ могло бы шпионить?

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

Вы узнаете это путем тестирования.Когда вы тестируете, вы тестируете и свой код, и компилятор.

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

Доступны костюмы для проверки компилятора.
Тот, который я помню, это «Многолетник».

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

Все сводится к доверию.Доверяет ли ваш клиент какому-либо компилятору?Используйте это или хотя бы сравните выходной код между вашим и их.

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

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

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

Откуда вы знаете, что используемый вами компилятор генерирует машинный код, который точно соответствует функциональности кода C, и что компилятор полностью детерминирован?

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

Единственное программное обеспечение, которое я сертифицировал, — это авионика.Сертификация FAA не является достаточно строгой, чтобы доказать правильность работы программного обеспечения, но в то же время она заставляет вас преодолевать определенное количество препятствий.Хитрость заключается в том, чтобы структурировать ваш «процесс» так, чтобы он максимально улучшал качество, с минимальными посторонними прыжками с обруча, которые вам сойдет с рук.Так что все, что вы знаете, бесполезно и не приведет к обнаружению ошибок, вы, вероятно, можете уйти.И все, что ты знаешь, ты должен делать, потому что это воля находите ошибки, о которых FAA явно не запрашивает, лучше всего искажать слова до тех пор, пока не будет звучать так, будто вы даете FAA/вашим специалистам по контролю качества то, о чем они просили.

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

Некоторые интеллектуальные боеприпасы можно найти в Crosstalk, журнале для разработчиков оборонного программного обеспечения.На этот вопрос они тратят много часов бодрствования. http://www.stsc.hill.af.mil/crosstalk/2006/08/index.html (Если я смогу найти свои старые заметки из старого проекта, я вернусь сюда...)

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

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

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

... вы безоговорочно доверяете своему компилятору

Вы перестанете это делать, как только впервые столкнетесь с ошибкой компилятора.;-)

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

Хорошо..вы не можете просто сказать, что доверяете выводам вашего компилятора, особенно если вы работаете со встроенным кодом.Нетрудно найти расхождения между кодом, сгенерированным при компиляции одного и того же кода разными компиляторами.Это происходит потому, что сам стандарт C слишком свободен.Многие детали могут быть реализованы по-разному разными компиляторами без нарушения стандарта.Как нам поступить с этим?Мы избегаем конструкций, зависящих от компилятора, когда это возможно.Мы можем справиться с этим, выбрав более безопасное подмножество C, например Мисра-С как ранее упоминалось пользователем cschol.Мне редко приходится проверять код, сгенерированный компилятором, но со мной такое тоже иногда случалось.Но в конечном итоге вы полагаетесь на свои тесты, чтобы убедиться, что код ведет себя так, как задумано.

Есть ли лучший вариант?Некоторые утверждают, что есть.Другой вариант — написать свой код в СПАРК/Ада.Я никогда не писал код в SPARK, но насколько я понимаю, вам все равно придется связать его с подпрограммами, написанными на C, которые будут работать с «голым железом».Прелесть SPARK/Ada в том, что вы абсолютно уверены в том, что код, сгенерированный любым компилятором, всегда будет одним и тем же.Никакой двусмысленности.Кроме того, язык позволяет вам аннотировать код с пояснениями того, как код должен вести себя.Набор инструментов SPARK будет использовать эти аннотации для формального доказательства того, что написанный код действительно делает то, что описано в аннотациях.Мне сказали, что для критически важных систем SPARK/Ada — довольно хороший выбор.Хотя сам я никогда этого не пробовал.

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

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

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

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

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

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

Попробуйте модульное тестирование.

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

Или предложите написать свой собственный компилятор и сообщите им, сколько это будет стоить.

Максимум, что вы можете легко подтвердить, — это то, что вы используете незащищенный компилятор от поставщика X.Если они не доверяют провайдеру X, это их проблема (если X достаточно надежен).Если они не доверяют ни одному поставщику компилятора, то это совершенно неразумно.

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

Все остальное начинает открывать банку с червями.«Надо где-то остановиться», — говорит Роб.

Иногда вы действительно получаете изменения в поведении, когда запрашиваете агрессивный уровень оптимизации.

А оптимизация и числа с плавающей запятой?Забудь это!

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

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

  1. Используйте квалифицированного компилятора, где «квалифицированный» означает, что он был проверен в соответствии со стандартами, установленными регулирующим органом.
  2. Выполните анализ объектного кода.По сути, вы компилируете фрагмент эталонного кода, а затем вручную анализируете выходные данные, чтобы продемонстрировать, что компилятор не вставил никаких инструкций, которые нельзя отследить до вашего исходного кода.

Вы получаете то, что написал Дейкстра.

Выберите официально проверенный компилятор, например компилятор Compcert C.

  1. Изменение уровня оптимизации компилятора приведет к изменению вывода.
  2. Небольшие изменения в функции могут сделать компилятор встроенным или перестать встраивать функцию.
  3. Изменения в компиляторе (например, версии gcc) могут изменить выходные данные.
  4. Некоторые библиотечные функции могут быть встроенными (т. е. создавать оптимизированную сборку), тогда как большинство других таковыми не являются.

Хорошей новостью является то, что для большинства вещей это не имеет большого значения.Там, где это так, вы можете рассмотреть возможность сборки, если это действительно важно (например, в ISR).

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

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

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

Я думаю, что можно свести эту проблему к Проблема с остановкой как-то.

Самая очевидная проблема заключается в том, что если вы используете какую-то программу для анализа компилятора и его детерминированности, откуда вы знаете, что твой программа компилируется правильно и выдает правильный результат?

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

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

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

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

Если ты беспокоишься о злонамеренный ошибки в компиляторе, одна из рекомендаций (IIRC, требование АНБ для некоторых проектов) заключается в том, что двоичный файл компилятора предшествует написанию кода.По крайней мере, тогда вы знаете, что ни у кого нет добавлен ошибки, нацеленные на твой программа.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top