Обычно я запускаю большинство программ на своём ноутбуке с Windows 10, нажимая клавишу <Win>, после чего ввожу несколько букв имени программы, а затем жму Enter. На моём мощном ноутбуке (SSD и 32 ГБ ОЗУ) этот процесс обычно занимает лишь время, необходимое мне для ввода символов, то есть считанные доли секунды.

Обычно.

Однако иногда он занимает больше, намного больше. Порядка десятков секунд. Торможения непредсказуемы, но недавно мне удалось записать трассировку Event Tracing for Windows (ETW) одной из таких задержек. При помощи людей в Twitter я смог проанализировать трассировку и понять, почему на запуск «Блокнота» требуется примерно минута.

Прежде чем приступать к описанию анализа, мне нужно сделать два заявления: 1) у меня есть достаточное понимание проблемы, но нет решения, и 2) если вы наблюдаете аналогичные симптомы, это не значит, что их причина та же, но я дам советы о том, как понять, в ней ли дело.

Мой анализ трассировки (трассировка выложена здесь, установщик инструментов анализа находится здесь, туториалы по анализу — тут, можете повторять за моими действиями) начался с изучения событий ввода и графика Window in Focus в Windows Performance Analyzer (WPA); и то, и другое показано ниже (для максимальных подробностей изображение увеличено):


Первый самородок в строке Multi-Input появился, когда я нажал клавишу Windows, а вскоре после этого были сгруппированы вместе несколько последовательных нажатий клавиш (в том числе и нажатие на Enter). На оси X отложены секунды, то есть мы видим, что весь ввод занял примерно 3/4 секунды.

События ввода инъецируются в трассировку моим инструментом записи трассировок UIforETW. Эти события ввода — одна из причин, по которым я предпочитаю UIforETW инструментам записи трассировок Microsoft. Я скрыл табличный вид, однако в нём перечисляются нажатые клавиши, в то же время анонимизируя буквы и цифры, чтобы UIforETW не мог работать кейлоггером. События ввода могут быть критически важным инструментом при разборе того, что и когда смотреть в трассировках, чтобы понять происходящее.

События ввода помогают установить контекст, однако главную информацию дают события Window in Focus. Мы видим, что SearchApp (меню «Пуск»?) получает фокус сразу же после нажатия на клавишу <Win>, однако тринадцать с лишним секунд больше ничего не происходит. Это и есть наглядное проявление проблемы.

Но почему?


Дальше нам нужно посмотреть, что вызывает задержку. Изучение графиков CPU Usage (Precise) и Disk Usage показало нам, что CPU и диск простаивают почти на 100%, то есть меню «Пуск», должно быть, ожидает чего-то другого:


Если процесс простаивает, когда он должен выполнять работу, то наша задача разобраться, чего же он ждёт. Я изучил события переключения контекста в CPU Usage (Precise). Некоторые из потоков SearchApp имели имена (ура!), но не все, и я не мог найти основной поток, чтобы увидеть, чего же он ждёт, поэтому я просто тыкался в надежде, что ситуация немного прояснится. Я увеличил отображение графика скачка активности CPU перед непосредственным запуском «Блокнота» и заметил, что WerFault.exe и wermgr.exe стали очень загруженными. «После» не значит «вследствие», но это определённо подозрительно.

WER расшифровывается как Windows Error Reporting — это система, отправляющая аварийные дампы в Microsoft для анализа с целью повышения надёжности ПО.

Изучение таблицы Processes показало мне, что командная строка для WerFault.exe имела вид C:\WINDOWS\system32\WerFault.exe -u -p 17804 -s 2124. Это даёт нам понять, что Windows Error Reporting должна была записать информацию о вылетевшем процессе 17804. Когда я поискал в таблице Processes этот Process ID (PID), то нашёл RuntimeBroker.exe <Microsoft.Windows.Search> (17804). Хм, похоже, это имя относится к делу.

Изучение всех «переходных» («Transient») процессов (тех, которые запускались или завершались во временном интервале трассировки) открыло мне глаза:


WerFault.exe и RuntimeBroker.exe (17804) (два верхних процесса RuntimeBroker.exe) работали, когда я начал запись трассировки, и завершились примерно одновременно, а WerFault.exe обрабатывал вылет RuntimeBroker.exe. Обратите также внимание, что после закрытия старой копии RuntimeBroker.exe запускается новая. Итак, у нас начинает появляться объяснение:

  1. RuntimeBroker.exe аварийно завершается
  2. WerFault.exe обрабатывает сбой, сохраняя процесс RuntimeBroker.exe открытым
  3. Затем запускается новый RuntimeBroker.exe и предоставляет то, что нужно SearchApp.exe

