Подтвердить что ты не робот

Как найти имя вызывающей функции?

Я использую PRETTY_FUNCTION для вывода текущего имени функции, однако я переопределил некоторые функции и хотел бы узнать, какие функции их вызывают.

В С++ как я могу получить имя функции вызывающей подпрограммы?

4b9b3361

Ответ 1

Вот два варианта:

  1. Вы можете получить полную трассировку стека (включая имя, модуль и смещение вызывающей функции) с последними версиями glibc с функциями обратной трассировки GNU. Смотрите мой ответ здесь для деталей. Это, наверное, самая легкая вещь.

  2. Если это не совсем то, что вы ищете, то вы можете попробовать libunwind, но это потребует больше работы.

Имейте в виду, что это не то, что вы можете знать статически (как с PRETTY_FUNCTION); вам действительно нужно пройтись по стеку, чтобы выяснить, какая функция вас назвала. Так что это не то, что действительно стоит делать в обычных отладочных printfs. Если вы хотите сделать более серьезную отладку или анализ, то это может быть полезно для вас.

Ответ 2

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

Простой пример

// orignal function name was 'FunctionName'
void FunctionNameReal(...)
{
  // Do Something
}

#undef FunctionName
#define FunctionName printf("Calling FunctionName from %s\n",__FUNCTION__);FunctionNameReal

Вы должны временно переименовать свою функцию, но см. примечание ниже для получения дополнительных предложений. Это приведет к выражению printf() в каждой точке вызова функции. Очевидно, что вам нужно выполнить некоторые действия, если вы вызываете функцию-член или хотите получить возвращаемое значение (например, передать вызов функции и __FUNCTION__ в пользовательскую функцию, возвращающую тот же тип...), но базовый техника такая же. Вы можете использовать __LINE__ и __FILE__ или некоторые другие макросы препроцессора в зависимости от того, какой у вас компилятор. (Этот пример специально предназначен для MS VС++, но, вероятно, работает и в других.)

Кроме того, вы можете поместить что-то вроде этого в свой заголовок, окруженный защитой #ifdef, чтобы условно включить его, что также может обрабатывать переименование фактической функции для вас.

ОБНОВЛЕНИЕ [2012-06-21]

У меня появился запрос на расширение моего ответа. Как оказалось, мой приведенный выше пример немного упрощен. Ниже приведены некоторые примеры компиляции с использованием С++.

Пример полного источника с возвращаемым значением

Использование class с operator() делает это довольно прямолинейно. Этот первый метод работает для автономных функций с возвратными значениями и без них. operator() просто должен отражать тот же возврат, что и соответствующая функция, и иметь соответствующие аргументы.

Вы можете скомпилировать это с помощью g++ -o test test.cpp для версии без отчета и g++ -o test test.cpp -DREPORT для версии, отображающей информацию о вызывающем абоненте.

#include <iostream>

int FunctionName(int one, int two)
{
  static int calls=0;
  return (++calls+one)*two;
}

#ifdef REPORT
  // class to capture the caller and print it.  
  class Reporter
  {
    public:
      Reporter(std::string Caller, std::string File, int Line)
        : caller_(Caller)
        , file_(File)
        , line_(Line)
      {}

      int operator()(int one, int two)
      {
        std::cout
          << "Reporter: FunctionName() is being called by "
          << caller_ << "() in " << file_ << ":" << line_ << std::endl;
        // can use the original name here, as it is still defined
        return FunctionName(one,two);
      }
    private:
      std::string   caller_;
      std::string   file_;
      int           line_;

  };

// remove the symbol for the function, then define a new version that instead
// creates a stack temporary instance of Reporter initialized with the caller
#  undef FunctionName
#  define FunctionName Reporter(__FUNCTION__,__FILE__,__LINE__)
#endif


void Caller1()
{
  int val = FunctionName(7,9);  // <-- works for captured return value
  std::cout << "Mystery Function got " << val << std::endl;
}

