Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004

.pdf
Скачиваний:
322
Добавлен:
13.08.2013
Размер:
3.3 Mб
Скачать

ГЛАВА 5 Эффективное использование отладчика Visual Studio .NET

211

 

 

В управляемых приложениях интересно то, что, когда вы смотрите свойство объекта в окне Watch, на самом деле вызывается аксессор get. Вы можете легко это проверить: поместите следующее свойство в класс, запустите отладку, пере ключитесь в окно This/Me и раскройте значение this/Me объекта. Вы увидите, что возвращаемое имя есть имя домена приложения, которому принадлежит свойство.

Public ReadOnly Property WhereAmICalled() As String

Get

Return AppDomain.CurrentDomain.FriendlyName

End Get

End Property

Конечно же, если есть свойство объекта, копирующее 3 гигабайтную базу дан ных, автоматическое вычисление может стать проблемой. К счастью, Visual Studio

.NET позволяет отключить просмотр свойств в диалоговом окне Options, папка Debugging (отладка), флажок Allow Property Evaluation In Variables Windows (раз решить вычисление свойств в окнах переменных). Еще лучше то, что вы можете включать и отключать вычисление свойств на лету, а отладчик тут же реагирует на это изменение. Вы можете сразу узнать, разрешено ли вычисление свойств, так как семейство окон Watch сообщает об этом в поле Value: «Function evaluation is disabled in debugger windows. Check your settings in Tools.Options.Debugging.General» (Вычисление функций в окнах отладчика запрещено. Проверьте параметры в Tools.Options.Debugging.General).

Неуправляемый код несколько отличается тем, что вы должны сказать окну Watch, что метод надо вызывать. Заметьте, что я использую здесь общий термин «метод». В действительности для неуправляемого кода вы наверняка можете вы зывать только функции C или статические методы C++, так как нормальные ме тоды неуправляемого C++ требуют указателя this, который вы можете иметь или не иметь в зависимости от контекста. Методы управляемого кода немного более дружественны к указателю this. Если программа остановлена внутри класса, со держащего метод, подлежащий вызову, окно Watch автоматически предполагает необходимость использования указателя this.

Как вы уже увидели, вызов свойств управляемого кода тривиален. Вызов мето дов в управляемом или неуправляемом коде выполняется так же, как и их вызов из вашего кода. Если методу не нужны какие либо параметры, просто напечатай те имя метода и добавьте открывающую и закрывающую скобки. Так, если отла живается метод MyDataCheck ( ), вы вызовете его из окна Watch строкой MyDataCheck ( ). Если отлаживаемый метод требует параметров, просто укажите их, как если бы вы вызывали метод обычным образом. Если отлаживаемый метод возвращает значение, оно отображается в колонке Value окна Watch.

Общая проблема вызова функций неуправляемого кода — необходимость убе диться, что они действительны. Для проверки функции введите ее имя в окне Watch и не добавляйте кавычек или параметров. Если она может быть найдена, окно Watch покажет тип и адрес функции в колонке Value. Кроме того, мы можем по жела нию также задать расширенный синтаксис точки прерывания (см. главу 7) для функции, сужая область видимости

Некоторые правила применимы как к управляемому, так и неуправляемому коду при вызове методов из окна Watch. Первое правило: метод должен выполняться

212 ЧАСТЬ II Производительная отладка

не более 20 секунд, так как пользовательский интерфейс отладчика не реагирует до завершения метода. Через 20 секунд управляемый код сообщает: «Evaluation of expression or statement stopped» (вычисление выражения или утверждения прекра щено), «error: function ‘<method name>‘ evaluation timed out» (ошибка: функция <имя метода> — тайм аут вычисления) или «Error: cannot obtain value» (Ошибка: невоз можно получить значение), а неуправляемый код сообщает: «CXX001: Error: error attempting to execute user function» (Ошибка при попытке выполнить функцию пользователя). Хорошо то, что ваши потоки будут продолжать выполняться. Это прекрасно, так как при вызове методов неуправляемого кода, которые зависали, Visual Studio 6 просто прерывала исполнение текущего исполняющегося потока. Другая хорошая новость: вы можете оставить в покое вызванные методы в окне Watch в многопоточных программах. Предыдущие версии Visual Studio прекра щали исполнение любых потоков, которые оказались активными к тому време ни, когда вы исполняли свой отладочный метод в другом потоке. Последнее пра вило имеет общий смысл: для проверки данных обращайтесь к памяти только на чтение. Если вы думаете, что отлаживать программу, меняющую свое поведение из за побочных эффектов в проверочных функциях, классно, то подождите не приятностей в окне Watch. Кроме того, если вам нужно что либо выводить, пользуй тесь трассировкой.

