28/12/2009

BadImageFormatException i TFS

Home

Przed paroma dniami zainstalowałem na swoim komputerze najnowszą wersję serwera TFS czyli Microsoft Visual Studio Team Foundation Server 2010 Beta 2. Instalacja przebiegła szybko i bezproblemowo z dokładnością do tego, że wcześniej musiałem jeszcze zainstalować SQL Server 2008 oraz, co trochę dziwne dla mnie, najnowszy Service Pack dla systemu Windows Vista.

Kiedy instalacja dobiegła końca uruchomiłem konsolę Team Foundation Administration Console, dalej użyłem kreatora konfiguracji i pomyślałem, że będę mógł się cieszyć nową zabawką. Niestety próba podłączenia się do serwera, czy to z poziomu VS 2010 czy to z poziomu programu Microsoft Test and Lab Manager, kończyła się błędem Unable to connect to Team Foundation Server.

Próbując rozwiązać problem dotarłem do systemowego logu zdarzeń. Tam znalazłem kilka podejrzanie wyglądających zdarzeń, których źródło nazywało się TFS Services. Aby upewnić się co do ich pochodzenia zatrzymałem pulę aplikacyjną Microsoft Team Foundation Server Application Pool, do której należą usługi TFS. Odczekałem chwilę i ponownie ją uruchomiłem. Zaglądam ponownie do logu zdarzeń i znajduję serię 3 komunikatów, których czas wystąpienia zgadzał się z czasem uruchomienia puli aplikacyjnej. Dwa pierwsze komunikaty miały źródło o nazwie TFS Services, a trzeci ASP.NET 4.0.21006.0. W trzecim komunikacie znalazłem interesującą informację o wystąpieniu nieobsłużonego wyjątku BadImageFormatException.

Jeszcze chwila szukania i znajduję rozwiązanie. Okazało się, że błąd był spowodowany nieprawidłową konfiguracja wspomnianej puli aplikacyjnej Microsoft Team Foundation Server Application Pool. Miała ona włączoną opcję Włącz aplikacje 32 bitowe podczas gdy ja korzystam z wersji 64 bitowej. Po wyłączeniu tej flagi problem ustąpił i wszystko zaczęło działać.

23/12/2009

Życzenie świąteczne

Home

Wszystkim czytelnikom i czytelniczkom składam życzenia wesołych i radosnych Świąt Bożego Narodzenia.

Serdecznie pozdrawiam
Michał Komorowski

18/12/2009

"Dzikie żądze"

Home

W pracy komputer, na studiach doktoranckich komputer... Nie ulega wątpliwości, że dużą część dnia spędzam wpatrzony w monitor. Dlatego tak bardzo lubię wyjść z żoną na miasto, a jedną z naszych ulubionych rozrywek jest dobra komedia w teatrze.

Przedwczoraj odwiedziliśmy ponownie teatr "Bajka" na deskach, którego można teraz oglądać brytyjską sztukę "Dzikie żądze". Opowiada ona o małżeństwie Państwa Griffin. On bogaty przemysłowiec, ona lekko znudzona. Aby ratować małżeństwo postanawiają na nowo rozbudzić w sobie żądzę... Więcej z fabuły nie zdradzę, dodam tylko, że jest nieprzewidywalna, pełna zwrotów akcji i po prostu przezabawna. Przez większość przedstawienia nie robiłem nic innego tylko się śmiałem. W pewnym momencie nawet aktorzy nie mogli się powstrzymać od śmiechu. Byłbym jednak niesprawiedliwy gdybym nie powiedział, że bardzo szybko się powstrzymali.

Zaskakujące było też zakończenie wieczoru. Jeden z aktorów zaprosił na scenę młodą parę, która wygrała bilety w konkursie radiowym żeby zrobić sobie z nimi zdjęcie. Jak się okazało był to tylko "niecny" wybieg. Gdzieś znalazły się kwiaty i za chwilę chłopak na kolanach prosił swoją wybrankę o rękę :) Brawo za odwagę!

W teatrze "Bajka" po raz kolejny spędziłem wspaniały wieczór i z czystym sumieniem mogę go polecić każdemu. Teatr naprawdę nie gryzie.

11/12/2009

Ciekawe zgłoszenie błędu

