
Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004
.pdf
ГЛАВА 19 Утилита Smooth Working Set |
663 |
|
|
Страница 1 |
Страница 5 |
Fn 3
Запуск Fn |
|
|
Fn 1 |
|
|
|
|
|
|||
|
|
|
|
|
|
Fn 2 |
Fn 7 |
Страница 2 |
Страница 6 |
|
Fn 6 |
Fn 10 |
|
Страница 3 |
Страница 7 |
Fn 5 |
Fn 8 |
Страница 4 |
Страница 8 |
Fn 4 |
|
|
Fn 9 |
Рис. 19 1. Неоптимизированная система
Вся работа, связанная с подсчетом числа вызовов функций, скрыта в SWS; под робнее о необходимых для этого действиях см. раздел «Работа с SWS». Указать компоновщику порядок размещения функций проще простого. Для этого LINK.EXE поддерживает ключ командной строки /ORDER. В качестве параметра после ключа /ORDER просто указывается текстовый файл, называемый файлом порядка, в кото ром нужно привести список всех функций в желательном для вас порядке. Инте ресно, что имена всех функций должны быть указаны в файле порядка в полном, расширенном виде. Идея, лежащая в основе SWS, по сути заключается в создании файла порядка для вашей программы.
Возможно, у тех из вас, кто давно работает с Microsoft Windows, где то в со знании сейчас замаячили буквы WST. WST означает Working Set Tuner (средство настройки рабочего набора). Эту программу Microsoft распространяла вместе с Platform SDK для… хм, для настройки рабочего набора. Однако в связи с этим вам могут прийти в голову и три других факта: во первых, утилиту WST было очень сложно использовать, во вторых, в ней было несколько ошибок, и в третьих, она



666 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
цию, вернуться в диспетчер задач и нажать F5 для обновления экрана. На блюдайте и за столбцом Mem Usage (память), потому что числу дескрипто ров соответствует определенный объем памяти: если оба значения растут, утечка ресурсов в самом разгаре.
Прежде чем описать работу с SWS, я должен вернуть вас на землю. Во первых, SWS — не панацея. Если до запуска SWS быстродействие вашей программы было ужасным, таким оно и останется. Во вторых, оптимизацию рабочего набора сле дует выполнять в самом конце работы над производительностью приложения, после улучшения его алгоритмов и исправления всех найденных проблем с быстродей ствием. Наконец, использовать SWS следует только в конце цикла разработки. Максимальное преимущество SWS обеспечит, когда изменения кода приближаются к минимуму.
SWS может и не понадобиться. Разрабатывая небольшую программу, откомпи лированные файлы которой занимают не более 2–3 Мб, вы можете вообще не заметить уменьшения рабочего набора. Максимальной выгоды от оптимизации рабочего набора вы добьетесь, работая над более крупными приложениями, по тому что чем больше страниц требуется программе, тем больше возможностей для ее улучшения.
Работа с SWS
Работа с SWS состоит из трех этапов. На первом вы должны перекомпилировать свою программу для установки ловушки SWS, позволяющей накопить данные о вызовах функций. Второй этап заключается в выполнении с этой специальной откомпилированной версией программы ряда наиболее частых пользовательских сценариев. Для правильного использования SWS вам нужно определить эти сце нарии, чтобы вы могли точно их воспроизвести. Случайное выполнение программы под управлением SWS может вообще не привести к сокращению рабочего набо ра. Третий этап включает генерирование для компоновщика файла порядка (что очень просто) и интеграцию этого файла в заключительную компоновку программы.
Настройка компиляндов SWS
Необходимость отдельной компиляции программы для использования SWS объ ясняется тем, что в основе SWS лежат те же принципы, что и в WST. Хотя я мог написать огромную, сложную и подверженную ошибкам программу для получе ния информации о функциях вашего приложения «на лету», гораздо проще пре доставить установку функции ловушки компилятору. Ключ /Gh (включить функ цию ловушку _penter) приказывает компилятору разместить в начале пролога всех генерируемых функций вызов функции _penter. В SWS функция _penter уже реа лизована; чтобы она могла продемонстрировать все свои магические возможно сти, вы должны скомпоновать с ней свою программу.
Скомпилировать программу для использования с SWS обычно просто — для этого надо только кое что сделать.
1.Прежде всего убедитесь в правильности параметров заключительной компо новки своего проекта, так как эта конфигурация будет клонирована. Если вы