Все, что я здесь говорил об окне Watch, является общим как для отладки .NET приложений, так и приложений неуправляемого кода. Вы также можете автома тически развернуть собственные типы в окне Watch, но для управляемого и неуп равляемого кода это делается разными способами. Кроме того, отладка неуправ ляемого кода имеет массу других возможностей форматирования данных и уп равления ими (см. главы 6 и 7).

Команда Set Next Statement

Одна из самых крутых скрытых возможностей отладчика Visual Studio .NET — команда Set Next Statement (установить следующий оператор), доступная как в окне исходного текста, так и в окне Disassembly через контекстное меню, но только в режиме отладки. Set Next Statement позволяет установить указатель команд на другое место в программе.

Хороший пример того, когда нужно использовать команду Set Next Statement, — заполнение структуры данных вручную. Вы по шагам проходите метод, вставля ющий данные, изменяете значения, передаваемые функции и с помощью Set Next Statement повторяете вызов метода. Таким образом, вы заполняете структуру дан ных путем изменения порядка исполнения кода.

Нужно отметить, что изменение указателя команд может легко «свалить» вашу программу, если вы не очень аккуратны. Работая с отладочной компоновкой, можно применять команду Set Next Statement в окне исходного текста программы без особых проблем. Если же вы работаете, скажем, с оптимизированной компонов кой неуправляемого кода, безопаснее всего использовать окно Disassembly. Ком пилятор перемещает код так, что строки исходного текста будут исполняться в другой последовательности. Кроме того, работая с командой Set Next Statement, вы должны знать о создаваемых в стеке временных переменных (см. главу 7).

ГЛАВА 5 Эффективное использование отладчика Visual Studio .NET

213

 

 

Если я ищу ошибку и мое предположение заключается в том, что ошибка на ходится в определенной части кода, я ставлю точку прерывания перед вызываю щей подозрение функцией или функциями. Я проверяю данные и параметры, пе редаваемые функциям, и прохожу их по шагам. Если проблема не повторяется, я использую команду Set Next Statement для возврата точки исполнения обратно к точке прерывания и изменяю данные, передаваемые функциям. Такая тактика по зволяет проверять несколько предположений в одном сеансе отладки, сокращая в конечном итоге время. Но делать так можно не всегда, поскольку исполнение некоторого кода один раз, а затем его повторение может разрушить состояние. Команда Set Next Statement лучше всего работает, когда код не сильно меняет со стояние программы.

Как я уже говорил, команда Set Next Statement пригодится при блочном тести ровании. Например, она удобна для проверки обработчиков ошибок. Скажем, име ется оператор if, и нужно проверить, что случится, если его условие не выполня ется. Все, что вам необходимо, — это сделать проверку условия и командой Set Next Statement перенести точку исполнения в ветвь, соответствующую невыпол нению условия. Кроме Set Next Statement, в меню имеется команда Run To Cursor, доступная также в контекстном меню, вызываемом правым щелчком в окне ис ходного текста; она позволяет установить «одноразовую» точку прерывания.

Заполнение структур данных, особенно списков и массивов, — другое прекрас ное применение команды Set Next Statement при отладке или тестировании. Если есть код, заполняющий структуру данных и добавляющий ее к связанному спис ку, командой Set Next Statement можно добавить дополнительные элементы к свя занному списку и увидеть, как программа работает в этих случаях. Такое приме нение команды Set Next Statement особенно полезно, если вам нужно обеспечить появление трудновоспроизводимых данных в процессе отладки.

Стандартный вопрос отладки

Может ли Visual Studio .NET отлаживать обычные Web-приложения ASP?

Конечно, может, но не сразу, так как сначала нужно добавить некоторые разделы реестра и установить разрешения для DCOM на Web сервере. Трудно найти правильные шаги, так как они зарыты очень глубоко в документации. Поищите «ASP Remote Debugging Setup» для определения необходимых шагов.

214 ЧАСТЬ II Производительная отладка

Резюме

Microsoft прислушалась к программистам и выпустила отладчик Visual Studio .NET, существенно упрощающий решение некоторых сверхтрудных проблем отладки. Эта глава познакомила с общими особенностями точек прерывания при работе с управляемым и неуправляемым кодом. Вы должны стараться отдать отладчику Visual Studio .NET как можно больше работы, чтобы сократить время, проводимое в нем.

