За исключением интерфейса Aero Glass, одним из наиболее заметных нововведений Windows Vista является Windows Sidebar с ее набором стандартных гаджетов, среди которых сборщик RSS, гаджет погоды и часы. Однако, как оказалось, с гаджетами тоже случаются неприятности.
Возможность всегда иметь под рукой различную полезную информацию и простота разработки привели к появлению буквально тысяч гаджетов от сторонних разработчиков, как, например, Windows Vista Gadget Gallery. Из любопытства я выбрал несколько гаджетов и загрузил, при этом некоторые из них получили мой персональный статус must have. Однако, один из гаджетов от сторонних разработчиков — часы — в какой-то момент попросту перестал обновляться. Возможно, данная проблема имела место лишь на моем компьютере и не повториться ни у кого из вас, но, тем не менее, дабы избежать ее неожиданного появления я решил разобраться, в чем же дело.
В остальном моя система работала без нареканий, поэтому я решил в первую очередь проверить, может ли проблема быть в конфигурации Sidebar. Я щелкнул правой кнопкой на Sidebar и из появившегося меню выбрал Properties, но вместо ожидаемого диалога настроек Sidebar завершила свою работу, отобразив ошибку:
Гаджеты выполняются внутри процессов Sidebar, поэтому мое первое предположение заключалось в том, что повреждение памяти процесса Sidebar и вызвало остановку гаджета часов и последующее завершение работы. Служба Windows Error Reporting (WER) в случае, если вы соглашаетесь отправить информацию об ошибке в Microsoft, создает dump-файл, в котором сохраняется состояние сбойного процесса. Затем я щелкнул по кнопке View Details с целью посмотреть, где Windows сохранила dump-файл:
Последний адрес, который был показан WERD8EE.tmp.mdmp — это сам дамп, так что я запустил утилиту Windows Windbg из комплекта Microsoft Debugging Tools и открыл файл дампа. Когда вы открываете файл дампа, Windbg показывает вам инструкции, которые, в конечном счёте, привели к завершению процесса. В данном случае это была операция копирования в памяти в Msvcrt, исполнительной среде Microsoft C.
В правой части строки показано, что адрес, в который осуществляется попытка копирования, есть 0. Когда ресурсы памяти исчерпаны, функции распределения памяти, как правило, возвращают 0, также известный как NULL, поскольку по умолчанию это неправильный адрес для процессов Windows (приложение может самостоятельно проводить чтение/запись по адресу 0, но обычно этого не происходит). Факт того, что Sidebar ссылается на адрес 0 вовсе не означает, что сбой вызван недостаточным количеством памяти, однако, такая причина вероятна.
Затем я обратил свой взор на код, приведший к завершению работы приложения, что должно было указать мне, гаджет ли вызвал сбой или Sidebar сама по себе. Для того, чтобы узнать наверняка, я открыл диалог Windbg
Ранее я настроил символьный путь Windbg с привязкой на Microsoft symbol server так, чтобы Windbg указывала имена внутренних функций в образах Windows, поскольку знание имени функции существенно облегчает понимание dump-файла. Функции, перечисленные в окне утилиты, показали, что перед завершением работы Sidebar обратилась за именем “пакета”. Не уверен, что Sidebar зовет пакетом, но лог показывал, что причина кроется в самой Sidebar, а не гаджете.
Получается, что Sidebar исчерпал всю память? Есть несколько типов истощения ресурсов, которые могут вызвать неприятности в процессе распределения памяти. Например, система исчерпала всю предоставленную память, процесс использовал всю доступную память в своём адресном пространстве или внутренняя динамическая память достигла своего предела.
Я начал проверять память, так как это самая быстрая проверка. Общее количество доступной памяти, также известное как, доступный лимит — это сумма размера файла подкачки и большей части физической памяти. Когда количество доступной памяти становится низким, инструкция Windows, оберегающая систему от исчерпывания ресурсов, предупреждает вас об этом, предоставляя список процессов, которые потребляют больше всего ресурсов и предоставляет возможность их завершить, чтобы уменьшить потребление памяти. У меня не было такого предупреждения, так что я сомневаюсь, что причина была в этом, но всё равно для проверки я открыл диалоговое окно информации о системе в Process Explorer.
Как я и ожидал, количество доступной памяти было достаточно большим. Следующее, на что я посмотрел — использование виртуальной памяти Sidebar. Утечки случаются, когда процесс распределяет виртуальную память, хранит в ней какие-то данные, использует их, но не освобождает память, когда закончил работать с данными. Виртуальная память, которую распределяют процессы для хранения в ней своих данных, называется Private Bytes, так что я открыл Process Explorer и добавил Private Bytes.
В 32-битной версии Windows по умолчанию процессам доступно около 2 Гб адресного пространства, так что максимальное значение Private Bytes тоже находится где-то около 2 Гб, где, собственно, и находится процесс Sidebar, с идентификатором 4680. Моя теория подтвердилась: утечка памяти в Sidebar вызвала исчерпание памяти в адресном пространстве, что вызвало сбой в распределении памяти, что, в свою очередь, привело к ссылке на нулевой и завершение работы. Я подозреваю, что гаджет остановился, когда Sidebar исчерпал доступное адресное пространство, и гаджет часов не смог распределить адресное пространство, чтобы обновить графику.
Следующее, что мне необходимо было определить — какой гаджет вызвал утечку, что могло вызвать, а могло и не вызвать, остановку гаджета часов. Sidebar состоит из двух процессов — один процесс Sidebar.exe, который является хостом для гаджетов от Microsoft, и один Sidebar.exe, который является дочерним первого процесса и служит для сторонних гаджетов. Итак, я узнал, что сторонний гаджет вызвал утечку памяти, но у меня было несколько сторонних гаджетов, и я не знал какой из них винить. К сожалению, Sidebar не предоставляет никаких способов отслеживания использования гаджетами памяти или, в данном случае, какого-либо другого ресурса, так что мне пришлось самому предпринять несколько шагов, чтобы изолировать утечку.
После перезапуска Sidebar я удалил все сторонние гаджеты, добавляя их по одному за раз и оставляя их работать минуту-две, пока я следил за использованием Private Bytes. Я добавил в Process колонку Private Bytes Delta, чтобы было легче определять уровень роста использования, и после добавления одного гаджета я заметил периодические позитивные значения Private Bytes Delta, вызывавшие утечку.
Теперь, когда я знал, какой гаджет виноват, я мог просто его удалить, и считать дело закрытым. Но мне было интересно, как гаджет мог вызвать утечку в Sidebar, тем более что она сохранилась даже после удаления гаджета.
Я зашёл в папку установленных гаджетов, и открыл его HTML-файл, чтобы увидеть, что там творится. Гаджет состоит примерно из 4 десятков строчек довольно простого JavaScript-кода и в нём ошибок я не нашёл. Чтобы сконцентрироваться на неправильном коде, я начал комментировать его части и повторно добавлять гаджет в Sidebar, пока утечка не исчезла. Код, который у меня остался, был функцией гаджета, настроенной на выполнение каждые 10 секунд, чтобы обновить графику. Она вызывала фоновый метод объекта в Sidebar — RemoveObjects, а потом снова добавляла графику и текст, вызывая фоновый метод AddImageObject. Вот упрощённая версия релевантного кода.
Факт того, что функция использовала данные API правильно, свидетельствует о том, что утечка была в коде Sidebar, но быстрый поиск в интернете не выявил никаких упоминаний об утечке в фоновых объектах. Если в API Sidebar есть утечка, то почему она не является широко известной? Я просканировал исходный код других гаджетов в моей системе и обнаружил, что не один из них не использовал данный API, что объясняет, почему данные об этой утечке не попадаются. Однако комментарии в Windows Gadget Gallery к данному гаджету, который неосторожно вызвал утечку, показали, что другие пользователи её также заметили.
Проследив за от первоначальной невосприимчивой проблеме в гаджете до API с утечкой, я зарегистрировал проблему в базе данных Windows и закрыл дело.
Источник: http://blogs.technet.com/markrussinovich
Tags: SQL, tun, Windows Vista