zHz00 Untitled

вторник, 23 января 2024
04:37 Тайна одного access violation
Обновил программу у пользователя -- начала падать без сообщений об ошибке.

Это очень неудобная ситуация, потому что у пользователя не стоит отладчик. Что там, где падает? Почему?

Штош, берём procdump. Запускаем, скачиваем дамп, включаем вижуал студию.

Наконец вижу сообщение, аксесс виолейшн по адресу 0x00000013. Как говорили в старину, "память не может быть "read".

В строчках, где возникла ошибка, происходит обращение к буферу символов. Но и буфер символов имеет нормальный адрес (не тот) и содержимое его я вижу хорошо. Что же там происходит? Приведу пример текста.

pObject->Func1();// ошибка где-то здесь
//...
void CClass::Func1()
{
//много операторов
this->Func2();//ошибка где-то здесь
}
void CClass::Func2()
{
//манипуляции со строками, ошибка где-то здесь
this->state=0;
}

Особенность вижуал студии в том, что она подсвечивает не ту строчку, что только что выполнила, а следующую. Это правильно, потому что при безошибочном выполнении программы следующая строчка интереснее, чем предыдущая. Но в случае с ошибками это сбивает с толку, потому что указывает не на ту строчку, где ошибка произошла.

Чтобы выяснить доподлинно, что выполняется, а что нет, я расставил отладочную печать во всех этих функциях и выяснил, что выполнение прекрасно доходит до строчки this->state=0; Таким образом, никаких ошибок со строками и массивами не происходит. Кто же генерирует аксесс виолейшн? Вот эта самая последняя строка.

Я думаю, все уже догадались. this был равен NULL.

В обновлённой версии программы требовались особые значения в конфигурационном файле, которые я по недосмотру не указал. Логика программы такова, что на pObject память выделяется только при правильных значениях. А если их нет, то pObject==NULL.

Что меня в этой ситуации удивило, так это то, что вызов методов по нулевому указателю не привёл к исключению сам по себе. Надо было дождаться именно обращения к полю. Это можно об'яснить, потому что все методы (кроме виртуальных) имеют фиксированные адреса, и настоящего разыменования указателя не происходит. Метод вызывается по известному адресу и получает NULL как this. Дальше он работает без обращений к полям и вызывает следующий метод, который тоже получает NULL. И только там, в конце, когда происходит действительное обращение к полю, этот this разыменовывается, и происходит генерация исключения...

Обращаю также внимание, что адрес разыменования не равен нулю, а равен другому числу. Это связано с тем, что state имеет смещение относительно начала об'екта.

@темы: Программирование, Борьба с техникой

URL
Момент смерти человека и все соответствующие с этим ритуа...
в белгородском проезде открылась кофейня-цирюльня, интере...
только что ходила покупать подарок и цветы коллеге по раб...
Черное и белое.... Когда я еду в маршрутке, то у меня м...
недавно Владимир поинтересовался, чем же мы занимаемся с ...
Травились ядом своих снов. Лечили раны водопроводной во...

23.01.2024 в 23:12

23.01.2024 в 23:12
> Особенность вижуал студии в том, что она подсвечивает не ту строчку, что только что выполнила, а следующую.

GDB делает так же.

> Это правильно, потому что при безошибочном выполнении программы следующая строчка интереснее, чем предыдущая.

А по-моему это правильно по другой причине: GDB отображает, на какую инструкцию сейчас указывает instruction pointer. А он всегда указывает на инструкцию, которую нужно исполнить следующей.

> Но в случае с ошибками это сбивает с толку, потому что указывает не на ту строчку, где ошибка произошла.

Wait, what? То есть в листинге выше VS указывала на строчку *после* pObject->Func1()? Вот это дичь!

-- Minoru
URL

09.02.2024 в 00:26

09.02.2024 в 00:26
>> Вот это дичь!

Ну и я о том же! Вопрос, как IP мог туда перейти, чтобы строчка перескочила? Разве при исключениях ИП не остаётся на текущей инструкции? Этих подробностей архитектуры я не знаю...
URL
Добавить комментарий

Расширенная форма

Подписаться на новые комментарии
Получать уведомления о новых комментариях на E-mail