Як налагодити купі-корупційні помилки?


165

Я налагоджую (нативну) багатопотокову програму C ++ під Visual Studio 2008. На вигляд випадкових випадків я отримую помилку "Windows викликала перерву ..." із зауваженням, що це може бути пов’язано з корупцією в купи. Ці помилки не завжди вийдуть із роботи програми відразу, хоча вона, швидше за все, вийде з ладу.

Велика проблема цих помилок полягає в тому, що вони з’являються лише після того, як корупція фактично відбулася, що робить їх дуже важкими для відстеження та налагодження, особливо у багатопотоковому додатку.

  • Які речі можуть спричинити ці помилки?

  • Як я їх налагоджую?

Поради, інструменти, методи, розваги ... вітаються.

Відповіді:


128

Перевірка програм у поєднанні з інструментами налагодження для Windows - це дивовижна настройка. Ви можете отримати як частину комплекту драйверів для Windows, чи легший SDK для Windows . (Дізнався про програму перевірки додатків, коли вивчав попереднє питання щодо проблеми з корупцією купи .) У минулому я також використовував BoundsChecker та Insure ++ (згадані в інших відповідях), хоча мене здивувало, наскільки функціональність була у програмі перевірки додатків.

Електричні огорожі (aka "efence"), dmalloc , valgrind тощо - все, що варто згадати, але більшість із них набагато простіше запуститись під * nix, ніж Windows. Valgrind є смішно гнучким: я налагодив велике серверне програмне забезпечення з багатьма проблемами купи.

Коли все інше не вдасться, ви можете надати своєму власному глобальному оператору нове / видалення та malloc / calloc / realloc перевантаження - як це зробити, буде дещо відрізнятися залежно від компілятора та платформи - і це буде трохи інвестицій - але це може окупитися в довгостроковій перспективі. Список бажаних функцій повинен виглядати знайомим з dmalloc та electricfence, і напрочуд чудова книга Writing Solid Code :

  • значення дозорних : дозволяють трохи більше місця перед і після кожного аллоку, дотримуючись вимоги максимальної вирівнювання; заповнення магічними цифрами (допомагає вловлювати переливи та підтоки буфера та випадкові "дикі" вказівники)
  • alloc fill : заповнити нові асигнування магічним значенням non-0 - Visual C ++ вже зробить це для вас у налагодженнях налагодження (допомагає задіяти використання неініціалізованих vars)
  • безкоштовне заповнення : заповніть звільнену пам’ять з магічним значенням не 0, розробленим для запуску сегмента за замовчуванням, якщо воно в більшості випадків відмежоване (допомагає ловити звисаючі покажчики)
  • відкладено безкоштовно : не повертайте звільнену пам’ять у купу на деякий час, тримайте її безкоштовно заповненою, але недоступною (допомагає ловити більше звисаючих покажчиків, ловить найближчі подвійні вільні)
  • відстеження : можливість запису, де було здійснено розподіл, іноді може бути корисним

Зауважте, що в нашій локальній системі домашнього домашнього перекладу (для вбудованої цілі) ми відстежуємо відстеження від більшості інших речей, оскільки накладні витрати на виконання набагато вище.


Якщо вас цікавить більше причин для перевантаження цих функцій / операторів розподілу, подивіться на мою відповідь "Будь-яка причина для перевантаження глобального оператора новим та видаленням?" ; безсоромне саморекламування, окрім того, у ньому перераховані інші методи, які корисні для відстеження помилок корупції, а також інші застосовні інструменти.


Оскільки я постійно шукаю тут свою власну відповідь, коли шукаю значення значень alloc / free / ogra / MS, тут використовується ще одна відповідь, яка охоплює значення заповнення Microsoft dbgheap .


3
Одне крихітне, про що варто звернути увагу про Verifier Application: ви повинні зареєструвати символи Verifier Application перед символами сервера символів мікрософт у вашому шляху пошуку символів, якщо ви використовуєте це ... Потрібно трохи пошукати, щоб з’ясувати, чому! Avrf не був знаходження потрібних символів.
арендодавець

Програма перевірки програм дуже допомогла, і в поєднанні з деякими здогадами я зміг вирішити проблему! Велике спасибі, і всім іншим за те, що ви отримали корисні бали.

Чи потрібно використовувати верифікатор додатків з WinDbg, чи він повинен працювати з налагоджувачем Visual Studio? Я намагався його використати, але він не викликає помилок або, мабуть, нічого не роблю, коли я налагоджую в VS2012.
Натан Рід

