Создать граф вызовов для кода 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 может решить большинство проблем, когда мы хотим сгенерировать граф вызовов, который затем будет передан рабочей силе.

Другие вопросы по тегам