Расширенные точки прерывания помогают избежать утомительных сеансов отладки, позволяя точно описать условия, при которых срабатывает точка пре рывания. Хотя у точек прерывания управляемого и неуправляемого кода свои особенности, модификаторы точек прерывания по месту, счетчики выполнений и условные выражения — ваши лучшие друзья, в основном потому, что сэконо мят уйму времени за счет более эффективного использования отладчика. Я на стоятельно рекомендую вам поиграть с ним, чтобы избежать необходимости изу чать их особенности в критический момент.

Г Л А В А

6

Улучшенная отладка приложений .NET

в среде Visual Studio .NET

Платформа Microsoft .NET Framework избавила нас от извечных проблем, свя занных с утечками и искажениями памяти, однако еще не наступило время, когда «код делает именно то, что я хочу, а не что пишу», а значит, нам все таки иногда придется испытывать сомнительное удовольствие отладки программ и искать при чины ошибок. В этой главе я опишу конкретные стратегии, которые помогут сде лать отладку приложений .NET менее тягостной. В предыдущей главе я уже упо минал некоторые методы отладки программ .NET в среде Visual Studio .NET, но в этой главе я опишу их подробнее. Я начну с рассмотрения нескольких факторов, специфических для отладки всех типов приложений .NET именно при помощи Visual Studio .NET, затем перейду к разным хитростям и методам, связанным с отладкой программ .NET в общем. В заключительной части главы я расскажу о ди зассемблере промежуточного языка Microsoft (Microsoft Intermediate Language Disassembler, ILDASM) и работе с ним.

В связи с Visual Studio .NET 2003 часто упоминается один новый инструмент (я не буду рассматривать его в данной главе и расскажу про него позднее) — под держка отладочного расширения SOS (Son of Strike), которая позволяет получать информацию о коде .NET из дампов памяти, а также при отладке неуправляемого кода. Речь о SOS пойдет в главе 8, так как я обнаружил, что интегрировать и ис пользовать его вместе с WinDBG гораздо легче, чем с Visual Studio .NET (странно, но это так!). Если в вашем случае есть хоть какая то вероятность получения дам пов памяти приложений .NET, я настоятельно рекомендую прочитать раздел, по священный SOS.

216 ЧАСТЬ II Производительная отладка

Усложненные точки прерывания для программ .NET

В главе 5 я показал, что отладчик Visual Studio .NET заметно облегчает прерыва ние выполнения программы именно в той части кода, где вы хотите. В случае кода

.NET модификаторы условных выражений для точек прерываний по месту имеют некоторые особенности.

Условные выражения

Один из самых частых вопросов про условные точки прерывания, с которым я сталкиваюсь уже много лет, таков: можно ли вызывать функции из модификато ров условных выражений для точек прерываний по месту? При отладке неуправ ляемого кода — нет, но в случае .NET вполне допустимо. Вызов функций или ме тодов из условных выражений способен облегчить отладку, однако, если делать это без должного внимания, побочные эффекты могут сделать отладку почти не возможной.

Когда я только приступил к изучению .NET, я не понимал эту дополнительную силу условных выражений, потому что их функциональность казалась вполне обычной. Скажем, я использовал такие выражения, как MyString.Length == 7, и ду мал, что отладчик достигает соответствующего места в отлаживаемой программе

иполучает значение длины строки, читая его прямо из памяти, точно так же, как

ипри отладке неуправляемого кода Win32. Попробовав выражение, вызывавшее более интересный и сложный аксессор чтения свойства (property get accessor), я начал экспериментировать, чтобы узнать все имеющиеся у меня возможности. В конце концов я обнаружил, что возможны любые допустимые вызовы, кроме вызовов методов Web.

Пожалуй, процесс вызова методов из условных выражений лучше всего про демонстрировать на примере. В программе ConditionalBP (листинг 6 1) из набо ра примеров к книге, используется класс TestO, следящий за числом вызовов ме тода. Установив условную точку прерывания на строке Console.WriteLine в функ ции Main при помощи выражения (x.Toggle() == true) || (x.CondTest() == 0), вы увидите, что выполнение программы будет прерываться, только когда поле m_bToggle име ет значение true, а поле m_CallCount — нечетное значение. Наблюдая при оста новках цикла за значениями полей экземпляра x класса TestO, вы сможете убе диться в том, что они изменяются, а это показывает, что код действительно вы полняется.

