У меня есть большое приложение, написанное на C + POSIX, внутри него много функций, которые никогда не вызываются. Однако из-за размера кода их сложно отследить вручную.
Некоторые люди предлагали использовать gcc с -Wunused
и lto, но он не вернул ни одной используемой функции, в то время как я продолжаю находить и удалять некоторые вручную.
Поэтому я думаю, что мне нужен инструмент покрытия кода для анализа программы во время выполнения. Люди предложили мне gcov или valgrind, но я не смог найти, как их использовать для печати списка мертвых функций. Только gcov показал, что используются только 68% скомпилированных функций, но я не могу их перечислить.
Итак, кто-нибудь знает хороший инструмент, и если да, то скажите мне, как именно я могу использовать его для этой цели (приветствуется пример командной строки) ?
Я удалил все функции, которые не используются в исходном коде. В исходном коде остаются только такие функции:
if(conditional statement) {
some stuff;
dead_function();
some_stuff;
}
Where conditional statement
никогда не бывает истинным во время выполнения, и удаление dead_function()
приведет к удалению оператора, чтобы избежать неопределенных ошибок.
Учитывая, что @user2284570 находится в комфортной ситуации, охватывая 100% вариантов использования с помощью тестов, динамический анализ кода даст ответ. В других случаях удаление функций, их вызовов и условий потребует тщательной проверки.
Любой инструмент покрытия кода будет сообщать о покрытии функций в той или иной форме. Основная проблема заключается в сообщении о точном местоположении неиспользуемых (здесь: мертвых) функций. Я не могу говорить о других инструментах, но у нашей компании есть опция отчетности в текстовом формате, в которой есть заполнители для имени исходного файла и данных строки. Поскольку был запрошен конкретный пример командной строки, вот один из них:
$ csgcc -o myapp mycode.c
$ ./myapp --run-all-tests
$ cmcsexeimport -m myapp.csmes -e myapp.csexe --title=mytests
$ cmreport --function-coverage -m myapp.csmes --format-unexecuted='%f:%l'
Это напечатает местоположения мертвых функций, например:
mycode.c:101
mycode.c:213
mycode.c:1032
Студент местного университета написал более подробные инструкции для этого подхода.
После удаления неиспользуемых функций вы также захотите проанализировать покрытие ветвей и удалить лишние операторы if() и другие. Просто остерегайтесь побочных эффектов оцениваемых выражений. Но, к счастью, ваше идеальное покрытие кода обнаружит регрессии.
Вы можете использовать splint
с alluse
флагом для проверки неиспользуемых функций, но лично я бы использовал doxygen
для создания карты вызовов - любые функции, у которых нет родителей, вероятно, не используются - просто ищите любые функции, которые находятся в таблицах функций, которые могут не вызываться напрямую но такие вещи, как конечные автоматы, могут вызываться из индекса таблицы.
Doxygen — бесценный инструмент для работы с большими базами кода, и в любом случае стоит научиться его использовать, он бесплатный и доступен для нескольких платформ, а также поощряет документирование вашего кода по ходу работы.
В случае кода, который вызывается, но только из недостижимого кода, вам придется использовать инструмент полного статического анализа, такой как LDRA (дорогостоящий), который укажет вам на недоступный код. В этом случае лучше сначала удалить весь недостижимый код, а затем искать неиспользуемые функции. В качестве альтернативы вам понадобится набор тестов, который, как вы уверены, реализует 100% функциональности - тогда вы можете использовать профилировщик или инструмент покрытия, такой как gcov, в своей программе во время запуска набора тестов. Если ваш тест задействовал все ваши функциональные возможности и у вас есть части с 0% покрытием, то они не вызываются, но тогда вам придется найти недоступные для них вызовы и удалить этот код, чтобы компоновщик все равно не жаловался.
Чтобы ответить на ваш пересмотренный вопрос - если вы скомпилируете весь свой код с gcc -fprofile-arcs -ftest-coverage
установленными параметрами, а затем запустите набор тестов, которые, как вы уверены, охватывают все функциональные возможности и все возможности (возможно, в нескольких прогонах).
Утилита gcov
требует, чтобы вы выполнили часть работы — у нее не просто есть опция «скажите мне, что не было вызвано», поэтому вам придется найти те функции, которые не были вызваны.
Вы можете использовать gcov
с --function-summaries
опцией для каждого исходного файла, который вы создадите набор выходных файлов, которые будут включать сводки функций - ищите любые из тех, которые включают never
или 0%
, чтобы найти невыполненные функции.
Я бы предложил либо добавить функцию, которая, как вы знаете, никогда не будет вызываться, либо узнать о той, которую вы еще не удалили - это позволит вам увидеть, как выглядит вывод - вы можете затем использовать grep
, чтобы найти их все.
Следующим шагом будет использование grep
или что-то подобное для просмотра gcov
вывода для всех мест, где эти функции присутствуют в вашем коде - вы должны увидеть счетчики выполнения 0 для всей ветки, содержащей вызов , это даст вам хорошую отправную точку либо для расширения ваших тестов — для вариантов использования, которые вы пропустили, — либо для удаления кода.
Жиль "ТАК - перестань быть злым"
dead_function
используется ), а мертвый код. Для этого нужны совсем другие техники! Имейте в виду, что анализ во время выполнения найдет только тот код, который не был запущен в конкретном выполнении программы — этот код может быть активен при различных обстоятельствах.пользователь 2284570
Ира Бакстер
пользователь 2284570
Стив Барнс
Мог говорит восстановить Монику
пользователь 2284570
пользователь 2284570
Стив Барнс
printf(__func__);
в качестве первой строки, в gcc это будет печатать имя функции каждый раз, когда она вызывается - запустите свой тестовый захват вывода, тогда любое имя, отсутствующее в выводе, не вызывается.пользователь 2284570
Стив Барнс
Стив Барнс
пользователь 2284570
Стив Барнс
Мог говорит восстановить Монику
пользователь 2284570
Мог говорит восстановить Монику