Сопоставьте идентификаторы из файлов, сгенерированных g , с параметрами -fstack-usage и -fdump-rtl-expand

#c #gcc #g #static-analysis #gcc8

#c #gcc #g #статический анализ #gcc8

Вопрос:

Введение

Я пытаюсь выполнить статическую оценку максимально возможного использования стека во встроенной программе. Мне удалось добиться использования стека каждой отдельной функции с помощью опции GCC -fstack-usage , и я построил дерево всех вызовов функций путем анализа RTL, созданного опцией -fdump-rtl-expand .

Теперь осталась только простая часть, когда я устанавливаю использование стека для каждой функции в моем дереве и ищу самый дорогой путь, используя какой-то алгоритм Дейкстры. Однако оказалось, что выяснить, какая функция из файла использования стека коррелирует с какой функцией из файла 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
 

Таким образом, функция названа test и _Z4testv в файле RTL, и 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

Обязательно используйте для этого 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
 

Правка2

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

Комментарии:

1. Я могу объединять искаженные и не искаженные имена, возвращаемые с помощью c filt using tmp=$(cat *expand | grep '^;; Function' | cut -d'(' -f2- | cut -d, -f1); paste <(cat <<<$tmp) <(<<<$tmp c filt) , но формат, используемый при использовании стека, сложный. Пожалуйста, опубликуйте пример с шаблонной функцией, где это сложно, например. template<typename T> long some_name(int); template<> long some_name<int>(int a) { return a; } template<> long some_name<double>(int a) { return a; }

2. Полагаю, я буду исходить из предположения, что порядок функций в обоих файлах одинаков, и молиться, чтобы ничего не пошло не так. К счастью, моя программа имеет достаточно небольшое количество функций, поэтому я могу проверить это вручную.