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

454 |
|
ЧАСТЬ IV |
Мощные средства и методы отладки неуправляемого кода |
||||||
|
|
|
|
||||||
|
|
|
|
||||||
|
Line |
numbers for f:\vs70builds\2292\vc\crtbld\crt\src\atonexit.c |
|
||||||
|
76 |
: |
0x00402810 |
81 |
: |
0x00402814 |
90 : 0x0040284B |
95 : 0x004 02850 |
|
|
96 |
: |
0x00402853 |
97 |
: |
0x00402866 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Использование CrashFinder
Как вы только что увидели, читать MAP и P2M файлы не так уж и сложно. Одна ко это довольно утомительное и определенно немасштабируемое решение для других членов вашей группы, таких как сотрудники отдела контроля качества, техподдержки и даже руководители. Для улучшения масштабируемости CrashFinder я решил включить в отчеты об ошибках максимально подробную информацию, чтобы сделать его полезным для всех членов группы разработки: от программис тов до тестировщиков и инженеров по сопровождению программы. Если вы сле дуете моим советам и правильно создаете символы отладки (см. главу 2), все чле ны вашей группы смогут работать с CrashFinder без всяких проблем.
При использовании CrashFinder в группе вам нужно быть особенно вниматель ным к поддержанию доступа к двоичным образам и связанным с ними PDB фай лам, поскольку CrashFinder не хранит информации о вашем приложении, кроме путей к двоичным файлам. Это позволяет вам использовать один проект CrashFinder на всем протяжении цикла разработки. Если бы CrashFinder хранил более подроб ные сведения о вашем приложении, такие как таблицы символов, то вам, вероят но, нужно было бы создавать отдельный проект CrashFinder для каждой компо новки. Если вы примете мой совет и обеспечите легкий доступ к двоичным фай лам и PDB файлам, то при возникновении ошибки тестировщики и члены груп пы сопровождения должны будут только запустить CrashFinder и добавить важ ную информацию в отчет об ошибке. Все мы знаем, что чем больше мы знаем о конкретной проблеме, тем проще будет ее решение.
Для некоторых приложений вам, наверное, захочется создать несколько про ектов CrashFinder. Скажем, вы могли бы иметь один проект, указывающий на ме сто ежедневной компоновки, а также проекты для каждой версии программы, соответствующей контрольной точке. Если вы решите включить в проект Crash Finder системные DLL, вам нужно будет создать отдельные проекты для каждой поддерживаемой вами ОС. Вам также понадобятся отдельные проекты CrashFinder для всех версий программы, отсылаемых тестировщикам за пределы группы раз работки, поэтому для каждой такой версии нужно будет отдельно хранить двоич ные образы и PDB файлы.
Работа над CrashFinder оказалась весьма интересной. Говорят, он значительно облегчает труд, и я очень горжусь этим. Более того, целым рядом усовершенство ваний пользовательского интерфейса и функциональности CrashFinder обязан не мне, а другим талантливым программистам. К версии CrashFinder, которую вы найдете на CD, приложили руку Скотт Блюм (Scott Bloom), Чинг Минг Квок (Ching Ming Kwok), Джефф Шанхольтц (Jeff Shanholtz), Рич Петерс (Rich Peters), Пабло Преседо (Pablo Presedo), Джулиан Онионс (Julian Onions) и Кен Глэдстоун (Ken Gladstone). Всем им я очень признателен.

ГЛАВА 12 Нахождение файла и строки ошибки по ее адресу |
455 |
|
|
На рис. 12 4 показан пользовательский интерфейс CrashFinder с одним из моих личных проектов. В верхней части дочернего окна — древовидный список, ото бражающий исполняемый файл и его DLL. Зеленые галочки говорят о том, что символы для каждого двоичного образа были загружены правильно. Если бы Crash Finder не смог загрузить символы, он указал бы на это при помощи красной бук вы X и развернул проблемный элемент списка, поясняя причину проблемы.
Рис. 12 4. Пользовательский интерфейс CrashFinder
Красный X появляется по трем причинам. Во первых, это может означать, что CrashFinder не нашел PDB файла, соответствующего двоичному файлу. Лучше всего хранить двоичные и PDB файлы в одном месте — тогда проблем быть не должно. Вторая причина в том, что при открытии сохраненного проекта CrashFinder больше не может найти двоичный файл. Наконец, проблема может быть обусловлена конфликтом адреса загрузки файла с какой либо из DLL проекта. ОС не допуска ет возможность конфликта DLL, поэтому CrashFinder также этого не позволяет. При конфликте загрузки вы можете изменить адрес конфликтующей DLL только для текущего проекта CrashFinder. Как я уже говорил, модификация базовых адресов DLL очень важна для успешного поиска ошибок.
Вся магия преобразования мистического адреса в информацию об исходном файле, функции и номере строки отображается в нижней части дочернего окна. Однако сначала я должен рассказать, как загрузить двоичные файлы в проект CrashFinder. Нажав на панели инструментов кнопку New, вы увидите стандартное диалоговое окно работы с файлами, предлагающее добавить в проект двоичный образ. Если у вас есть EXE файл, его нужно добавить в проект в первую очередь. В диалоговом окне Add Binary Image (добавить двоичный образ) вы можете выб рать несколько двоичных файлов, включив тем самым в CrashFinder сразу весь свой проект.