Теперь у нас появился новый вопрос: почему WerFault.exe простаивает так долго?

Я посмотрел на данные CPU Usage (Precise) и увидел, что WerFault.exe имеет не менее тринадцати потоков, но ни у одного из них нет имени (да ладно, Microsoft, имена потоков очень полезны!), однако основной поток найти легко, именно он потребляет больше всего времени CPU. Затем я выполнил сортировку по Time Since Last и заметил, что в один момент основной поток ждал 15,572 с, чтобы заработать. На самом деле, он, возможно, ждал ещё дольше, но начало его ожидания было ещё до начала трассировки. Подробнее прочитать о том, как выполнять анализ простоев, можно здесь.

Ниже показан стек, в котором основной поток WerFault.exe ожидал в течение 15,572 с:


В итоге оказалось, что ожидание происходило внутри UploadReport.


Итак, теперь мы разобрались в проблеме. RuntimeBroker.exe аварийно завершился (согласно показанному выше стеку вызовов аварийного дампа RuntimeBroker.exe, из-за повреждения кучи), и для загрузки на сервер аварийного дампа потребовалось более 15 секунд; вероятно, из-за ненадёжного WiFi в моём отеле. В течение этого времени с моим меню «Пуск» невозможно было работать.

Это стоит повторить. Меню «Пуск» зависло из-за сочетания повреждения кучи и того, что WerFault.exe принял решение о необходимости загрузки на сервер аварийного дампа до освобождения старого процесса, а запуск нового процесса возможен только после этого.

Чтобы произошло это зависание, потребовались два бага (повреждение кучи и загрузка дампа перед перезапуском), но они возникли.


Мы можем двинуться ещё глубже. Функция UploadReport была заблокирована в течение 15,567 с, а в Readying Process/Readying Thread Id показано, кто же в конечном итоге разблокировал функцию. Оказалось, что это ещё один поток WerFault.exe, который, как показано ниже, был заблокирован в каких-то функциях. Это не сильно расширяет понимание проблемы, но демонстрирует, как можно оттрасировать зависание обратно сквозь множество процессов и потоков.

Выслеживаем эту проблему


В общем случае, если вы хотите понять, почему компьютер плохо работает, то нужно записать и проанализировать трассировку. Однако если вы хотите увидеть, действительно ли возникает конкретно эта проблема, то можно выполнить более простые действия.

Первым делом нужно сконфигурировать локальную запись аварийных дампов. Это в целом является хорошей идеей, потому что позволяет отслеживать стабильность компьютера в процессе вашей работы.

Тогда если после записи аварийного дампа вы увидите, что меню «Пуск» тормозит, вы можете просто заглянуть в %localappdata%\crashdumps и поискать, есть ли там недавний сбой RuntimeBroker.exe. Если есть, то, предположительно, вы наблюдаете именно этот баг.

Ожидание загрузки на сервер


Рэймонд Чен привёл множество причин того, почему Windows Error Reporting не перезапускает вылетевшие процессы до загрузки отчёта на сервер (в 2012 году), но мне эти причины не кажутся особо убедительными, особенно в случае меню «Пуск». Если убивать старый процесс перед запуском нового и отвечать на вопросы о версии DLL из аварийного дампа, то большинство приведённых Рэймондом проблем можно избежать. С остальным позволяет справиться экспоненциальная задержка при перезапусках процессов. И, как мы видели, последствием такого ожидания могут быть зависания меню «Пуск» произвольной длительности с полным отсутствием указания на то, что проблема была в сбое. Кроме того, есть некоторая путаница в связи с поведением; возможно, за последние десять лет архитектура изменилась.

Устраняем сбой



Баги повреждения кучи бывают очень сложными в поиске и устранении, но, похоже, с нашим всё может быть просто. Я включил pageheap для RuntimeBroker.exe, убил соответствующую версию, чтобы заставить его перезапуститься, и применил параметры pageheap, после чего он начал вылетать каждый раз, когда я открывал меню «Пуск». Я настроил WER так, чтобы она сохраняла полные аварийные дампы и вскоре у меня на руках было с полдесятка дампов с полной информацией о том, что происходит.

Вылеты обычно происходят в этом стеке вызовов:


При включенном pageheap вылет происходит в очень похожем стеке вызовов, но чуть раньше. Вылет происходит раньше (и надёжнее), потому что при освобождении памяти с запущенным pageheap она становится неотображаемой, поэтому её разыменование стабильно вызывает вылет, а не чтение из освобождённой памяти:


Вылет происходит при разыменовании [rcx], поэтому я пропустил его значение через команду !heap (подробности см. в моём посте про pageheap) и получил следующий стек вызовов:


