В разработке масштабных программных проектов, таких как СУБД PostgreSQL, крайне важна возможность оценивать качество тестирования. Ключевую роль здесь играет анализ показателей покрытия кода (code coverage), который позволяет увидеть, какие фрагменты кода были задействованы во время тестов, а какие — нет. Это помогает обнаружить потенциальные не покрытые тестами «белые пятна» и, как следствие, повысить надежность системы.

Однако при использовании компилятора clang для сбора покрытия кода в PostgreSQL разработчики сталкивались с проблемой, ограничивающей возможности этого инструмента.
Как тесты ломали покрытие и при чём тут коллизии хэшей
Тестирование — важная составляющая разработки. Она позволяет удостовериться, что изменения в одной части проекта не приводят к ошибкам в других, связанных с ней, частях. Особенно это важно в больших проектах, где связи между компонентами не всегда очевидны.
Для комплексной проверки кода разработчики используют тесты и измеряют покрытие кода. Это позволяет понять, какая строка кода была затронута при выполнении теста, и фиксирует эту информацию в режиме реального времени. Полученные данные можно обработать и преобразовать в понятные форматы для дальнейшего анализа.
Это помогает увидеть, насколько хорошо тестируется программа, и все ли участки кода проверяются должным образом. Если какие-то части кода не были затронуты при тестировании, это сигнал, что там могут быть ошибки, о которых разработчики узнают только на этапе эксплуатации ПО.
При записи данных о покрытии на лету clang использует хэширование для генерации имен файлов. В идеале, различные входные данные должны порождать различные хэши, которые используются для именования файлов. Но на практике возможны коллизии — ситуации, когда разным входным данным соответствует один и тот же хэш.
Проблему порождал тот факт, что тесты PostgreSQL весьма интенсивны и выполняются в несколько потоков. Слабый хэш, применяемый clang, приводил к возникновению коллизий: несколько процессов, выполнявших тесты, пытались записать информацию о покрытии в один и тот же файл, что вызывало ошибки и повреждение метрик покрытия. .Чтобы не сталкиваться с подобными ошибками сообщество предпочитало использовать компилятор gcc.
Решение проблемы: binary ID как дополнение к хэшу
Разработчик Postgres Professional Николай Шаплов, тесно сотрудничая с Институтом системного программирования РАН (ИСП РАН), нашёл решение. Он сообщил о проблеме компилятора clang и одному из студентов — Артёму Сенкевичу — была дана задача решить проблему с коллизиями.
Специалисты ИСП РАН модифицировали компилятор clang, добавив к имени файла с информацией о покрытии дополнительный параметр — binary ID. Этот параметр, используемый вместе с хэшем, позволил однозначно именовать каждый файл и свести к нулю коллизии.
Исправление уже принято в основную ветку llvm. Сейчас версия clang, содержащая патч, всё ещё находится в стадии разработки. Чтобы воспользоваться найденным решением, нужно:
Собрать clang из главной ветки разработки (где патч уже применен).
Скомпилировать PostgreSQL, используя флаги -fprofile-instr-generate -fcoverage-mapping -Wl,--build-id. Флаг -Wl,--build-id отвечает за добавление binary ID в метаданные программы.
Запуская тесты с покрытием, задать переменную окружения LLVM_PROFILE_FILE, указав в формате имени файла %m (хэш) и %b (binary ID).
В дальнейшем, при работе с clang нужно будет использовать более свежую версию компилятора и не забывать при запуске тестов с покрытием применять информацию из пунктов 2 и 3.
Что изменилось для сообщества PostgreSQL
Устраненная проблема мешала использовать clang для задач SDL.И теперь разработчики могут не переключаться между компиляторами gcc и clang, а комфортно работать с одним инструментом.
Теперь же, благодаря внесенным изменениям, сообщество PostgreSQL получило возможность применять clang для сбора покрытия кода, не опасаясь коллизий хэшей. Это упрощает разработку, повышает качество тестирования и, как результат, способствует созданию еще более надежной и стабильной СУБД.
Важно, что патч вошел в основную ветку и станет доступен в релизе 21. Таким образом, решение проблемы стало частью открытого проекта clang, принося пользу всему сообществу разработчиков, а не только пользователям PostgreSQL.