LINUX.ORG.RU

Автоматически вставить макрос в начало функции и в конец

 , , , ,


1

2

Для регистрации входа в функцию и выхода есть -finstrument-functions который укажут вызываемую и вызывающую функцию, ну и вход и выход из функций.

#include <stdio.h>


char * str = 0;
    void __cyg_profile_func_enter (void *, void *) __attribute__((no_instrument_function));
    void __cyg_profile_func_exit (void *, void *) __attribute__((no_instrument_function));
    void __cyg_profile_func_enter (void *func,  void *caller)
    {
        printf("start \n");
    }


    void __cyg_profile_func_exit (void *func, void *caller)
    {
        printf("end \n");
    }

void func(){}

int main(int argc, char *argv[])
{
    func();
    return 0;
}
dron@gnu:~/Рабочий-стол$ gcc cc.c -Wall -pedantic -std=c11 -finstrument-functions
dron@gnu:~/Рабочий-стол$ ./a.out 
start 
start 
end 
end 
dron@gnu:~/Рабочий-стол$ 

Есть ли что-то такое же, но для макросов? Суть. Я для себя лабаю трассировщик вызовов, да я знаю про backtrace_*() функции. Но без объяснений почему я его не хочу, я его не хочу в данном случае. Ну если кратко быстрее __func__,__FILE__ из тела при вызове отдать чем дрыгать backtrace_*() который будет дрыгать таблицы. Но но это не суть вообще.

Сейчас я делаю тупо вставку макросов в начало и конец функции, если есть return то перед каждым return то есть перед выходом

/*
RS --Record Start
RE --Record End
PB --Print Backtrace
*/
void function_5() {RS   RE}
void function_4() {RS   function_5();  function_5(); function_5();  RE}
void function_3() {RS   function_4();  function_4(); function_4();  RE}
void function_1() {RS   function_3();  PB;  RE}
void function_2() {RS   function_1();  function_1(); function_1();  RE}
int main()
{
RS
    function_2();
RE
}

с выхлопом

dron@gnu:~/Рабочий-стол/HUNTER$ gcc cc.c
dron@gnu:~/Рабочий-стол/HUNTER$ ./a.out 
┣━━━━━━ frame ━━━━━━━>
cc.c:main() 
└─> cc.c:function_2() 
│   └─> cc.c:function_1() 
│   │   └─> cc.c:function_3() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   ┗━> backtrace from ━━━> cc.c:function_1() 
┣━━━━━━ frame ━━━━━━━>
cc.c:main() 
└─> cc.c:function_2() 
│   └─> cc.c:function_1() 
│   │   └─> cc.c:function_3() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   └─> cc.c:function_1() 
│   │   └─> cc.c:function_3() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   ┗━> backtrace from ━━━> cc.c:function_1() 
┣━━━━━━ frame ━━━━━━━>
cc.c:main() 
└─> cc.c:function_2() 
│   └─> cc.c:function_1() 
│   │   └─> cc.c:function_3() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   └─> cc.c:function_1() 
│   │   └─> cc.c:function_3() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   └─> cc.c:function_1() 
│   │   └─> cc.c:function_3() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   └─> cc.c:function_4() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   │   │   └─> cc.c:function_5() 
│   │   ┗━> backtrace from ━━━> cc.c:function_1() 
dron@gnu:~/Рабочий-стол/HUNTER$ 


Всё хорошо (ну ещё доделывать надо но суть ясна) только расставлять макросы муторно.

Вариантов вижу три

  • 1 руками

  • 2 парсером каким, который определит что это функция, вот её начало, а вот конец, а вот return и return вдруг сам не макрос

  • 3 чем то типа -finstrument-functions но для макросов

Вот собсна всё. Чво посоветовать по этому поводу есть?

★★★★★

Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)

Ответ на: комментарий от pon4ik

Ну это что-то серьёзное. Гляну спасибо. Но ты предлагаешь мне выкинуть на помойку мою поделку. В принципе здравая мысль, но вопрос не в этом вообще. Вопрос в автоматической вставке макроса, примитивный трассировщик это просто пример что-бы лишний раз не отвечать на вопросы «а тебе зачем», «а какой в этом смысл» и прочее =)

Порою что-бы выяснить где что не так мы как деды дебаг printfом делаем. Это часто быстрее, удобнее, надёжнее. Если случаи простые. Пример именно про такой подход. А так можно просто в библиотеку компильнуть и через ltrace увидеть всё подробно. Но опять же это просто про пример и к сути вопроса отношение имеет косвенное.

