Тестирование-книга. Ю. Н. Артеменко Научный редактор
Скачать 6.27 Mb.
|
5 2 0 Часть III: Управление проектами и группами Запись/чтение за границами структуры данных или ее элемента В качестве примера структуры данных лучше всего подойдет массив. Программа может неправильно вычислять длину его элементов и из-за этого неправильно прочитать значение конкретного элемента. При этом она может обратиться по адресу памяти, лежащему далеко за пределами массива. Такое возможно и в случае, если подпрограмма предполагает, что в массиве больше элементов, чем есть на самом деле. Подобные ошибки часто происходят при передаче массива из одной подпрограммы в другую, когда определения этой переменной в подпрограммах не совпадают. Чтение за пределами буфера сообщения Буфером называется область памяти, используемая для временного хра нения данных. Буферы часто используются при обмене сообщениями меж ду процессами: передающий процесс включает в сообщение указатель на начало буфера, из которого принимающий процесс может прочитать допол нительную информацию. Процесс-получатель читает данные и освобождает память буфера, после чего операционная система может ее снова исполь зовать. Если процесс получит неверный адрес буфера или его неверный размер, он прочитает содержимое другого участка памяти. Дополнение переменных до полного слова Слово — это единица, используемая компилятором для операций с па мятью. Оно может быть длиной в 12 бит, 1, 2, 3 или 4 байта и т.д. Если длина переменной меньше слова, некоторые компиляторы дополняют ее до полного слова, так что переменная, в которую записано значение 255, на самом деле может хранить 00255. Дополняться могут как отдельные пере менные или элементы массива, так и массив целиком — правила зависят исключительно от компилятора. Более того, два компилятора одного и того же языка могут действовать по-разному. Так что, если программист рассчи тывает длину определенной структуры данных и основывает некоторые действия программы на этой информации, а затем меняет компилятор, программа может работать неправильно. Переполнение и выход за нижнюю границу стека данных Несколько ранее рассматривались проблемы, связанные с хранением в стеке адресов возврата и параметров вызываемых подпрограмм. Стеки используются программистами не только для этого — в них могут хранить ся данные. Приложение: Распространенные программные ошибки 5 2 1 Предположим, что размер стека — 256 байтов, а программист пытает ся записать в него 300 байтов. Стек переполняется. В нем обычно остаются последние 256 байтов данных, а первые 44 затираются. Когда программа извлекает данные из стека, она получает 256 байтов и выходит на нижнюю границу стека, т.е. обнаруживает, что он пуст. Затирание кода или данных другого процесса Это возможно в случае, если два процесса имеют доступ к одной и той же области памяти. Если при записи данных в эту память одним из про цессов произойдет ошибка, например, процесс неверно рассчитает длину структуры данных и запишет больше информации, чем предполагалось, он может затереть данные или код другого процесса. Проблемы с обменом сообщений Наиболее безопасным способом взаимодействия между процессами считается обмен сообщениями. Если вместо этого процессы будут переда вать друг другу данные через общую область памяти, ошибка в одном процессе может привести к порче данных обоих, как бы аккуратно ни был написан второй процесс. Что касается обмена сообщениями, то здесь наи более распространенной ошибкой является ситуация гонок, описываемая в следующем разделе. Кроме того, возможны ошибки, связанные с отправ кой и получением включаемых в сообщение данных. Отправка сообщения не тому процессу или не в тот порт Сообщение может не попасть в место назначения. Даже если сообще ние будет направлено нужному процессу, он может ожидать его получения только из одного определенного порта (под портом в данном случае пони мается виртуальная область памяти Для получения данных). Кроме того, для взаимодействия процессы могут пользоваться разными протоколами или разной идентификационной информацией. Ошибка распознавания полученного сообщения Получив сообщение, процесс должен убедиться, что оно предназначе но именно для него, содержит правильные идентификаторы и т.п. Ведь полу ченное сообщение могло попасть в данную область памяти и по ошибке. Недостающие или несинхронизированные сообщения Один процесс может посылать сообщения другому в заранее определен ном порядке. Однако иногда получается, что СООБЩЕНИЕ_1 приходит 522 Часть III: Управление проектами и группами после СООБЩЕНИЯ_2. Например, команда записать файл на диск может поступить до сообщения с информацией об имени и местоположении файла. Причин подобных проблем может быть множество, и не все они связаны с ошибками в программе. Получающая программа вполне может справиться с некоторыми из них, сохранив, например, первое из получен ных сообщений и дождавшись второго или же сообщив процессу-отправи телю, что его сообщение отвергнуто из-за нарушения порядка следования. Распространенным симптомом ошибок в обмене сообщениями являет ся несоответствие информации о состоянии ресурсов: один процесс счита ет, что файл открыт, принтер инициализирован и телефонная трубка снята, а в таблице состояний другого записано обратное. Неважно, какой из про цессов прав. Такие несоответствия приводят к множеству недоразумений. Сообщение передано только N процессам из N+1 Возможно, что несколько процессов хранят копии одних и тех же дан ных и обновляют свои копии после получения определенного сообщения. Или же несколько пользователей работают за своими терминалами, и с главного компьютера им направляется сообщение, что через три минуты работа системы будет прекращена. Бывает, что один из процессов не по лучает сообщение. Обычно это процесс, который либо последним активи зирован, либо последним запрограммирован. Порча данных, хранящихся на внешнем устройстве Данные могут храниться на дисковых накопителях различных типов, магнитных лентах и т.п. Один из процессов может запортить данные, за писав поверх них неверную информацию. Потеря изменений Два процесса работают с одними и теми же данными. Они одновремен но считали их с диска. Затем первый процесс сохранил свои изменения, а второй, не зная об этом, сохранил поверх них свою измененную копию данных. В результате изменения, внесенные первым процессом, оказались потерянными. Для предотвращения подобных неприятностей обычно ис пользуются блокировки отдельных элементов данных (полей, записей, файлов), когда один процесс не может работать с данными, которые в это время модифицируются другим процессом. Однако при реализации этой схемы работы также часто допускаются ошибки. Не сохранены введенные данные Программа запрашивает у пользователя определенные данные и не сохраняет их. Причиной может быть, например, недоступность файла, в котором должна быть сохранена информация. Приложение: Распространенные программные ошибки 5 2 3 Объем данных слишком велик для процесса-получателя У процесса-получателя могут быть определенные ограничения на объем или скорость поступления данных. Он может отвергать лишние данные, сбоить или выводить сообщение об ошибке. Неудачная попытка отмены записи данных Пользователь вводит данные, но затем пытается предотвратить их за пись на диск, остановив программу. Однако программа сохраняет новые (плохие) данные и только затем завершает свою работу. Ситуации гонок В классической ситуации гонок возможны два события. Назовем их СОБЫТИЕ_1 и СОБЫТИЕ_2. Оба они произойдут, но важно, какое произойдет первым. Предполагается, что первым произойдет СОБЫ- ТИЕ_1. Однако в некоторых крайне редких или неожиданных ситуациях СОБЫТИЕ_2 может "выиграть гонки" и произойти первым. Если это приведет к сбою программы, значит, имеет место ошибка, связанная с ситуацией гонок. Программист был уверен, что событие СОБЫТИЕ_2 не может произойти первым, и не предусмотрел на этот счет адекватных дей ствий программы. Редко тестировщики ищут подобные ошибки, а столкнувшись с якобы невоспроизводимой ошибкой, мало кто предполагает, что ее причиной могла быть ситуация гонок. Многие вообще находят вопросы, связанные со временными характеристиками процессов, трудными для понимания. По этому ниже мы постарались привести как можно больше примеров. Гонки при обновлении данных Предположим, что одна подпрограмма считывает с диска остаток на банковском счете клиента, вычитает из него стоимость последних покупок и записывает на диск новый остаток. Вторая считывает с диска остаток и добавляет к нему последние поступления. А третья учитывает валютные операции. Все эти подпрограммы могут работать одновременно. Процеду ры работают так быстро, а одновременность операций так маловероятна, что программист не предусмотрел никаких блокировок. В результате мог ло получиться следующее. Клиент банка получил $500 и потратил $100, до этого на его счете было $1000. Первая процедура считала с диска остаток в $1000 и вычла из него $100. До того, как она записала результат на диск, вторая процедура счи тала остаток и добавила к нему $500. Затем первая процедура записала свой результат, а вторая — свой. В результате вместо $1400 остаток получился 5 2 4 Часть III: Управление проектами и группами равным $1500. Это классическая ситуация гонок, когда последовательность событий (чтение остатка одной процедурой до его записи другой) оказалась такой, которой программист не предусмотрел. Предположение, что одно задание завершится до начала другого Примеры этого типа проблем приведены в предыдущем и следующем разделах. Предположение, что в течение определенного короткого интервала времени не будет ввода данных Пользователь вводит символ. Текстовый редактор, в котором он рабо тает, перемещает на экране другие символы, чтобы освободить место для только что введенного, затем отображает его в позиции курсора. После этого программа ждет дальнейших действий пользователя. Поскольку ком пьютер обычно работает гораздо быстрее человека, он успеет проделать все это до того, как пользователь нажмет следующую клавишу, — по крайней мере, так думал программист. Однако то ли компьютер был перегружен, то ли пользователь попался быстрый, но получилось наоборот — пользователь вводил данные быстрее, чем программа успевала их отобразить. В резуль тате каждый второй введенный символ оказывался потерянным. Предположение, что в течение определенного короткого интервала времени не будет прерываний Программа выполняет некоторую чувствительную ко времени опера цию, например: • записывает биты в конкретное место вращающегося диска или дви жущейся ленты; • чертит на движущемся листе бумаги; • быстро отвечает на сообщение. Программист понимает, что эти операции выполняются очень быстро, поэтому он не блокирует прерывания на время их выполнения, рассчиты вая, что накладок не будет. А зря. Однажды прерывание все же произой дет во время выполнения подобной операции — и что же будет тогда? Ресурс только что стал недоступен Двум процессам нужен один и тот же принтер. Один получает к нему доступ. Другой вынужден ждать. Как правило, программы учитывают такую возможность и прекрасно с ней справляются. Приложение: Распространенные программные ошибки 5 2 5 Однако здесь есть один нюанс. Между проверкой доступности принтера и началом его использования программой проходит некоторое время. В этот короткий период другая, более быстрая программа может захватить принтер и начать его использовать. Некоторые программисты скажут, что это очень маловероятно. Они правы. Однако программа может выполняться пользователями миллионы раз, так что возможно все. А поскольку последствия ошибок, связанных с условиями гонок, бывают очень серьезными, такие ошибки необходимо ис правлять, какой бы маленькой ни была вероятность их проявления. Предположение, что человек, устройство или процесс ответят быстро Программа выводит на экран сообщение и несколько секунд ожидает ответа. Если пользователь не отвечает, программа считает, что его нет за компьютером, и прекращает свою работу. Подобным образом другая про грамма пытается инициализировать принтер и некоторое время ждет его ответа, после чего выдает сообщение, что принтер недоступен. Использу ются тайм-ауты и при ожидании ответа от другого процесса. Если тайм-аут слишком короток, вероятно возникновение ситуации гонок, когда процесс, человек или устройство просто не успели ответить. Если интервал времени очень короткий, не обязательно имеет место классическое условие гонок. Программист, скорее всего, предполагал, что ответ может быть и не получен, и корректно отработал эту ситуацию. Если же интервал лишь чуть короче, чем необходимо, риск ошибки больше. Что произойдет, если ответ на сообщение поступит через несколько миллисе кунд после окончания тайм-аута? Может быть, он будет интерпретирован как ответ на следующее сообщение? Это уже серьезная ошибка. Реальный набор опций в процессе перерисовки экрана Компьютер отображает меню и ждет ответа пользователя. Тайм-аут или другое событие (сообщение, получение сигнала от устройства) заставляют программу отобразить другое меню. Пока она перерисовывает экран, пользователь нажимает на клавишу. В этой ситуации возможны два вида ошибок. • Программа интерпретирует нажатие клавиши как выбор команды старого меню, хотя новое уже на экране. • Программа интерпретирует нажатие клавиши как выбор команды нового меню, хотя на экране все еще старое. Эта проблема реальной эксплуатации. Опытный пользователь знает, какое меню сейчас появится на экране, и может нажать на клавишу еще до того, как оно будет прорисовано до конца. 5 2 6 Часть III: Управление проектами и группами Задание начинается до того, как выполнены подготовительные действия Программа отсылает данные на принтер до его готовности, пытается записать данные в область памяти, которая ей еще не выделена, и т.п. Возможно, программа должна дождаться определенного сообщения и толь ко после этого начинать выполнение задания. Но, основываясь на другой информации (например, на других сообщениях), программа определяет, что ожидаемое событие вот-вот наступит. Поэтому ради повышения произво дительности программист идет на то, чтобы не дожидаться подтверждения. Иногда его ожидания не оправдываются. Сообщения приходят одновременно или не в том порядке, в котором они были отправлены Предположим, что на банковском счету пользователя лежит $1000 и он пытается выполнить три действия в следующем порядке: • снять со счета $1000; • положить на счет $500; • снять со счета $100. Первая операция выполняется, $500 также принимаются. Но когда пользователь пытается снять со счета $100, он получает сообщение, что на его счете нулевой остаток. По какой-то причине операция внесения денег заняла больше времени, чем ожидалось, и к моменту их снятия еще не была завершена. Проблемы такого рода широко распространены в системах, функциони рование которых основано на обмене сообщениями. Сообщения могут передаваться по кругу, они могут проверяться дополнительными запроса ми, так что далеко не всегда можно сказать с уверенностью, какое из от правленных сообщений достигнет места назначения первым. Самым неприятным проявлением этой проблемы являются противоре чащие друг другу сообщения, которые меняются местами. Один процесс просит другой выполнить некоторое действие. Второй процесс отправляет сообщение, подтверждающее, что он может выполнить задачу (принять $500), а затем обнаруживает, что не может этого сделать (остаток остается нулевым), и посылает второе сообщение. При этом базы данных сообще ния достигают в ином порядке, так что ей кажется, что пользователь сна чала запрашивает $100, а затем кладет на счет $500. У пользователя же своя картина: он сначала получил сообщение, что $500 положены на счет, а затем попытался снять $100 и получил сообщение о нулевом остатке. Приложение: Распространенные программные ошибки 5 2 7 Повышенные нагрузки При перегрузках программа может вести себя неожиданным образом. К таким перегрузкам может относиться большое количество работы в течение продолжительного времени или высокая скорость работы. В том и другом случае программе может не хватить определенных ресурсов: например, ей может не хватить памяти или принтер не будет успевать печатать переда ваемую ею информацию. Возможности любой программы ограничены. Важно то, насколько вероятно, что пользователь столкнется с этими огра ничениями, и какими будут их последствия. Из-за использования неэффективных алгоритмов некоторые программы сами создают себе проблемы, а в мультипрограммной среде еще и затруд няют работу других приложений. Требуемый ресурс недоступен Программе не удается воспользоваться устройством или иным ресурсом компьютера. Причины могут быть такими. • Диск полон • Дисковый каталог полон • Область памяти заполнена • Очередь печати заполнена • Очередь сообщений заполнена • Стек полон • Диск отсутствует в дисководе • Дисковод не работает • Дисковод отсутствует • Принтер в режиме off line • В принтере кончилась бумага • В принтере нет ленты • Принтер отсутствует • Дополнительная память отсутствует Не освобожден ресурс В системе может не быть свободных ресурсов из-за того, что их захва тил и не освобождает один из процессов. Программисты тщательно проду мывают процедуру получения ресурсов, но они далеко не так аккуратны в вопросе их освобождения. А поскольку ничего особенного с программой не |