Uploaded by Руслан Ахметзянов

Объяснение CTest и CPack

advertisement
СЛАЙД 3
Язык программирования C — это высокоуровневый язык программирования общего назначения
C предоставляет баланс между выразительностью высокоуровневых языков программирования и
контролем над низкоуровневыми операциями.
СЛАЙД 4
Компиляция программы — это процесс преобразования исходного кода, написанного на языке
программирования высокого уровня (например, C, C++, Java), в машинный код, который может
исполняться на компьютере. Этот процесс обычно состоит из нескольких стадий, каждая из
которых выполняет определенную задачу. Рассмотрим основные стадии компиляции:
1. Препроцессинг (Preprocessing)
На этапе препроцессинга происходит обработка исходного кода до его фактической компиляции.
Препроцессор выполняет директивы препроцессора, такие как включение заголовочных файлов
(#include), макрозамену (#define), условную компиляцию (#ifdef, #ifndef, #endif) и удаление
комментариев. Результатом этой стадии является "расширенный" исходный код, готовый к
компиляции.
2. Компиляция (Compilation)
На стадии компиляции "расширенный" исходный код преобразуется в ассемблерный код или в
промежуточное представление. Компилятор анализирует код, проверяет синтаксис и семантику
программы, выполняет оптимизацию кода для улучшения производительности и, наконец,
генерирует ассемблерный код соответствующий архитектуре целевой машины.
3. Ассемблирование (Assembly)
Ассемблер преобразует ассемблерный код в машинный код, представленный в виде объектных
файлов. Машинный код состоит из инструкций, которые может непосредственно исполнять
процессор компьютера. Этот процесс включает в себя преобразование ассемблерных инструкций
и адресов данных в числовые коды, которые понимает процессор.
4. Линковка (Linking)
На последней стадии, линкер объединяет один или несколько объектных файлов с библиотеками,
чтобы сформировать исполняемый файл. Линковка включает в себя разрешение ссылок на
внешние функции и переменные, которые были объявлены, но не определены в исходном коде
программы. Линкер также определяет окончательные адреса функций и переменных,
обеспечивая, что все ссылки в коде корректно указывают на их местоположение.
СЛАЙД 5
Пример компиляции, как скомпилировать
СЛАЙД 6
Пример как получить промежуточные файлы
СЛАЙД 7
Как работает препроцессинг
1. Обработка Директив Препроцессора
Препроцессор обрабатывает все директивы препроцессора в исходном коде, такие как #include,
#define, #ifdef, #ifndef, #endif, #if, #else, и #elif. Эти директивы выполняют различные функции:
#include вставляет содержимое указанного файла в текущий исходный файл. Это обычно
используется для включения заголовочных файлов, содержащих объявления функций и макросов.
#define создает макросы, которые заменяются их значениями во время препроцессинга.
#if, #ifdef, #ifndef, и связанные с ними директивы позволяют условно компилировать части кода в
зависимости от определенных условий.
2. Макрозамена
Препроцессор заменяет все макросы, определенные директивами #define, их соответствующими
значениями в тексте программы. Это включает в себя как простые замены текста, так и макросы с
аргументами, которые действуют подобно функциям.
3. Удаление Комментариев
Препроцессор удаляет все комментарии из исходного кода, оставляя только исполняемый код и
директивы препроцессора. Это означает, что комментарии не влияют на результат компиляции и
служат только для удобства разработчиков.
4. Включение Условной Компиляции
Используя директивы условной компиляции (#if, #ifdef, #ifndef), препроцессор определяет, какие
участки кода должны быть включены в конечный исходный файл в зависимости от определенных
условий. Это позволяет разработчикам создавать код, который может быть скомпилирован поразному в зависимости от конфигурационных опций или целевых платформ.
5. Генерация Расширенного Исходного Кода
После обработки всех директив препроцессора, макрозамены и удаления комментариев,
препроцессор генерирует расширенный исходный код. Этот код уже не содержит директив
препроцессора и комментариев и готов к следующему этапу компиляции.
СЛАЙД 8
На выходе этапа препроцессинга получается исходный код, который уже не содержит директив
препроцессора и готов к непосредственной компиляции компилятором в объектный код.
Препроцессинг является важной частью процесса компиляции, поскольку он подготавливает код,
выполняя множество важных преобразований и оптимизаций на раннем этапе.
Препроцессор работает именно с исходным кодом!!!!
СЛАЙД 9
Основная задача линкера — разрешить внешние ссылки между объектными файлами и
библиотеками. В объектных файлах могут содержаться ссылки на функции и переменные,
определения которых находятся в других объектных файлах или библиотеках. Линкер анализирует
эти ссылки и связывает их с соответствующими определениями.
Статическая линковка: при статической линковке код из библиотек копируется в исполняемый
файл. Это означает, что все необходимые функции и данные становятся частью исполняемого
файла, увеличивая его размер, но делая программу самодостаточной.
Динамическая линковка: при динамической линковке исполняемый файл содержит только ссылки
на функции или данные в динамических библиотеках. Сами библиотеки загружаются в память во
время выполнения программы. Это уменьшает размер исполняемого файла и позволяет
нескольким программам делить одну и ту же библиотеку в памяти, но требует, чтобы библиотеки
были доступны на целевой системе во время выполнения.
СЛАЙД 10
Первая домашка
СЛАЙД 11
Версии C89, C99, C11, C17, и C23 относятся к различным версиям стандарта C, принятому
Международной организацией по стандартизации (ISO).
Год выпуска: 1989
Это первый официальный стандарт языка C, также известный как ANSI C, поскольку он был
одобрен Американским национальным институтом стандартов (ANSI). Этот стандарт
формализовал множество существующих конструкций языка и ввел некоторые нововведения,
такие как прототипы функций, стандартные заголовочные файлы и тип void.
C99
Год выпуска: 1999
C99 внес значительные улучшения в язык, включая новые типы данных (например, long long int),
поддержку однострочных комментариев, которые начинаются с //, переменные могут быть
объявлены в любом месте блока кода (а не только в начале), введение комплексных чисел и
булевого типа bool.
C11
Год выпуска: 2011
Эта версия стандарта принесла дополнительные улучшения, такие как анонимные структуры и
объединения, улучшенная поддержка многопоточности (включая новые типы данных и операции
атомарного программирования), статический ассерт (_Static_assert) и тип _Generic для
обобщенного программирования.
C17 / C18
Год выпуска: 2017 / 2018
Эта версия стандарта не внесла значительных изменений в язык и в основном фокусировалась на
исправлении ошибок и неопределенностей предыдущего стандарта C11. Иногда его называют C18
из-за года публикации.
Версии GNU (gnu11, gnu17, и т.д.)
GNU C — это расширение стандарта C, предоставляемое компилятором GCC (GNU Compiler
Collection). Версии GNU, такие как gnu11 или gnu17, соответствуют стандарту ISO C (например, C11
или C17) с дополнительными расширениями и возможностями, специфичными для GCC
СЛАЙД 12
Домашка
СЛАЙД 13
-Wall: Включает все основные предупреждения. Очень полезно для нахождения потенциальных
проблем в коде.
-Werror: Превращает все предупреждения в ошибки. Компиляция не будет завершена, пока все
предупреждения не будут исправлены.
-Wpedantic: Выводит предупреждения, если код не соответствует стандарту ISO C или ISO C++.
-Wshadow: Предупреждает, если одна локальная переменная "затеняет" другую.
-Wconversion: Предупреждает о потенциальных проблемах при преобразовании типов.
СЛАЙД 14
Домашка
СЛАЙД 15
-O0, -O1, -O2, -O3, -Os: Уровни оптимизации.
-O0 отключает оптимизацию, что упрощает отладку.
-O2 и -O3 предоставляют более высокие уровни оптимизации за счёт времени компиляции и
потенциального увеличения размера исполняемого файла.
-Os оптимизирует размер исполняемого файла.
-Og: Оптимизация для отладки. Предоставляет некоторый уровень оптимизации без ухудшения
возможностей отладки.
-Ofast: Включает все оптимизации из -O3 плюс дополнительные оптимизации, которые могут
нарушать стандартные правила.
СЛАЙД 16
Домашка
СЛАЙД 17
Зачем нужен дебаг, как запустить
-g: Добавляет отладочную информацию в исполняемый файл, что позволяет отладчикам, таким
как GDB, работать с вашей программой.
СЛАЙД 18
Начало работы с GDB
gdb <программа>: запускает GDB для указанной программы.
run (r): запускает программу в GDB.
quit (q): выход из GDB
Точки останова
break (b) <местоположение>: устанавливает точку останова; местоположение может быть
номером строки или именем функции.
info breakpoints (i b): показывает список всех установленных точек останова.
delete breakpoints <номер>: удаляет точку останова с указанным номером.
continue (c): продолжает выполнение программы после останова.
Исполнение шаг за шагом
next (n): выполняет следующую строку программы, пропуская вызовы функций.
step (s): выполняет следующую строку программы, заходя внутрь вызовов функций.
finish: выполняет программу до выхода из текущей функции.
Инспекция состояния программы
print (p) <выражение>: выводит значение указанного выражения.
info locals: показывает значения локальных переменных текущей функции.
info registers: отображает значения регистров процессора.
backtrace (bt): показывает стек вызовов функций.
Работа с исходным кодом и ассемблером
list (l): показывает исходный код вокруг текущей строки.
disassemble <функция>: показывает ассемблерный код указанной функции.
Интерфейс пользователя
layout split: включает режим разделенного экрана, показывая исходный код и ассемблер
одновременно.
tui enable/disable: включает или отключает текстовый пользовательский интерфейс.
Работа с памятью
x/<формат> <адрес>: изучает содержимое памяти на указанном адресе. Формат может
указывать на количество и тип отображаемых данных (например, x/10x $esp показывает 10
шестнадцатеричных значений начиная с адреса, на который указывает регистр ESP).
СЛАЙД 19
Домашка
СЛАЙД 21
Проблема больших файлов
СЛАЙД 22
Что такое хедер файл
Хедер файлы (header files) в программировании — это файлы, содержащие объявления функций,
макроопределения, структуры данных и другие конструкции, которые могут быть использованы в
нескольких исходных файлах или проектах. В языках программирования C и C++ хедер файлы
обычно имеют расширение .h.
Зачем нужны хедер файлы?
Повторное использование кода: Хедер файлы позволяют разработчикам определять интерфейсы
(например, функции и структуры данных), которые могут быть использованы в различных частях
программы или даже в разных проектах без необходимости повторного написания кода.
Организация кода: Они помогают организовать код программы, разделяя объявления и
определения. Это упрощает понимание структуры программы и облегчает её поддержку.
Уменьшение времени компиляции: При правильном использовании, хедер файлы могут помочь
уменьшить время компиляции, поскольку изменения в исходном коде не всегда требуют
перекомпиляции всех файлов, которые включают измененный хедер.
1. Проверка Соответствия между Объявлением и Определением
Когда вы включаете хедер файл в исходный файл, это позволяет компилятору проверить,
соответствуют ли определения функций в .c файле объявлениям в хедер файле. Это важно для
обнаружения ошибок, таких как несоответствие типов аргументов или возвращаемого значения
функции, что может привести к неопределенному поведению программы.
2. Доступ к Объявлениям
Включая хедер файл, вы предоставляете исходному файлу доступ к необходимым объявлениям
функций, типов, макроопределений и глобальных переменных. Это позволяет использовать эти
элементы в определении функций, содержащихся в .c файле.
СЛАЙД 23
Примеры плюс
Шаги компиляции и линковки:
Препроцессинг: Во время препроцессинга, директива #include "mylib.h" в файле main.c заставит
препроцессор заменить эту строку содержимым файла mylib.h. Это позволяет компилятору знать о
объявлениях функций, типах и прочих элементах, определенных в mylib.h, при компиляции main.c.
Компиляция: Компилятор компилирует main.c и mylib.c (и любые другие .c файлы, если они есть в
проекте) в объектные файлы (main.o, mylib.o и т.д.). В этот момент код из mylib.c еще не
объединен с кодом из main.c.
Линковка: Последний шаг — линковка (связывание), на котором линкер объединяет все
объектные файлы (включая main.o, mylib.o и объектные файлы из других .c файлов, если они есть)
в один исполняемый файл. Во время этого процесса линкер разрешает все внешние ссылки,
например, когда функция, объявленная в mylib.h и определенная в mylib.c, вызывается из main.c.
Именно на этом шаге код из mylib.c становится частью исполняемого файла.
СЛАЙД 24
Домашняя работа
СЛАЙД 25
Утилита make — это инструмент автоматизации сборки, который автоматически строит
исполняемые программы и библиотеки из исходного кода путём чтения файлов, называемых
Makefile, которые содержат информацию о том, как собирать программу. В основе его работы
лежит концепция правил, которые определяют, как генерировать цели (targets) из зависимостей
(dependencies). Утилита make широко используется в разработке программного обеспечения для
автоматизации процесса компиляции и сборки, уменьшения времени и усилий, необходимых для
повторной сборки проектов.
СЛАЙД 26
Тестовый проект
СЛАЙД 27-31
Простой маейкфайл и его модификации
СЛАЙД 32
Домашка
СЛАЙД 34
Пример: утечка памяти
Valgrind — это инструментарий для отладки использования памяти, обнаружения утечек памяти и
профилирования. Memcheck — самый известный и широко используемый инструмент в наборе
Valgrind, предназначенный для обнаружения ошибок работы с памятью в программах,
написанных на C и C++. Он помогает разработчикам находить проблемы, такие как использование
неинициализированной памяти, чтение и запись за пределами выделенных блоков, утечки
памяти, неправильное использование API для работы с памятью (например, malloc, free, new,
delete), и многие другие
СЛАЙД 35
Тоже утечка памяти, но 10 блоков
СЛАЙД 36
Утечка памяти, пример с++
СЛАЙД 37
Доступ за границы выделенной памяти
СЛАЙД 38
Чтения до записи
СЛАЙД 39
Кэшгринд
Cachegrind является частью Valgrind, набора инструментов для отладки и профилирования
программного обеспечения. Cachegrind специализируется на анализе работы кэша и ветвления
программ, что делает его мощным инструментом для выявления узких мест производительности,
связанных с использованием кэша процессора и предсказателя ветвлений.
Симуляция кэша: Cachegrind симулирует как кэш данных, так и кэш инструкций процессора,
включая кэш первого уровня (L1) и кэш второго уровня (L2). Это позволяет точно оценить, как
программа использует кэш, и выявить проблемные места, где происходит большое количество
кэш-промахов.
Анализ ветвлений: Кроме анализа кэша, Cachegrind также анализирует предсказания ветвлений,
что позволяет оценить эффективность предсказателя ветвлений процессора при выполнении
программы. Это может помочь оптимизировать код для улучшения его предсказуемости и, как
следствие, производительности.
Детализированный вывод: Cachegrind предоставляет подробную статистику о количестве
промахов и попаданий в кэш, использовании кэша инструкций и данных, а также о работе
предсказателя ветвлений. Эти данные могут быть представлены на уровне отдельных функций, что
упрощает оптимизацию кода.
СЛАЙД 41
Massif является инструментом в наборе Valgrind, предназначенным для профилирования
использования памяти программами. Он особенно полезен для выявления участков кода, которые
потребляют большое количество памяти, а также для обнаружения утечек памяти и других
проблем, связанных с динамическим выделением памяти. Massif помогает разработчикам
понимать, как их программа использует кучу и стек во время выполнения, что является ключом к
оптимизации использования памяти и повышению эффективности программ.
Детализированное профилирование кучи: Massif собирает информацию о выделениях памяти в
куче, включая как явные выделения с помощью malloc и free (или new и delete в C++), так и
неявные, например, через использование стандартных библиотек.
Профилирование стека: Помимо кучи, Massif может профилировать использование памяти
стеком, что позволяет получить полное представление об использовании памяти программой.
СЛАЙД 42
Helgrind
DRD (Data Race Detector) и Helgrind являются инструментами в наборе Valgrind, предназначенными
для обнаружения проблем в многопоточных программах, таких как гонки данных и взаимные
блокировки. Оба инструмента анализируют выполнение программы на предмет
синхронизационных ошибок между потоками, но они делают это по-разному и фокусируются на
разных аспектах многопоточности.
Область применения: DRD оптимизирован для обнаружения гонок данных, в то время как Helgrind
нацелен на широкий спектр проблем синхронизации, включая, но не ограничиваясь, гонками
данных.
СЛАЙД 43-46
Оставшиеся профайлеры
CMake — это кросс-платформенная система автоматизации сборки программного обеспечения,
которая использует конфигурационные файлы (называемые CMakeLists.txt) для генерации
стандартных файлов сборки (например, Makefiles в Unix/Linux или проекты Visual Studio в
Windows). Это позволяет разработчикам использовать один и тот же набор исходных файлов для
сборки проектов на разных платформах и с разными компиляторами.
Преимущества CMake над Make
Платформенная независимость: В отличие от make, который прямо использует Makefile для сборки
проекта на конкретной платформе, CMake позволяет генерировать файлы сборки для множества
платформ, делая процесс сборки более гибким и удобным.
Удобство использования: CMake предоставляет высокоуровневые конструкции и команды,
упрощающие написание конфигурационных файлов и управление проектом, в то время как
Makefiles могут быть довольно низкоуровневыми и сложными для понимания.
СЛАЙД 35
Объяснение CTest и CPack
CTest — это инструмент тестирования, входящий в состав CMake. CTest позволяет
автоматизировать выполнение тестов: от юнит-тестирования до регрессионного и ночного
тестирования. С его помощью можно легко добавлять, запускать и отслеживать результаты тестов.
Тесты, определенные с использованием CTest, могут быть запущены индивидуально или все сразу.
Использование CTest облегчает интеграцию процесса тестирования в системы непрерывной
интеграции (CI).
CPack — это инструмент для генерации пакетов (дистрибутивов), который также входит в состав
CMake. CPack используется для создания архивов, инсталляторов и пакетов различных форматов
(например, DEB, RPM, NSIS для Windows и другие) из собранных файлов проекта. Настройка CPack
осуществляется через файлы конфигурации CMake (CMakeLists.txt), что позволяет управлять
процессом пакетирования прямо из проекта. CPack облегчает распространение готового ПО между
пользователями, обеспечивая удобную упаковку и инсталляцию программ.
Термин "cache predictor" не является стандартным термином в компьютерной архитектуре,
возможно, вы имеете в виду одну из двух концепций, связанных с кэшированием данных в
компьютерах: предсказание ветвлений (branch prediction), о котором мы говорили ранее, или
алгоритмы предсказания замещения в кэше (cache replacement policies). В контексте кэш-памяти,
близкой темой может быть также предсказание попаданий/промахов в кэш (cache hit/miss
prediction), хотя оно редко упоминается именно в таких терминах.
Предсказание попаданий и промахов в кэш
Эта концепция связана с оптимизацией работы кэш-памяти путём предсказания, какие данные
скорее всего будут запрошены процессором в ближайшем будущем, чтобы заранее загрузить эти
данные в кэш. Это может включать в себя анализ обращений к памяти и попытки предсказать
последовательность доступа к данным на основе предыдущих обращений. Однако из-за
сложности и динамичности поведения программ такие предсказания могут быть сложными для
точного выполнения.
Алгоритмы замещения в кэше
Возможно, под "cache predictor" подразумевается механизм, связанный с алгоритмами
замещения в кэше, которые определяют, какие данные из кэша должны быть заменены при
необходимости загрузить новые данные, когда кэш заполнен. Эти алгоритмы стараются
предсказать, какие данные будут наименее вероятно использоваться в ближайшем будущем,
чтобы минимизировать количество промахов в кэш (cache misses). Существует множество таких
алгоритмов, включая:
LRU (Least Recently Used): Заменяет данные, которые были использованы дольше всего назад.
FIFO (First-In, First-Out): Заменяет данные в порядке их загрузки в кэш, независимо от того,
насколько часто они использовались после этого.
RR (Random Replacement): Выбирает данные для замены случайным образом.
Каждый из этих алгоритмов имеет свои преимущества и недостатки и может быть более или
менее подходящим в зависимости от конкретного применения и характеристик доступа к данным.
git clone https://sourceware.org/git/valgrind.git
Эта команда использует Git для клонирования (создания локальной копии) исходного кода
Valgrind из репозитория, расположенного по указанному URL. После выполнения этой
команды в текущей директории создаётся новая папка valgrind, содержащая все файлы
исходного кода.
./autogen.sh
Запускает скрипт autogen.sh, который предварительно настраивает проект для
компиляции. Этот скрипт автоматически генерирует необходимые скрипты для сборки,
такие как configure.
В конечном итоге, он упрощает процесс сборки проекта, особенно когда этот проект
распространяется в исходном коде и должен быть скомпилирован на различных системах.
1. aclocal
aclocal является частью набора инструментов Automake и предназначен для генерации
файла aclocal.m4. Этот файл содержит макросы M4, которые используются autoconf. aclocal
собирает макросы из различных источников, включая стандартные макросы,
предоставляемые Automake, и пользовательские макросы, определенные в m4 файлах
внутри проекта или предоставленные сторонними пакетами. Эти макросы затем
используются при генерации скрипта configure.
Проверка компиляторов: Макросы могут автоматически проверять наличие и
настроить использование различных компиляторов C или C++, адаптируясь под
конкретные системные требования или предпочтения пользователя.
Проверка библиотек: С помощью макросов можно проверять наличие
определенных библиотек и функций в системе, что позволяет скрипту configure
настраивать компиляцию проекта с учетом доступных ресурсов.
Генерация файлов конфигурации: Макросы могут использоваться для генерации
файлов Makefile и заголовочных файлов, таких как config.h, которые адаптируют
исходный код к особенностям целевой системы.
2. autoheader
autoheader используется для создания файла заголовков config.h.in. Этот файл содержит
макросы, которые configure скрипт заменяет на реальные значения во время его
выполнения, создавая файл config.h. Файл config.h используется исходным кодом
программы для условной компиляции в зависимости от доступности определенных
функций, библиотек или особенностей системы, на которой происходит сборка. Это
позволяет программе адаптироваться к различным системным средам.
3. Automake
automake преобразует файлы Makefile.am, написанные разработчиками проекта, в
стандартные Makefile.in файлы, которые используются configure скриптом для генерации
окончательного Makefile. Файлы Makefile.am содержат более простой и более
высокоуровневый синтаксис по сравнению с обычными Makefile файлами и предназначены
для описания, как должна происходить сборка и установка программы. automake
обеспечивает соблюдение стандартов GNU для сборочных систем и автоматизирует многие
рутинные аспекты создания мощных Makefile.
4. autoconf
autoconf генерирует скрипты настройки (configure скрипты) из спецификаций в файле
configure.ac (ранее configure.in). Скрипт configure — это портативный скрипт оболочки,
который проверяет характеристики системы, на которой происходит сборка программы
(например, наличие определенных функций, компиляторов, библиотек), и адаптирует
программу к этой системе, создавая файлы Makefile из Makefile.in и настраивая файлы
заголовков, такие как config.h. Это позволяет программе быть собранной на широком
диапазоне систем, даже если между этими системами существуют значительные различия.
./configure
Скрипт configure является центральным элементом в процессе сборки программного обеспечения,
использующего систему автоматизации сборки GNU Autotools. Он предназначен для
автоматической настройки исходного кода программы так, чтобы она могла быть скомпилирована
и установлена на целевой системе.
Вот основные задачи, которые выполняет скрипт configure:
1. Обнаружение характеристик системы
Скрипт configure проверяет целевую систему на наличие необходимых компиляторов,
интерпретаторов и других инструментов, таких как gcc, make, и shell. Он определяет версии этих
инструментов и проверяет, соответствуют ли они требованиям для сборки программы.
2. Проверка наличия библиотек и функций
configure проверяет, доступны ли необходимые библиотеки и системные вызовы, которые
требуются для работы программы. Если программа зависит от определенных функций или
библиотек, скрипт может проверить, поддерживает ли система эти функции и где находятся
соответствующие библиотеки.
3. Генерация файлов Makefile
На основании проверок, проведенных на предыдущих шагах, configure генерирует Makefile(ы) из
файлов Makefile.in. Эти Makefile содержат все необходимые инструкции для сборки и установки
программы, включая правила для компиляции исходного кода и ссылки на используемые
библиотеки.
4. Создание заголовочных файлов
Скрипт может сгенерировать или настроить заголовочные файлы, такие как config.h, которые
используются исходным кодом для условной компиляции в зависимости от доступных на системе
функций и библиотек. Это позволяет программе адаптироваться к различным системным
особенностям.
5. Конфигурационные параметры
configure предоставляет пользователю возможность задавать различные параметры
конфигурации, такие как пути установки, выбор компиляторов, включение или отключение
определенных функций программы. Это делается через командную строку при запуске скрипта.
Download