Единственная сложность заключается в том, что это не происходит на всех машинах с Windows 10. Похоже, существует какое-то обязательное состояние, приводящее к этому, и я не знаю, какое оно. От себя добавлю, что с радостью поделюсь своими аварийными дампами с любым человеком из Microsoft, который захочет провести исследование.

Я не знаю кода и не понимаю, что происходит, но я имел дело с достаточным количеством багов use-after-free, чтобы сказать, что, скорее всего, при помощи аварийных дампов проблему легко выявить и устранить. Впрочем, у меня получилась пара разных стеков вызовов, так что багов может быть несколько.

Выводы


Свой изначальный пост в Twitter я завершил словами о том, что эти зависания меня бесили и я задавался вопросом, не превратилась ли Windows 10 в abandonware. Позже мне сказали, что у некоторых людей зависания меню «Пуск» происходят и в Windows 11, но это может быть и какая-то другая проблема.

Внесу ясность: у Microsoft есть технология для записи трассировок зависаний меню «Пуск» на клиентских машинах. В этих трассировках будет примерно то же, что и в моей. Также компания получает аварийные дампы с клиентских машин. Возможно, у компании даже есть способ установить между ними корреляцию (а если нет, такой способ стоит внедрить). И компания создала pageheap, упрощающий исследование вылетов use-after-free.

Почему же эту проблему не решили? На своём ноутбуке я вижу, что в среднем RuntimeBroker.exe в этом году вылетал через день. На мой взгляд, как-то слишком часто для зависания меню «Пуск». Я не знаю, как долго это происходит, возможно, исправление уже в пути; если это так, то я был бы рад это услышать. Если же не так, то меня продолжит это бесить; надеюсь, ситуация послужит хорошим напоминанием о важности использования всей этой сложной телеметрии для решения подобных проблем.

Или, возможно, мне просто не повезло, и я стал одним из тех немногих (но не единственным), кто столкнулся с этим сбоем.

Если вкратце, мне очень нравятся созданные и выпущенные Microsoft инструменты, позволяющие анализировать подобные проблемы с производительностью. Однако мне бы хотелось, чтобы не приходилось использовать их так часто для анализа самой Windows.

