Your SlideShare is downloading. ×
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows

167

Published on

В результате появления на рынке персональных компьютеров 64-битных процессоров перед разработчиками программ возникает задача переноса старых 32-битных приложений на новую платформу. После переноса …

В результате появления на рынке персональных компьютеров 64-битных процессоров перед разработчиками программ возникает задача переноса старых 32-битных приложений на новую платформу. После переноса кода приложения высока вероятность его некорректной работы. В статье рассмотрены вопросы, связанные с верификацией и тестированием программного обеспечения. Обозначены сложности, с которыми может столкнуться разработчик 64-битных Windows приложений и пути их преодоления.

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
167
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Поиск ловушек в Си/Си++ коде припереносе приложений под 64-битнуюверсию WindowsАвторы: Андрей Карпов, Евгений РыжковДата: 10.11.2007АннотацияВ результате появления на рынке персональных компьютеров 64-битных процессоров передразработчиками программ возникает задача переноса старых 32-битных приложений на новуюплатформу. После переноса кода приложения высока вероятность его некорректной работы. Встатье рассмотрены вопросы, связанные с верификацией и тестированием программногообеспечения. Обозначены сложности, с которыми может столкнуться разработчик 64-битныхWindows приложений и пути их преодоления.ВведениеПоявление 64-битных процессоров - это очередной этап в эволюции вычислительной техники. Нополучить преимущества от использования нового 64-битного аппаратного обеспечения, можноиспользуя только новые наборы инструкций и регистров. Для программ, написанных на языкахСи/Си++, это означает необходимость их перекомпиляции. При этом происходит изменениеразмеров типов данных, что может приводить к возникновению неожиданных ошибок придальнейшей работе этих программ на 64-битных системах [1].В основном, проблемы при переносе кода обнаруживаются в приложениях, разработанных сиспользованием низкоуровневых языков программирования, каковыми и являются языки Си иСи++. В языках с четко структурированной системой типов (например, языки .NET Framework), какправило, таких проблем не возникает.Поставим задачу следующим образом. Необходимо убедиться в том, что 64-битное приложениепосле перекомпиляции имеет такое же поведение, что и 32-битное (за исключением очевидныхархитектурных изменений). Процесс проверки работоспособности 64-битной версии программы ибудем называть верификацией.В следующей части статьи мы опишем основные методы тестирования и верификацииприложений. Те, кто хорошо знаком с этими методами, могут пропустить следующий раздел исразу же перейти к третьей части, в которой рассматриваются особенности применения методиктестирования для 64-битных систем.Существующие подходы к тестированию приложенийСуществуют различные подходы к обеспечению корректности кода приложений, какподдающиеся автоматизации, так и нет. Среди неподдающихся автоматизации методов выделяютпросмотр кода вручную, тестирование методом белого ящика и ручное тестирование. К
  • 2. автоматизируемым методам относятся статические анализаторы кода и тестирование методомчерного ящика. Рассмотрим эти методы подробнее.Просмотр кодаСамым старым, проверенным и надежным подходом к поиску дефектов является совместныйпросмотр кода (англ. code review). Эта методика основана на совместном чтении кода, свыполнением ряда правил и рекомендаций [2]. К сожалению, эта практика неприменима длякрупномасштабной проверки современных программных систем в силу их большого объема. Хотяэтот способ и дает наилучшие результаты, он не всегда используется в условиях современныхжизненных циклов разработки программного обеспечения, где немаловажным моментомявляется срок разработки и время выхода продукта на рынок. Поэтому просмотр кода чаще всегосводится к нечастым встречам, целью которых ставится обучение новых и менее опытныхсотрудников написанию качественного кода, нежели чем проверка работоспособности рядамодулей. Это очень хороший способ повышения квалификации программистов, но его нельзярассматривать как полноценное средство контроля качества разрабатываемой программы.Статические анализаторы кодаНа помощь разработчикам, которые осознают необходимость регулярного просмотра кода, но неимеют достаточного количества времени, приходят средства статического анализа кода [3]. Ихосновной задачей является сокращение объема кода, требующего внимания человека и темсамым сокращение времени его просмотра. К статическим анализаторам кода относитсядостаточно большой класс программ, реализованных для различных языков программирования иимеющих разнообразный набор функций от простейшего контроля выравнивания кода досложного анализа потенциально опасных мест. Систематизированное использование статическиханализаторов позволяет существенно повысить качество кода и найти многие ошибки. У подхода,основанного на статическом анализе, много поклонников и ему посвящено много интересныхработ. Преимущество данного подхода заключается в том, что он не зависит от сложности иразмера разрабатываемого программного решения.Динамические анализаторы кодаДинамический анализ кода - анализ программного обеспечения, проводимый при помощивыполнения программ на реальном или виртуальном процессоре. Часто под динамическиманализом понимают исследование кода программы с целью ее оптимизации. Но мы будемговорить о динамическом анализе как о методе тестирования программ.Динамический анализ не позволяет найти многие ошибки, так как часто невозможно выполнитьвесь тестируемый программный код, или последовательность его выполнения сильно отличаетсяот реальной системы. Динамический анализ также вносит накладные расходы в моментвыполнения. Поэтому тщательный (то есть вычислительно сложный) анализ истории обычнооткладывается на момент завершения работы программы. Все это уменьшает привлекательностьметода, особенно в случае, если необходимо тестировать приложение на больших объемахданных, где чаще всего и используются 64-битные системы.Метод белого ящикаПод тестированием методом белого ящика принято понимать выполнение максимальнодоступного количества различных веток кода с использованием отладчика или иных средств. Чембольшее покрытие кода было достигнуто, тем более полно выполнено тестирование. Подтестированием по методу белого ящика также иногда понимают простую отладку приложения с
  • 3. целью поиска известной ошибки. Полноценное тестирование методом белого ящика всего кодапрограмм уже давно стало невозможным в силу огромного объема кода современных программ.Сейчас тестирование по методу белого ящика удобно применять на этапе, когда ошибка найдена,и необходимо понять причину ее возникновения. У тестирования методом белого ящикасуществуют оппоненты, которые отрицают полезность отладки программ в реальном времени.Основной их мотив заключается в том, что возможность наблюдать ход работы программы и приэтом вносить изменения в ее состояние, порождает недопустимый подход в программировании,основанный на большом количестве исправлений кода методом проб и ошибок. Мы не будемкасаться данных споров, но заметим, что тестирование по методу белого ящика в любом случаеочень дорогой способ повышения качества больших и сложных программных систем.Метод черного ящикаНамного лучше себя зарекомендовал метод черного ящика. Сюда же можно отнести юнит-тестирование (англ. unit test). Основная идея метода заключается в написании набора тестов дляотдельных модулей и функций, проверяющих все основные режимы их работы. Ряд источниковотносят юнит-тестирование к методу белого ящика, поскольку оно основывается на знанииустройства программы. Однако функции и модули следует рассматривать как черные ящики, таккак юнит-тесты не должны учитывать внутреннее устройство функции. Обоснованием этомуможет служить методология разработки, когда тесты разрабатываются до начала написаниясамих функций, что способствует повышению контроля их функциональности с точки зренияспецификации.Юнит-тестирование хорошо зарекомендовало себя при разработке как простых, так и сложныхпроектов. Одно из преимуществ юнит-тестирования состоит в том, что легко можно проверитькорректность вносимых в программу исправлений прямо в ходе разработки. Стараются делатьтак, чтобы все тесты проходили в течение нескольких минут, что позволяет разработчику, которыйвнес изменения в код, сразу заметить ошибку и исправить ее. Если прогон всех тестовневозможен, то обычно длительные тесты выносят отдельно и запускают, например, ночью. Этотакже способствует оперативному обнаружению ошибок, по крайней мере, на следующее утро.Ручное тестированиеЭто, пожалуй, завершающий этап любой разработки, но его не следует рассматривать какхорошую и надежную методику. Ручное тестирование обязательно должно существовать, так какневозможно обнаружить все ошибки в автоматическом режиме или просмотром кода. Однакоесли программа имеет низкое качество и большое количество внутренних дефектов, еетестирование и исправление может затянуться на очень продолжительное время, и все равно приэтом нельзя обеспечить надлежащее качество программы. Единственный метод получениякачественной программы - качественный код! Поэтому мы также не будем рассматривать ручноетестирование как полноценную методику при разработке больших проектов.Выводы по методам тестированияЧто же заслуживает наибольшего внимания при разработке крупных программных систем? Этостатический анализ и юнит-тестирование. Эти подходы способны существенно повысить качествои надежность программного кода, и им следует уделить наибольшее внимание, хотя, безусловно,не стоит забывать и про другие.
  • 4. Особенности тестирования и верификации 64-битных приложенийПерейдем к вопросу тестирования 64-битных программ, так как применение выбранных намиметодик сталкивается с несколькими неприятными затруднениями.Использование статических анализаторов кодаКак это ни странно, несмотря на все свои огромные возможности, длительный период разработкии практику использования, статические анализаторы оказались плохо готовы к поиску ошибок в64-битных программах. Рассмотрим ситуацию на примере анализа Си++ кода как область, гдестатические анализаторы нашли наибольшее применение. Многие статические анализаторыподдерживают ряд правил, связанных с поиском кода, имеющего некорректное поведение припереносе его на 64-битные системы. Но реализуют они это весьма разрозненными методами ивесьма неполно. Особенно хорошо это проявилось после начала массовой разработкиприложений под 64-битную версию операционной системы Windows в среде Microsoft Visual C++2005.Объяснением этого может служить то, что большинство проверок основано на достаточно старыхматериалах по исследованию проблем переноса программ на 64-битные системы с точки зренияязыка Си. В результате ряд конструкций, появившихся в языке Си++, был обделен вниманием сточки зрения контроля переносимости и не нашел своего отражения в анализаторах [4]. Не учтени ряд других изменений, таких как, например, существенно возросший объем оперативнойпамяти и использование в разных компиляторах различных моделей данных. Модель данных -это соотношение размеров базовых типов в языке программирования (см. таблицу 1.). В 64-битных Unix-системах принято использовать модель данных LP64 или ILP64, а в Windows-модельLLP64. Более подробно с моделями данных можно познакомиться в [5]. ILP32 LP64 LLP64 ILP64char 8 8 8 8short 16 16 16 16int 32 32 32 64long 32 64 32 64long long 64 64 64 64size_t, ptrdiff_t 32 64 64 64pointers 32 64 64 64Таблица N1. Размерности типов в различных моделях данных.Для наглядности рассмотрим несколько примеров.double *BigArray;int Index = 0;while (...) BigArray[Index++] = 3.14f;Получить диагностическое предупреждение при статическом анализе на подобный код непросто.Это не удивительно. Приведенный код не вызывает никаких подозрений у рядовогоразработчика, привыкшего к практике использования в качестве индексов массивов переменныетипа int или unsigned. К сожалению, приведенный код на 64-битной системе будетнеработоспособен, если объем обрабатываемого массива BigArray превысит размер в четырегигабайта элементов. В этом случае произойдет переполнение переменной Index, и результат
  • 5. работы программы будет некорректен. Корректным вариантом будет использование типа size_tпри программировании под Windows x64 (модель данных LLP64) или size_t/unsigned long припрограммировании под Linux (модель данных LP64).Причина, по которой статические анализаторы не могут диагностировать подобный код, пожалуй,скрывается в том, что когда исследовались вопросы переноса под 64-битные системы, то вряд ликто-то представлял себе массивы более чем из 4 миллиардов элементов. А 4 миллиардаэлементов типа double - это 4 * 8 = 32 гигабайта памяти для одного массива. Огромный объем, темболее, если учесть, что это 1993-1995 год. Именно на это время приходится большинствопубликаций и обсуждений, посвященных использованию 64-битных систем.В результате, на возможную некорректную индексацию при использовании типа int никто необратил внимания, а в дальнейшем вопросы переноса исследовались достаточно редко.Рассмотрим другой пример:char *pointer;long g=(long)(pointer);С помощью этого простого примера можно проверить, какие модели данных умеет пониматьиспользуемый Вами статический анализатор. Проблема в том, что большинство из них рассчитанытолько на модель данных LP64. Это вновь вызвано историей развития 64-битных систем. Именномодель данных LP64 на начальных этапах развития 64-битных систем получила наибольшуюпопулярность и сейчас широко используется в Unix-мире. В этой модели данных тип long имеетразмер 8 байт, а, значит, такой код полностью корректен. Но в 64-битных системах Windows, какуже упоминалось, реализована модель данных LLP64, где размер long остался 4-байтовым иприведенный код будет некорректен. В Windows в таких случаях принято использовать типLONG_PTR или ptrdiff_t.К счастью, приведенный код будет диагностироваться как опасный даже самим компиляторомMicrosoft Visual C++ 2005. Но всегда следует помнить о подобных подводных камнях прииспользовании статических анализаторов.Получилась интересная ситуация. Вопрос переноса программ на 64-битные системы былподробно обсужден, были реализованы различные методики и правила проверки в статическиханализаторах, после чего интерес к этой тематике угас. Прошло много лет, многое изменилось, ноправила, по которым осуществляется анализ, остаются без изменений и модификаций. Чем этовызвано - объяснить сложно. Возможно, разработчики просто не замечают изменений, считая, чтовопрос тестирования и проверки 64-битных приложений давно решен. Однако то, что былоактуально 10 лет назад, сейчас может таковым не являться, но зато появилось много нового.Используя средства статического анализа, убедитесь, что они совместимы с используемой вами64-битной моделью данных. Если анализатор не удовлетворяет необходимым условиям, неполенитесь поискать другой или восполнить пробел, используя узконаправленный анализатор.Усилия, потраченные на это, с лихвой окупятся повышением надежности программы,уменьшением сроков отладки и тестирования.Для Unix-систем с моделью LP64 таким анализатором может стать один из таких известныхинструментов, как Gimpel Software PC-Lint или Parasoft C++test, а для Windows c моделью LLP64 -специализированный статический анализатор Viva64 [6].
  • 6. Использование метода черного ящикаТеперь поговорим о юнит-тестах. C ними на 64-битных системах разработчиков также ожидает ряднеприятных моментов. Стремясь сократить время выполнения тестов, при их разработкестараются использовать небольшой объем вычислений и объем обрабатываемых данных.Например, разрабатывая тест на функцию поиска элемента в массиве, не имеет большогозначения, будет она обрабатывать 100 элементов или 10.000.000. Ста элементов будетдостаточно, а вот по сравнению с обработкой 10.000.000 элементов скорость выполнения тестаможет быть существенно выше. Но если Вы хотите разработать полноценные тесты, чтобыпроверить эту функцию на 64-битной системе, Вам потребуется обработать более 4 миллиардовэлементов! Вам кажется, что если функция работает на 100 элементах, она будет работать и намиллиардах? Нет! Вот демонстрация кода, который Вы можете попробовать на 64-битнойсистеме:bool FooFind(char *Array, char Value, size_t Size){ for (unsigned i = 0; i != Size; ++i) if (i % 5 == 0 && Array[i] == Value) return true; return false;}#ifdef _WIN64 const size_t BufSize = 5368709120ui64;#else const size_t BufSize = 5242880;#endifint _tmain(int, _TCHAR *) { char *Array = (char *)calloc(BufSize, sizeof(char)); if (Array == NULL) std::cout << "Error allocate memory"; if (FooFind(Array, 33, BufSize)) std::cout << "Find"; free(Array);}
  • 7. Некорректность кода заключается в возникновении бесконечного цикла, так как счетчикпеременная i не превысит значения UINT_MAX и условие i != Size не выполнится.Как видно из примера, если Ваша программа на 64-битной системе начнет обрабатывать большийобъем данных, то не стоит рассчитывать на старые наборы юнит-тестов. Следует их обязательнорасширить с учетом обработки больших объемов данных.Но, к сожалению, написать новые тесты мало. Здесь мы сталкиваемся с проблемой скоростивыполнения модифицированного набора тестов, охватывающего обработку больших объемовданных. Первым следствием станет то, что такие тесты нельзя будет добавить в набор тестов,запускаемых программистом в ходе разработки. С внесением их в ночные тесты тоже могутвозникнуть сложности. Суммарное время выполнения всех тестов может вырасти на порядок илидва, а то и более. В результате тест может не уложиться даже в 24 часа. Следует помнить об этом иподойти к доработке тестов для 64-битной версии программы со всей серьезностью.Выходом из создавшейся ситуации может стать разбиение всех тестов на несколько групп,выполняемых параллельно на нескольких компьютерах. Также можно использоватьмногопроцессорные системы. Конечно, это несколько усложнит систему тестирования и потребуетдополнительных аппаратных ресурсов, но это будет являться самым правильным и, в итоге,простым шагом к решению задачи построения системы юнит-тестирования.Естественно, для этого потребуется воспользоваться системой автоматизированноготестирования, которая позволит организовать запуск тестов на нескольких машинах. Примеромможет служить система автоматизации тестирования Windows-приложений AutomatedQATestComplete. С ее помощью, можно выполнять распределенное тестирование приложений нанескольких рабочих станциях, осуществлять синхронизацию и сбор результатов.Использование метода белого ящикаВ конце хочется еще раз вернуться к вопросу тестирования методом белого ящика, который мыпосчитали неприемлемым для больших систем. Нужно добавить, что при отладке 64-битныхприложений, обрабатывающих большие массивы данных, этот способ становится совсемнеприменимым. Отладка таких приложений может занимать намного больше времени или бытьзатруднительной на машинах разработчика. Поэтому стоит заранее обдумать возможностьиспользования систем логирования для отладки приложений или использовать иные методы.Например, удаленную отладку в случае использования для разработки нескольких компьютеров.ЗаключениеПодводя итоги, хочется сказать, что не стоит полагаться на единственную отдельную методику.Качественное приложение может быть разработано только с применением нескольких израссмотренных подходов к тестированию и верификации. Причем задуматься об этих методикахнеобходимо до начала переноса кода на новую архитектуру, чтобы сразу же можно былоконтролировать качество приложения.Резюмируя проблемы разработки и тестирования 64-битных систем, хочется еще раз напомнитьключевые моменты: • будьте готовы к неожиданностям при разработке и тестировании 64-битных приложений;
  • 8. • будьте готовы к тому, что отладка 64-битных приложений методом белого ящика может стать невозможной или крайне затруднительной, если обрабатываются большие массивы данных; • внимательно ознакомьтесь с возможностями вашего статического анализатора, и если он не удовлетворяет всем необходимым требованиям, не поленитесь найти другой или использовать дополнительный специализированный статический анализатор; • не стоит доверять старым наборам юнит-тестов. Обязательно просмотрите их и дополните новыми тестами, учитывающими особенности 64-битных систем; • помните о существенном замедлении наборов юнит-тестов и заранее позаботьтесь о новых компьютерах для их запуска; • используйте систему для автоматизации тестирования, поддерживающую распределенный запуск приложений для обеспечения быстрой проверки приложений; • наилучшего результата можно достичь, только используя сочетание различных методик.Библиографический список 1. Andrey Karpov, Evgeniy Ryzhkov, "20 issues of porting C++ code on the 64-bit platform", http://www.viva64.com/art-1-1-1958348565.html 2. Steve McConnell, "Code Complete, 2nd Edition" Microsoft Press, Paperback, 2nd edition, Published June 2004, 914 pages, ISBN: 0-7356-1967-0. 3. Scott Meyers, Martin Klaus "A First Look at C++ Program Analyzers.", 1997, http://www.viva64.com/go.php?url=13 4. Andrey Karpov, "The forgotten problems of 64-bit programs development", http://www.viva64.com/art-1-1-1609701563.html 5. Andrew Josey, "Data Size Neutrality and 64-bit Support", http://www.viva64.com/go.php?url=51 6. Evgeniy Ryzhkov. Viva64: what is it and for whom is it meant? http://viva64.com/art-1-1-2081052208.html

×