Листинг 6-1. Пример модификатора условной точки прерывания

using System ; namespace ConditionalBP

{

class TestO

{

public TestO ( )

{

ГЛАВА 6 Улучшенная отладка приложений .NET в среде Visual Studio .NET

217

 

 

m_CallCount = 0 ; m_bToggle = false ;

}

private Int32 m_CallCount ;

public Int32 CondTest ( )

{

m_CallCount++ ;

return ( m_CallCount ) ;

}

private Boolean m_bToggle ;

public Boolean Toggle ( )

{

m_bToggle = !m_bToggle ; return ( m_bToggle ) ;

}

}

class App

{

static void Main(string[] args)

{

TestO x = new TestO ( ) ;

for ( Int32 i = 0 ; i < 10 ; i++ )

{

// Точка прерывания: (x.Toggle() == true) || (x.CondTest() == 0 ) Console.WriteLine ( "{0}" , i ) ;

}

x = null ;

}

}

}

Прежде чем включать вызов свойства или метода в модификатор условного выражения точки прерывания по месту, надо проверить, что свойство или метод делают. Если метод копирует 3 Гбайтную базу данных или приводит к каким то другим нежелательным изменениям, вам, вероятно, не захочется вызывать его. Еще один интересный аспект вычисления отладчиком методов и свойств: 20 секунд ный лимит времени окна Watch не имеет отношения к вызову методов из услов ных выражений. Если у вас есть метод, для вычисления которого нужно несколь ко дней, отладчик будет покорно ждать. К счастью, пользовательский интерфейс Visual Studio .NET при этом не блокируется, так что, нажав Ctrl+Alt+Break или выбрав в меню Debug пункт Break All (прервать отладку всех программ), вы сможете сра зу прекратить отладку.

218 ЧАСТЬ II Производительная отладка

Эта удивительная возможность вызывать методы и свойства из условных то чек прерывания имеет в среде Visual Studio .NET один минус, про который я дол жен рассказать. Если задать некорректное условие до начала отладки, отладчик сообщит, что он не может вычислить выражение и остановится. Однако, если некорректное условие, которое не может быть вычислено или генерирует исклю чение, задать после начала отладки, отладчик не остановится. Если вы задаете неверное выражение после начала отладки, вам очень не повезло.

При недопустимом условии отладчик сообщит, что он не смог установить точку прерывания (рис. 6 1), но не остановится, как вы предполагали, а продолжит вы полнение отлаживаемой программы. Ничто так не огорчает, как воспроизведение почти неуловимой ошибки только для того, чтобы отладчик просто пропустил ее. Этот недочет имелся в Visual Studio .NET 2002, сохранился он, увы, и в Visual Studio

.NET 2003. Я очень надеюсь, что Microsoft решит эту проблему в будущих версиях Visual Studio .NET, чтобы при задании недопустимого условия отладчик останав ливался и позволял исправить его.

Рис. 6 1. Visual Studio .NET сообщает о невозможности установки точки прерывания

Проблема модификаторов условных выражений точек прерывания так ковар на, что я хочу привести несколько ситуаций, в которых с ней можно столкнуться. Первый пример показывает, насколько внимательно нужно относиться к побоч ным эффектам условных выражений. Как вы думаете, что случится, если в следу ющем фрагменте кода C# установить условную точку прерывания на строке с вызовом Console.WriteLine и ввести условие i = 3 (заметьте: в условии только один знак равенства)? Если вы думаете, что условие изменит значение i и вызовет бес конечный цикл, вы абсолютно правы.

for ( Int32 i = 0 ; i < 10 ; i++ )

{

Console.WriteLine ( "{0}" , i ) ;

}

Второй пример. Допустим, у нас есть приложение Microsoft Visual Basic .NET на базе Windows Forms, включающее простой метод:

Private Sub btnSkipBreaks_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) _

Handles btnSkipBreaks.Click

Dim i As Integer

ГЛАВА 6 Улучшенная отладка приложений .NET в среде Visual Studio .NET

219

 

 

' Очистка поля вывода. edtOutput.Clear()

m_TotalSkipPresses += 1

edtOutput.Text = "Total presses: " + _ m_TotalSkipPresses.ToString() + _ vbCrLf

For i = 1 To 10

'Добавление символов в поле вывода. edtOutput.Text += i.ToString() + vbCrLf

'Обновление поля вывода при каждой итерации цикла. edtOutput.Update()

Next

End Sub

