28/07/2010

Używanie IntelliTrace poza Visual Studio 2010!

Home

W oficjalnej dokumentacji IntelliTrace można przeczytać, że narzędzie to jest dostępne tylko i wyłącznie z poziomu środowiska Visual Studio 2010. To bardzo źle ponieważ z góry przekreśla użycie IntelliTrace do diagnozowania błędów u klienta. Wyobraźmy sobie, że dostajemy zgłoszenie trudnego do powtórzenia błędu. Czy nie byłoby wspaniale uruchomić aplikację w środowisku produkcyjnym, pod kontrolą IntelliTrace i w razie wystąpienia błędu poprosić klienta o przekazanie nam plików z logami (*.iTrace). Nie wszystko jest jednak stracone. Pomimo, że nie jest to oficjalnie wspierane można uruchomić IntelliTrace z poza Visual Studio 2010!

Na początek należy odpowiedzieć na pytanie czym jest IntelliTrace: wątkiem działającym w ramach procesu Visual Studio 2010, niezależnym procesem, a może jeszcze czymś innym? Odpowiedź na to pytanie można uzyskać przy pomocy programu Process Explorer. Na poniższym obrazku widać fragment drzewa procesów dla węzła devenv.exe (czyli dla Visual Studio).



Pod nim znajdziemy węzeł odpowiadający debug'owanemu programowi oraz węzeł IntelliTrace, a w jego właściwościach ścieżkę z jakiej jest uruchamiany (VS_2010_INSTALL_DIR to katalog instalacyjny środowiska) np.:

VS_2010_INSTALL_DIR\Team Tools\TraceDebugger Tools\IntelliTrace.EXE

Reasumując IntelliTrace to niezależny proces, który komunikuje się z Visual Studio przy użyciu jakiegoś mechanizmu IPC (ang. Inter-process communication ). Jeśli spróbujemy go uruchomić z linii poleceń na ekran zostanie wypisana lista dostępnych komend. Z kilku dostępnych interesująca jest komenda launch, która zgodnie z podanym opisem uruchamia podaną aplikację i rozpoczyna jej monitorowanie. Po wpisaniu w linię poleceń: IntelliTrace help launch uzyskamy bardziej szczegółowe informacje na jej temat.

Z dostępnych parametrów najważniejsze to /logfile (w skrócie /f) przy pomocy, którego wskazujemy docelowy plik z logiem oraz /collectionplan (w skrócie /cp) przy pomocy, którego wskazujemy plik z planem działania, konfiguracją IntelliTrace. Skąd go wziąć? Domyślny plik używany przez Visual Studio 2010, a który jest dokumentem XML, znajdziemy w lokalizacji:

VS_2010_INSTALL_DIR\Team Tools\TraceDebugger Tools\en\CollectionPlan.xml

Mając już wszystkie elementy układanki, spróbujmy uruchomić jakiś program pod kontrolą IntelliTrace ale bez pomocy Visual Studio. Poniżej pokazano takie przykładowe wywołanie. W miejsce LOG_PATH należy wstawić ścieżkę do wyjściowego pliku z logiem (najlepiej nadać mu rozszerzenie iTrace), w miejsce COLLECTION_PLAN_PATH ścieżkę do pliku CollectionPlan.xml (może nazywać się inaczej i mieć inne rozszerzenie). PROGRAM_PATH to oczywiście ścieżka do programu jaki chcemy monitorować.

VS_2010_INSTALL_DIR\Team Tools\TraceDebugger Tools\IntelliTrace.EXE launch /f:LOG_PATH /cp:COLLECTION_PLAN_PATH PROGRAM_PATH

Po wykonaniu tego polecenia we wskazanym przez nas katalogu pojawi się plik z logiem. Jeśli będzie miał rozszerzenie iTrace to po dwukliku uruchomi się Visual Studio 2010. W tym momencie czeka nas jednak niemiła niespodzianka ponieważ okaże się, że plik z logiem nie zawiera żadnych informacji tak jakby program nie był monitorowany.