Home

Jest to druga, poprawiona wersja tego postu. Za wcześniejsze pomyłki przepraszam.

W poście tym chciałbym opisać interesujący błąd. Wszystko zaczęło się od zgłoszenia od klienta dotyczącego problemów z wydrukami. Nie wchodząc w szczegóły okazało się, że cały problem sprowadza się do utworzenia dostatecznie dużej bitmapy. Co jednak ciekawe analiza pokazała, że system dysponuje znaczną ilością wolnej pamięci (ok 1.5 GB) podczas gdy do utworzenia bitmapy potrzebne było "raptem" kilkaset megabajtów. Tutaj dodam, że mówimy o systemie 32 bitowym.

Z pomocą przyszedł tutaj program vnmap, który służy do analizy pamięci wirtualnej i fizycznej procesu. Pokazał on, że proces rzeczywiście dysponuje znaczną ilością pamięci ale największy ciągły jej obszar to tylko 200 MB. Do zaalokowania bitmapy potrzeba natomiast właśnie ciągłego obszaru pamięci. Nie dotyczy to zresztą tylko bitmap, podobny problem możemy wystąpić przy ładowaniu bibliotek dll.

Taką sytuację nazywamy defragmentacją pamięci. Co było jej przyczyną? Zgodnie z tym co pokazał wspomniany program vnamp pamięć w dużym stopniu była poszatkowana przez biblioteki dynamiczne. Nie bez znaczenia jest tutaj fakt, że rozpatrywany przypadek dotyczył dość dużego systemu zbudowanego z wielu modułów.

Problem próbowałem zaleczyć przy użyciu narzędzia rebase.exe, które służy do ustawienia preferowanego adresu pod jaki ma zostać załadowana dll'ka. Testy niestety pokazały, że to nic nie daje.

Pytanie co jest przyczyną takiego położenia bibliotek w pamięci? Tutaj nie pozostaje mi nic innego jak rozłożyć ręce. Wcześniej byłem przekonany, że jest to związane z mechanizmem bezpieczeństwa, który losowo rozrzuca biblioteki po pamięci. Zwrócono mi jednak uwagę, że taki mechanizm (ASLR) pojawił się dopiero w Windows Vista. Sprawa jest więc otwarta. Jakieś pomysły?

Jak sobie z tym poradzić? Generalnie jednoznacznej odpowiedzi nie ma, ja znam trzy podejścia. Po pierwsze przejście na system 64 bitowy rozwiąże problem ale nie jest to zawsze możliwe. Po drugie można próbować wyeliminować konieczność alokacji tak dużej bitmapy ale może to być bardzo trudne. Można też użyć przełącznika /3GB, który pozwala procesom użytkownika użyć 3 GB pamięci wirtualnej zamiast domyślnych 2 GB ale nie jest to zalecane rozwiązanie.

Na zakończenie chciałbym podziękować koledze Damianowi z pracy, który analizował zgłoszenie klienta i podsunął mi pomysł na ten post.

30/11/2009

Uwierzytelnienie, ASP.NET i IIS

Home

Post ten postanowiłem napisać kiedy zorientowałem się, że kilku kolegów poświęciło sporo czasu aby rozwiązać problem z uwierzytelnieniem usługi sieciowej zainstalowanej na IIS'ie podczas gdy nie posiadali dokładnej wiedzy jak ten mechanizm działa. Problem udało się rozwiązać ale jestem przekonany, że można to było zrobić mniejszym nakładem czasu. Na początku zaznaczę również, że wpis ten dotyczy IIS w wersjach wcześniejszych niż IIS 7.

Jak wszyscy wiemy aplikacje ASP.NET i usługi sieciowe (ang. Web Services) hostowane są na serwerze IIS. Do swojego działania wymagają one dostępu do różnych zasobów. Aby możliwe było określenie czy aplikacja lub usługa posiadają uprawnienie do jakiegoś zasobu muszą one posiadać jakąś tożsamość czyli być uwierzytelnione (ang. authentication). Powstaje pytanie jaką tożsamość posiada aplikacja ASP.NET lub usługa sieciowa?

