Как сгенерировать граф вызовов для кода 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 является функцией-членом класса, объект которого будет обернут в интеллектуальный указатель. Клиенты всегда будут получать объект интеллектуального указателя через factory, чтобы вызвать D.

Кто-нибудь знает, как достичь этого?

Ответ 1

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

Устанавливает какую-то блестящую картинку (есть внешний node ", потому что main имеет внешнюю связь и может быть вызван извне в эту единицу перевода):

Callgraph

Возможно, вы захотите выполнить постпроцесс с помощью 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    

Уступает этой красоте (о мой, размер без включенной оптимизации был слишком большим!)

Beauty

Эта мистическая неназванная функция, Node0x884c4e0, является заполнитель, который предполагается вызывать любой функцией, определение которой неизвестно.

Ответ 2

Это можно сделать с помощью doxygen (с возможностью использования точки для генерации графов).

введите описание изображения здесь

С Johannes Schaub - litb main.cpp он генерирует это:

введите описание изображения здесь

doxygen/dot, вероятно, проще, чем clang/opt для установки и запуска. Мне не удалось установить его самостоятельно, и поэтому я попытался найти альтернативное решение!

Ответ 3

Статически вычислить точный граф вызовов C++ сложно, потому что вам нужен точный синтаксический анализатор языка, правильный поиск имени и хороший анализатор точек, который должным образом учитывает семантику языка. У Doxygen нет ни одного из них, я не знаю, почему люди утверждают, что ему это нравится C++; легко построить пример из 10 строк C++, который Doxygen ошибочно анализирует).

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

РЕДАКТИРОВАТЬ: я внезапно вспомнил Понимание для C++, который утверждает, что для создания графов вызовов. Я не знаю, что они используют для синтаксического анализатора, или делают ли они подробный анализ правильно; У меня нет конкретного опыта работы с их продуктом.

Я впечатлен ответом Шауба, используя Clang; Я ожидаю, что у Clang все элементы будут правильными.

Ответ 4

Вы можете использовать CppDepend, он может генерировать много видов графиков

  • График зависимостей
  • График звонков
  • График наследования классов
  • График сцепления
  • Путь График
  • График всех путей
  • Цикл графика

enter image description here

Ответ 5

Чтобы команда clang++ находила стандартные файлы заголовков, такие как mpi.h, следует использовать две дополнительные опции -### -fsyntax-only, то есть полная команда должна выглядеть как:

clang++ -### -fsyntax-only -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph

Ответ 6

"С++ Bsc Analyzer" может отображать графики вызовов - считывая файл, созданный с помощью утилиты bscmake.

Ответ 7

doxygen + graphviz может решить большинство проблем, когда мы хотим сгенерировать граф вызовов, который затем будет передан рабочей силе.

Ответ 8

Scitools Understand - это фантастический инструмент, но довольно дорогой, лучше, чем все, что я знаю для реверс-инжиниринга, и генерирующий высококачественные графики.

Но обратите внимание, что в пробной версии график вызовов "бабочки" ограничен только одним уровнем вызовов (ИМХО, я думаю, они не помогают себе в этом…)

Ответ 9

Scitools Understand - это фантастический инструмент, который лучше, чем все, что я знаю для реверс-инжиниринга, генерирует высококачественные графики.

Но обратите внимание, что это довольно дорого, и что в пробной версии график вызовов "бабочка" ограничен только одним уровнем вызова (ИМХО, я думаю, они не помогают себе в этом…)