Методи динамічного аналізу
Тут я описую кілька динамічних методів аналізу.
Динамічні методи фактично запускають програму для визначення графіка виклику.
Протилежністю динамічним методам є статичні методи, які намагаються визначити його лише з джерела, не запускаючи програму.
Переваги динамічних методів:
- ловить покажчики функцій та віртуальних викликів C ++. Вони присутні у великій кількості в будь-якому нетривіальному програмному забезпеченні.
Недоліки динамічних методів:
- Ви повинні запустити програму, яка може бути повільною або вимагати установки, якої у вас немає, наприклад, перехресна компіляція
- показуватимуться лише ті функції, які насправді викликали. Наприклад, деякі функції можна викликати чи не залежно від аргументів командного рядка.
KcacheGrind
https://kcachegrind.github.io/html/Home.html
Тестова програма:
int f2(int i) { return i + 2; }
int f1(int i) { return f2(2) + i + 1; }
int f0(int i) { return f1(1) + f2(2); }
int pointed(int i) { return i; }
int not_called(int i) { return 0; }
int main(int argc, char **argv) {
int (*f)(int);
f0(1);
f1(1);
f = pointed;
if (argc == 1)
f(1);
if (argc == 2)
not_called(1);
return 0;
}
Використання:
sudo apt-get install -y kcachegrind valgrind
# Compile the program as usual, no special flags.
gcc -ggdb3 -O0 -o main -std=c99 main.c
# Generate a callgrind.out.<PID> file.
valgrind --tool=callgrind ./main
# Open a GUI tool to visualize callgrind data.
kcachegrind callgrind.out.1234
Тепер ви залишилися в дивовижній програмі GUI, яка містить безліч цікавих даних про ефективність.
У нижньому правому куті виберіть вкладку «Графік викликів». Це показує графік інтерактивного виклику, який співвідноситься з показниками ефективності в інших вікнах під час натискання функцій.
Щоб експортувати графік, клацніть правою кнопкою миші та виберіть "Експорт графіка". Експортований PNG виглядає приблизно так:
З цього ми бачимо, що:
- кореневий вузол є
_start
фактичною точкою входу ELF і містить котло ініціалізації glibc
f0
, f1
і f2
називаються так, як очікували одне від одного
pointed
також показано, навіть якщо ми його назвали функцією вказівника. Можливо, його не викликали, якби ми передали аргумент командного рядка.
not_called
не показано, тому що його не викликали під час запуску, оскільки ми не передали додатковий аргумент командного рядка.
Класна річ у valgrind
тому, що вона не потребує спеціальних варіантів компіляції.
Тому ви можете використовувати його, навіть якщо у вас немає вихідного коду, а лише виконуваний файл.
valgrind
вдається це зробити, запустивши свій код через легку "віртуальну машину". Це також робить виконання надзвичайно повільним у порівнянні з нативним виконанням.
Як видно на графіку, також отримується інформація про терміни про кожен виклик функції, і це може бути використане для профілювання програми, яка, ймовірно, оригінальний випадок використання цього налаштування, а не просто для перегляду графіків викликів: Як я можу надати профіль Код C ++, що працює в Linux?
Тестовано на Ubuntu 18.04.
gcc -finstrument-functions
+ етрація
https://github.com/elcritch/etrace
-finstrument-functions
додає зворотні дзвінки , etrace аналізує файл ELF та реалізує всі зворотні виклики.
Я, на жаль, не міг змусити його працювати: Чому "-finstrument-функции" не працюють на мене?
Заявлений вихідний формат:
\-- main
| \-- Crumble_make_apple_crumble
| | \-- Crumble_buy_stuff
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | \-- Crumble_prepare_apples
| | | \-- Crumble_skin_and_dice
| | \-- Crumble_mix
| | \-- Crumble_finalize
| | | \-- Crumble_put
| | | \-- Crumble_put
| | \-- Crumble_cook
| | | \-- Crumble_put
| | | \-- Crumble_bake
Мабуть, найефективніший метод, окрім конкретної підтримки апаратного відстеження, але має і недолік, який вам доведеться перекомпілювати.