Как создать граф вызовов для кода C ++

87

Я пытаюсь создать граф вызовов, с помощью которого можно узнать все возможные пути выполнения, которые попадают в конкретную функцию (так что мне не нужно выяснять все пути вручную, поскольку есть много путей, которые приводят к этой функции ). Например:

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.

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

шиумин
источник

Ответы:

118
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имеет внешнюю связь и может быть вызван также извне этой единицы перевода):

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    

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

Красота

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

Йоханнес Шауб - litb
источник
22
Вы сделали это в многофайловом проекте? выглядит очень круто как инструмент
dirvine
2
+1 По какой-то причине мне пришлось передать параметр -n в c ++ filter, чтобы имена можно было распутать. Думал, что упомяну об этом здесь, если кто-то еще столкнется с той же проблемой.
Aky
1
Я получаю сообщение об ошибке: Pass::print not implemented for pass: 'Print call graph to 'dot' file'!Что с этим делать? clang 3.8
Arne
2
Нашел: мне -analyzeпо какой-то причине приходится убрать опцию. Другой вопрос: могу ли я установить для выходного файла другое значение, кроме ./callgraph.dot?
Арне
2
Второй вопрос, который у меня есть, как запустить эту команду для нескольких файлов в разных каталогах?
Newbie
18

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

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

С Йоханнесом Шаубом - litb main.cpp он генерирует следующее:

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

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

jpo38
источник
1
Не могли бы вы добавить пример того, как запустить doxygen, чтобы получить окно, которое вы включили?
nimble_ninja 07
@nimble_ninja: Разве не достаточно скриншота из диалогового окна настройки doxywizard?
jpo38 08
1
Я не знал, что это было от doxywizard. Благодарность!
nimble_ninja 08
1
Лучший способ! :)
Leslie N
Не совсем жизнеспособен для большого проекта, работающего 24 часа, гигабайты HTML-документации, все еще не завершено ... пропуская этот. Мне просто нужны графики вызовов для нескольких конкретных функций (полное дерево в / из / между main () <=> SQL_COMMIT ()).
Гизмо
9

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

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

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

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

Ира Бакстер
источник
К сожалению, мне неизвестны все варианты использования, которые могут вызвать эту функцию :(. На самом деле, моя конечная цель - выяснить точный список вариантов использования, в которых эта функция используется для отладки. Я могу это выяснить прямые вызовы с помощью инструмента индексации кода, но им необходимо выяснить все пути выполнения для дальнейшего анализа.
shiouming
Итак, что вам действительно нужно, так это условие выполнения, при котором вызывается метод? Затем вам потребуется полный и точный граф вызовов и способность инструмента перемещаться по потоку управления в различных узлах графа вызовов, собирая условные выражения, пока не будет обнаружен нужный метод. Я не знаю каких-либо готовых инструментов, которые позволят это сделать (этот комментарий на 7 лет позже вопроса); для этого вам, вероятно, понадобится специальный механизм анализа. К этому можно было принуждать Clang; для этого можно использовать наш инструментарий DMS.
Ира Бакстер
5

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

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

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

Иссам
источник
3

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

clang++ -### -fsyntax-only -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
мабаленк
источник
1

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

Резонантия
источник
0

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

Crawl.W
источник
0

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

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

франкспайк
источник