Комментарии (26)


  1. Komrus
    19.01.2023 10:35
    +13

    Интересно, а просто отключить отправку отчетов куда-то там этим "Windows Error Reporting" - нельзя ли?


  1. xBrowser
    19.01.2023 10:51
    +10

    Несколько секунд уходит чтобы подкрасить ячейку в excel. Мне даже страшно вспоминать, что иногда требуется нажать на ctrl+1, чтобы зайти в свойства ячейки только что созданного документа. Сейчас посчитал, 14 секунд. Лишний раз думаешь, поправить формат или как-нибудь в следующий раз, заодно с чем-то ещё.


    1. larasage
      20.01.2023 07:21

      LibreOffice Calc - аналогично. Но только в первый раз. Шуршит винт. Если на SSD (и система и LO) так же?


      1. xBrowser
        20.01.2023 11:49

        Да, SSD под ОС и ПО. Ничего не шуршит. Win7, MS Office 2010. Все обновы. i7 второго поколения, 16гб ОЗУ.


  1. chernish2
    19.01.2023 10:58
    +3

    А у меня в Libre office Calc (аналог Excel) на линуксе простой ввод числа в ячейку занимает несколько секунд, там как будто что-то отрисовывается после каждой цифры, выглядит крайне странно, и тоже дико бесит.


    1. perfect_genius
      20.01.2023 14:33

      что-то отрисовывается после каждой цифры

      Например, всё окно.


  1. v1000
    19.01.2023 11:55
    +16

    Напоминает шутку про то, что интел постоянно выпускает все более мощные процессоры, в то время как майкрософт постоянно замедляет виндовс, чтобы для пользователя скорость работы системы оставалась неизменной.


    1. perfect_genius
      20.01.2023 14:35
      +2

      Всё это, чтобы не возвращать кнопку Turbo на системном блоке.


  1. SystemXFiles
    19.01.2023 12:20
    +8

    Замечал зависание аналогичное, когда с помощью твикера отключил Cortana.
    Зависание пуска было в районе 1,5 секунды, что раздражало, ибо поиском в пуске регулярно пользуюсь.

    Как оказалось, при запуске пуска происходила инициализация Cortana, что приводило к падению и формированию отчета. Это занимало как раз 1,5 секунды где-то.

    Чудом смог найти способ вернуть в рабочее состояние Cortana обратно, ибо твикер как оказалось не может корректно откатить изменения.
    Для этого пришлось в SQL БД залазить (даже не знал, что у Windows она есть) и там править запись приложения Cortana.
    После восстановления Cortana зависание пропало.


    1. Mingun
      19.01.2023 18:21
      +1

      На скриншотах в конце, кстати, как раз от кортаны стек


  1. Rive
    19.01.2023 13:04
    +4

    Я однажды работал с внутрикорпоративным редактором кода, который вызывал удалённый сервер подсветки кода на каждое нажатие клавиши. Иногда сетка барахлила, и тогда работа с редактором становилась в высшей степени дискомфортной.


    1. cdriper
      19.01.2023 14:05
      +2

      очевидно, что такие штуки надо делать асинхронно


    1. vesper-bot
      19.01.2023 14:12
      +4

      На стрелочки тоже вызывал? Кто такое решение-то запилил, почему в редактор не воткнули? Банально на сетевых задержках можно погореть, не говоря уже о том, чтобы заддосить собственный сервер. Если знаете автора, как-нибудь объясните ему, что он был неправ.


      1. Rive
        19.01.2023 16:33
        +2

        Непосредственный автор был из другой компании и из другого времени, и я даже не знал его имени; а отвечающий за бэкоффис отдел был постоянно загружен другими задачами.


  1. speshuric
    19.01.2023 17:41
    +2

    Быстро перевели - статья буквально на днях была, читаю блог Брюса в оригинале, поэтому в курсе.


  1. Mingun
    19.01.2023 17:53
    +6

    Ясно-понятно. По первым строкам понял, что опять будет описание эпичного бага из-за какого-то рукожопа из Microsoft. Не прогадал.


  1. chnav
    19.01.2023 20:55
    +14

    24-ядерный CPU, а я не могу сдвинуть курсор (Bruce Dawson, 2017) https://habr.com/ru/post/332816/
    24-ядерный CPU, а я не могу набрать электронное письмо (Bruce Dawson, 2018) https://habr.com/ru/post/420579/
    Почему для открытия меню Windows читает один файл сто тысяч раз? (Bruce Dawson, 2019) https://habr.com/ru/post/466941/
    Почему Windows около 20 секунд упорядочивает невидимые значки Рабочего стола? (Bruce Dawson, 2021) https://habr.com/ru/company/vdsina/blog/544218/

    PS: "Никогда такого не было, и вот опять..." (c)


    1. baldr
      19.01.2023 22:17
      +1

      Вот я помню что были похожие статьи тут, но не смог найти! А как вы искали?


    1. kisskin
      19.01.2023 22:30
      +4

      Как то ему не везёт на тормоза винды, никогда не сталкивался ни с чем им описанным... Хотя может дело в том, что я до сих пор на 7-ке работаю...


  1. maxlilt
    19.01.2023 21:00
    +1

    В 9x винде такого безобразия не было. Другое было )


    1. strvv
      19.01.2023 22:36
      +2

      Тогда процессоры не были мощными и программистов, которые со вторым подходом к программированию убивали на собеседовании или если те этот метод использовали эпизодически - в пределах года.

      Калькулятор, ничего не делающий, зато с тенями и общающийся с создателем обо всём что происходит на компьютере в пределах его прав занимает больше чем даже windows 20 летней давности. Если не специально введенные различные kernelex - народ в большинстве сидеть на win2k/xp и не кряхтел. Дальше просто ужас, количество ресурсов на какое-либо нововведение уходит в экспоненциальном виде относительно предыдущего нововведения. Постоянно все эти особенности с друг-другом конфликтуют, утечки памяти...


      1. ZetaTetra
        20.01.2023 17:29
        +1

        Кряхтел ещё как, как минимум с перехода с 98 на NT/2000 был жуткий бугурт (В XP многое пофиксили). Да и до этого тоже было полно факапов. WinME для примера.


        1. Maks_K2
          21.01.2023 12:26
          +1

          XP и Win7 наиболее безглючные системы на моей памяти....


          1. strvv
            21.01.2023 23:37
            +1

            Старшие сервис-паки. Родная хр не присутствовала, сразу сменило на сп1, сп2 добавило глюков, почему сразу пошёл сп3.

            Как и на нт4 сп4..сп6, 2000 сп2..сп4. 3.51 слабо помню.


        1. SCHWEJK
          22.01.2023 00:48

          Линолеум у меня работал не хуже 98. Даже лучше, там спящий режим появился и на моем компе даже работал.


  1. SCHWEJK
    22.01.2023 00:56

    Пуск у меня тормозит ещё со времен превью Win8, на всех компах. Каких то надежд, что MS сделает нормально, уже давно нет.