文章

GCC -finstrument-functions 选项介绍

GCC -finstrument-functions 选项介绍

介绍

开启 -finstrument-functions 选项后,编译器会在所有函数的进入和退出位置插入检测函数:

1
2
3
4
void __cyg_profile_func_enter (void *this_fn,
                               void *call_site);
void __cyg_profile_func_exit  (void *this_fn,
                               void *call_site);

使用

编译时使用 -finstrument-functions 参数,链接时不需要。

__cyg_profile_func_enter__cyg_profile_func_exit 这两个函数必须被声明并定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
extern "C" {
// 必须为其添加 no_instrument_function 说明,防止其自身发生嵌套
void __cyg_profile_func_enter(void *func, void *caller)
    __attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *func, void *caller)
    __attribute__((no_instrument_function));
}

// 功能示例:进入函数时判断 SP 位置是否超出界限
void __cyg_profile_func_enter(void *func, void *caller) {
  __asm__ volatile(
      "MOV   R2, %0       \n\t"    // 加载 currPoolTop 的值到 R0
      "LDR   R0, [R2]     \n\t"  // 加载 currPoolTop 的值到 R0
      "CMP   R0, SP       \n\t" // 比较 currPoolTop (R0) 与 SP
      "BLS   1f    \n\t" // 如果 currPoolTop > SP,异常处理;否则直接返回
      "B .         \n\t"  // 无限循环
      "1:          \n\t"
      :
      : "r"(&currPoolTop) // 输入参数:currPoolTop 的地址
      : "r0", "r2"        // 使用 R0 和 R2,所以要指定它们为修改过的寄存器
  );
}

// 可以使用空的实现
void __cyg_profile_func_exit(void *func, void *caller) {}

注意事项

  • 该编译选项将在所有函数中插入这两个函数,如果不希望某个函数被插入,使用 __attribute__((no_instrument_function)) 说明符
  • 这两个函数实现的时候要足够简单,必要时最好使用汇编来实现
  • 该功能最好仅用于调试而不是生产,因为会带来较大的性能损耗
  • 开启 LTO 后这两个函数可能被内联,如果是空实现就不会体现在最终二进制文件中了

参考

本文由作者按照 CC BY 4.0 进行授权

© Kai. 保留部分权利。

浙ICP备20006745号-2,本站由 Jekyll 生成,采用 Chirpy 主题。