ГЛАВА 19 Утилита Smooth Working Set |
667 |
|
|
установили при помощи надстройки SettingsMaster из главы 9 ключи компи лятора и компоновщика, которые я рекомендовал в главе 2, у вас все в порядке.
2.Клонируйте конфигурацию, с которой вы хотите работать. Нажмите правой кнопкой название решения в окне Solution Explorer (проводник решений) и выберите в меню пункт Configuration Manager (менеджер конфигураций), после чего появится одноименное диалоговое окно. Новый конфигурационный па раметр скрывается в списке Active Solution Configuration (активная конфигу рация решения). Доступ к нужному нам полю <New…> показан на рис. 19 4 (я искал его довольно долго). Как правило, следует клонировать заключительные компоновки. Открыв диалоговое окно New Solution Configuration (конфигура ция нового решения), дважды убедитесь в том, что вы выбрали нужное значе ние в списке Copy Settings From (копировать параметры из), потому что, если вы будете наследовать решение от <Default>, ваша конфигурация компоновки не будут перенесена. Называя новый проект, я предпочитаю указывать тип ком поновки и буквы « SWS», в результате чего получается имя Release SWS. Диало говое окно New Solution Configuration с этими новыми параметрами показано на рис. 19 5. Помните, что при создании новых решений каталог вывода и про межуточный каталог изменяются.
Рис. 19 4. Выбор новой конфигурации при помощи окна Configuration Manager
Рис. 19 5. Создание новой конфигурации Release$SWS
3.Если вы пользуетесь утилитой SettingsMaster (как и следует делать), настройка компилятора и компоновщика тривиальна. Нажмите кнопку SettingsMaster