456 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
После выбора пункта Open (открыть) происходит много операций. В новую версию CrashFinder внесено одно отличное усовершенствование: теперь он авто матически ищет все неявно загружаемые DLL и добавляет их в проект. Если вы явно загрузили DLL как объекты COM, добавить их можно, выбрав в меню Edit (изме нить) пункт Add Image (добавить образ). CrashFinder также добавит в проект все дополнительные неявно скомпонованные модули.
Добавляя в проект двоичные образы, помните: проект CrashFinder может вклю чать только один EXE файл. Если приложение состоит из нескольких EXE фай лов, создайте для каждого отдельный проект CrashFinder. CrashFinder — приложе ние с многодокументным интерфейсом (multiple document interface, MDI), поэтому вы легко сможете открыть все проекты для всех EXE файлов. Когда вы добавляете в проект DLL, CrashFinder проверяет, не конфликтует ли ее адрес загрузки с про чими DLL проекта. Обнаружив конфликт, CrashFinder позволит изменить адрес заг рузки конфликтующей DLL только для текущего проекта. Это очень удобно, когда у вас есть проект CrashFinder для отладочной компоновки и вы случайно забыли модифицировать базовый адрес своих DLL.
По мере развития приложения вы можете удалять двоичные образы из проек та командой Remove Image (удалить образ) из меню Edit. Кроме того, вы всегда можете изменить адрес загрузки двоичного образа, выбрав пункт Image Properties (свойства образа) из меню Edit. Так как CrashFinder автоматически добавляет в проект системные DLL, нужные вашим двоичным файлам, вы сможете облегчить отладку, если используете сервер символов (см. главу 2). Теперь у вас есть еще более веская причина установки сервера символов: благодаря его поддержке Crash Finder’ом вы сможете изучать ошибки даже в системных модулях. Просмотрев информацию, изображенную для VERSION.DLL, вы увидите, что она загружает символы из моего сервера символов.
Назначение CrashFinder заключается в преобразовании адреса ошибки в ин формацию об исходном файле, имени функции и номере строки. Интересующие вас адреса нужно вводить в поле Hexadecimal Address(es) (шестнадцатеричные адреса) — в нижней половине дочернего окна. Когда вы нажмете на кнопку Find (найти), исходный файл, имя функции и номер строки появятся в поле вывода в низу окна. При желании вы можете ввести несколько адресов, разделив их про белами или запятыми. Так, вы можете ввести полный список адресов стека вызо вов из журнала Dr. Watson, получив сразу все нужные сведения.
По умолчанию CrashFinder не показывает смещение адреса от начала функции или от строки исходного кода. Чтобы увидеть эти сведения, укажите это в диало говом окне Options (свойства). Смещение от начала функции показывает, на сколько байт адрес отстоит от начала функции. Смещение от строки говорит, на сколько байт отстоит адрес от начала ближайшей строки исходного кода. Помните, что одной строке исходного кода могут соответствовать много ассемблерных команд, особенно если вы используете вызовы функций как параметры. Работая с Crash Finder, вы не сможете просмотреть адрес, не являющийся корректным адресом команды. Скажем, при неправильном обращении с указателем this вы можете столкнуться с ошибкой по такому адресу, как 0x00000001. К счастью, эти типы ошибок не так часты, как обычные ошибки нарушения доступа к памяти, кото рые CrashFinder находит с легкостью.


458ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
//Проверка корректности образа. Если он корректен, я проверяю,
//не включен ли он уже в список и не конфликтует ли он с другими
//адресами загрузки. Если образ некорректен, я все равно добавляю
//его в список, потому что игнорировать данные пользователя нельзя.
//Если образ плохой, я просто отображаю его с соответствующим значком
//и не загружаю его в символьную машину.
if ( TRUE == pImage >IsValidImage ( ) )
{
//В этом блоке выполняется просмотр элементов массива данных,
//при этом осуществляется поиск следующих проблем.
//1. Двоичный образ уже находится в списке. Если это так,
//я могу только прервать операцию.
//2. Двоичный файл хочет загрузиться по адресу, который уже
//имеется в списке. В этом случае я вывожу для двоичного
//образа окно Properties, чтобы его адрес загрузки можно
//было изменить.
//3. Проект уже включает образ EXE, и pImage также
//указывает на исполняемый файл.
//Я всегда исхожу из предположения, что данные, на которые
//указывает pImage, корректны. Я — оптимист!
BOOL bValid = TRUE ;
INT_PTR iCount = m_cDataArray.GetSize ( ) ; for ( INT_PTR i = 0 ; i < iCount ; i++ )
{
CBinaryImage * pTemp = (CBinaryImage *)m_cDataArray[ i ] ;
ASSERT ( NULL != pTemp ) ; if ( NULL == pTemp )
{
// Недопустимое значение указателя! return ( FALSE ) ;
}
// Совпадают ли имена образов (два значения CString)?
if ( pImage >GetFullName ( ) == pTemp >GetFullName ( ) )
{
if ( FALSE == bIgnoreDups )
{
// Сообщить об этом пользователю!! sMsg.FormatMessage ( IDS_DUPLICATEFILE ,
pTemp >GetFullName ( ) ) ; AfxMessageBox ( sMsg ) ;
}
return ( FALSE ) ;
}
//Если текущий образ из структуры данных некорректен,
//у меня неприятности. Выше я смог проверить совпадение


460 |
ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода |
|
|
|
|
|
|
|
|
|
|
|
pImage >GetLoadAddress(), |
|
|
iIndex |
)) |
|
{ |
|
sMsg.FormatMessage
( IDS_DUPLICATELOADADDRFINAL , pImage >GetFullName ( ) ,
((CBinaryImage*)m_cDataArray[iIndex]) >GetFullName()); AfxMessageBox ( sMsg ) ;
//Данные pImage некорректны, поэтому
//выполняется выход из цикла.
bValid = FALSE ; break ;
}
}
else
{
//Данные pImage некорректны, поэтому
//выполняется выход из цикла.
bValid = FALSE ;
pImage >SetBinaryError ( eAddressConflict ) ; break ;
}
}
}
}
if ( TRUE == bValid )
{
// Этот образ в порядке (по крайней мере до загрузки символов). iState = STATE_VALIDATED ;
}
else
{
iState = STATE_NOTVALID ;
}
}
else
{
// Этот образ некорректен. iState = STATE_NOTVALID ;
} |
|
if ( STATE_VALIDATED == iState ) |
|
{ |
|
bRet = (BOOL) |
|
m_cSymEng.SymLoadModule64 ( NULL |
, |
(PWSTR)pImage > |
|
GetFullNameString ( ) , |
|
NULL |
, |
pImage >GetLoadAddress ( ) |
, |


462 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
else
{
pImage >SetExtraData ( FALSE ) ;
}
//Элемент помещается в массив. m_cDataArray.Add ( pImage ) ;
//Изменился ли документ в результате добавления элемента? if ( TRUE == bModifiesDoc )
{
SetModifiedFlag ( ) ;
}
//Образ помещается в дерево.
bRet = m_cTreeDisplay.InsertImageInTree ( pImage , iState ) ;
ASSERT ( bRet ) ;
// Все OK!! return ( bRet ) ;
}
Наконец, два слова об архитектуре данных CrashFinder. Основная структура дан ных CrashFinder — простой массив объектов класса CBinaryImage. Объект CBinaryImage соответствует одному двоичному образу и содержит базовую информацию о нем, такую как адрес загрузки, свойства и имя. При добавлении в проект двоичного образа документ помещает объект CBinaryImage в основной массив данных и со храняет соответствующий указатель в слоте данных узла дерева. При выборе элемента дерева оно возвращает его указатель документу, который получает CBinary Image и просматривает его символьную информацию.
Что после CrashFinder?
Первая версия CrashFinder поддерживала все описанные функции, но была недо статочно удобной в использовании, что было исправлено мной и упомянутыми выше людьми с учетом многих пожеланий. Тем не менее CrashFinder всегда мож но усовершенствовать. Если вы хотите лучше разобраться в образах двоичных фай лов, попытайтесь добавить в CrashFinder какие нибудь из следующих функций.
Реализуйте поддержку двоичных файлов ОС и сделайте так, чтобы CrashFinder автоматически переключался между ними. Это повысит эффективность поис ка ошибок в коде ОС. В настоящий момент CrashFinder работает только с сис темными DLL, установленными на компьютере пользователя.
Выводите в древовидном списке более подробную информацию для каждого двоичного файла. Класс CBinaryImage позволяет сделать это при помощи мето да GetAdditionalInfo. Вы можете добавить функцию вывода информации о за головке, импортируемых и экспортируемых функциях.