Як створити графік виклику для коду на 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- це заповнювач, який, як вважається, викликається будь-якою функцією, визначення якої невідоме.


22
Ви робили це у багатофайловому проекті? виглядає дуже круто як інструмент
dirvine

2
+1 З якихось причин мені довелося передати опцію -n на фільтр c ++, щоб імена розбиралися. Думаю, я згадаю це тут на випадок, якщо хтось інший зіткнеться з такою ж проблемою.
Aky

1
Під час спроби з’являється помилка: Pass::print not implemented for pass: 'Print call graph to 'dot' file'!що з цим? clang 3.8
Arne

2
Знайшов: мені -analyzeз якихось причин потрібно вилучити опцію. Ще одне запитання: чи можу я встановити для імені вихідного файлу щось інше, ніж ./callgraph.dot?
Арне

2
Друге запитання у мене, як запустити цю команду для декількох файлів у різних каталогах?
Новачок

18

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

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

З Йоханнесом Шаубом - litb main.cpp це генерує це:

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

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


1
Не могли б ви додати приклад того, як запустити кисень, щоб отримати вікно, яке ви включили?
nimble_ninja

@nimble_ninja: Чи недостатньо знімка екрана з діалогового вікна налаштування doxywizard?
jpo38

1
Я не знав, що це від doxywizard. Дякую!
nimble_ninja

1
Найкращий метод! :)
Leslie N,

Насправді не життєздатний для великого проекту, який працював протягом 24 годин, гігабайти HTML-документації, все ще не зроблено .. пропускаючи цей. Мені просто потрібні графіки викликів для кількох конкретних функцій (повне дерево до / з / між main () <=> SQL_COMMIT ()).
Gizmo

9

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

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

EDIT: Я раптом згадав Understand для C ++ , який стверджує, що будує графіки викликів. Я не знаю, що вони використовують для синтаксичного аналізатора, чи правильно вони роблять детальний аналіз; У мене немає конкретного досвіду з їхнім продуктом.

Я вражений відповіддю Шауба, використовуючи Clang; Я би очікував, що у Кланга все в порядку.


На жаль, я не знаю всіх випадків використання, які можуть викликати цю функцію :(. Насправді, моєю кінцевою метою є з’ясувати точний перелік випадків використання, які використовують цю функцію з метою налагодження. Я можу з’ясувати, прямі абоненти з інструментом індексації коду, але їм потрібно з'ясувати всі шляхи виконання для подальшого аналізу.
shiouming

Отже, що вам насправді потрібно - це умова виконання, за якої викликається метод? Тоді вам потрібен повний, точний графік викликів та неспроможність інструменту для проходження вздовж потоку управління в різних вузлах графіка викликів, збираючи умовні вирази, доки не зустрінеться потрібний метод. Я не знаю жодного готового інструменту, який би це зробив (цей коментар через 7 років після запитання); для цього вам, ймовірно, знадобиться спеціальний механізм аналізу. Кланг може бути натиснутий на це; для цього може бути використаний наш набір інструментів DMS.
Ira Baxter

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


0

Scitools Understand - це фантастичний інструмент, кращий за все, що я знаю для реверсивного проектування , і створює високоякісні графіки .

Але зауважте, що це досить дорого, і тестова версія має графік виклику метеликів, обмежений лише одним рівнем дзвінка (ІМХО, я вважаю, вони не допомагають собі робити це ...)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.