Получить указатель на текущую функцию в C (GCC)? - PullRequest
12 голосов
/ 28 января 2010

есть ли в gcc магическая переменная, содержащая указатель на текущую функцию?

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

Я знаю, что есть переменная __func__, содержащая имя текущей функции в виде строки, но не в качестве указателя на функцию.

Это не для вызова функции, а для использования в качестве индекса.

EDIT По сути, я хотел бы иметь возможность запускать вложенные функции непосредственно перед выполнением текущей функции (а также захватывать возврат для выполнения некоторых действий). По сути, это похоже на __cyg_profile_func_enter и __cyg_profile_func_exit (функции инструментов) ... Но проблема в том, что эти функции инструментов являются глобальными, а не выделенными для функций.

EDIT В ядре Linux вы можете использовать unsigned long kallsyms_lookup_name(const char *name) из include/linux/kallsyms.h ... Обратите внимание, что опция CONFIG_KALLSYMS должна быть активирована.

Ответы [ 12 ]

6 голосов
/ 28 января 2010
void f() {
   void (*fpointer)() = &f;
}
3 голосов
/ 28 января 2010
#define FUNC_ADDR (dlsym(dlopen(NULL, RTLD_NOW), __func__))

И скомпилируйте вашу программу как

gcc -rdynamic -o foo foo.c -ldl
2 голосов
/ 28 января 2010

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

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

UPDATE:

Я имею в виду что-то вроде:

typedef struct {
  char[MAX_FUNC_NAME_LENGTH] func_name;
  //rest of the info here
} func_info;

func_info table[N_FUNCS];

#define CHECK_AND_GET_FUNC_NAME(f) ({void (*tmp)(int); tmp = f; #f})

void fill_it()
{
  int i = -1;
  strcpy(table[++i].func_name, CHECK_AND_GET_FUNC_NAME(foo));
  strcpy(table[++i].func_name, CHECK_AND_GET_FUNC_NAME(bar));
  //fill the rest
}

void lookup(char *name) {
  int i = -1;
  while(strcmp(name, table[++i]));
  //now i points to your entry, do whatever you need
}

void foo(int arg) {
  lookup(__func__);
  //do something
}

void bar(int arg) {
  lookup(__func__);
  //do something
}

(код может нуждаться в некоторых исправлениях, я не пытался его скомпилировать, это просто для иллюстрации идеи)

1 голос
/ 28 января 2010

Вот трюк, который получает адрес звонящего, возможно, его можно немного почистить. Для получения значения метки .

использует расширение GCC.
#include <stdio.h>

#define MKLABEL2(x) label ## x
#define MKLABEL(x) MKLABEL2(x)
#define CALLFOO do { MKLABEL(__LINE__): foo(&&MKLABEL(__LINE__));} while(0)

void foo(void *addr)
{
    printf("Caller address %p\n", addr);
}

int main(int argc, char **argv)
{
    CALLFOO;
    return 0;
}
1 голос
/ 28 января 2010

Если вы выбрали C ++, вам может помочь следующая информация:

Объекты набираются, функторы - это функции, обернутые в объекты, RTTI позволяет идентифицировать тип во время выполнения.

Функторы несут с собой накладные расходы времени выполнения, и если это проблема для вас, я бы предложил жестко кодировать знания, используя генерацию кода или используя OO-иерархию функторов.

0 голосов
/ 17 сентября 2012

static const char * const cookie = __FUNCTION__;

__FUNCTION__ будет храниться в текстовом сегменте вашего двоичного файла, и указатель всегда будет уникальным и действительным.

0 голосов
/ 29 января 2010

Другой вариант, если переносимость не является проблемой, это настроить исходный код GCC ... какие-либо добровольцы?!

0 голосов
/ 28 января 2010

Если вы хотите создать код, я бы порекомендовал GSLGen от Imatix. Он использует XML для структурирования модели вашего кода, а затем простой PHP-подобный язык генерации сверху вниз, чтобы выплевывать код - он использовался для генерации кода на языке Си.

Я лично играл с Луа, чтобы сгенерировать код.

0 голосов
/ 28 января 2010

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

Эта структура крайне непереносима, но вы явно упоминаете GCC, так что, вероятно, это не проблема блокировки. Посмотрите этот пример GCC / x86 , чтобы понять, как он работает приблизительно.

0 голосов
/ 28 января 2010

Если вы хотите сделать это «общим» способом, вам следует использовать средства, о которых вы уже упоминали (__cyg_profile_func*), поскольку именно для этого они и предназначены. Все остальное должно быть таким же специальным, как ваш профиль.

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

...