armcc 编译选项添加 --gnu_instrument (比如 CFLAGS += --gnu_instrument)
armclang 编译选项添加 -finstrument-functions (比如 CFLAGS += -finstrument-functions)
void __cyg_profile_func_enter(void *this, void *callsite)__attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *this, void *callsite)__attribute__((no_instrument_function));
void __cyg_profile_func_enter( void *this, void *callsite )
{
fprintf(fp, "E%p\n", (int *)this);
}
void __cyg_profile_func_exit( void *this, void *callsite )
{
fprintf(fp, "X%p\n", (int *)this);
}
这里输出当前函数的地址(instrument 函数: __cyg_profile_func_enter 和 __cyg_profile_func_exit 会分别在使用 -finstrument-functions 或 --gnu_instrument 编译过的模块函数进入和退出时自动调用, 这两个函数的第一个参数是当前函数的函数地址,第二参数是调用该函数的函数地址).
编译器生成的maps 文件,其中生成的函数段格式如下:
function1 0x882f29a9 Thumb Code 28 module.o(.text)
function2 0x882f2861 Thumb Code 28 module.o(.text)
假设 maps 文件名为 arm.map
运行使用 instrument 函数重新编译过的模块,假设 fp 的文件名是 addr.txt, 运行程序后addr.txt 的内容如下
E0x882f29a9
E0x882f2861
X0x882f2861
X0x882f29a9
原理很简单,就是到 map 文件中通过函数地址,找到对应的函数名称,这个过程我们使用 Pyhon 脚步完成
$ python3 addr2name.py arm.map addr.txt
Start address to name
function1
function2
addr2name.py 源码如下:
import sys
def find_name(fmap_name, addr):
result=""
with open(fmap_name, 'r') as fmap:
for line in fmap:
if line.find(addr) == -1:
continue
if line.split()[1] == addr:
result = line[:]
break
return result
def main():
print('Start address to name')
fmap_name = sys.argv[1]
faddr_name = sys.argv[2]
with open(faddr_name, 'r') as faddr:
for addr in faddr:
#print(repr(addr))
addr = addr.strip()
if not addr.startswith("E"):
continue
line = find_name(fmap_name, addr[1:])
print(line.split()[0]);
if __name__ == '__main__':
main()