Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004
.pdf
ГЛАВА 2 Приступаем к отладке |
31 |
|
|
Во всех случаях я следую схеме: <Название проекта> <Контрольная точка/При чина> <Дата>, чтобы названия меток были описательными.
Есть и третье правило записи меток, о котором многие забывают. Сотрудники отдела контроля качества обычно работают с компоновками контрольных точек и ежедневными компоновками, поэтому, сообщая об ошибках, они имеют в виду конкретные версии программы. Разработчики изменяют код довольно быстро, поэтому нужно позаботиться, чтобы можно было легко вернуться к версии фай лов, нужных для воспроизведения ошибки.
Стандартный вопрос отладки
Что делать, если мы не можем воспроизвести компоновку, посланную за пределы группы?
Отсылая компоновку за пределы группы, обязательно делайте полную ко пию ее каталога на CD/DVD или ленточном накопителе. Копия должна вклю чать все исходные и промежуточные файлы программы, файлы символов и окончательный результат. Включайте в нее также пакет для установки, отсылаемый заказчику. Следует даже подумать о создании копии средств компоновки программы. CD/DVD и ленточные накопители — очень недо рогой способ страховки от будущих проблем.
Даже когда я делал все для сохранения конкретной компоновки в сис теме управления версиями, повторное создание программы порой приво дило к получению двоичных файлов, отличающихся от первоначальной версии. Имея архив полного дерева компоновки, вы сможете отлаживать программы пользователей при помощи тех же двоичных файлов, которые вы им в свое время послали.
Системы отслеживания ошибок
Система отслеживания ошибок не только накапливает сведения об ошибках, но и является прекрасным средством для хранения разных заметок и списка зада ний, особенно на этапе разработки исходного кода. Некоторые программисты любят хранить заметки и списки заданий в мобильных ПК, но при этом важная информация часто теряется среди отладочных файлов со случайными шестнад цатеричными данными и рисунков, выполненных для борьбы со сном во время планерок. Сохранив эти заметки в системе отслеживания ошибок и пометив, что они принадлежат вам, вы консолидируете их в одном месте, что облегчит их по иск. Кроме того, хотя вам, возможно, нравится думать, что код, над которым вы работаете, «принадлежит» вам, на самом деле это не так — он принадлежит груп пе. Если вы будете хранить свой список заданий в системе отслеживания ошибок, другие члены группы, которым понадобится ваш код, смогут проверить ваш спи сок и узнать, что именно вы сделали. И еще одно преимущество хранения спис ков заданий и заметок в системе отслеживания ошибок: они будут постоянно напоминать вам, что нужно сделать, поэтому вам не придется лихорадочно отла живать ошибку в последний момент из за того, что вы про нее забыли или поче му либо еще. Я использую систему отслеживания ошибок постоянно, чтобы важ
32 ЧАСТЬ I Сущность отладки
ные заметки и задания можно было внести в нее сразу, как только они придут мне
вголову.
Ялюблю назначать заметкам и спискам заданий в системе отслеживания ошибок наименьший приоритет ошибок. Это позволяет отделить их от настоящих оши бок, и в то же время ничто не мешает повысить их приоритет, если надо. При этом следует организовать методику сообщений об ошибках так, чтобы они не вклю чали кодов ошибок с наименьшим приоритетом, иначе можно будет запутаться.
Не бойтесь изучать данные системы отслеживания ошибок: они содержат всю правду о проекте. Планируя модернизацию программы, поработайте с системой отслеживания ошибок и найдите модули или функции, в которых было зарегис трировано наибольшее число проблем. Выделите некоторое время на то, чтобы члены группы лучше поработали над соответствующими разделами программы.
При внедрении системы отслеживания ошибок убедитесь, что к ней имеют доступ все, кому это нужно: как минимум, это члены групп разработки и техни ческой поддержки. Если система отслеживания ошибок позволяет назначить раз ные уровни доступа, возможно, стоит разрешить соответствующий доступ к ней другим людям, скажем, инженерам по сбыту (технические эксперты, работающие
вторговых организациях и оказывающие продавцам помощь при продаже слож ной продукции) и сотрудникам отдела маркетинга. Например, некоторым членам отделов продаж и маркетинга можно разрешить регистрировать сообщения об ошибках и запросы о реализации функций, но не получать информацию об об наруженных ошибках. Представители этих двух групп, как правило, больше об щаются с клиентами, чем обычные инженеры, и могут предоставить бесценную обратную связь. Естетственно, вы должны обучить их составлению отчетов об ошибках. Они с радостью согласятся помочь, но им нужно дать необходимые указания, чтобы они делали это правильно. Если представители этих двух групп будут оставлять свои запросы и сообщения о проблемах в той же системе, что и другие сотрудники, эффективность работы с ней только повысится. Идея систе мы отслеживания ошибок как раз в том, чтобы все сообщения об ошибках и за просы о реализации функций находились в одном месте. Если эта информация будет храниться в разных местах — в электронном почтовом ящике руководите ля проекта, в записных книжках инженеров и, конечно, в системе отслеживания ошибок — уследить за ней будет гораздо сложнее.
Выбор правильных систем
Система управления версиями должна соответствовать вашим потребностям. Очевидно, если вы работаете в компании с требованиями класса «high end», та кими как поддержка нескольких платформ, вам скорее всего придется выбирать более дорогую систему или использовать решение с открытым кодом, скажем, CVS. Если же вы работаете в небольшой группе и разрабатываете программу только для Windows, можно рассмотреть варианты подешевле. Потратьте некоторое время на тщательную оценку системы, которую вы планируете внедрить, уделив особое внимание прогнозированию будущих потребностей. Вам придется работать с системой управления версиями довольно долго, поэтому убедитесь, что она бу дет развиваться вместе с вашим проектом. Выбор правильной системы управле
ГЛАВА 2 Приступаем к отладке |
33 |
|
|
ния версиями очень важен, но еще важнее, чтобы вы вообще ее использовали: хоть какая то система управления версиями лучше, чем никакая.
Я знаю массу случаев, когда разработчики пытались использовать собственные системы отслеживания ошибок, однако я настоятельно рекомендую потратить средства на коммерческий продукт или использовать решение с открытым кодом. Информация системы отслеживания ошибок слишком важна, чтобы ее хранение можно было доверить приложению, которое трудно поддерживать и которое не сможет развиваться вместе с вашими потребностями в течение длительного сро ка. Кроме того, это позволяет избежать траты времени на разработку внутренней системы вместо работы над коммерческой программой.
При выборе системы отслеживания ошибок следует руководствоваться теми же критериями, что и при выборе системы управления версиями. Однажды я, как руководитель проекта, выбрал систему отслеживания ошибок, не уделив должно го внимания ее наиболее важной части, составлению отчетов об ошибках. Систе ма была достаточно проста в плане внедрения и использования, однако поддер жка отчетов в ней была столь ограниченна, что сражу же по достижении первой внешней контрольной точки нам пришлось переносить все наши ошибки на другую систему. Мне было очень неловко перед коллегами за эту оплошность.
Как я уже говорил, очень важно, чтобы система отслеживания ошибок обеспе чивала интеграцию с системой управления версиями. Большинство систем управ ления версиями для Windows поддерживают Интерфейс контроля над исходным кодом Microsoft (Microsoft Source Code Control Interface, MSSCCI). Если ваша сис тема отслеживания ошибок также поддерживает MSSCCI, вы сможете согласовать исправления ошибок с конкретными версиями файлов.
Некоторые люди называют код «кровью» группы разработчиков. Если это так, то системы управления версиями и отслеживания ошибок — артерии, от которых зависит правильное кровообращение. Не работайте без них.
Планирование времени
построения систем отладки
Составляя план и расписание проекта, выделите время на создание систем отлад ки. Вам нужно заранее решить, как вы будете реализовывать обработчики крити ческих ошибок (см. главу 13), средства создания дампов файлов и другие инстру менты, которые понадобятся для воспроизведения реальных проблем. Мне все гда нравилось рассматривать системы обработки ошибок так, как если бы они являлись одной из функций программы. Это позволяет другим сотрудникам ком пании узнать, что вы собираетесь делать с ошибками при их появлении.
Планируя системы отладки, разработайте политику предупредительной отладки. Первые и наиболее сложные этапы этого процесса заключаются в определении способа возвращения ошибок. Какое бы решение вы ни приняли, всегда исполь зуйте только один способ. Мне известен один давний проект (к счастью я в нем не участвовал), в котором применялись три способа возвращения ошибок: при помощи возвращаемых функциями значений, при помощи исключений setjmp/ longjmp и при помощи глобальной переменной, аналогичной переменной errno стандартной библиотеки C. Эти разработчики провели немало тяжелых минут, пытаясь отследить ошибки, пересекающие границы различных подсистем.
34 ЧАСТЬ I Сущность отладки
При разработке приложений для платформы .NET выбрать способ обработки ошибок довольно легко. Вы можете или продолжать использовать возвращаемые значения, или применить исключения. Привлекательность .NET в том, что в от личие от неуправляемого кода в ней есть стандартный класс исключений, Sys tem.Exception, который является базовым для всех прочих исключений. Механизм исключений .NET имеет и один недостаток: вам все же придется вести подроб ную документацию и проводить инспекцию кода программы, чтобы точно знать, какое исключение генерируется методом. Как вы увидите по моим программам, для сообщений о нормальном завершении блока программы и ожидаемых ошибках я все же предпочитаю использовать возвращаемые значения, так как это немного быстрее, чем выполнение кода throw и catch. Однако для всех непредвиденных ошибок я всегда использую исключения.
С другой стороны, при написании неуправляемого кода вы по сути вынужде ны применять только возвращаемые значения. Проблема в том, что в C++ нет стан дартного класса исключений, генерируемых автоматически, а такие технологии, как COM, не позволяют исключениям пересекать границы адресного простран ства отделенного потока или процесса. Как я покажу в главе 13, исключения C++ — одна из самых проблемных в смысле производительности и ошибок областей. Ока жите себе большую услугу и забудьте об исключениях в языке C++. С теоретичес кой точки зрения они великолепны, но реальность далеко не всегда соответству ет теории.
Создавайте все компоновки с использованием символов отладки
Некоторые из моих советов по поводу систем отладки не вызывают никаких со мнений. Так, я годами твержу о том, что все компоновки, в том числе заключи тельные (release), нужно создавать, применяя полный набор символов отладки — данные, позволяющие отладчику показывать исходные тексты, номера строк, имена переменных и информацию о типах данных вашей программы. Вся эта инфор мация хранится в файлах с расширением .PDB (Program Database, база данных программы), связанных с конкретными модулями. Разумеется, если вы работаете на условиях почасовой оплаты, нет ничего плохого в том, чтобы проводить все рабочее время за отладкой на уровне ассемблера. Увы, большинство из нас не может позволить себе такой роскоши и поэтому нуждается в средствах быстрого обна ружения ошибок.
Конечно, у отладки заключительных компоновок при помощи символов есть свои минусы. Например, оптимизированный код, создаваемый компилятором по требованию (just in time compiler, JIT compiler) или компилятором неуправляемого кода, не всегда соответствует потоку исполнения исходного кода, поэтому рабо тать с заключительным кодом сложнее, чем с отладочным. Другая проблема ис следования неуправляемых заключительных компоновок в том, что компилятор иногда оптимизирует регистры стека так, что это не позволяет увидеть полный стек вызовов, как при обычной отладочной компоновке. Кроме того, при вклю чении символов отладки в двоичный файл он слегка увеличивается из за строки раздела отладки, определяющей файл .PDB. Однако эти несколько байтов — нич
ГЛАВА 2 Приступаем к отладке |
35 |
|
|
то в сравнении с тем, насколько символы облегчают и ускоряют исправление ошибок.
В проектах, создаваемых при помощи мастеров (wizard), отладочные симво лы для заключительных компоновок применяются по умолчанию, но при необ ходимости это можно сделать и вручную. Если вы работаете над проектом C#, от кройте диалоговое окно Property Pages (страницы свойств) (рис. 2 1) и выберите папку Configuration Properties (свойства конфигурации). Щелкните в раскрываю щемся списке Configuration (конфигурация) пункт All Configurations (все конфи гурации) или Release (заключительная конфигурация); выберите в папке Configu ration Properties страницу свойств Build (компоновка программы) и задайте в поле Generate Debugging Information (генерировать отладочную информацию) значе ние True. Это устанавливает флаг /debug:full для компилятора CSC.EXE.
По непонятной мне причине диалоговое окно Property Pages проекта, разра батываемого в среде Microsoft Visual Basic .NET, отличается от аналогичного окна проекта C#, однако ключ компилятора в обоих случаях один и тот же. Подключе ние полного набора отладочных символов для заключительных компоновок Visual Basic .NET показано на рис. 2 2. Откройте диалоговое окно проекта Property Pages и выберите папку Configuration Properties. Щелкните в раскрывающемся списке Con figuration пункт All Configurations или Release; выберите в папке Configuration Pro perties страницу свойств Build и установите флажок Generate Debugging Information.
Рис. 2 1. Включение генерирования отладочной информации для проекта C#
Чтобы включить создание PDB файла для неуправляемой программы C++, нужно задать компилятору ключ /Zi. Откройте в окне Property Pages папку C/C++ стра ницу свойств General (общие свойства) и задайте в поле Debug Information Format (формат отладочной информации) значение Program Database (/Zi). Убедитесь, что вы не выбрали пункт Program Database For Edit & Continue (база данных программы для режима «отредактировать и продолжить»), а то заключительная компоновка окажется большой и медленной, так как в нее будет занесена вся дополнительная информация, необходимая для специфического режима отладки, позволяющего внести изменение в программу и продолжить ее выполнение. Правильные пара метры компилятора см. на рис. 2 3, где показаны и другие параметры, позволяю щие оптимизировать создание компоновок; их я опишу в разделе «Какие допол
36 ЧАСТЬ I Сущность отладки
нительные параметры компилятора и компоновщика помогут заранее позаботиться об отладке неуправляемого кода?».
Рис. 2 2. Включение генерирования отладочной информации для проекта Visual Basic .NET
Рис. 2 3. Настройка компилятора C++ для генерирования отладочной информации
После установки ключа компилятора вам понадобится задать соответствующие ключи компоновщика: /INCREMENTAL:NO, /DEBUG и /PDB. Для указания параметров ком поновки с приращением нужно открыть окно Property Pages, выбрать папку Linker (компоновщик), страницу свойств General и задать соответствующее значение в поле Enable Incremental Linking (включить компоновку с приращением). Распо ложение ключа см. на рис. 2 4.
Выберите в окне Property Pages папку Linker, перейдите на страницу Debugging (отладка) и задайте в поле Generate Debug Info (генерировать отладочную инфор мацию) значение Yes (/DEBUG). Чтобы задать ключ /PDB, введите в поле Generate Program Database File (генерировать файл базы данных программы), находящее ся сразу же под полем Generate Debug Info, значение $(Каталог_файла)/$(Наз вание_проекта).PDB. Если вы не заметили, в системе проектов Microsoft Visual Studio
ГЛАВА 2 Приступаем к отладке |
37 |
|
|
.NET наконец то решены серьезные проблемы предыдущих версий, связанные с общими ключами компоновки. Значения, начинающиеся с символа $ и заключен ные в скобки, являются макрокомандами, о назначении которых часто можно догадаться по названиям. Об остальных макрокомандах можно узнать, щелкнув на странице свойств почти любое поле ввода и выбрав из списка пункт <Edit…>. Во всплывающем диалоговом окне будут указаны все макрокоманды и во что они преобразуются. Установка ключей /DEBUG и /PDB показана на рис. 2 5. Остальные параметры важны для неуправляемого кода C++. Я опишу их в разделе «Какие дополнительные параметры компилятора и компоновщика помогут заранее по заботиться об отладке неуправляемого кода?».
Рис. 2 4. Отключение компоновки с приращением для компоновщика C++
Правильная настройка создания отладочных символов для C++ требует зада ния еще двух ключей: /OPT:REF и /OPT:ICF. Они находятся в папке Linker на страни це Optimization (рис. 2 6). Выберите в разделе References (ссылки) значение Elimi nate Unreferenced Data (/OPT:REF) (удалять неиспользуемые данные). В поле Enable COMDAT Folding (удаление избыточных записей COMDAT) выберите Remove Redun dant COMDATs (/OPT:ICF) (удалять избыточные записи COMDAT). При установлен ном ключе /DEBUG компоновщик включает в итоговый файл все функции незави симо от того, вызываются они или нет; в случае отладочных компоновок это за дано по умолчанию. Ключ /OPT:REF указывает компоновщику включать в итоговый файл только те функции, что вызываются программой. Если вы забудете добавить ключ /OPT:REF, заключительное приложение будет содержать функции, которые никогда не вызываются, что сделает его гораздо более объемным, чем следовало бы. Ключ /OPT:ICF задает комбинирование идентичных записей данных COMDAT, так что для всех ссылок на постоянное значение у вас будет только одна констан тная переменная.
После создания заключительных компоновок с PDB файлами, содержащими полную информацию, храните эти файлы в безопасном месте с двоичными фай лами, которые вы поставляете заказчику. В случае утраты PDB файлов вам при дется вернуться к отладке на уровне ассемблера. Обращайтесь с ними так же, как с распространяемыми двоичными файлами.
38 ЧАСТЬ I Сущность отладки
Рис. 2 5. Настройка отладочных параметров компоновщика C++
Рис. 2 6. Оптимизация компоновщика C++
Если мысль о ручном изменении параметров проекта с целью его компонов ки с символами отладки, а также о правильном указании всех остальных ключей компоновки внушает вам страх, не волнуйтесь, все не так уж и плохо. Для главы 9 я написал очень полезный модуль надстройки SettingsMaster, который возьмет всю работу по изменению параметров проекта на себя. SettingsMaster по умолчанию сконфигурирован так, чтобы задавать все ключи, рекомендуемые в этой главе.
При работе над управляемым кодом рассматривайте предупреждения как ошибки
Если вы писали на управляемом коде что нибудь более серьезное, чем «Hello World!», вы наверняка заметили, что его компиляторы гораздо строже относятся к ошиб кам компилирования. Программисты, привыкшие работать с C++ и не очень хо рошо знакомые с .NET, часто удивляются числу дополнительных ограничений этой платформы: например, в C++ вы могли приводить переменные почти к любому типу, и компилятор смотрел на это сквозь пальцы. Компиляторы управляемого кода
ГЛАВА 2 Приступаем к отладке |
39 |
|
|
не только гарантируют явный тип данных, но и помогут в исправлении ошибок, но для этого их нужно настроить, скажем, сделать инструменты как можно более интеллектуальными.
Если вы откроете документацию к Visual Studio .NET, выберете панель Contents (содержание) и перейдете к разделу Visual Studio .NET\Visual Basic and Visual C#\Reference\Visual C# Language\C# Compiler Options\Compiler Errors CS0001 Thro ugh CS9999, вы увидите список всех ошибок компилятора C#. (Ошибки компиля тора Visual Basic .NET также включены в документацию, но, к великому удивлению, они не проиндексированы в разделе Contents.) Просматривая список ошибок, вы заметите, что некоторые из них называются предупреждениями (Compiler Warning) и имеют определенный уровень диагностики, например, Compiler Warning (level 4) CS0028. Затем вы обнаружите уровни диагностики от 1 до 4. Генерируя предуп реждение, компилятор сообщает, что конкретная конструкция исходного кода пра вильна с точки зрения синтаксиса, но может быть неверна в данном контексте. В качестве показательного примера можно привести предупреждение CS0183 (The given expression is always of the provided (‘type’) type [Данное выражение всегда имеет тип (‘type’)]), проиллюстрированное следующим фрагментом кода:
//Генерирует предупреждение CS0183, потому что строка (или, точнее, любой
//тип в .NET) ВСЕГДА имеет базовый тип Object.
public static void Main ( )
{
String StringOne = " Something pithy. . ." ; if ( StringOne is String ) // CS0183
{
Console.WriteLine ( StringOne ) ;
}
}
Если компилятор настолько любезен, что сообщает обо всех контекстуальных проблемах подобного рода, разве разумно не обращать на них внимания? Я не люблю называть их предупреждениями, так как на самом деле это ошибки. Если вы когда нибудь интересовались разработкой компиляторов, особенно синтакси ческим анализом, вероятно, вам в голову приходили две мысли: во первых, что синтаксический анализ очень сложен, и во вторых, что люди, создающие компи ляторы, сделаны из особого теста. (Хорошо это или плохо, решайте сами.) Если разработчики компилятора пошли на то, чтобы включить в него конкретное пре дупреждение, значит, они хотели сообщить вам нечто очень важное, что, по их мнению, может являться ошибкой. Когда кто нибудь просит нас помочь найти ошибку, первое, что мы делаем, — проверяем, компилируется ли код без предуп реждений. Если это не так, я говорю, что буду рад помочь, но только после того, как будут устранены все предупреждения.
К счастью, Visual Studio .NET по умолчанию создает проекты с подходящим уровнем диагностики, так что вам не понадобится задавать его вручную. Если вы создаете проект C# вручную, присвойте ключу /WARN значение / WARN:4. В создава емых вручную проектах Visual Basic .NET рассмотрение предупреждений как оши бок по умолчанию отключено, так что включите его.
40 ЧАСТЬ I Сущность отладки
Уровни диагностики заданы в Visual Studio .NET правильно, однако обращение с предупреждениями как с ошибками по умолчанию отключено. Это неверно. Чистая компиляция кода близка к благочестию, поэтому для компиляторов C# и Visual Basic .NET нужно задать ключ /WARNASERROR+. Это не позволит даже начать отладку, пока код не скомпилируется абсолютно чисто. Если вы работаете над проектом C#, откройте окно Property Pages, выберите папку Configuration Properties, страницу Build и задайте в поле Treat Warnings As Errors (считать предупрежде ния ошибками), расположенном в столбце Errors And Warnings (ошибки и предуп реждения), значение True (рис. 2 1). В случае проекта Visual Basic .NET нужно от крыть окно Property Pages, папку Configuration Properties, выбрать страницу Build и установить флажок Treat Compiler Warnings As Errors (рис. 2 2).
Если компилятор будет считать предупреждения ошибками, он окажет вам огромную помощь (особенно при работе над проектами C#), прекращая сборку программы при обнаружении таких проблем, как CS0649 [Field ‘field’ is never assigned to, and will always have its default value ‘value’ (Полю ‘field’ никогда не присваива ется значение, поэтому оно всегда будет иметь значение ‘value’, заданное по умол чанию)], которая показывает, что у вас не инициализирован член класса. Однако другие сообщения, такие как CS1573 [Parameter ‘parameter’ has no matching param tag in XML comment (but other parameters do) (Параметр ‘parameter’ не имеет со ответствующего ему тега в комментарии XML (хотя другие параметры имеют та кие теги)], могут быть настолько надоедливыми, что вы захотите отключить об ращение с предупреждениями как с ошибками. Не делайте этого!
Сообщение CS1573 выводится, когда вы задаете крайне полезный ключ /DOC для создания XML документации для вашей сборки и не комментируете какой то ис пользованный параметр. (Я считаю большим преступлением то, что Visual Basic
.NET и C++ не поддерживают ключ /DOC и документацию XML.) Это самая настоя щая ошибка, потому что, если вы создаете документацию XML, кто нибудь из ва шей группы скорее всего будет читать ее, и если вы не опишете все параметры или что нибудь в этом роде, вы окажете своей группе очень плохую услугу.
Есть одно предупреждение, которое неверно считать ошибкой. Это предупреж дение CS1596: [XML documentation not updated during this incremental rebuild; use /incremental to update XML documentation (Во время этой компиляции с прира щением документация XML не была обновлена; для ее обновления используйте ключ /incremental )] Документация XML чрезвычайно полезна, но отключение ком пиляции с приращением очень замедляет создание программы. Отключить эту ошибку невозможно, поэтому эту проблему можно решить, лишь отключив ком пиляцию с приращением или для отладочных, или для заключительных компо новок. Быстрая компиляция нравится всем, поэтому я отключаю компиляцию с приращением и создаю документацию XML только для заключительных компо новок. Так я обеспечиваю быстроту компиляции и при этом получаю документа цию XML, когда она мне нужна.