Если при установке точки прерывания в цикле For вы будете думать на C# и введете модификатор условного выражения точки прерывания как i==3 (при программировании на Visual Basic .NET правильное выражение должно было бы иметь вид i=3), то из за того, что программа выполняется, вы увидите информа ционное окно (рис. 6 1). Печально то, что в поле будет выведен весь текст, т. е. выполнение кода не прервется. При отладке приложений Windows Forms и кон сольных приложений условные выражения всегда надо задавать до запуска отлад чика. Тогда, если какое то выражение окажется неправильным, Visual Studio .NET сообщит об этом и остановится на первой строке программы, позволяя испра вить проблему. Что до приложений Microsoft ASP.NET и Web сервисов XML, то в этих случаях средств, позволяющих быстро решить проблему модификаторов ус ловных выражений точек прерываний, просто нет, поэтому при работе над про граммами таких типов нужно вводить выражения особенно тщательно.

Проблемы с условными выражениями точек прерывания могут возникнуть и тогда, когда выражение почему либо вызывает исключение. Вы увидите то же простое информационное окно (рис. 6 1). Хорошая новость в том, что исключе ние не вызовет крах программы, потому что оно никогда не передается вашему приложению, но вы все же не сможете остановить программу и исправить выра жение. Используя в выражении переменные, будьте особенно внимательны к тому, чтобы написать его правильно.

Наконец, я хочу напомнить, что языки C# и J# позволяют применять в услов ных выражениях точек прерываний значения null, true и false. Странно, но в Visual Studio .NET 2002 значение null не поддерживается. В Visual Basic .NET при логи ческом сравнении можно использовать True и False. Сравнение переменной с Nothing можно выполнить оператором Is (MyObject Is Nothing).

220 ЧАСТЬ II Производительная отладка

Стандартный вопрос отладки

Как сделать, чтобы программа прерывалась, только когда метод вызывается конкретным потоком?

Чтобы установить точку прерывания для конкретного потока, нужно опре делить его уникальный идентификатор. Нам повезло: разработчики Microsoft

.NET Framework включили в класс System.Threading.Thread свойство Name, чем сделали задачу идентификации потока тривиальной. Благодаря этому вы можете использовать в условных выражениях точек прерываний что то вроде

"ThreadIWantToStopOn" == Thread.CurrentThread.Name. Конечно, это подразуме вает, что, начиная поток, вы всегда задаете его свойство Name.

Первый способ получения уникального идентификатора потока — ука зать имя потока вручную путем изменения значения свойства Name в окне Watch. Если у вас есть переменная экземпляра MyThread, можете ввести MyThread.Name и указать новое имя потока в столбце Value (значение). Если у вас нет переменной потока, задать имя текущему потоку можно через свой ство Thread.CurrentThread.Name. Указывая имя потока при помощи отладчи ка, вы не должны задавать его в своем коде. Свойство Name может быть зада но только раз, и, если вы попытаетесь задать его повторно, будет сгенери ровано исключение InvalidOperationException. Если вы работаете с собствен ным кодом, не включая в него много элементов управления сторонних фирм, задавать имя потока из отладчика довольно безопасно, так как библиотека классов .NET Framework не использует свойство Name.

Однако, если вы работаете с большим числом элементов управления сторонних фирм или включаете в свою программу код, в котором, как вы подозреваете, свойство Name может использоваться, я расскажу про другой метод получения уникального идентификатора потока, также предоставля ющий уникальное значение, хотя и в менее удобной форме. Глубоко в не драх класса Thread есть закрытая целочисленная переменная — DONT_USE_In ternalThread, имеющая уникальное значение для каждого потока. (Да, вы можете применять в условных выражениях закрытые переменные.) Чтобы установить точку прерывания для конкретного потока, выберите его в окне Threads (Потоки). В окне Watch введите Thread.CurrentThread.DONT_USE_In ternalThread, чтобы увидеть значение DONT_USE_InternalThread, которое позволит создать подходящее условное выражение точки прерывания. Помните: лю бая переменная с именем DONT_USE_xxx может исчезнуть в будущих версиях Visual Studio .NET.

Окно Watch

Как я упоминал в главе 5, окно Watch — одно из самых лучших отладочных средств в Visual Studio .NET. Мы уже обсудили вызов методов и задание свойств, поэтому мне осталось рассказать об отладке управляемого кода при помощи окна Watch только одно: как настроить окно Watch , чтобы сделать отладку еще быстрее.

Соседние файлы в предмете Программирование на C++