Поскольку вы публикуете этот вопрос в SO и помечаете его как llvm Я предполагаю, что это означает, что вы ищете решение для программирования с использованием LLVM.
Напишите проход, который преобразует каждую функцию вВаша программа добавляет три новые инструкции перед каждым вызовом.Примерно так:
struct RecordCallGraph : public PassInfoMixin<RecordCallGraph> {
RecordCallGraph() = default;
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
};
Вам необходимо реализовать этот run (), который будет содержать около 15 строк кода.Просканируйте основные блоки в функции, проверьте, каждая ли инструкция isa<CallBase>
и, если она есть, то вставьте немного дополнительного кода перед CallBase.(CallBase - это базовый класс инструкций, которые вызывают функции.) Вы вставляете вызов для своей новой новой функции, void emitTraceInfo(char* caller, char* called)
или что-то в этом роде.Так как LLVM IR безопасен для типов, вам необходимо привести вызывающую функцию (&F
) и вызываемую функцию (callBase->getCalledValue()
) к нужному типу для вашей функции (char*
в примере).
Самый простой способчтобы получить этот бросок, скорее всего, будет CastInst::Create(CastInst::BitCast, &F, charStarType, "", callBase)
, который создаст новый бросок с &F
до charStarType
и вставит его непосредственно перед callBase
.
Наконец вы должны реализовать свой новый emitTraceInfo
и свяжите это в программу.Он будет вызываться каждый раз, когда одна функция вызывает другую, и может записывать вызов.Вы найдете это примерно в сто раз быстрее, чем GDB.Самая медленная часть может состоять в записи 16-ти с лишним байтов в файл.