@NathanReed: Я вважаю, що він працює і з VS - див. Msdn.microsoft.com/en-us/library/ms220944(v=vs.90).aspx - хоча зауважте, що це посилання стосується VS2008, я не впевнений у пізніших версіях. Пам'ять трохи нечітка, але я вважаю, що коли у мене виникли проблеми у посиланні "раніше питання", я просто запустив програму перевірки додатків і зберег параметри, запустив програму, і коли вона вийшла з ладу, я вибрав VS для налагодження. AV тільки зробив це крахом / стверджувати раніше. Наскільки я знаю, команда! Avrf характерна для WinDbg. Сподіваємось, інші можуть надати більше інформації!
лизинг

Дякую. Я фактично вирішив свою первісну проблему, і виявилося, що це не корупція зрештою, а щось інше, так що, ймовірно, пояснює, чому App Verifier нічого не знайшов. :)
Натан Рід

35

Ви можете виявити безліч проблем з корупцією, включивши Page Heap у свою програму. Для цього вам потрібно використовувати gflags.exe, що входить до складу інструментів налагодження для Windows

Запустіть Gflags.exe і в параметрах файлу зображення для вашого виконуваного файлу встановіть прапорець "Увімкнути кучу сторінки".

Тепер перезапустіть свій exe та приєднайте до налагоджувача. Якщо увімкнено Page Heap, додаток перетворюється на налагоджувач кожного разу, коли виникає будь-яка пошкодження купи.


так, але як тільки я отримаю цю функцію виклику в моєму дамп-станції (після збою пам'яті): wow64! Wow64NotifyDebugger, що я можу зробити? Я досі не знаю, що відбувається в моїй заяві
Guillaume07

Просто випробував gflags, щоб налагодити купу корупції тут, ДУЖЕ корисний невеликий інструмент, дуже рекомендується. Виявилось, я отримував доступ до звільненої пам’яті, яка при інструментації з gflags одразу ж прорветься до налагоджувача… Handy!
Дейв Ф

Чудовий інструмент! Щойно знайшов помилку, на яку я полював цілими днями, тому що Windows не каже адреси корупції, лише що "щось" не так, що насправді не допомагає.
Деволус

Трохи запізнювався на вечірку, але я помітив значне збільшення використання пам'яті моїм додатком, який я налагоджую, коли ввімкнув Page Heap. На жаль, до моменту (32-бітового) додатку не вистачає пам’яті до початку виявлення пошкодження купи. Будь-які ідеї, як вирішити цю проблему?
уцеумер

13

Щоб дійсно сповільнити роботу та здійснити багато перевірки часу, спробуйте додати наступне у верхній частині вашого main()чи іншого еквівалента в Microsoft Visual Studio C ++

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );


8

Які речі можуть спричинити ці помилки?

Робити неслухняні речі з пам’яттю, наприклад, писати після закінчення буфера або записувати в буфер після того, як він буде звільнений назад у купу.

Як я їх налагоджую?

Використовуйте інструмент, який додає автоматичну перевірку меж до вашого виконуваного файлу: тобто valgrind на Unix, або такий інструмент, як BoundsChecker (Вікіпедія пропонує також очистити та застрахувати ++) у Windows.

Будьте уважні, що це сповільнить вашу програму, тому вони можуть бути непридатними, якщо ваша програма в режимі реального часу.

Іншим можливим засобом / інструментом для налагодження може бути HeapAgent MicroQuill.


1
Перебудова програми за допомогою програми налагодження (/ MDd або / MTd прапор) - мій перший крок. Вони виконують додаткові перевірки на malloc та free та часто припиняють свою ефективність при звуженні розташування помилок.
працевлаштований росіянин

HeapAgent MicroQuill: Про це не багато написано та чуто, але щодо корупції він має бути у вашому списку.
Самрат Патіль

1
BoundsChecker прекрасно працює як тест на дим, але навіть не думайте про запуск програми під ним, намагаючись також запустити цю програму у виробництві. Уповільнення може бути від 60x300 до 300x, залежно від того, які параметри ви використовуєте, і ви використовуєте функцію інструментарію компілятора чи ні. Відмова: Я один із хлопців, який підтримує продукт для Micro Focus.
Рік Папо

8

Один швидкий підказки, який я отримав від виявлення доступу до звільненої пам'яті , такий:

Якщо ви хочете швидко знайти помилку, не перевіряючи кожне твердження, що отримує доступ до блоку пам'яті, ви можете встановити покажчик пам'яті на недійсне значення після звільнення блоку:

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif

5

Найкращим інструментом, який я вважав корисним і щоразу працював, є перегляд коду (з хорошими оглядачами коду).

Крім огляду коду, я спершу спробую Page Heap . Page Heap потребує декількох секунд, щоб налаштувати, і на щастя це може визначити вашу проблему.

Якщо вам не пощастило з Page Heap, завантажте інструменти налагодження для Windows у Microsoft та навчіться користуватися програмою WinDbg. Вибачте, ви не можете надати більш конкретну допомогу, але налагодження багатопотокової корупції купи - це більше мистецтво, ніж наука. Google для "WinDbg купи корупції", і ви повинні знайти багато статей на цю тему.