Na brakujący element układanki naprowadzi nas ponownie Process Explorer, który umożliwia podejrzenie parametrów wywołania programu. Interesuje nas oczywiście proces IntelliTrace. Jeśli odczytamy jego parametry wywołania to dowiemy się, że został uruchomiony z komendą run, a nie launch. Różnica polega na tym, że run w przeciwieństwie do launch uruchamia tylko proces monitorujący i nie wskazuje konkretnej aplikacji do monitorowania. To jaka aplikacja będzie monitorowana zależy od Visual Studio 2010. Kolejna, ważna dla nas różnica to wartość parametru /cp, który nie wskazuje na domyślny plan wykonania ale na jakiś "dziwny" plik np.:

C:\Users\user\AppData\Local\Microsoft\VisualStudio\10.0\TraceDebugger\Settings\frueouq2.vfs

Jest to plik tymczasowy, a jego nazwa generowana jest w sposób losowy. Jeśli spróbujemy go otworzyć to okaże się, że bardzo przypomina domyślny plik CollectionPlan.xml. Dokładniej mówiąc schemat XML jest taki sam, inne są natomiast wartości atrybutów, liczba węzłów itd. czyżby brakujący element układanki? Tworzymy, więc kopię tego pliku, wskazujemy go w parametrze \cp i ponownie wydajemy wcześniej pokazane polecenie. Tym razem po otworzeniu wynikowego pliku z logiem zobaczymy listę monitorowanych wątków, drzewo wywołań itd.



Pełny sukces? Niestety nie do końca. Po bliższym przyjrzeniu się zauważymy, że mamy dostęp tylko do drzewa wywołań, a brakuje informacji o zdarzeniach diagnostycznych (ważne punkty w historii wykonania programu np.: wykonanie zapytania do bazy danych). Tego ograniczenia nie udało mi się jeszcze ominąć. Przypuszczam, że przy uruchamianiu IntelliTrace przez Visual Studio 2010 do programu przekazywane są jakieś dodatkowe opcje. Być może do rejestrowania zdarzeń potrzebne jest Visual Studio, a logger IntelliTrace nie potrafi tego robić?

Najważniejsze jest jednak to, że już teraz IntelliTrace można uruchomić niezależnie od środowiska programistycznego, chociaż z pewnymi ograniczeniami. Biorę to za dobrą monetę i mam nadzieję, że pełne wsparcie dla uruchamiania IntelliTrace z poza Visual Studio jest w planach Microsoft'u i pojawi się, jeśli nie z którymś service pack'iem to w kolejnej edycji Visual Studio.

Artukuł opublikowałem również w serwisie software.com.pl.

20/07/2010

Android 2.1

Home

Pierwszy post po długiej przerwie spowodowanej urlopem :) będzie trochę z innej beczki niż zwykle, a będzie dotyczył aktualizacji systemu Android do wersji 2.1 na telefonie Samsung Galaxy GT-I5700. Postanowiłem o tym napisać ponieważ spędziłem nad tym trochę czasu, na różnych forach można znaleźć sporo pytań o to, a okazało się, że sprawa jest dość prosta. Ja napotkałem dwa główne problemy:
  • Problem z wykryciem telefonu, po podłączeniu go do komputera przez kabel USB, przez oprogramowanie Samsung PC Studio.
  • Zawieszenie się telefonu w czasie aktualizacji.
Pierwszy problem rozwiązuje się właściwie sam. Jakiś czas temu pojawiła się aktualizacja aplikacji Samsung PC Studio. Po uruchomieniu programu otrzymamy komunikat z informacją o możliwość pobrania i zainstalowania uaktualnienia. Po zakończeniu procesu problem z wykrywaniem telefonu powinien zniknąć (u mnie ustąpił). Jeśli PC Studio cały czas uparcie nie widzi telefonu należy sprawdzić czy telefon ma włączone Debugowanie USB. Opcję tą znajdziemy tutaj: Ustawienia -> Aplikacje -> Programowanie -> Debugowanie USB.

Drugi problem polegał na tym, że po rozpoczęciu aktualizacji telefon w pewnym momencie wyłączył się, a przy ponownym uruchomieniu zawiesił na czarnym ekranie z napisem Samsung. Po przeczekaniu kilkunastu minut straciłem cierpliwość i odłączyłem (wbrew zaleceniom) telefon od komputera oraz go wyłączyłem wyjmując baterie (również niezalecane). Kolejne uruchomieniu telefonu odbyło się szczęśliwie bez kłopotów ale system oczywiście nie został uaktualniony.

