Создать граф вызовов для кода C++
Я пытаюсь сгенерировать вызывающий граф, с помощью которого можно найти все возможные пути выполнения, которые попадают в конкретную функцию (чтобы мне не приходилось выяснять все пути вручную, так как существует множество путей, ведущих к этой функции). Например:
path 1: A -> B -> C -> D
path 2: A -> B -> X -> Y -> D
path 3: A -> G -> M -> N -> O -> P -> S -> D
...
path n: ...
Я пробовал Codeviz и Doxygen, так или иначе оба результата показывают только вызываемые объекты целевой функции D. В моем случае D является функцией-членом класса, объект которого будет заключен в интеллектуальный указатель. Клиенты всегда получают объект смарт-указателя через фабрику для вызова D.
Кто-нибудь знает, как этого добиться?
9 ответов
static void D() { }
static void Y() { D(); }
static void X() { Y(); }
static void C() { D(); X(); }
static void B() { C(); }
static void S() { D(); }
static void P() { S(); }
static void O() { P(); }
static void N() { O(); }
static void M() { N(); }
static void G() { M(); }
static void A() { B(); G(); }
int main() {
A();
}
затем
$ clang++ -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
$ dot -Tpng -ocallgraph.png callgraph.dot
Выдает некоторую блестящую картинку (есть "внешний узел", потому что main
имеет внешнюю связь и может также вызываться извне этой единицы перевода):
Вы можете постобработать это с c++filt
, так что вы можете получить незапятнанные имена участвующих функций и классов. Как в следующем
#include <vector>
struct A {
A(int);
void f(); // not defined, prevents inlining it!
};
int main() {
std::vector<A> v;
v.push_back(42);
v[0].f();
}
$ clang++ -S -emit-llvm main1.cpp -o - |
opt -analyze -std-link-opts -dot-callgraph
$ cat callgraph.dot |
c++filt |
sed 's,>,\\>,g; s,-\\>,->,g; s,<,\\<,g' |
gawk '/external node/{id=$1} $1 != id' |
dot -Tpng -ocallgraph.png
Даёт такую красоту (о боже, размер без включенных оптимизаций был слишком большим!)
Эта мистическая безымянная функция, Node0x884c4e0
, является заполнителем, предположительно вызываемым любой функцией, определение которой неизвестно.
Вы можете добиться этого, используя doxygen (с возможностью использовать точку для генерации графиков).
С Йоханнесом Шаубом - litb main.cpp, он генерирует это:
doxygen / dot, вероятно, проще, чем clang/opt, установить и запустить. Мне не удалось установить его самостоятельно, поэтому я попытался найти альтернативное решение!
Статически вычислить точный граф вызовов C++ сложно, потому что вам нужен точный синтаксический анализатор языка, правильный поиск имени и хороший анализатор точек, который должным образом учитывает семантику языка. Doxygen не имеет ничего из этого, я не знаю, почему люди утверждают, что им нравится C++; легко построить пример C++ из 10 строк, который Doxygen ошибочно анализирует).
Возможно, вам лучше запустить профилировщик синхронизации, который динамически собирает граф вызовов (это описывает наш) и просто выполнять множество случаев. Такие профилировщики покажут вам действительный график вызовов.
РЕДАКТИРОВАТЬ: я вдруг вспомнил Understand для C++, который утверждает, что для создания графов вызовов. Я не знаю, что они используют для синтаксического анализатора, или делают ли они подробный анализ правильно; У меня нет конкретного опыта работы с их продуктом.
Я впечатлен ответом Шауба, используя Clang; Я ожидаю, что у Clang все элементы будут правильными.
Вы можете использовать CppDepend, он может генерировать много видов графиков
- График зависимостей
- График звонков
- График наследования классов
- График сцепления
- Путь График
- График всех путей
- Цикл графика
Для того, чтобы clang++
Команда для поиска стандартных заголовочных файлов, таких как mpi.h
следует использовать две дополнительные опции -### -fsyntax-only
полная команда должна выглядеть так:
clang++ -### -fsyntax-only -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
GNU cflow
cflow --tree --number main.c a.c b.c
Он генерирует граф вызовов в текстовом стиле и поддерживает несколько файлов.
"C++ Bsc Analyzer" может отображать графики вызовов, читая файл, сгенерированный утилитой bscmake.
Scitools Understand - это фантастический инструмент, который лучше, чем все, что я знаю для реверс-инжиниринга, генерирует высококачественные графики.
Но обратите внимание, что это довольно дорого, и что в пробной версии график вызовов "бабочка" ограничен только одним уровнем вызова (ИМХО, я думаю, они не помогают себе в этом…)
doxygen + graphviz может решить большинство проблем, когда мы хотим сгенерировать граф вызовов, который затем будет передан рабочей силе.