WedX - журнал о программировании и компьютерных науках

Как сгенерировать граф вызовов для кода 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.

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


Ответы:


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

Дает некоторую блестящую картинку (есть «внешний узел», потому что 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, является заполнителем, который, как предполагается, вызывается любой функцией, определение которой неизвестно.

21.03.2011
  • Вы сделали это в многофайловом проекте? выглядит очень круто как инструмент 11.10.2012
  • Есть ли способ сделать это, чтобы функции, которые не являются локальными для файла/файлов, такие как все стандартные функции, которые вызывают друг друга, не вызывались? 16.03.2013
  • +1 По какой-то причине мне пришлось передать параметр -n в c++filt, чтобы имена расшифровывались. Думал, что упомяну об этом здесь, если кто-то еще столкнется с той же проблемой. 04.01.2014
  • @dirvine, поскольку для создания такой информации требуется весь контекст флагов компиляции, было бы проще всего добавить его в качестве шага в ваши сценарии сборки (Make, CMAKE и т. д.) 06.03.2014
  • При попытке сделать это я получаю сообщение об ошибке: Pass::print not implemented for pass: 'Print call graph to 'dot' file'! Что с этим делать? лязг 3.8 11.09.2015
  • Нашел: по какой-то причине мне нужно удалить параметр -analyze. Еще один вопрос: могу ли я установить для выходного файла имя, отличное от ./callgraph.dot? 11.09.2015
  • Когда я делаю это с clang-3.5 в Ubuntu, я получаю. opt: ‹stdin›:26:93: ошибка: вызов токена ожидаемого значения void @_ZNSt6vectorI1ASaIS0_EE9push_backERKS0_(%class.std::vector* %v, %struct.A* разыменовываемый(1) %1) 16.09.2015
  • Есть ли способ, чтобы граф вызовов также отображал структуры потока управления, как в конструкции if-then-else и циклах for и [do-]while? 31.03.2016
  • @ppetraki у меня такая же ошибка! решение для меня состояло в том, чтобы убедиться, что команды opt и clang исходят из одной и той же версии llvm (один и тот же путь)! 28.04.2016
  • @IvanMarinov, как это сделать? 28.05.2016
  • @ar2015 ar2015 Я указал путь явно. Я думаю, что использовал тот, который поставляется с XCode (в OS X). 30.05.2016
  • @IvanMarinov, у меня Ubuntu. у вас есть какие-либо рекомендации для этого? 30.05.2016
  • @ar2015 ar2015 в моей Ubuntu У меня уже установлены две версии LLVM по следующим путям: /usr/lib/llvm-3.4 и /usr/lib/llvm-3.5, поэтому, если я перепутаю /usr/lib/llvm-3.4/clang++ с /usr/lib/llvm-3.5/opt, будет ошибка, но если я использую /usr/lib/llvm-3.5/clang++ и /usr/lib/llvm-3.5/opt вместе, все будет работать нормально. Вы можете использовать команду which, чтобы проверить путь для команды, попробуйте which opt и which clang++. 31.05.2016
  • @IvanMarinov: Тогда вам следует взглянуть на мой ответ, который не требует лязга! 20.08.2018
  • Кто-нибудь знает, можно ли сделать этот график для модульных тестов Google? У меня есть main.cpp который запускает все тесты в разных файлах и как это сделать? Я хотел бы знать, какую функцию вызывает каждый тест. 12.11.2018
  • Второй вопрос у меня есть, как запустить эту команду для нескольких файлов в разных каталогах? 12.11.2018

  • 2

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

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

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

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

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

    03.12.2015
  • Не могли бы вы добавить пример того, как запустить doxygen, чтобы получить окно, которое вы включили? 08.03.2017
  • @nimble_ninja: Разве недостаточно снимка экрана из диалогового окна конфигурации doxywizard? 08.03.2017
  • Я не знал, что это было от doxywizard. Спасибо! 08.03.2017
  • Самый лучший метод! :) 04.09.2019
  • Не совсем жизнеспособно для большого проекта, работает 24 часа, гигабайты HTML-документации, все еще не закончено ... пропуская это. Мне просто нужны графики вызовов для нескольких конкретных функций (полное дерево до/от/между main() ‹=› SQL_COMMIT() ). 20.10.2020

  • 3

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

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

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

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

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

  • 4

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

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

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

    06.02.2018

    5

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

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

    6

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

    24.09.2014

    7

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

    17.09.2018

    8

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

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

    21.05.2019
    Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: [email protected]