LINUX-ORG-RU ★★★★★
() автор топика
Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)
Ответ на: комментарий от LINUX-ORG-RU

А, ты в вакууме задачку решаешь, я думал тебе для дела :)

Вставку макросов надо думать как делать на основе чего-то типа clang-format. Но это заведомо провальная идея учитывая что

дрыгать backtrace_*() который будет дрыгать таблицы

типа медленно, хотя имхо твой мопед может оказаться медленнее.

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

Единственное достоинство подхода с макросами, это возможность трэйсить встроенные функции.

pon4ik ★★★★★
()

А чем тебе -finstrument не подходит? Получай рантайме имена функций через парсинг elf’а. Я как-то баловался, так получал имя передавая адрес функции:

__attribute__((no_instrument_function))
static uintptr_t get_base()
{
   FILE *f = fopen("/proc/self/maps", "r");
   if (! f)
      return 0;
   void *base;
   int res = fscanf(f, "%p", &base);
   fclose(f);
   if (res < 1)   
      return 0;
   return (uintptr_t)base;
}

__attribute__((no_instrument_function))
static const char *address_to_name(void *fn, int *o_local)
{
   static uintptr_t gdb_base_address = 0;
   static Elf64_Sym *sym_table = 0;
   static int symtab_sz = 0;
   static const char *string_sec = 0;
   static void *elf_buf;
   static enum {
      uninit,
      ok,
      error
   }state = uninit;

   switch (state) {
      case error:
         return 0;
      case ok: 
         break;
      default: {
         FILE *f = fopen("/proc/self/exe", "r");
         if (! (gdb_base_address = get_base()))
            goto err_label;
         { //block
            if (! f  ||  fseek(f, 0, SEEK_END))
               goto err_label;
            size_t fsz = (size_t)ftell(f);
            fseek(f, 0, SEEK_SET);

            if (! (elf_buf = malloc(fsz))  ||
                  fread(elf_buf, 1, fsz, f) != fsz)
               goto err_label;
            fclose(f), f = 0;

            Elf64_Ehdr *head = (Elf64_Ehdr*)elf_buf;
            Elf64_Shdr *sec_head = (Elf64_Shdr*)((char*)elf_buf + head->e_shoff);
            for (int i = 0;  i < head->e_shnum;  ++ i) {
               if (sec_head[i].sh_type == SHT_SYMTAB) {
                  sym_table = (Elf64_Sym*)((char*)elf_buf + sec_head[i].sh_offset);
                  symtab_sz = sec_head[i].sh_size;
               }
               else if (sym_table  &&  sec_head[i].sh_type == SHT_STRTAB) {
                  string_sec = (char*)elf_buf + sec_head[i].sh_offset;
                  break;
               }
            }
            if (! sym_table  ||  ! string_sec)
               goto err_label;
            state = ok;
            break;
         }
err_label:
         if (f != 0)
            fclose(f);
         state = error;
         return 0;
      }
   }

   for (Elf64_Sym *it = sym_table; (char*)it < (char*)sym_table+symtab_sz;
         ++ it) {
      if (fn == (char*)it->st_value + gdb_base_address) {
         *o_local =  ELF64_ST_BIND(it->st_info) == STB_LOCAL ? 1 : 0;
         return string_sec + it->st_name;
      }
   }
   return 0;
}

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)

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

SR_team ★★★★★
()
Ответ на: комментарий от kvpfs

*бушки воробушки. Если оперировать адресами я из backtrace_symbols имена возьму =). Но спасибо за прикол. Может пригодится.

LINUX-ORG-RU ★★★★★
() автор топика

<troll>Можно написать свой шаблонизатор на python или на regexp’ах</troll>

Что вообще за магия написана в первом куске кода, пойду почитаю про это.

snake266 ★★
()
Ответ на: комментарий от pon4ik

Ну, да. Ты прав. Но вопрос остаётся открыт. Есть ли некая встроенная фича по втыкиванию макросов. С учётом что макросы должны развернутся то вопрос к препроцессору скорее. И да можно оперировать адресами, а дальше уже получать имена только при выводе. Ну блин. А вдлуг можна как я хачу гыыы ^.^

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: комментарий от SR_team

Можно, но надо руками перефигачивать опять же. Я так же могу руками RS RE проставить, с там же успехом =) А потом прописать в своём кодовом стиле что мол RS|RE обязательны в любом теле функции =)

LINUX-ORG-RU ★★★★★
() автор топика
Ответ на: gcc plugin от anonymous

Сща глянем

UDP: Не, ну нахер =)

LINUX-ORG-RU ★★★★★
() автор топика
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.