我正在尝试对嵌入式程序中最大可能的堆栈使用情况进行静态评估。
我已经通过使用GCC选项-fstack-usage
设法获得了每个函数的堆栈使用率,并且通过解析选项-fdump-rtl-expand
产生的RTL构建了所有函数调用的树。
现在,当我为树中的每个函数设置堆栈使用率并通过使用某种Dijkstra算法寻找最昂贵的路径时,只剩下了最简单的部分。 但是,事实证明,从堆栈使用情况文件中找出哪个功能与RTL文件中的哪个功能相关联似乎是整个工作中最困难的部分。
那到底是什么问题?
简而言之,GCC为这两个文件生成的功能标识符是不同的。 RTL同时使用错误的标识符和人类可读的名称。后者与您在源代码中可以看到的相似。 堆栈用法文件只有人类可读的名称,但是它们以某种怪异的符号编写,与RTL文件中的符号完全不同。
我们来看一个例子。
源文件test.cpp
:
void test() {}
命令(工具链:GCC v8.2.0):
g++ -c test.cpp -fdump-rtl-expand -fstack-usage
RTL文件./test.cpp.234r.expand
:
;; Function test (_Z4testv, funcdef_no=0, decl_uid=2358, cgraph_uid=0, symbol_order=0)
;; Generating RTL for gimple basic block 2
try_optimize_cfg iteration 1
Merging block 3 into block 2...
Merged blocks 2 and 3.
Merged 2 and 3 without moving.
Merging block 4 into block 2...
Merged blocks 2 and 4.
Merged 2 and 4 without moving.
try_optimize_cfg iteration 2
;;
;; Full RTL generated for this function:
;;
(note 1 0 3 NOTE_INSN_DELETED)
(note 3 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
(note 2 3 7 2 NOTE_INSN_FUNCTION_BEG)
(insn 7 2 0 2 (const_int 0 [0]) "test.cpp":1 -1
(nil))
堆栈使用情况文件test.su
:
test.cpp:1:6:void test() 16 static
因此该函数在RTL文件中分别命名为test
和_Z4testv
,在堆栈使用情况文件中分别命名为void test()
。
当我们从我的代码中得到一些真实的例子时,要分辨哪些功能是相同的就变得更加困难。 名称将是(按顺序):
devices::Button<virtualIo::Port_<utils::type::list::List<virtualIo::IndexedPin_<0, virtualIo::PortD, 7, true> >, 1, unsigned char, utils::type::list::List<virtualIo::PortShifter_<virtualIo::PortD, unsigned char, unsigned char, false, 128, 1, utils::type::list::List<virtualIo::PartShifter_<unsigned char, unsigned char, 1, 7, 128> > > >, true>, true>::isUp
_ZNK7devices6ButtonIN9virtualIo5Port_IN5utils4type4list4ListIJNS1_11IndexedPin_ILh0ENS1_5PortDELh7ELb1EEEEEELh1EhNS6_IJNS1_12PortShifter_IS8_hhLb0ELh128ELh1ENS6_IJNS1_12PartShifter_IhhLh1ELa7ELh128EEEEEEEEEEELb1EEELb1ELb0EE4isUpEv
bool devices::Button<PORT, POSITIVE_INPUT, PULL_UP>::isUp() const [with PORT = virtualIo::Port_<utils::type::list::List<virtualIo::IndexedPin_<0, virtualIo::PortD, 7, true> >, 1, unsigned char, utils::type::list::List<virtualIo::PortShifter_<virtualIo::PortD, unsigned char, unsigned char, false, 128, 1, utils::type::list::List<virtualIo::PartShifter_<unsigned char, unsigned char, 1, 7, 128> > > >, true>; bool POSITIVE_INPUT = true; bool PULL_UP = false]
确定这是否是同一功能几乎是人类无法做到的,我也不知道如何使用脚本/程序来实现。
理想的解决方案是在堆栈使用情况文件中包含名称混乱的名称。它们是统一且明确的(除了对构造函数和析构函数的一些小麻烦)。
我会接受任何允许可靠地将一个文件中的名称与另一个文件中的名称匹配的方法。
在万不得已的情况下,我将被迫按照它们在两个文件中看起来相同的顺序来匹配功能。不过,我很犹豫做出这个假设。
请确保为此使用GCC版本8。看起来版本7以不同的方式生成名称。 我测试了8.2.0和8.3.0版本。两者都有所描述的行为。
我不能使用版本7。它有一些错误,导致我的代码无法编译。
源文件test.cpp
:
template<typename T> long some_name(int); template<> long some_name<int>(int) { return 0; }
RTL文件./test.cpp.234r.expand
的第一行:
;; Function some_name<int> (_Z9some_nameIiEli, funcdef_no=0, decl_uid=2364, cgraph_uid=0, symbol_order=0)
堆栈使用情况文件test.su
:
test.cpp:5:6:long int some_name(int) [with T = int] 16 static
实验表明,RTL文件中的人类可读标识符不是唯一的。 对于重载的功能,它们是相同的。