Na początek przyjrzyjmy się poniższemu scenariuszowi:
  • Użytkownik uruchamia przeglądarkę internetową i wpisuje adres aplikacji.
  • Serwer IIS otrzymuje żądanie i w zależności od ustawień żąda lub nie uwierzytelnienia.
  • Serwer IIS sprawdza czy użytkownik uwierzytelniony (bądź anonimowy) może uzyskać dostęp do żądanego zasobu.
  • Żądanie przekazywane jest do obsługi do silnika ASP.NET.
  • Aplikacja w zależności od ustawień ponownie żąda lub nie uwierzytelnienia się użytkownika.
  • Aplikacja obsługuje żądanie w razie potrzeby legitymując się pewną tożsamością.

Poziom IIS

Na poziomie IIS domyślnie włączone jest uwierzytelnienie anonimowe czyli innymi słowy brak uwierzytelnienia. W takim wypadku zgodnie z domyślnymi ustawieniami przyjmuje się, że użytkownik uwierzytelnił się jako IUSR_machinename. Dostępne są oczywiście inne tryby uwierzytelnienia np.: uwierzytelnienie zintegrowane z Windows (ang. Integrated Windows Authentication) czy uwierzytelnienia podstawowe (ang. Basic) gdzie hasło przekazywane jest czystym tekstem.

Uwierzytelnienia na poziomie IIS jest potrzebne aby stwierdzić czy użytkownik może żądać danego zasobu znajdującego się na serwerze. Zasobem tym może być aplikacja ASP.NET ale również inne rzeczy np.: obrazki, archiwa itd. Uwierzytelnienia na tym poziomie konfigurowne jest przy pomocy aplikacji administratora IIS: Control Panel -> Administrative Tools -> Internet Information Services.

Poziom silnika ASP.NET

Następnie mamy uwierzytelnienia na poziomie silnika ASP.NET. W tym przypadku można włączyć uwierzytelnienie anonimowe, zaimplementować cały mechanizm samemu czy użyć uwierzytelnienia Windows. Należy jednak zaznaczyć, że nie należy mylić uwierzytelnienia Windows na poziomie ASP.NET z uwierzytelnienia zintegrowanym z Windows na poziomie IIS.

Włączenie uwierzytelnienia Windows na poziomie ASP.NET oznacza, że silnik ASP.NET będzie widział użytkownika pod tożsamością wyznaczoną przez IIS. Innymi słowy IIS uwierzytelni użytkownika i przekaże jego tożsamość do ASP.NET. Metoda jaka zostanie użyta przez IIS do uwierzytelnienia zależy od ustawień. W szczególności, tak jak napisałem wyżej, może to być uwierzytelnienia zintegrowane z Windows ale również podstawowe czy inne. W przypadku braku uwierzytelnienia do silnika ASP.NET zostanie natomiast przekazana tożsamość anonimowa. Uwierzytelnienia na tym poziomie konfigurowane jest przy pomocy pliku web.config w sekcji <authentication>.

Tożsamość silnika ASP.NET

Teraz dochodzimy do setna sprawy czyli z jaką tożsamością działa silnik ASP.Net. Odpowiedź: „To zależy” :) Już odpowiadam ale zanim przejdziemy dalej należy jeszcze wyjaśnić czym jest impersonacja. Otóż impersonacja to mechanizm pozwalający przyjąć cudzą tożsamość i wykonywać w jej kontekście różne działania.

A więc jeśli wyłączona jest impersonacja (ustawienia domyślne) to silnik ASP.NET w przypadku systemów Windows Server 2000 i Windows XP działa na koncie o nazwie ASPNET, a przypadku Windows Server 2003 używa konta Network Service. Natomiast jeśli impersonacja jest włączona ASP.NET działa z tożsamością uwierzytelnionego użytkownika nawet jeśli jest to tożsamość anonimowa. Za te kwestie odpowiada sekcja <identity>.

Sprawa wygląda trochę inaczej w przypadku IIS 7, który posiada mocno zmienioną, a właściwie zupełnie inną architekturę w stosunku do IIS 6 i wcześniejszych. W szczególności w IIS 7 nie ma podziału na uwierzytelnienia na poziomie serwera i ASP.NET. Serwer IIS7 może być również konfigurowany z poziomu sekcji <system.WebServer> w pliku web.config. To jednak temat na kolejny wpis.