Po chwili przypomniałem sobie, że używam dobrze znanej wielu osobom aplikacji APNdroid dzięki, której telefon nie połączy sie z netem poprzez sieci 2G/3G/EDGE bez naszej wiedzy (tak na marginesie polecam). Standardowo dostęp do sieci mam wyłączony i miałem niejasne przeczucie, że tutaj tkwi rozwiązanie problemu. Moje przeczucie okazało się trafne. Po włączeniu dostępu do sieci 2G/3G/EDGE i ponownym rozpoczęciu procesu aktualizacji wszystko się udało.

Na zakończenie kilka uwag. Przed rozpoczęciem aktualizacji warto wykonać kopię zapasową danych na telefonie. Pytanie o to czy chcemy wykonać taką kopię pojawi się w kreatorze aktualizacji. Po drugie zwracam uwagę, że odłączania telefonu od komputera w czasie aktualizacji jest niebezpieczne i może doprowadzić do utraty danych lub uszkodzenia telefonu. Ja byłem zmuszony to zrobić i się udało ale w żadnym stopniu nie zalecam powielania tej procedury. Najlepiej zrobić wszystko od początku do końca prawidłowo :) Na koniec przypominam, że po wykonaniu aktualizacji nie będzie już można korzystać z PC Studio do komunikacji z telefonem. Zamiast tej aplikacji należy pobrać nową o nazwie KIES

27/06/2010

Słów kilka o ASP.NET, IIS, corflags i opcjach kompilacji

Home

Jakiś czas temu przenosiłem aplikację ASP.NET z środowiska developerskiego do testowego i jak często bywa w takich sytuacjach migracja nie obyła się bez pewnych kłopotów. Przy próbie uruchomienia aplikacji użytkownik otrzymywał informację o tym, że nie udało się załadować jednej z bibliotek. Po chwili zauważyłem, że bezpośrednim winowajcą był wyjątek BadImageFormatException. Z podobnym problem już się spotkałem dlatego szybko skojarzyłem, że przyczyną problemu może być próba załadowania 32 bitowej biblioteki do procesu 64 bitowego (środowisko testowe było 64 bitowe). Dla przypomnienia nie jest możliwe aby proces 64 bitowy korzystał z bibliotek 32 bitowych i na odwrót.

Przyjrzałem się więc bliżej kłopotliwej bibliotece przy pomocy programu corflags (pisałem już o nim w tym poście) i okazało się, że ma ona ustawiona flagę 32BIT wskazującą, że biblioteka może być uruchamiana tylko w trybie 32 bitowym. Sprawdziłem również inne biblioteki ale nie miały ustawione tej flagi. W tym momencie wszystko było już jasne, aplikacja ASP.NET hostowana była przez 64 bitowy serwer i w związku z tym nie mogła skorzystać z biblioteki 32 bitowej. Wyczyściłem flagę, również przy pomocy programu corflags i zgodnie z oczekiwaniami problem ustąpił.

Następnie postanowiłem wyjaśnić przyczynę czemu jedna biblioteka miała ustawioną flagę 32BIT. W tym celu przyjrzałem się ustawieniom projektu w Visual Studio i okazało się, że opcja Platform target ustawiona jest na x86 zamiast na Any CPU jak w innych projektach. W tym momencie przypomniałem sobie, że sam to zrobiłem żeby móc używać funkcji Edit and Continue, a po skończeniu pracy zapomniałem przywrócić odpowiednią konfigurację.

Na koniec pozostaje wyjaśnić czemu aplikacja działała w 64 bitowym środowisku developerskim, a w testowym już nie. Otóż, w środowisku testowym pula aplikacyjna w jakiej została umieszczona aplikacja miała wyłączoną flagę Włącz aplikacje 32 bitowe, która umożliwia hostowanie takich aplikacji na 64 bitowym serwerze IIS. Flaga ta dostępna jest w zaawansowanych ustawieniach puli aplikacji w grupie Ogólne. W środowisku developerskim używałem natomiast serwera zintegrowanego z Visual Studio. Nie jestem tego pewien ale prawdopodobnie ma on domyślnie włączoną tą flagę albo środowisko samo określa czy ta flaga ma być włączona w zależności od ustawień uruchamianych projektów.