void Caller2()
{
  // Works for inline as well.
  std::cout << "Mystery Function got " << FunctionName(11,13) << std::endl;
}

int main(int argc, char** argv)
{
  Caller1();
  Caller2();
  return 0;
}

Пример вывода (отчетность)

Reporter: FunctionName() is being called by Caller1() in test.cpp:44
Mystery Function got 72
Reporter: FunctionName() is being called by Caller2() in test.cpp:51
Mystery Function got 169

В принципе, в любом месте, где FunctionName, он заменяет его на Reporter(__FUNCTION__,__FILE__,__LINE__), чистым эффектом которого является препроцессор, записывающий некоторый объект с немедленным вызовом функции operator(). Вы можете просмотреть результат (в gcc) подпроцессорных подстановок с помощью g++ -E -DREPORT test.cpp. Caller2() становится следующим:

void Caller2()
{
  std::cout << "Mystery Function got " << Reporter(__FUNCTION__,"test.cpp",51)(11,13) << std::endl;
}

Вы можете видеть, что __LINE__ и __FILE__ были заменены. (Я не уверен, почему __FUNCTION__ по-прежнему показывает на выходе, если честно, но скомпилированная версия сообщает о правильной функции, поэтому она, вероятно, имеет какое-то отношение к многопроходной предварительной обработке или ошибке gcc.)

Пример с полным источником с функцией члена класса

Это немного сложнее, но очень похоже на предыдущий пример. Вместо того чтобы просто заменить вызов функции, мы также заменяем класс.

Как и в предыдущем примере, вы можете скомпилировать это с помощью g++ -o test test.cpp для версии без отчета и g++ -o test test.cpp -DREPORT для версии, которая отображает информацию о вызывающем абоненте.

#include <iostream>

class ClassName
{
  public:
    explicit ClassName(int Member)
      : member_(Member)
      {}

    int FunctionName(int one, int two)
    {
      return (++member_+one)*two;
    }

  private:
    int member_;
};

#ifdef REPORT
  // class to capture the caller and print it.  
  class ClassNameDecorator
  {
    public:
      ClassNameDecorator( int Member)
        : className_(Member)
      {}

      ClassNameDecorator& FunctionName(std::string Caller, std::string File, int Line)
      {
        std::cout
          << "Reporter: ClassName::FunctionName() is being called by "
          << Caller << "() in " << File << ":" << Line << std::endl;
        return *this;
      }
      int operator()(int one, int two)
      {
        return className_.FunctionName(one,two);
      }
    private:
      ClassName className_;
  };


// remove the symbol for the function, then define a new version that instead
// creates a stack temporary instance of ClassNameDecorator.
// FunctionName is then replaced with a version that takes the caller information
// and uses Method Chaining to allow operator() to be invoked with the original
// parameters.
#  undef ClassName
#  define ClassName ClassNameDecorator
#  undef FunctionName
#  define FunctionName FunctionName(__FUNCTION__,__FILE__,__LINE__)
#endif


void Caller1()
{
  ClassName foo(21);
  int val = foo.FunctionName(7,9);  // <-- works for captured return value
  std::cout << "Mystery Function got " << val << std::endl;
}

void Caller2()
{
  ClassName foo(42);
  // Works for inline as well.
  std::cout << "Mystery Function got " << foo.FunctionName(11,13) << std::endl;
}

int main(int argc, char** argv)
{
  Caller1();
  Caller2();
  return 0;
}

Вот пример вывода:

Reporter: ClassName::FunctionName() is being called by Caller1() in test.cpp:56
Mystery Function got 261
Reporter: ClassName::FunctionName() is being called by Caller2() in test.cpp:64
Mystery Function got 702

Высокими точками этой версии являются класс, который украшает исходный класс, и функцию замены, которая возвращает ссылку на экземпляр класса, позволяя operator() выполнять фактический вызов функции.

Надеюсь, что это поможет кому-то!

Ответ 3

