gcc 您所在的位置:网站首页 funcl函数 gcc

gcc

2023-08-20 03:06| 来源: 网络整理| 查看: 265

    我们在阅读源码的时候经常会碰到很多回调函数,而单步调试又比较麻烦,所以我们可以用gcc 的-finstrument-functions 选项打印函数调用栈。

   如果我们在编译、链接的时候在gcc加上-finstrument-functions,gcc会自动在函数的入口调用 __cyg_profile_func_enter函数,在函数出口调用__cyg_profile_func_exit函数。参数this_fn 为当前函数的起始地址,call_site为返回地址,即caller函数中的地址。注意inline函数也会调用这两个函数,如果不想调用这两个函数,在声明函数时增加no_instrument_function属性。

void __cyg_profile_func_enter (void *this_fn, void *call_site); void __cyg_profile_func_exit (void *this_fn, void *call_site);

举例说明,假如我们有函数  

void func () { printf("Hello world!\n"); }

在编译时gcc增加了-finstrument-functions选项,函数就会变成

void func() { __cyg_profile_func_enter(this_fn, call_site); printf("Hello world!\n"); __cyg_profile_func_exit(this_fn, call_site); }

如果不想让这个函数调用,声明时我们需要这样

void func() __attribute__((no_instrument_function));

顺便说一下__attribute__关键字

作用:__attribute__可以设置函数属性,变量属性和类型属性。

格式:放在声明的尾部;之前,attribute之前和后面各有两个下划线,后面是一对括弧,括弧里是相应的属性。

举例:(packed属性)如下结构体

typedef struct STUDENT { int age; char c; }student;

我们sizeof这个结构体会发现结果是8,这是因为编译器会对结构体调整,内存对齐的结果。如果我们不需要内存对齐,可以这样声明

typedef struct STUDENT { int age; char c; }__attribute__((packed)) student;

我们再sizeof结果就会为5.

再来一个__attribute__((weak)) 

当我们不确定外部是否提供一个函数func时,但是我们又必须使用这个函数,就可以用到__attribute__((weak)) 

int __attribute__((weak)) func(......) { ........ return 0; }

他的作用是当外部定义func函数时,调用外部的func函数,当外部没有定义func函数时,调用自己定义的func函数。即将本模块的func转成弱符号类型,如果遇到强符号类型(即外部模块定义了func),那么我们在本模块执行的func将会是外部模块定义的func。注意:weak属性只会在静态库(.o .a )中生效,动态库(.so)中不会生效。

言归正传

我们有程序finstrument.c

#include #include int func(int a, int b) { return a + b; } static inline void print(int n) { printf("%d\n", n); } int main() { func(3, 4); print(func(3,4)); return 0; }

实现__cyg_profile_func_enter和__cyg_profile_func_exit函数在my_debug.c中

#ifndef __MY_DEBUG_H__ #define __MY_DEBUG_H__ #include void __attribute__((no_instrument_function)) debuf_log(const char *format,...); void __attribute__((no_instrument_function)) __cyg_profile_func_enter(void*, void*); void __attribute__((no_instrument_function)) __cyg_profile_func_enter(void*, void*); #endif #include #include #include "my_debug.h" #define DEBUG_FILE_PATH "./mydebug.log" void __attribute__((no_instrument_function)) debug_log(const char *format,...) { FILE *fp; va_list ap; va_start(ap, format); fp = fopen(DEBUG_FILE_PATH, "a"); if(NULL == fp) { printf("Can not open debug file.\n"); return; } vfprintf(fp, format, ap); va_end(ap); fflush(fp); fclose(fp); } void __attribute__((no_instrument_function)) __cyg_profile_func_enter(void *this, void *call) { debug_log("Enter\n%p\n%p\n", call, this); } void __attribute__((no_instrument_function)) __cyg_profile_func_exit(void *this, void *call) { debug_log("Exit\n%p\n%p\n", call, this); }

编译命令

gcc finstrument.c my_debug.c -g -finstrument-functions -o finstrument

执行可执行文件生成mydebug.log文件

看不懂没关系,我们addr2line工具

一条条转换太麻烦,可以用如下脚本批量转换

#!/bin/sh if [ $# != 3 ]; then echo 'Usage: addr2line.sh executefile addressfile functionfile' exit fi; cat $2 | while read line do if [ "$line" = 'Enter' ]; then read line1 read line2 addr2line -e $1 -f $line1 -s >> $3 echo "-----> call" >> $3 addr2line -e $1 -f $line2 -s | sed 's/^/ /' >> $3 echo >> $3 elif [ "$line" = 'Exit' ]; then read line1 read line2 addr2line -e $1 -f $line2 -s | sed 's/^/ /' >> $3 echo "


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有