Krótki wstęp
“Trwa uruchamianie systemu Windows”...”Czekaj”...”Zapraszamy”...mijają sekundy, minuty, aż po którymś razie, gdy chcesz tylko ‘szybko wysłać przelew’ zaczynasz się zastanawiać ‘co u licha ten system robi w tym czasie?’ i ‘dlaczego to tak długo trwa?’.
W następnych akapitach spróbuję przynajmniej częściowo odpowiedzieć na te pytania i podać kilka wskazówek pozwalających samodzielnie dojść do źródła problemu.
Fazy startu
W dokumencie “Windows On/Off Transition Performance Analysis”, który będziemy dalej traktować jako podstawową referencję Microsoft dzieli start systemu na kilka faz:
1. BIOSInitialization
Pierwsza faza startu - od włączenia komputera, poprzez inicjalizację urządzeń w ramach firmware, przez fazę POST, aż po załadowanie MBR, start Bootmgr.exe, załadowanie Winload.exe z partycji startowej.
2. OSLoader
Windows loader (Winload.exe) ładuje podstawowe sterowniki, które wymagane są do wczytania najważniejszych danych z dysku, w tym jądra, najważniejszych gałęzi rejestru (SYSTEM) oraz kluczowych sterowników, oznaczonych jako BOOT_START.
3. MainPathBoot
W czasie tej fazy, podzielonej dodatkowo na mniejsze następuje załadowanie wszystkich najważniejszych elementów systemu i kończy się ona wraz z pojawieniem się pulpitu.
3.1 PreSMSS (Pre Session Init)
Wraz z rozpoczęciem tej fazy pojawia się ekran “Trwa uruchamianie systemu Windows”. W tej fazie system inicjalizuje jądro wraz z podstawowymi strukturami danych, uruchamia menadżera PnP, który inicjalizuje sterowniki oznaczone jako BOOT_START, załadowane w poprzedniej fazie:
- Wykrywa urządzenie;
- Ładuje do pamięci sterownik i dokonuje walidacji sygnatury sterownika;
- Wykonuje funkcję DriverEntry (‘Main’ sterownika);
- Wysyła pakiety IRP_MN_START_DEVICE (uruchom urządzenie) oraz IRP_MN_QUERY_DEVICE_RELATIONS (wylicz urządzenia potomne).
Wszystkie te kroki powtarzane są dla każdego urządzenia potomnego, aż do przejścia przez całe drzewo urządzeń.
3.2 SMSSInit (Session Init)
W tej fazie następuje przekazanie sterowania do menadżera sesji (Smss.exe). Następuje inicjalizacja rejestru, załadowanie sterowników nie oznaczonych jako BOOT_START i uruchomienie procesów podsystemów. Koniec fazy następuje wraz z przekazaniem sterowania do Winlogon.exe
3.3 WinlogonInit (Winlogon Init)
Faza rozpoczyna się tuż przed pojawieniem się okna logowania i obejmuje start usług przez Service Control Manager (SCM), załadowanie skryptów Group Policy komputera oraz okna logowania. Faza kończy się wraz ze startem Explorer.exe.
3.4 ExplorerInit (Explorer Init)
Podczas tej fazy następuje załadowanie explorer.exe, procesu menadżera pulpitu (DWM), który tworzy i wyświetla okno pulpitu.
4. PostBoot (Post Boot)
Ta faza obejmuje wszelką aktywność komputera, która odbywa się w tle i ma miejsce po pojawieniu się pulpitu. A zatem w tej fazie pojawiają się w zasobniku wszelkie ikonki uruchamianych w autostarcie aplikacji, następuje dalszy ciąg startu usług systemowych, etc. Przyjęło się, że koniec tej fazy następuje w momencie, gdy komputer jest względnie bezczynny. Aby precyzyjnie określić ten moment, próbkuje się system co 100ms i jeśli system jest bezczynny w >= 80% (pomija się aktywność o niskim priorytecie procesora oraz dysku, a więc przede wszystkim jest to aktywność związana z funkcją nt!KiIdleLoop) i taki stan utrzymuje się przez 10 kolejnych sekund (a więc przez 100 następujących po sobie próbek), to określa to koniec tej fazy. Tu uwaga: w logach faza PostBoot kończy się w tym momencie, co oznacza, że faktycznie koniec nastąpił 10 sekund wcześniej i po prostu należy te 10 sekund odjąć.
Gdy już mamy przyjęte podstawowe definicje i posługujemy się wspólnym językiem, możemy przejść do warsztatu i zapoznać się z narzędziami.
Narzędzia
Microsoft dostarcza zestaw darmowych narzędzi analitycznych w postaci pakietu Windows Performance Toolkit (WPT), który jest częścią większego Windows Assessment and Deployment Kit (ADK) i będziemy się na nich opierać przy dalszych zabawach. Jako podstawę do przygotowania pierwszego logu przyjmijmy następujące polecenie:
>xbootmgr -trace boot -traceflags latency+dispatcher -stackwalk profile+cswitch+readythread -notraceflagsinfilename -postbootdelay 180 -resultpath c:\xperf
a do następnych:
>xbootmgr -trace boot -traceflags latency+dispatcher -stackwalk profile+cswitch+readythread -notraceflagsinfilename -postbootdelay 180 -resultpath c:\xperf -prepsystem
W tym drugim przypadku następuje 6 restartów, po których odbywa się właściwe przygotowanie logu.
Uwaga! Pliki .etl są względnie duże (przeciętnie setki megabajtów), więc w przypadku małej ilości wolnego miejsca na dysku mogą pojawić się problemy.
Gdy mamy już wygenerowane logi, do ich analizy będziemy wykorzystywać Xperf Viewer (xperfview.exe) oraz Windows Performance Analyzer (wpa.exe), oczywiście oba pochodzą ze wspomnianego pakietu WPT.
Przykład 1. Windows XP
Jakkolwiek Windows XP nie jest oficjalnie wspierany przez narzędzia ADK, to do wykonania testu możemy pobrać wcześniejszą wersję WPT, wypakować zawartość i przygotować log. Samą analizę tak przygotowanego logu będziemy musieli zrobić na Viście, lub jakimś nowszym systemie, ale jest to wykonalne.
Korzystając z polecenia
>xperf -i boot_1.etl -o summary.xml -boot
możemy przygotować plik zawierający podsumowanie startu, przy czym nas będzie interesować w szczególności sekcja <timing>
![]()
Rys 1. Summary.xml - podsumowanie startu Windows XP
Z łatwością odnajdziemy w niej odpowiedniki wyżej opisanych faz, a także atrybut bootDoneViaPostBoot, w którym zapisana jest długość startu (+10 sek). Po rozpisaniu odpowiednich faz otrzymamy:
Nazwa | start(ms)|koniec(ms)| suma(ms)
-------------------------------------------------
Pre Session Init| 0| 4005| 4005
Session Init | 4005| 4179| 174
Winlogon Init | 4179| 8063| 3883
Explorer Init | 8063| 8243| 179
Post Boot | 8243| 20443| 12200
-------------------------------------------------
System gotowy po: 10,443 sek
Możemy przygotować sobie inne zestawienia, korzystając z innych poleceń, których listę znajdziemy:
przy czym
>xperf -i boot_1.etl -o all.txt -a dumper
zrzuci nam wszystkie zdarzenia do pliku tekstowego.
Niestety, w przypadku Windows XP sporej części informacji nie znajdziemy w pliku .etl i dlatego w następnych przykładach przechodzimy do nowszych systemów.
Przykład 2 Jedna usługa opóźnia start innych usług
Dalsze analizy będziemy przeprowadzali w środowisku graficznym. Mamy do dyspozycji dwa narzędzia: xperfview (Xperf Viewer) oraz nowszy wpa (Windows Performance Analyzer). Będę je stosował przemiennie.
Na samym początku wszelkich analiz spoglądamy na podsumowanie startu. Tabelka z przykładu pierwszego przedstawia się (inny przypadek, inne czasy) graficznie tak, jak to widać na kolejnym zrzucie.
![]()
Rys 2. Fazy startu - graficznie
W tym przypadku faza PostBoot trwa o wiele dłużej, niż pozostałe i jakkolwiek pulpit pojawił się w ciągu pierwszej minuty, to jednak pracę można było sensownie rozpocząć dopiero mniej-więcej w połowie piątej minuty od startu systemu.
Kolejny wykres, na który warto spojrzeć przedstawia start usług. Nierzadko zdarza się tak, że któraś z usług startuje dłużej, niż powinna i wówczas usługi należące do kolejnych grup muszą czekać na swoją kolejkę.
![]()
Rys 3. Długi start usługi TuneUpSvc
W przykładzie z rysunku usługa TuneUp UtilitiesSvc startuje ok 20 sekund, wstrzymując start innych usług należących do następnych grup. Zauważmy, że wszystkie te usługi należą do grupy <AutoStart>, o tyle istotnej, że zakończenie tej fazy wyznacza 120-sekundowe oczekiwanie na start usług należących do grupy “Automatycznie (opóźnione uruchomienie)”. Usługi te możemy znaleźć dalej na wykresie.
![]()
Rys 4. Usługi o opóźnionym starcie
Przy okazji krótka ściąga: zielony - ‘Start Pending’ - usługa jest tworzona i przypisywana do kontenera, seledynowy - ‘Start Pending’ - już w danym kontenerze, pomarańczowy - ‘SvcMain Enter’, czerwony - ‘Running’ - oznacza koniec startu, ‘SvcExit’ nie jest oznaczane na wykresie.
Wykresy wykresami, ale mamy również dostępne tabelki ze szczegółowymi danymi dotyczącymi kolejnych zdarzeń usług.
W przypadku xperfview mamy je dostępne pod prawą myszą (“Summary table” i “Events table”), w WPA mamy możliwość dynamicznego oglądania tabelki razem z wykresem (na pewno warto zwrócić tu uwagę na przyjaźniejsze zarządzanie kolumnami).
![]()
Rys 5. WPA - wykres & tabelka w jednym miejscu
Aby włączyć równoczesny podgląd wykresu i tabelki klikamy w odpowiednią ikonkę (wskazana strzałką na powyższym rysunku).
Przykład 3. Dysk
Bardzo często wąskim gardłem okazuje się dysk. Aby szybko zorientować się, że badany przez nas przypadek należy do tej kategorii, wystarczy rzucić okiem na wykres ‘Disk Utilization’
![]()
Rys 6. Disk utilization
W skrajnych przypadkach będzie to wyglądało tak, jak na rysunku powyżej. Przez 4 minuty dysk obciążony jest praktycznie w 100%. Gdy zajrzymy do ‘Detail Graph’, to zobaczymy w szczegółach co też ciekawego działo się na dyskach fizycznych.
![]()
Rys 7. Disk utilization - Detail Graph
Jak widać na rysunku mamy pełną informację o modelu dysku, podziale na partycje, a dla każdego zdarzenia szczegółowe informacje o pliku, typie dostępu, odpowiednich czasach, etc. Wygląda na to, że partycja C była cała przeczesana.
No tak, ale co konkretnie było czytane i przez jaki proces? Odpowiedź na to pytanie znajdziemy na wykresie ‘Disk Utilization by Process’, sięgając do podręcznego ‘Summary Table’.
![]()
Rys 8. Disk utilization - Summary Table
Jak widać, w naszym przypadku najdłuższy service time zaserwował nam process svchost o PIDzie=1108, przy czym po rozwinięciu ‘+’ po najdłuższej ścieżce dojdziemy szybko do
![]()
Rys 9. Disk utilization - szczegóły
gdzie okazuje się, że mamy całą masę odczytów z c:\Windows\system32\DriverStore\FileRepository.
Mamy do czynienia z kontenerem usług, więc chcielibyśmy wiedzieć coś więcej odnośnie procesu o PID=1108. Nic prostszego - zaglądamy do ‘System Configuration’, gdzie szybko znajdujemy listę usług w ramach svchost(1108).
![]()
Rys 10. System configuration - usługi
Odpowiedź na pytanie ‘która konkretnie usługa odpowiada za takie obciążenie?’ zostawimy na później, teraz przechodzimy do przypadku kolejnego.
Przykład 4. Prefetcher
Nierzadko w parze z dużym obciążeniem dysku idzie martwy prefetcher. Aby mieć pewność, że to nasz przypadek, zaglądamy do wykresu ‘ReadyBoot I/O’ i patrzymy czy jest bardziej zielono, czy czarno. Czerń oznacza ‘misses’, czyli nietrafienia w kesz, natomiast zieleń oznacza, iż system otrzymał dane z kesza.
Weźmy przykład komputera z praktycznie nieaktywną usługą Sysmain
![]()
Rys 11. Wykres ReadyBoot I/O w zestawieniu z Disk Utilization
i teraz przełączmy zbędną usługę na start ręczny i wytrenujmy prefetcher
![]()
Rys 12. Prefetcher po wytrenowaniu
Różnica widoczna gołym okiem, a całkowity czas startu spadł z ponad 224 do 59 sekund.
Przykład 5. Sterowniki
Aby zorientować się, co dzieje się ze sterownikami, musimy zajrzeć do wykresu DPC CPU Usage. Czasem bowiem trafimy na przypadek taki, jak na rysunku
![]()
Rys 13. DPC CPU Usage - sterowniki
Gdy teraz powiększymy interesujący nas obszar i przejdziemy do ‘Summary Table’, to bez problemu dowiemy się, w kodzie którego sterownika spędziliśmy najwięcej czasu.
![]()
Rys 14. DPC CPU Usage Summary - duża aktywność usbport.sys
Wydaje się, że tragedii nie ma - przecież mówimy o max 15% obciążeniu CPU. Tak, jednak w systemie mamy 6-rdzeniowy procesor (do sprawdzenia w zakładce PnP ‘System Configuration’), więc w sumie mamy sytuację, gdzie jeden rdzeń jest w 100% obciążony obsługą usbport.sys. W powyższym przypadku okaże się, że po USB podłączony był 1TB dysk zewnętrzny i system zmarnotrawił morze czasu na jego inicjalizację.
Przykład 6. Symbole
Jak dla mnie najciekawszy element związany z analizą startu. O symbolach pisałem na blogu wielokrotnie, w przypadku xperf mamy jednak kilka nowości.
- poza _NT_SYMBOL_PATH ustawionym na serwer symboli Microsoft dobrze jest mieć zdefiniowaną zmienną określającą ‘prywatny kesz’ xperf, co znacząco przyspieszy ładowanie symboli. Ustawia się go poprzez zmienną środowiskową _NT_SYMCACHE_PATH, np:
_NT_SYMBOL_PATH=srv*C:\websymbols*http://msdl.microsoft.com/downloads/symbols
_NT_SYMCACHE_PATH=c:\symcache
- symbole można przygotować ‘ręcznie’ poleceniem:
>xperf -i boot_1.etl -symbols -a symcache -build
- WPA ma znacznie lepszą informacyjność odnośnie pobierania symboli, xperfview najwyraźniej wykorzystuje główny wątek do obsługi symboli i po prostu ‘zawisa’ podczas ich przetwarzania.
- aby uniknąć stronicowania kodu sterowników, które w przypadku systemów 64-bit mogą być niezbędne do odtworzenia stosu wywołań, należy ustawić w rejestrze dla obsługi pamięci Session Managera wartość DisablePagingExecutive na 1, co najszybciej możemy osiągnąć poleceniem:
>reg add "HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management" -v DisablePagingExecutive -d 0x1 -t REG_DWORD -f
Po zakończeniu analiz możemy wyłączyć tę opcję, ustawiając domyślną (dla Windows 7) wartość 0. Oczywiście jeśli interesuje nas stos wywołań dla kodu pracującego w trybie użytkownika, to nie musimy ustawiać tej wartości - ma ona znaczenie wtedy, gdy chcemy zajrzeć głębiej w trybie jądra.
- call stack nie jest dostępny w przypadku Windows XP. Po prostu całkowity brak takiej funkcjonalności :(
Po załadowaniu pliku .etl dobrze jest od razu skorzystać z podręcznego ‘Load Symbols’. Trochę to potrwa, jednak później nie powinniśmy tego żałować.
Wróćmy zatem do przykładu 3 i problemów z dyskiem, a dokładniej jednej z usług, która wczytywała mnóstwo informacji o sterownikach. Zaglądamy do wykresu ‘CPU Sampling by Thread’, ładujemy symbole (potwierdzamy przy tym standardowy komunikat licencyjny)
![]()
Rys 15. Licencja Microsoft - Symbol Server
i przechodzimy do ‘Summary Table’
![]()
Rys 16. Stos wywołań usługi iphlpsvc
Bez problemu znajdujemy interesujący nas proces (svchost.exe (1108)) oraz jego najaktywniejszy wątek (TID=3200). Rozwijamy stos idąc ścieżką o największej wadze i po kilku chwilach dochodzimy do interesującej nas usługi (Pomocnik IP). Interfejs isatap i w ogóle obsługa IPv6 w przypadku laptopa pracującego w domu nie jest konieczna, wystarczy więc proste
>netsh int isa set state disabled
i po kłopocie, a dokładniej - obcinamy prawie o połowę czas startu systemu.
Mam nadzieję, że te kilka przykładów daje obraz siły, jaką daje zestaw WPT. Nie sposób w jednej notce jest opisać wszystkie przypadki z jakimi można mieć do czynienia, ale obiecuję, że będę jeszcze wracał do tego tematu w przyszłych wpisach.