4

Ви також можете перевірити, чи пов’язані ви з динамічною або статичною бібліотекою виконання C. Якщо ваші DLL-файли посилаються на статичну бібліотеку виконання C, то файли DLL мають окремі купи.

Отже, якби ви створили об’єкт в одній DLL і спробували звільнити його в іншій DLL, ви отримаєте те саме повідомлення, яке ви бачите вище. На цю проблему посилається в іншому питанні про переповнення стека, звільнення пам'яті, виділеної в іншій DLL .


3

Який тип функцій розподілу ви використовуєте? Нещодавно я потрапив на подібну помилку за допомогою функцій розподілу стилю Heap *.

Виявилося, що я помилково створив купу з HEAP_NO_SERIALIZEопцією. Це по суті змушує функції Heap працювати без безпеки потоку. Це правильна ефективність при правильному використанні, але ніколи не повинна використовуватися, якщо ви використовуєте HeapAlloc у багатопотоковій програмі [1]. Я згадую це лише тому, що у вашій публікації згадується, що у вас є багатопотокове додаток. Якщо ви використовуєте HEAP_NO_SERIALIZE де-небудь, видаліть це, і це, ймовірно, виправить вашу проблему.

[1] Існують певні ситуації, коли це легально, але це вимагає, щоб ви серіалізували дзвінки до Heap *, і зазвичай це не стосується багатопотокових програм.


Так: перегляньте параметри компілятора / збірки програми та переконайтеся, що вона побудована для зв’язування з "багатопотоковою" версією бібліотеки часу виконання C.
ChrisW

@ChrisW для API стилів HeapAlloc це інше. Це фактично параметр, який можна змінити під час створення купи, а не час зв'язку.
JaredPar

Ой. Мені не прийшло в голову, що ОП може говорити про цю групу, а не про купу в CRT.
ChrisW

@ChrisW, питання досить розпливчасте, але я просто потрапив на проблему, яку я детально розробив ~ 1 тиждень тому, так що це свіже на мою думку.
JaredPar

3

Якщо ці помилки трапляються випадковим чином, велика ймовірність того, що ви зіткнулися з перебігом даних. Перевірте: чи ви змінюєте вказівники спільної пам’яті з різних потоків? Intel Thread Checker може допомогти виявити подібні проблеми в багатопотоковій програмі.


1

Окрім пошуку інструментів, подумайте, як шукати ймовірного винуватця. Чи є якийсь компонент, який ви використовуєте, можливо, не написаний вами, який, можливо, не був розроблений і перевірений для роботи в багатопотоковому середовищі? Або просто той, якого ви не знаєте , біг у такому середовищі.

Востаннє, коли це трапилось зі мною, це був рідний пакет, який успішно використовувались у пакетних роботах протягом багатьох років. Але вперше в цій компанії було використано веб-сервіс .NET (який є багатопоточним). Це було все - вони брехали про те, що код захищений від потоку.


1

Ви можете використовувати макроси VC CRT Heap-Check для _CrtSetDbgFlag : _CRTDBG_CHECK_ALWAYS_DF або _CRTDBG_CHECK_EVERY_16_DF .. _CRTDBG_CHECK_EVERY_1024_DF .


0

Я хотів би додати свій досвід. В останні кілька днів я вирішив приклад цієї помилки у своїй програмі. У моєму конкретному випадку помилки в коді були:

  • Видалення елементів із колекції STL під час ітерації над ним (я вважаю, що у Visual Studio є налагодження для налагодження цих речей; я потрапив під час перегляду коду)
  • Цей складніший, я поділю його на кроки:
    • З рідної C ++ потоку передзвоніть у керований код
    • У керованій землі зателефонуйте Control.Invokeта розпоряджайтесь керованим об'єктом, який загортає нативний об’єкт, до якого належить зворотний виклик.
    • Оскільки об’єкт все ще живий всередині рідного потоку (він залишатиметься заблокованим у виклику зворотного виклику до Control.Invokeкінця). Я повинен уточнити, що я використовую boost::thread, тому я використовую функцію члена як функцію потоку.
    • Рішення : Control.BeginInvokeНатомість використовуйте (мій графічний інтерфейс призначений для Winforms), щоб рідний потік міг закінчитися до знищення об'єкта (мета зворотного виклику - саме повідомлення про те, що нитка закінчилася і об'єкт можна знищити).

0

У мене була подібна проблема - і вона вискочила досить випадково. Можливо, щось було пошкоджено у файлах збірки, але я закінчив виправити це, очистивши спочатку проект, а потім відновивши.

Тож на додаток до інших відповідей:

Які речі можуть спричинити ці помилки? У файлі збірки щось пошкоджено.

Як я їх налагоджую? Прибирання проекту та відновлення. Якщо це виправлено, це, ймовірно, проблема.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.