С версией GCC ≥ 4.8 вы можете использовать __builtin_FUNCTION - не путать с __FUNCTION__ и аналогичным - кажется, это немного неясно.

Пример:

#include <cstdio>

void foobar(const char* str = __builtin_FUNCTION()){
    std::printf("called by %s\n", str);
}

int main(){
    foobar();
    return 0;
}

выход:

called by main

пример в WandBox

Ответ 4

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

Ответ 5

Вариация ответа Аарона. Я не уверен, имеет ли этот ответ эту проблему, но когда вы выполняете функцию #define function, она становится глобальной переменной, а затем, если в вашем проекте есть несколько классов с одинаковым именем функции класса-члена, у всех классов будет переопределено имя функции. к той же функции.

#include <iostream>

struct ClassName {
    int member;
    ClassName(int member) : member(member) { }

    int secretFunctionName(
              int one, int two, const char* caller, const char* file, int line) 
    {
        std::cout << "Reporter: ClassName::function_name() is being called by "
                << caller << "() in " << file << ":" << line << std::endl;

        return (++member+one)*two;
    }
};

#define unique_global_function_name(first, second) \
        secretFunctionName(first, second, __FUNCTION__,__FILE__,__LINE__)

void caller1() {
    ClassName foo(21);
    int val = foo.unique_global_function_name(7, 9);
    std::cout << "Mystery Function got " << val << std::endl;
}

void caller2() {
    ClassName foo(42);
    int val = foo.unique_global_function_name(11, 13);
    std::cout << "Mystery Function got " << val << std::endl;
}

int main(int argc, char** argv) {
    caller1();
    caller2();
    return 0;
}

Результат:

Reporter: ClassName::function_name() is being called by caller1() in D:\test.cpp:26
Mystery Function got 261
Reporter: ClassName::function_name() is being called by caller2() in D:\test.cpp:33
Mystery Function got 702

Ответ 6

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

Ответ 7

В первом приближении просто используйте grep для базы данных для имен функций. Затем идет Doxygen, а затем динамическое ведение журнала (оба обсуждаются другими). ​​

Ответ 8

Вы можете использовать этот код, чтобы отслеживать локусы контроля в последних n точках вашей программы. Использование: см. Основную функцию ниже.

// What: Track last few lines in loci of control, gpl/moshahmed_at_gmail
// Test: gcc -Wall -g -lm -std=c11 track.c
#include <stdio.h>
#include <string.h>

#define _DEBUG
#ifdef _DEBUG
#define lsize 255 /* const int lsize=255; -- C++ */
struct locs {
  int   line[lsize];
  char *file[lsize];
  char *func[lsize];
  int  cur; /* cur=0; C++ */
} locs;

#define track do {\
      locs.line[locs.cur]=__LINE__ ;\
      locs.file[locs.cur]=(char*)__FILE__ ;\
      locs.func[locs.cur]=(char*) __builtin_FUNCTION() /* __PRETTY_FUNCTION__ -- C++ */ ;\
      locs.cur=(locs.cur+1) % lsize;\
  } while(0);

void track_start(){
  memset(&locs,0, sizeof locs);
}

void track_print(){
  int i, k;
  for (i=0; i<lsize; i++){
    k = (locs.cur+i) % lsize;
    if (locs.file[k]){
      fprintf(stderr,"%d: %s:%d %s\n",
        k, locs.file[k],
        locs.line[k], locs.func[k]);
    }
  }
}
#else
#define track       do {} while(0)
#define track_start() (void)0
#define track_print() (void)0
#endif


// Sample usage.
void bar(){ track ; }
void foo(){ track ; bar(); }

int main(){
  int k;
  track_start();
  for (k=0;k<2;k++)
    foo();
  track;
  track_print();
  return 0;
} 

Ответ 9

Cflow может использоваться для получения графа вызовов исходного кода, написанного на C/C++. Вы можете проанализировать этот график вызовов, чтобы получить то, что вы хотите.