Хочется статический call graph. В частности clang/llvm умеет, примерно вот так: clang++ -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
Но во-первых дот-файлы мало интересны, граф скорей нужен для дальнейшего программного анализа (в реальном проекте его глазами смотреть невозможно из-за размера).
Во-вторых, и самое главное -- указатели на функции. Я эту штуку пробовал пару лет назад, если не больше и тогда там эта проблема кажется никак решена не была. А без указателей совсем не интересно. Сразу огромные ветки отсекаются.
Понятно, что может быть два подхода. Или не видим указатели вообще. Или считаем, что по-указателю может быть вызвана любая возможная функция. Но не любая, а адрес которой брали в явном виде. Или совсем не любая, а у которой прототип совпадает. Т.е. окно возможностей эффективно сужается. Подобный подход используют компиляторы Hitech-C и KEIL для построения "компилированного стека" для архитектур микроконтроллеров без аппаратного стека (там рекурсии невозможны, а все возможные варианты распределения памяти стека нужно предусмотреть в момент компиляции). Далее можно пойти дальше, и искусственно ввести различающиеся прототипы для функций вызываемых по-указателю и используемых в разных, не пересекающихся задачах (что-то наподобии tag-dispatch, когда вводится лишний параметр с типом специфичным для данного использования функции/указателя). И таким образом можно уже построить реалистичные графы вызовов и с указателями.
Реализовано ли кем-либо и где-либо что-то подобное?
Как по-вашему можно такое реализовать? Просматривается, что для llvm нужно написать собственный FunctionPass в котором (когда он итерируется по скомпилированным функциям) найти все места вызовов и если там вызов функции, то всё понятно добавляем в граф, а если вызов по указателю -- то предусматриваем все возможные функции от которых брали адрес (как это понять? в llvm промежуточном коде видно, а в C++ интерфейсе непонятно) и выяснем прототип для callsite (https://stackoverflow.com/questions/14811587/how-to-get-functiontype-from-callinst-when-call-is-indirect-in-llvm) и пересечение первого и второго множества с одинаковыми прототипами и есть множество возможных функций которые здесь и сейчас будут вызваны...
В целом хочется получить две вещи: 1) гарантий, что не существует путей в графе между множествами функций которые не могут вызывать друг-друга (вручную заданными). В идеале неплохо бы чтоб функциям в коде просто можно было бы навешивать (ни на что не влияющие) атрибуты для того. 2) получить максимальный объём стека используемый всеми функциями во всех возможных путях графа вызова. По последнему пункту; clang кажется (есть отдельный патч...) не поддерживает -fstack-usage, но по крайней мере интересна если не точная, то хотя бы оценка порядка. В том же llvm-промежуточном коде виден порядок (размер переменных). Например наличие рекурсии сразу требует бесконечного стека. 3) как-то доказать, что рекурсии строго ограничены в глубину, но видимо никак. только вручную.
Обсуждают сегодня