[Powered by Google Translate] [Valgrind]
[Nate Хардісон, Гарвардський університет]
Це CS50, CS50.TV]
Деякі з найбільш важких помилок в програмах C
приходять від неправильного управління пам'яттю.
Є величезна кількість способів, щоб ввернути речі,
в тому числі виділяючи неправильний обсяг пам'яті,
забуваючи при ініціалізації змінних,
письмовій формі до чи після закінчення буфера,
і звільнення зберегти пам'ять кілька разів.
Симптоми варіюються від збої
таємничо перезаписані значеннями,
Часто на місцях і часах далеких від первісної помилки.
Відстеження спостережуваного проблеми назад в основний першопричини
може бути складним завданням,
але, на щастя, є корисний програму під назвою Valgrind
що може зробити багато, щоб допомогти.
>> Ви запускаєте програму під Valgrind щоб
обширні перевірки динамічної пам'яті асигнувань і доступів.
Коли Valgrind виявляє проблему, він дає вам негайний,
прямої інформації, яка дозволяє
легше знайти і виправити помилку.
Valgrind також повідомлення про менш небезпечні проблем з пам'яттю,
таких як витоку пам'яті, виділення динамічної пам'яті,
і забути, щоб звільнити його.
Подобається наш компілятор, Clang, на наш відладчик, GDB,
Valgrind є вільним програмним забезпеченням, і вона встановлена ​​на прилад.
Valgrind працює на вашому виконуваний файл,
не ваш. або с. ч. файлів вихідного коду,
так що ви зібрали останню дату копії вашої програми
використанням Clang або зробити.
Потім запустити програму під Valgrind може бути
так простий, як тільки префікс стандартної команди програми зі словом Valgrind,
який запускає Valgrind і запускає програму всередині нього.
При запуску Valgrind робить деякі складні
перетасовують налаштувати виконуваний файл для перевірки пам'яті,
так що це може зайняти трохи, щоб встати і бігти.
Програма буде виконувати нормально, будь то набагато повільніше,
і коли він закінчиться, Valgrind буде друкувати звіт про свою пам'яті.
Якщо все піде добре, то вона буде виглядати приблизно так:
У цьому випадку,. / Clean_program
це шлях до програми, я хочу бігти.
І хоча це не приймати ніяких аргументів,
якщо це так я б просто треку їх на кінці команди, як зазвичай.
Чистий програма просто дурна маленька програма, яку я створив
що виділяє місце для блоку цілими в купі,
поставити деякі значення в них, і звільняє весь блок.
Це те, що ви знімаєте для, без помилок і без витоків.
>> Іншим важливим показником є ​​загальна кількість байт виділяється.
Залежно від програми, якщо асигнування в мегабайтах або вище,
Ви, ймовірно, робите щось неправильно.
Ви надмірно зберігання дублікатів?
Чи використовуєте ви купу для зберігання, коли це було б краще використовувати стек?
Таким чином, помилки пам'яті може бути по-справжньому зло.
Більш відверті з них викликають захоплюючі аварії,
але навіть тоді вона може бути важко визначити
що саме призвело до аварії.
Більш підступно, програма з пам'яттю помилки
все ще може скомпільовано
і все ще може здаватися правильної роботи
тому що вам вдалося зловити успіх більшу частину часу.
Після декількох "успішних результатів"
Ви могли б просто думаю, що крах щасливому випадку на комп'ютері,
але комп'ютер ніколи не помиляється.
>> Запуск Valgrind може допомогти вам відстежити причину видимих ​​помилок пам'яті
а також знайти переховуються помилок, які ви навіть не знали ще про.
Кожен раз, коли Valgrind виявляє проблему, він виводить інформацію про те, що він спостерігав.
Кожен елемент є досить коротким -
Джерело лінія порушника інструкцій, в чому справа,
і трохи інформації про пам'ять беруть участь -
але часто це досить інформації, щоб звернути вашу увагу на правильне місце.
Ось приклад з Valgrind працює на баггі програми
, Що робить недійсним читання динамічної пам'яті.
Ми не бачимо ніяких помилок або попереджень в компіляцію.
Ой-ой, помилка Загальний говорить, що є дві помилки -
два недійсним причетний розміром 4 - байт, тобто.
Обидва погано читає сталася в основною функцією invalid_read.c,
першим на лінії 16, а другий на лінії 19.
Давайте подивимося на код.
Схоже, що перший виклик Printf намагається прочитати одну Int минулому кінця нашого блоку пам'яті.
Якщо ми оглянемося на виході Valgrind, в
ми бачимо, що Valgrind сказав нам саме це.
Адреса ми намагаємося читати починає 0 байт
в кінці минулого блок розміром 16 байта -
чотирьом 32-розрядних цілих чисел, що ми виділили.
Тобто, адреси ми намагалися прочитати починається в самому кінці нашого блоку,
так само, як ми бачимо в наших поганих Printf виклику.
Тепер, недійсними причетний не здається, що великі угоди,
Але якщо ви використовуєте ці дані для управління потоком ваша програма -
Наприклад, як частина, якщо заяву або петля -
Потім речі можуть мовчки йдуть погано.
Дивіться, як я можу запустити програму invalid_read
і нічого незвичайного не відбувається.
Страшно, так?
>> Тепер, давайте подивимося на кілька видів помилок, які можуть виникнути в коді,
і ми побачимо, як Valgrind виявляє їх.
Ми тільки що бачили приклад invalid_read,
так що тепер давайте подивимося invalid_write.
Знову ж таки, ніяких помилок або попереджень в компіляцію.
Добре, Valgrind говорить, що є дві помилки в цій програмі -
і invalid_write і invalid_read.
Давайте перевіримо цей код.
Схоже, що у нас є екземпляр класичного StrLen плюс одна помилка.
Кодекс не Танос додатковий байт простору
для / 0 характеру,
тому, коли копія вул пішла писати на ssubstrlen "CS50 камені!"
він написав 1 байт в кінці минулого нашого блоку.
Invalid_read приходить тоді, коли ми звертаємося до Printf.
Printf закінчується читанням недійсним пам'яті при читанні / 0 символів
як це виглядає в кінці цієї струни це друк.
Але все це не уникнув Valgrind.
Ми бачимо, що він спійманий invalid_write як частина вул копію
у рядку 11 основних і invalid_read є частиною Printf.
Rock On, Valgrind.
Знову ж, це може здатися не має великого значення.
Ми можемо запустити цю програму знову і знову поза Valgrind
і не бачу ніякої помилки симптомів.
>> Тим не менш, давайте подивимося на невелику зміну цього, щоб побачити
як речі можуть стати дуже погано.
Так що, правда, ми зловживання речей більше, ніж просто трохи в цьому коді.
Ми тільки виділення місця в динамічній пам'яті для двох рядків
Довжина CS50 порід,
на цей раз, пам'ятаючи / 0 характеру.
Але тоді ми кидаємо в супер-довгий рядок в блоці пам'яті
що S вказує.
Який ефект буде, що є на блок пам'яті, що T вказує на?
Ну, якщо T вказує на пам'ять, що просто поруч з S,
майбутні тільки після цього,
Потім ми могли б написано над частиною T.
Давайте запустимо цей код.
Подивіться, що відбулося.
Ми рядків зберігається в нашій купі блоків як видається, належним чином не друкується.
Ніщо, здається, в корені хибна.
Тим не менш, давайте повернемося в наш код і
закоментуйте рядок, де ми копіюємо CS50 порід
у другій блок пам'яті, на який вказує тонн.
Тепер, коли ми запустимо цей код, ми повинні
Тільки побачити вміст першого блоку пам'яті роздрукувати.
Ух ти, навіть якщо ми цього не зробили, вул копію
будь-які символи, у другому блоці купа, на яку вказує T,
ми отримуємо друк.
Дійсно, рядок, яку ми заправлені в нашому першому блоці
захопили перший блок, а в другому блоці,
робить все здається нормальним.
Valgrind, однак, говорить нам правду.
Там ми йдемо.
Всі ці недійсним читає і пише.
>> Давайте подивимося на приклад іншого роду помилки.
Тут ми робимо щось досить невдало.
Ми захопити простір для Int в купі,
і ми ініціалізували покажчик Int - P - вказати на це простір.
Тим не менш, у той час як наш покажчик ініціалізується,
дані, які він вказує тільки має всі барахло знаходиться в тій частині купи.
Тому, коли ми завантажити дані в Int я,
Ми технічно ініціалізації я,
але ми робимо це з небажаних даних.
Виклик стверджують, що це зручний макрос налагодження
визначені в влучно назвав стверджують, бібліотеки,
перерве програму, якщо її тест умова не виконується.
Тобто, якщо я не дорівнює 0.
В залежності від того, що було в купі простір, на яке вказує р,
ця програма може працювати іноді і не в інший час.
Якщо це працює, ми просто пощастило.
Компілятор не буде ловити цю помилку, але впевнений, Valgrind волі.
Там ми бачимо помилки, що випливають з нашого використання, що небажані дані.
>> При виділенні динамічної пам'яті, але не звільняє його або звільнити його,
що називається витік.
Для невеликої, недовгою програма, яка запускається і тут же виходить,
Витоку досить нешкідливі,
але для проектів більшого розміру і / або довговічність,
навіть невеликі витоку можуть скласти в щось майора.
Для CS50, ми очікуємо, що ви
піклуватися про звільняючи все купи пам'яті, які ви виділяєте,
так як ми хочемо, щоб ви розвивати навички правильно поводитися з ручний процес
потрібно C.
Щоб зробити це, ваша програма повинна мати точну
одно-однозначна відповідність між Танос і безкоштовні дзвінки.
На щастя, Valgrind може допомогти вам з витоками пам'яті теж.
Ось яка витікає програму під назвою leak.c, що виділяє
простору в купі, пише він, але не звільнити його.
Ми скомпілювати його з Марка і запустити його під Valgrind,
і ми бачимо, що, хоча у нас немає ніяких помилок пам'яті,
у нас є один витік.
Є 16 байт виразно втратив,
Це означає, що покажчик на цю пам'ять не була в рамки, коли програма завершиться.
Тепер, Valgrind не дає нам масу інформації про витік,
Але якщо ми будемо слідувати цій невеликій записці, що він дає вниз, до нижньої частини його доповіді
перезапустити з - витік перевірити = повний
, Щоб побачити повну інформацію про витік пам'яті,
ми отримаємо більше інформації.
Тепер, в купу резюме,
Valgrind говорить нам, де пам'ять, що було втрачено було спочатку виділено.
Так само, як ми знаємо, від пошуку в вихідному коді,
Valgrind повідомляє нам, що ми витік пам'яті
виділяється із закликом Танос на лінії 8 з leak.c
В основні функції.
Досить відмінний.
>> Valgrind класифікує витоків за допомогою цих термінів:
Виразно втратили - це динамічної пам'яті
в якому програма не має покажчика.
Valgrind знає, що ти колись був покажчик, але з тих пір втратив його.
Ця пам'ять виразно витік.
Побічно втратили - це динамічної пам'яті
до якої тільки покажчики до нього також будуть втрачені.
Наприклад, якщо ви втратили ваш покажчик на перший вузол пов'язаного списку,
Потім перший вузол сам би виразно втратив,
в той час як у всіх вузлах буде побічно втратив.
Можливо, втратив - це динамічної пам'яті
до якої Valgrind не може бути впевнений, чи є покажчик чи ні.
Тим не менш досяжним є динамічної пам'яті
до якого програма як і раніше має покажчик на виході,
Зазвичай це означає, що глобальна змінна вказує на це.
Щоб перевірити ці витоку, ви також повинні включити опцію
- Все ще досяжна = да
У вашому виклику Valgrind.
>> Ці різні випадки можуть знадобитися різні стратегії для очищення їх,
але витоку повинні бути усунені.
На жаль, фіксуючи витоку може бути важко зробити,
З неправильні виклики безкоштовно можна підірвати вашу програму.
Наприклад, якщо ми подивимося на invalid_free.c,
ми бачимо приклад поганого звільнення пам'яті.
Яким має бути жодного дзвінка, щоб звільнити весь блок
пам'яті, на яку вказує int_block,
замість цього стала спроба звільнити кожен Int розміру розділу
в пам'яті окремо.
Це катастрофічно не вдасться.
Boom! Які помилки.
Це, безумовно, не є добре.
Якщо ви застрягли з такою помилкою, хоча, і ви не знаєте, де шукати,
падати назад на ваш новий кращий друг.
Ви вже здогадалися - Valgrind.
Valgrind, як завжди, точно знає, в чому справа.
Ідентифікатор і безкоштовно розраховує не збігаються.
Ми отримали 1 ідентифікатор і 4 звільняє.
І Valgrind також говорить нам, де перший дзвінок безкоштовний погано -
той, який викликав руйнування - йде від -
рядок 16.
Як бачите, погано дзвінків, щоб звільнити дійсно погані,
тому ми рекомендуємо дозволяючи вашій програмі витоку
в той час як ви працюєте на отримання функціональних правильно.
Почніть шукати витік тільки після того, як ваша програма працює правильно,
без будь-яких інших помилок.
>> І це все, що у нас є для цього відео.
Тепер, що ж ви чекаєте?
Перейти запустити Valgrind ваших програм прямо зараз.
Мене звати Нейт Хардісон. Це CS50. [CS50.TV]