668 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода
Custom Project Update (обновление проекта) и, когда появится приглашение, выберите файл Release SWS.XML. Это установит минимально необходимые параметры. Чтобы использовать SWS с отладочной компоновкой, выберите файл Debug SWS.XML (оба файла находятся в каталоге SettingsMaster\SettingsMaster).
4.Если вы не используете SettingsMaster (позор!), вам нужно установить для проекта Release SWS следующие параметры компилятора (CL.EXE) (см. по этому пово ду главу 2):
/Zi (формат отладочной информации);
/Gy (включить компоновку на уровне функций);
/Gh (включить функцию ловушку _penter); стандартного способа установки этого ключа нет, поэтому вам придется выбрать в папке C/C++ пункт Com
mand Line (командная строка) и ввести его вручную.
Кроме того, для проекта Release SWS нужно задать ключи компоновщика (LINK.EXE):
/DEBUG (генерировать отладочную информацию);
/OPT:REF (оптимизация: удалять неиспользуемые функции).
5.Добавьте SWSDLL.LIB в список зависимостей (dependencies).
Чтобы сконфигурировать проект Debug SWS, установите ключ /Gh для компи лятора CL.EXE и добавьте зависимость от SWSDLL.LIB.
Выполнение приложений вместе с SWS
После настройки и компиляции приложения наступит самый трудный для вас этап работы с SWS — выполнение вашей программы. Вам придется самым тщательным образом обдумать, каковы наиболее частые сценарии использования вашего при ложения. Если вы оптимизируете готовую программу, вам, вероятно, следует по сетить своих клиентов, чтобы увидеть, что они чаще всего с ней делают. Если же вы работаете над новым приложением, обсудите это с сотрудниками службы мар кетинга или представителями заказчика. Определив сценарии, их нужно будет выполнить на разных машинах с различной загрузкой. Скорее всего вам захочет ся сделать эти сценарии воспроизводимыми при помощи такой утилиты автома тизации, как Tester из главы 16.
Запустив программу вместе с SWS, вы обнаружите два новых файла в каталоге, в котором находится каждый двоичный файл, скомпилированный для SWS: <имя файла>.SWS и <имя файла>.1.SWS. Файл SWS без цифры — это базовый файл, со держащий адреса и размеры каждой функции модуля, а также пространство для счетчиков числа вызовов. При каждом запуске вашей программы этот файл ко пируется в новый файл, чтобы избежать повторного просмотра символов. Файлы с цифрой соответствуют запускам программы и содержат счетчики вызовов. При каждом выполнении вашего специального откомпилированного двоичного фай ла создается новый — <имя файла>.#.SWS.
Нужные компоненты SWS находятся в исполняемом файле SWS.EXE. Эта про грамма позволяет просматривать отдельные SWS файлы, создавать новые файлы и выполнять финальную оптимизацию. Запустив SWS.EXE без всяких параметров или с ключом 6?, вы увидите:
|
|
|
|
ГЛАВА 19 Утилита Smooth Working Set |
669 |
|
|
|
|||
SWS (Smooth Working Set) 2.0 |
|
|
|||
John Robbins 6 Debugging Applications for Microsoft .NET and Microsoft Windows |
|
||||
Usage: SWS [6t <module>]|[6d |
<module>]|[6g <module>]|[6?] [6nologo] |
|
|||
6t |
<module> 6 Tune the module's working set |
|
|||
|
|
|
(run from |
directory with .SWS file) |
|
6d |
<module> 6 |
Dump the raw data for the module or #.SWS file |
|
||
6g |
<module> 6 |
Generate the initial SWS file for the module |
|
||
6? |
|
6 |
Show this |
help screen |
|
6nologo |
6 |
Do not display the program information |
|
Чтобы увидеть, какие именно функции вызываются, вы можете просматривать все итоговые файлы .SWS; это позволяет гарантировать, что вы выполняете имен но те функции, которые собирались. Вывод файла, соответствующего запуску ва шей программы (<имя файла>.#.SWS), имеет формат:
Link time |
: 0x3E13849C |
|
|
Entry count |
: 12 |
|
|
Image base |
: 0x00400000 |
|
|
Image size |
: 0x00007000 |
|
|
Module name : SimpleSWSTest.exe |
|||
Address |
Count |
Size |
Name |
————— ———— |
—— —— |
|
|
0x00401050 |
2 |
22 |
?Bar@@YAXXZ |
0x00401066 |
2 |
22 |
?Baz@@YAXXZ |
0x0040107C |
2 |
22 |
?Bop@@YAXXZ |
0x00401092 |
4 |
10 |
?Foo@@YAXXZ |
0x0040109C |
1 |
49 |
_wmain |
0x004010CD |
2 |
10 |
_YeOlCFunc |
0x004011E0 |
0 |
422 |
_wmainCRTStartup |
0x00401C50 |
0 |
63 |
__onexit |
0x00401C90 |
0 |
24 |
_atexit |
0x00401DC0 |
0 |
23 |
__setdefaultprecision |
0x00401DE0 |
0 |
7 |
__matherr |
0x00401DF0 |
0 |
7 |
__wsetargv |
При запуске вашей программы, использующей функцию _penter утилиты SWS, первоначальный файл .SWS генерируется автоматически. Однако это требует зна чительного объема работы с символьной машиной DBGHELP.DLL, поэтому вы сможете уменьшить время запуска, если будете предварительно генерировать SWS файлы для модуля, указывая программе SWS.EXE ключ 6g.
Генерирование и использование файла порядка
После завершения всех запусков вашей программы вам нужно оптимизировать ее и сгенерировать файл порядка, который будет передан компоновщику. Для этого SWS.EXE предоставляет ключ командной строки 6t, после которого следует про сто указать имя модуля оптимизируемого двоичного файла. В результате оптими зации создается действительный файл порядка с расширением .PRF; я выбрал это расширение потому, что такие файлы генерировала программа Working Set Tuner.

ГЛАВА 19 Утилита Smooth Working Set |
671 |
|
|
После создания файла порядка нужно просто указать его компоновщику при помощи ключа /ORDER. Всегда сохраняйте файл порядка в том же каталоге, что и файл проекта и исходные файлы. Ключ /ORDER имеет одну особенность, которая часто вызывает замешательство: перед именем файла должен быть указан символ @. Если вы не используете SettingsMaster, вы можете указать файл порядка для двоичного файла своей заключительной компоновки, открыв окно Property Pages (страницы свойств), папку Linker (компоновщик), страницу Optimization и введя нужное выражение в поле Function Order (порядок функций) (рис. 19 6). При использовании SettingsMaster просто нажмите кнопку SettingsMaster Custom Project Update и выберите файл ReleaseOrderFile.XML.
Реализация SWS
Как это бывает со многими проектами, когда я впервые подумал о реализации SWS, это представлялось довольно сложным делом, однако при написании кода все оказалось проще. Я планировал сначала разобраться с функцией _penter, после это го — с двоичными файлами и перечислением символов и, наконец, — с перио дом выполнения и алгоритмом оптимизации.
Функция _penter
Должен признать, что до того, как я решил использовать функцию _penter при помощи ключа компилятора /Gh, я хотел заставить SWS работать с немодифици рованными двоичными файлами. Конечно, это возможно, но довольно затрудни тельно: во первых, чтобы гарантировать включение кода ловушки в первую оче редь, мне нужно было написать отладчик, а во вторых, я должен был включить 5 байтовые переходы для всех функций, обнаруживаемых в таблице символов. У меня был опыт разработки похожего кода для коммерческих программ, так что я знал, насколько это сложно. В конце концов я счел специальную компоновку вполне приемлемой, особенно когда у нас есть такие средства, как SettingsMaster, благо даря которым добавить в конфигурацию компоновки SWS совсем легко.
Разработав установку ловушки, я приступил к рассмотрению действий, кото рые нужно выполнять при каждом вызове _penter. Моя функция _penter и ее вспо могательный код приведены в листинге 19 1. Как вы можете видеть, она исполь зует соглашение вызова naked, поэтому я сам генерирую пролог и эпилог. Согла шение naked требуется в документации к _penter; кроме того, это облегчает полу чение адреса возврата из функции. К счастью, когда компилятор обещает, что он сгенерирует вызов _penter до всех остальных команд, он держит свое слово! Ре зультат установки ключа /Gh показан в дизассемблированном фрагменте, из ко торого видно, что вызов _penter выполняется даже до команды PUSH EBP, одного из элементов стандартного пролога функции:
Bar: |
|
|
00401050: E8B7000000 |
call |
_penter |
00401055: 55 |
push |
ebp |
00401056: 8BEC |
mov |
ebp,esp |
00401058: E8A8FFFFFF |
call |
ILT+0(?Foo |
0040105D: 3BEC |
cmp |
ebp,esp |

672 |
ЧАСТЬ IV |
Мощные средства и методы отладки неуправляемого кода |
|
|
|
|
|
0040105F: E8AE000000 |
call |
_chkesp |
|
00401064: 5D |
pop |
ebp |
|
00401065: C3 |
ret |
|
Немного пофантазировав, вы поймете, что ключ /Gh позволяет создать неко торые другие интересные утилиты. Первая, что пришла мне в голову, — это ути лита контроля производительности. Благодаря тому, что Microsoft уже предлага ет нам ключ /GH (включить функцию ловушку _pexit), использовать такое средство будет гораздо проще, так как вы будете знать момент окончания функции. Сове тую вам получше обдумать силу функций _penter и _pexit.
Стандартный вопрос отладки
Почему в окне Disassembly вызовы некоторых функций начинаются с «ILT»?
Вотрывке, сгенерированном ключом /Gh, вы видели вызов функции CALL ILT+0(?Foo, что многих прогhраммистов приводит в недоумение. Подобные вызовы свидетельствуют о магической компоновке с приращением в дей ствии. Аббревиатура ILT означает Incremental Link Table (таблица компоновки с приращением).
При создании отладочной компоновки компоновщик хочет скомпоно вать двоичный файл как можно быстрее. Для этого он добавляет в каждую функцию довольно много пустого места, благодаря чему при изменении функции ему нужно только перезаписать функцию, не перемещая код в двоичном файле. Между прочим, это пустое пространство заполняют коман ды INT 3. Однако размер функции может превысить объем выделенного места.
Вэтом случае компоновщик должен переместить функцию в другое место двоичного файла.
Если б каждая функция, вызывающая перемещенную функцию, делала это при помощи ее действительного адреса, то при каждом перемещении функции в результате новой компоновки компоновщик должен был бы искать и обновлять все команды CALL. Поэтому для экономии времени ком поновщик создает специальную таблицу компоновки с приращением, ко торую использует для всех вызовов. Теперь при изменении функции ком поновщик должен обновить в двоичном файле только одно выражение. Таблица ILT показана в листинге 19 1.
@ILT+0(_wmain): |
|
|
00401005 |
jmp |
wmain (401070h) |
@ILT+5(??_GCResString@@UAEPAXI@Z): |
||
0040100A |
jmp |
CResString::'scalar deleting destructor' (401B40h) |
@ILT+10(?ParseCommandLine@@YAHHQAPAGAAUtag_CMDOPTS@@@Z): |
||
0040100F |
jmp |
ParseCommandLine (401439h) |
@ILT+15(?ShowHelp@@YAXXZ): |
||
00401014 |
jmp |
ShowHelp (401644h) |
@ILT+20(??1CResString@@UAE@XZ): |
||
00401019 |
jmp |
CResString::~CResString (401A00h) |
@ILT+25(?LoadStringW@CResString@@QAEPBGI@Z): |
||
0040101E |
jmp |
CResString::LoadStringW (401A30h) |