Методи динамічного аналізу
Тут я описую кілька динамічних методів аналізу.
Динамічні методи фактично запускають програму для визначення графіка виклику.
Протилежністю динамічним методам є статичні методи, які намагаються визначити його лише з джерела, не запускаючи програму.
Переваги динамічних методів:
- ловить покажчики функцій та віртуальних викликів 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
Мабуть, найефективніший метод, окрім конкретної підтримки апаратного відстеження, але має і недолік, який вам доведеться перекомпілювати.