Reasumując warto znać narzędzie corflags, opcję konfiguracji projektów Platform target oraz przełącznik Włącz aplikacje 32 bitowe .

16/06/2010

Własne zdarzenia IntelliTrace 2

Home

Dzisiaj, zgodnie z wcześniejszą obietnicą, chciałem pokazać w jaki sposób zdefiniować nowe zdarzenie diagnostyczne dla naszej własnej metody. W tym celu użyję bardzo prostej klasy pokazanej poniżej:

namespace SmallTest
{
    public class Fun
    {
        public void Hello(int count, string msg)
        {
            for (int i = 0; i < count; ++i)
                Console.WriteLine(msg);
        }
    }
}

Przyjmijmy, że klasa ta znajduje się w projekcie o nazwie SmallTest po skompilowaniu, którego powstanie plik SmallTest.dll. Skoro mamy już z czym pracować przystąpmy do modyfikacji pliku ColllectionPlan.xml. Cały proces będzie bardzo podobny do tego co pokazałem w poprzednim poście. Zaczynamy od znalezienia węzła o nazwie ModuleList i umieszczamy pod nim węzeł wskazujący na naszą bibliotekę:

  <ModuleSpecification Id="SmallTest">SmallTest.dll</ModuleSpecification>

Co istotne biblioteka nie musi być podpisana, ani znajdować się w jakimś konkretnym katalogu. Następnie tworzymy nową kategorię dla zdarzeń. W tym celu pod węzłem Categories powinniśmy umieścić pokazany poniżej węzeł XML. Jeśli nie chcemy tworzyć nowej kategorii możemy pominąć ten krok.

  <Category Id="my" _locID="category.My">My</Category>

Na koniec najważniejsza rzecz, zdefiniowanie nowego zdarzenia. Postępujemy dokładnie tak samo jak przy definiowaniu zdarzenia dla metody platformy .NET. Potrzebny kod XML został pokazany poniżej. Umieszczamy go pod węzłem DiagnosticEventSpecifications. Jeśli w poprzednim kroku nie tworzyliśmy nowej kategorii wartość węzła CategoryId powinna odpowiadać nazwie innej, już istniejącej kategorii.

    <DiagnosticEventSpecifications>
      <DiagnosticEventSpecification>
        <CategoryId>my</CategoryId>
        <SettingsName _locID="settingsName.Fun.Hello">Fun.Hello</SettingsName>
        <SettingsDescription _locID="settingsDescription.Fun.Hello">Fun.Hello</SettingsDescription>
        <Bindings>
          <Binding>
            <ModuleSpecificationId>SmallTest</ModuleSpecificationId>
            <TypeName>SmallTest.Fun</TypeName>
            <MethodName>Hello</MethodName>
            <MethodId>SmallTest.Fun.Hello(System.Int32, System.String):System.Void</MethodId>
            <ShortDescription _locID="shortDescription.Fun.Hello">Fun.Hello({0},{1})</ShortDescription>
            <LongDescription _locID="longDescription.Fun.Hello">Fun.Hello(count={0},msg={1})</LongDescription>
            <DataQueries>
              <DataQuery index="1" maxSize="0" type="Int32" _locID="dataquery.Fun.Hello.count" _locAttrData="name" name="count" query="" />
              <DataQuery index="2" maxSize="256" type="String" _locID="dataquery.Fun.Hello.msg" _locAttrData="name" name="msg"  query=""></DataQuery>
            </DataQueries>
          </Binding>
        </Bindings>
      </DiagnosticEventSpecification>

Szczegółowy opis znaczenia poszczególnych węzłów XML użytych w tej definicji podałem w poprzednim poście, a więc nie będę go tutaj powielał. Uruchamiany Visual Studio i jeśli nie popełniliśmy żadnego błędu możemy już korzystać z nowego zdarzenia. W celu sprawdzenia czy wszystko jest w porządku najpierw zaglądamy do Tools->Options->IntteliTrace->IntteliTrace Events. Powinniśmy zobaczyć nową kategorię, a po jej rozwinięciu nowe zdarzenie:



