Поиск графа вызовов C ++ рекурсивно для определенных функций

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

Ex для чего-то вроде:

void A_Func1(){}
void A_Func2(){}

void IntermediateFunc()
{
A_Func1();
A_Func2();
}

void StartFunc()
{
IntermediateFunc();
}

Я хотел бы получить список функций, которые начинаются с «A_», вызываемых прямо или косвенно из StartFunc.

Моей первой мыслью было использование clang с действием CallGraph, но документации немного, и я постепенно прихожу к выводу, что не могу использовать его так, как хочу.

Итак, вопрос:
Как использовать библиотеки инструментов Clangs для создания такого списка?

3

Решение

Clang выходы .dot файл для его callgraph. Это довольно простой формат это можно легко разобрать с помощью такого инструмента, как PEG.js. Когда вы получите звонок DAG, Вы можете запустить простой ДФС, который отмечает все узлы с A_ префикс на нем.

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

Также вы должны знать, что clang генерирует оптимистический граф вызовов, то есть «какие функции могут быть вызваны». Например, void f() { if (g()) h(); } «звонит» обоим g а также h, даже если bool g() { return false; },

Возможно, вы ищете «все функции, которые действительно вызываются оттуда». Тогда вам нужно бежать какой-то профилировщик собирать стеки вызовов.

Лучшее решение было бы сделать скрипт отладчика для некоторой IDE, но я не знаю ни одной IDE для C ++, у которой есть такая опция.

3

Другие решения

Я нахожу ответ от polkovnikov.ph быть более чистым способом сделать это. Я не знал, что .dot это так просто, и я определенно буду использовать этот способ для подобных проблем.

К сожалению, я также должен проанализировать программные компоненты, где интерфейс с другими компонентами С функции — которые используются через extern ключевое слово. Они не появляются в лязг граф вызовов из-за внутреннего фильтра (includeInGraph (const Decl *D)) в лязг :: граф вызовов.

Поэтому мне пришлось скопировать clang::CallGraph, удалите ограничение и используйте его в clang::ASTConsumer лайк:

virtual void HandleTranslationUnit(clang::ASTContext &Context) {
_visitor.TraverseDecl(Context.getTranslationUnitDecl());
for (auto root : _visitor)
{
if (const clang::NamedDecl* namedDecl = llvm::dyn_cast_or_null<clang::NamedDecl>(root.first))
if(namedDecl->getIdentifier() != nullptr && namedDecl->getIdentifier()->getName().startswith("Start"))
{
llvm::outs() << "StartFunc: " << namedDecl->getName() << "\n";
printAFunctions(root.second);
}
}
}
void printAFunctions(const clang::CallGraphNode* node)
{
if (node != nullptr)
{
if (const clang::NamedDecl* namedDecl = llvm::dyn_cast_or_null<clang::NamedDecl>(node->getDecl()))
{
if (namedDecl->getName().startswith("A_"))
{
llvm::outs() << "A_ call: " << namedDecl->getName() << "\n";
}
}
for (auto subNode : *node)
{
printAFunctions(subNode);
}
}
}
1