Поиск неиспользуемых функций в проекте C с помощью статического анализа - PullRequest
3 голосов
/ 17 июня 2011

Я пытаюсь запустить статический анализ C-проекта для определения мертвого кода, т.е. функций или строк кода, которые никогда не вызывались. Я могу построить этот проект с Visual Studio .Net для Windows или с помощью gcc для Linux. Я пытался найти какой-то разумный инструмент, который мог бы сделать это для меня, но пока мне это не удалось. Я прочитал похожие вопросы по переполнению стека, т.е. this и this , и я попытался использовать -Wunreachable-code с gcc, но вывод в gcc не очень полезен. Это в следующем формате

/home/adnan/my_socket.c: In function ‘my_sockNtoH32’: 
/home/adnan/my_socket.c:666: warning: will never be executed

но когда я смотрю на строку 666 в my_socket.c, она фактически находится внутри другой функции, которая вызывается из функции my_sockNtoH32 () и не будет выполняться для этого конкретного экземпляра, но будет выполняться при вызове из некоторых других функций.

Мне нужно найти код, который никогда не будет выполнен. Может кто-нибудь PLZ помочь с этим?

PS: я не могу убедить руководство купить инструмент для этой задачи, поэтому, пожалуйста, пользуйтесь бесплатными / открытыми инструментами.

Ответы [ 3 ]

3 голосов
/ 17 июня 2011

Если GCC не подходит для вас, попробуйте лязг (или, точнее, статический анализатор ). Он (как правило, ваш пробег может меняться, конечно) имеет гораздо лучший статический анализ, чем GCC (и дает гораздо лучший результат). Он используется в Apple Xcode, но с открытым исходным кодом и может использоваться отдельно.

1 голос
/ 17 июня 2011

Когда GCC говорит «никогда не будет выполнен», это означает, что это так. У вас может быть ошибка, которая фактически делает этот мертвый код. Например, что-то вроде:

if (a = 42) {
    // some code
} else {
    // warning: unreachable code
}

Не видя код, конечно, невозможно быть конкретным.

Обратите внимание, что если в строке 666 есть макрос, возможно, что GCC также ссылается на часть этого макроса.

0 голосов
/ 06 июля 2011

GCC поможет вам найти мертвый код в компиляции.Я был бы удивлен, если бы он мог найти мертвый код в нескольких единицах компиляции.Объявление на уровне файла функции или переменной в модуле компиляции означает, что какой-то другой модуль компиляции может ссылаться на него.Таким образом, все, что объявлено на верхнем уровне файла, GCC не может устранить, поскольку он, вероятно, видит только одну единицу компиляции за раз.

Проблема становится все труднее.Представьте, что модуль компиляции A объявляет функцию a, а модуль B компиляции имеет функцию b, которая вызывает a.Мертв?На первый взгляд, нет.Но на самом деле это зависит;если b мертв, и единственная ссылка на a находится в b, то a тоже мертв.Мы получаем ту же проблему, если b просто берет & a и помещает его в массив X. Теперь, чтобы решить, является ли a мертвым, нам нужен анализ точек по всей системе, чтобы увидеть, является ли этот указатель наa используется везде .

Чтобы получить точную «мертвую» информацию такого рода, вам необходимо глобальное представление всего набора единиц компиляции, а также вычисление анализа точек с последующим построением графа вызовов на основена что указывает на анализ.Функция a является мертвой только в том случае, если граф вызовов (как дерево, с основным в качестве корня) не ссылается на него где-либо(Некоторые предостережения необходимы: какой бы анализ ни был, с практической точки зрения он должен быть консервативным, поэтому даже полный анализ может не правильно определить функцию как мёртвую. Вам также нужно беспокоиться об использовании артефакта C извненабор функций C, например, вызов a из некоторого кода ассемблера).

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

Вы не получаете много ответов о том, как получить правильный ответ.Хотя он не является открытым исходным кодом, наш набор инструментов для реинжиниринга программного обеспечения DMS с C Front End имеет все необходимое для этого оборудование, включая синтаксические анализаторы C, control- и dataflow-анализ, локальный и глобальный точечный анализ и построение графа глобальных вызовов. DMS легко настраивается для включения дополнительной информации, такой как внешние вызовы из ассемблера, и / или список корней потоков или определенных шаблонов источников, которые являются потокамивызовы инициализации, и мы фактически (легко) сделали это для некоторых крупных встроенных контроллеров движков с миллионами строк кода.DMS была применена к системам размером до 26 миллионов строк кода (около 18 000 единиц компиляции) с целью построения таких графов вызовов.

[Интересное в стороне: при обработке отдельных модулей компиляции, DMS по причинам масштабированияфактически удаляет символы и связанный код, которые не используются в этом модуле компиляции.Примечательно, что это избавляет от примерно 95% кода по объему, если принять во внимание айсберг, обычно скрывающийся в гнезде включаемых файлов.Это говорит о том, что в программном обеспечении на Си плохо включены факторинговые файлы.Я подозреваю, что вы все это уже знаете.]

Такие инструменты, как GCC, удаляют мертвый код при компиляции .Это полезно, но мертвый код все еще лежит в вашем модуле компиляции исходный код , привлекая внимание разработчика (они должны выяснить, если он тоже мертв!).DMS в режиме программной трансформации может быть настроен по модулю некоторых проблем препроцессора, чтобы фактически удалить этот мертвый код из источника .В очень больших программных системах вы не хотите делать это вручную.

...