Teraz możemy stworzyć nowy projekt konsolowy i dodać referencję to biblioteki SmallTest. Kiedy to zrobimy, nie pozostaje nic innego jak utworzyć obiekt klasy Fun i wywołać metodę Hello:

            Fun f = new Fun();
            f.Hello(2, "Hello world!");

Po skompilowaniu i uruchomieniu programu pod kontrolą debugger'a i z włączonym mechanizmem IntteliTrace w oknie z zarejestrowanymi zdarzeniami zobaczymy nasze zdarzenie:



W następnym poście pokażę w jaki sposób analizować wystąpienia zdarzeń w sposób programowy.

11/05/2010

Własne zdarzenia IntelliTrace!

Home

IntelliTrace, znane również pod nazwą historycznego debugger'a, to narzędzie jakie pojawiło się w Visual Studio 2010, a które stanowi rozwinięcie "tradycyjnych" debugger'ów o możliwość nagrywania historii wykonania programu w celu jej późniejszej analizy. Post ten rozpoczyna serię dotyczącą tej technologii, a w której chcę opisać zaawansowane techniki użycia IntelliTrace.

IntelliTrace posiada dwa tryby pracy: podstawowy oraz rozszerzony. W trybie podstawowym stan programu zapisywany jest w momencie wystąpienia tzw. zdarzeń diagnostycznych. W trybie rozszerzonym rejestrowane są dodatkowo wywołania metod, dostęp do właściwości itd. Zdarzenia diagnostyczne to ważne punkty w historii wykonania programu np.: wykonanie zapytania do bazy danych, dostęp do pliku, wystąpienie wyjątku, zatrzymanie programu na pułapce i wiele innych. Poniższy rysunek pokazuje penel z listą dostępnych zdarzeń. Możemy się do niego dostać wybierając Tools->Options->IntelliTrace->IntelliTrace Events.



Większość zdarzeń związana jest z wywołaniem określonych metod np.: ExecuteReader. Zdarzenia tego rodzaju zdefiniowane są w pliku CollectionPlan.xml. Istotne jest to, że plik ten można modyfikować poprzez zmianę istniejących zdarzeń albo dodawanie właśnych. Plik ten znajduje się w lokalizacji:

VS_2010_INSTALL_DIR\Team Tools\TraceDebugger Tools\LANGUAGE\CollectionPlan.xml

VS_2010_INSTALL_DIR to katalog instalacyjny Visual Studio 2010. Wartość LANGUAGE zależy od używanego przez nas języka. Domyślnie po zainstalowaniu środowiska zostanie utworzony katalog en. Jeśli jednak pracujemy z językiem polskim musimy samemu utworzyć katalog o nazwie pl i skopiować do niego plik CollectionPlan.xml. Jest to konieczne jeśli chcemy wprowadzić jakieś zmiany do podstawowego zestawu zdarzeń. Przed przystąpieniem do pracy zalecam utworzenie kopii zapasowej tego pliku.

Modyfikację ColllectionPlan.xml pokażę na przykładzie dodania nowego zdarzenia do kategorii Console. Kategoria ta zawiera zdarzenia skojarzone z wywołaniem metody Console.WriteLine. Z niewiadomych powodów nie przewidziano zdarzenia dla metody Console.Write. Naprawmy to! Poniżej zamieściłem XML stanowiący definicję nowego zdarzenia dla tej metody:

      <DiagnosticEventSpecification enabled="false">
        <CategoryId>console</CategoryId>
        <SettingsName _locID="settingsName.Console.Write">Write (1 args)</SettingsName>
        <SettingsDescription _locID="settingsDescription.Console.Write">Console Output with a String passed in.</SettingsDescription>
        <Bindings>
          <Binding>
            <ModuleSpecificationId>mscorlib</ModuleSpecificationId>
            <TypeName>System.Console</TypeName>
            <MethodName>Write</MethodName>
            <MethodId>System.Console.Write(System.String):System.Void</MethodId>
            <ShortDescription _locID="shortDescription.Console.Write.String">{0}</ShortDescription>
            <LongDescription _locID="longDescription.Console.Write">Console Output "{0}".</LongDescription>
            <DataQueries>
              <DataQuery index="0" maxSize="256" type="String" name="value" _locID="dataquery.Write.value" _locAttrData="name" query=""></DataQuery>
            </DataQueries>
          </Binding>
        </Bindings>
      </DiagnosticEventSpecification>

Węzeł Category określa kategorię do jakiej zostanie dodane nowe zdarzenie. SettingsName definiuje nazwę zdarzenia jaka będzie widoczna w ustawieniach VS 2010. Kolejny węzeł o nazwie SettingsDescription ma podobne znaczenie. Zawiera opis zdarzenia jak będzie wyświetlany dla użytkownika. Poniżej zamieścilem obrazek pokazujący w jaki sposób środowisko zinterpretuje te parametry.



Dalsza część definicji zdarzenia jest ważniejsza. Przede wszystkim zawiera ona dokładną specyfikację metody czyli nazwę modułu w jakim znajduje sie jej kod (węzeł ModuleSpecificationId), nazwę klasy w jakiej została zdefiniowana (węzeł TypeName), nazwę metody (węzeł MethodName) oraz pełną sygnaturę metody (węzeł MethodId). Jeśli podamy błędne dane nasze zdarzenie nie zadziała dlatego trzeba być uważnym.

Większą swobodę dają nam węzły ShortDescription oraz LongDescription definiujące opis podstawowy/skrócony i rozszerzony zdarzenia. O co dokładnie chodzi? Zostało to pokazane na poniższym obrazku przedstawiającym zdarzenia zarejestrowane przez IntelliTrace w czasie monitorowania programu.



Opis skrócony zdarzenia widoczny jest zawsze, podczas gdy opis rozszerzony dopiero po kliknięciu zdarzenia. Opis te mogą zawierać dowolne napisy, a także odwoływać się do wyniku zwróconego przez metodę, do wartości parametrów wywołania czy też do składowych klasy. O tym jakie informacje są dostępne dla zdarzenia decyduje zawartość węzła DataQueries. Może on zawierać dowolną liczbę podwęzłów DataQuery. Każdy podwęzeł o takiej nazwie definiuje coś w rodzaju zapytania, które zwróci np.: wartość parametru wywołania metody. Aby odwołać się do jakiegoś zapytania używamy składni {index} gdzie index to numer zapytania w numeracji od zero. Znaczenie poszczególnych atrybutów jest następujące.

  • index Przyjmuje odpowiednio wartości: -1 gdy odwołujemy się do wyniku zwracanego przez metodę, 0 gdy odwołujemy się do obiektu na rzecz, którego została wywołana metoda lub do pierwszego parametru jeśli to metoda statyczna, 0,1,2... gdy odwołujemy się do kolejnych parametrów wywołania metody.
  • maxSize Określa maksymalny rozmiar wyniku zwróconego przez zapytanie. Jeśli zostanie przekroczony to wynik zostanie obcięty.
  • type Typ wyniku. Na przykład jeśli pobieramy wartości parametru wywołania metody, który jest typu String to wartość tego atrybutu powinna być równa String.
  • name, _locID, _locAttrData Z mojego doświadczenia wynika, że można tu wpisać co kolwiek. Osobiście jednak wypełniając te atrybuty wzoruję się na innych definicjach zdarzeń.
  • query Atrybut używany kiedy chcemy odwołać się do składowej obiektu. W najprostszej postaci zawiera po prostu nazwę składowej.
Skoro już wiemy jak zbudowana jest definicja zdarzenia umieśćmy ją w pliku CollectionPlan.xml jako dziecko węzła DiagnosticEventSpecifications. Teraz konieczne jest ponowne uruchomienie VS 2010 i już możemy cieszyć się nowym zdarzeniem. Nic trudnego, prawda?

Na koniec ostrzeżenie. Przy dodawaniu nowych zdarzeń należy zachować umiar. Domyślny zestaw zdarzeń został dobrany tak aby nie wpływać na wydajność monitorowanego programu. Jeśli dodamy zdarzenie dla metody wywoływanej bardzo często wpłynie to negatywnie na jego wydajność podczas pracy z IntelliTrace.

W następnym poście na temat IntelliTrace pokażę jak dodać zdarzenie związane z naszą własną metodą, w jaki użyć węzłów DataQuery aby odwołać się do składowej obiektu oraz jak analizować wystąpienia zdarzeń w sposób programistyczny.