Debugowanie języka Python i języka C++ razem w programie Visual Studio

Większość zwykłych debugerów języka Python obsługuje tylko debugowanie kodu w języku Python, ale często zdarza się, aby deweloperzy używali języka Python z językiem C lub C++. Niektóre scenariusze korzystające z kodu mieszanego to aplikacje wymagające wysokiej wydajności lub możliwość bezpośredniego wywoływania interfejsów API platformy są często kodowane w językach Python i C lub C++.

Program Visual Studio zapewnia zintegrowane, jednoczesne debugowanie w trybie mieszanym dla języka Python i natywnego kodu C/C++. Obsługa jest dostępna po wybraniu opcji natywnych narzędzi programistycznych języka Python dla obciążenia Programowanie w języku Python w instalatorze programu Visual Studio:

Zrzut ekranu przedstawiający opcję natywnych narzędzi programistycznych języka Python wybraną w Instalator programu Visual Studio.

W tym artykule przedstawiono sposób pracy z następującymi funkcjami debugowania w trybie mieszanym:

  • Połączone stosy wywołań
  • Krok między językiem Python i kodem natywnym
  • Punkty przerwania w obu typach kodu
  • Wyświetlanie reprezentacji obiektów w języku Python w ramkach natywnych i na odwrót
  • Debugowanie w kontekście projektu w języku Python lub w projekcie C++

Zrzut ekranu przedstawiający przykład debugowania w trybie mieszanym dla języka Python i kodu C++ w programie Visual Studio.

Wymagania wstępne

  • Program Visual Studio 2017 lub nowszy. Debugowanie w trybie mieszanym nie jest dostępne w narzędziach Python Tools for Visual Studio 1.x w programie Visual Studio 2015 i starszych wersjach.

  • Program Visual Studio zainstalowany z obsługą obciążeń języka Python. Aby uzyskać więcej informacji, zobacz Instalowanie obsługi języka Python w programie Visual Studio.

Włączanie debugowania w trybie mieszanym w projekcie języka Python

W poniższych krokach opisano sposób włączania debugowania w trybie mieszanym w projekcie języka Python:

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt języka Python i wybierz polecenie Właściwości.

  2. W okienku Właściwości wybierz kartę Debugowanie, a następnie wybierz opcję Debuguj> Włącz debugowanie kodu natywnego:

    Zrzut ekranu przedstawiający sposób ustawiania właściwości Włącz debugowanie kodu natywnego w programie Visual Studio.

    Ta opcja umożliwia tryb mieszany dla wszystkich sesji debugowania.

    Napiwek

    Po włączeniu debugowania kodu natywnego okno danych wyjściowych języka Python może zostać zamknięte natychmiast po zakończeniu działania programu bez wstrzymywania i wyświetlania klawisza Naciśnij dowolny klawisz, aby kontynuować monit. Aby wymusić wstrzymanie i monit po włączeniu debugowania kodu natywnego, dodaj -i argument do pola Uruchom>argumenty interpretera na karcie Debugowanie . Ten argument umieszcza interpreter języka Python w tryb interaktywny po uruchomieniu kodu. Program czeka na wybranie klawiszy Ctrl+Z+Enter, aby zamknąć okno.

  3. Wybierz pozycję Zapisz plik>(lub Ctrl+S), aby zapisać zmiany właściwości.

  4. Aby dołączyć debuger trybu mieszanego do istniejącego procesu, wybierz pozycję Dołącz debugowanie>do procesu. Zostanie otwarte okno dialogowe.

    1. W oknie dialogowym Dołączanie do procesu wybierz odpowiedni proces z listy.

    2. W polu Dołącz do użyj opcji Wybierz, aby otworzyć okno dialogowe Wybieranie typu kodu.

    3. W oknie dialogowym Wybieranie typu kodu wybierz opcję Debuguj te typy kodu.

    4. Na liście zaznacz pole wyboru Python (natywne) i wybierz przycisk OK:

      Zrzut ekranu przedstawiający sposób wybierania typu kodu języka Python (natywnego) na potrzeby debugowania w programie Visual Studio.

    5. Wybierz pozycję Dołącz , aby uruchomić debuger.

    Ustawienia typu kodu są trwałe. Jeśli chcesz wyłączyć debugowanie w trybie mieszanym i dołączyć go do innego procesu później, wyczyść pole wyboru Typ kodu języka Python (natywny) i zaznacz pole wyboru Typ kodu natywnego .

    Możesz wybrać inne typy kodu oprócz lub zamiast opcji Natywna. Jeśli na przykład aplikacja zarządzana hostuje środowisko CPython, które z kolei korzysta z modułów rozszerzeń natywnych i chcesz debugować wszystkie trzy projekty kodu, zaznacz pola wyboru Python, Native i Managed . Takie podejście zapewnia ujednolicone środowisko debugowania, w tym połączone stosy wywołań i przechodzenie między wszystkimi trzema środowiskami uruchomieniowymi.

Praca ze środowiskami wirtualnymi

Jeśli używasz tej metody debugowania w trybie mieszanym dla środowisk wirtualnych (venvs), język Python dla systemu Windows używa python.exe pliku wycinków dla programów venvs, które program Visual Studio znajduje i ładuje jako podproces.

  • W przypadku języka Python w wersji 3.8 lub nowszej tryb mieszany nie obsługuje debugowania wieloprocesowego. Po uruchomieniu sesji debugowania podprocesy wycinków są debugowane zamiast aplikacji. W przypadku scenariuszy dołączania obejście polega na dołączeniu do poprawnego python.exe pliku. Po uruchomieniu aplikacji z debugowaniem (na przykład za pomocą skrótu klawiaturowego F5 ) można utworzyć venv za pomocą polecenia C:\Python310-64\python.exe -m venv venv --symlinks. W poleceniu wstaw preferowaną wersję języka Python. Domyślnie tylko administratorzy mogą tworzyć symlinki w systemie Windows.

  • W przypadku wersji języka Python starszych niż 3.8 debugowanie w trybie mieszanym powinno działać zgodnie z oczekiwaniami w przypadku venvs.

Uruchamianie w środowisku globalnym nie powoduje tych problemów dla żadnej wersji języka Python.

Instalowanie symboli języka Python

Po pierwszym uruchomieniu debugowania w trybie mieszanym może zostać wyświetlone okno dialogowe Wymagane symbole języka Python. Należy zainstalować symbole tylko raz dla dowolnego środowiska języka Python. Symbole są automatycznie dołączane, jeśli instalujesz obsługę języka Python za pośrednictwem Instalator programu Visual Studio (Visual Studio 2017 i nowszych). Aby uzyskać więcej informacji, zobacz Instalowanie symboli debugowania dla interpreterów języka Python w programie Visual Studio.

Uzyskiwanie dostępu do kodu źródłowego języka Python

Kod źródłowy standardowego języka Python można udostępnić podczas debugowania.

  1. Przejdź do https://www.python.org/downloads/source/.

  2. Pobierz archiwum kodu źródłowego języka Python odpowiednie dla używanej wersji i wyodrębnij kod do folderu.

  3. Gdy program Visual Studio wyświetli monit o lokalizację kodu źródłowego języka Python, wskaż określone pliki w folderze wyodrębniania.

Włączanie debugowania w trybie mieszanym w projekcie C/C++

Program Visual Studio 2017 w wersji 15.5 lub nowszej obsługuje debugowanie w trybie mieszanym z projektu C/C++. Przykładem tego użycia jest to, że chcesz osadzić język Python w innej aplikacji zgodnie z opisem w python.org.

W poniższych krokach opisano sposób włączania debugowania w trybie mieszanym dla projektu C/C++:

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt C/C++, a następnie wybierz polecenie Właściwości.

  2. W okienku Strony właściwości wybierz kartę Debugowanie właściwości>konfiguracji.

  3. Rozwiń menu rozwijane debugera , aby uruchomić opcję, a następnie wybierz pozycję Python/Native Debugowanie.

    Zrzut ekranu przedstawiający sposób wybierania opcji Debugowanie natywne języka Python dla projektu C/C++ w programie Visual Studio.

    Uwaga

    Jeśli nie widzisz opcji Debugowanie języka Python/natywnego, musisz najpierw zainstalować natywne narzędzia programistyczne języka Python przy użyciu Instalator programu Visual Studio. Opcja debugowania natywnego jest dostępna w ramach obciążenia programowanie w języku Python. Aby uzyskać więcej informacji, zobacz Instalowanie obsługi języka Python w programie Visual Studio.

  4. Wybierz przycisk OK , aby zapisać zmiany.

Debugowanie uruchamiania programu

Jeśli używasz tej metody, nie można debugować uruchamiania py.exe programu, ponieważ powoduje ono zduplikowanie podrzędnego python.exe podprocesu. Debuger nie jest dołączany do podprocesu. W tym scenariuszu obejściem jest uruchomienie python.exe programu bezpośrednio z argumentami w następujący sposób:

  1. W okienku Strony właściwości dla projektu C/C++ przejdź do karty Debugowanie właściwości>konfiguracji.

  2. Dla opcji Polecenie określ pełną ścieżkę python.exe do pliku programu.

  3. Określ żądane argumenty w polu Argumenty poleceń.

Dołączanie debugera w trybie mieszanym

W przypadku programu Visual Studio 2017 w wersji 15.4 i starszych debugowanie bezpośredniego trybu mieszanego jest włączone tylko podczas uruchamiania projektu języka Python w programie Visual Studio. Obsługa jest ograniczona, ponieważ projekty C/C++ używają tylko natywnego debugera.

W tym scenariuszu obejściem jest osobne dołączenie debugera:

  1. Uruchom projekt C++ bez debugowania, wybierając pozycję Debuguj>rozpocznij bez debugowania lub użyj skrótu klawiaturowego Ctrl+F5.

  2. Aby dołączyć debuger trybu mieszanego do istniejącego procesu, wybierz pozycję Dołącz debugowanie>do procesu. Zostanie otwarte okno dialogowe.

    1. W oknie dialogowym Dołączanie do procesu wybierz odpowiedni proces z listy.

    2. W polu Dołącz do użyj opcji Wybierz, aby otworzyć okno dialogowe Wybieranie typu kodu.

    3. W oknie dialogowym Wybieranie typu kodu wybierz opcję Debuguj te typy kodu.

    4. Na liście zaznacz pole wyboru Python i wybierz przycisk OK.

    5. Wybierz pozycję Dołącz , aby uruchomić debuger.

Napiwek

Możesz dodać wstrzymanie lub opóźnienie w aplikacji C++, aby upewnić się, że nie wywołuje kodu języka Python, który chcesz debugować przed dołączeniem debugera.

Eksplorowanie funkcji specyficznych dla trybu mieszanego

Program Visual Studio udostępnia kilka funkcji debugowania w trybie mieszanym, aby ułatwić debugowanie aplikacji:

Używanie połączonego stosu wywołań

W oknie Stos wywołań są wyświetlane ramki stosu natywnego i python przeplatane z przejściami oznaczonymi między nimi:

Zrzut ekranu przedstawiający okno połączonego stosu wywołań z debugowaniem w trybie mieszanym w programie Visual Studio.

  • Aby przejść wyświetlane jako [Kod zewnętrzny] bez określania kierunku przejścia, ustaw opcję Narzędzia>Opcje>Debugowanie>ogólne>Włącz tylko mój kod.

  • Aby uaktywnić dowolną ramkę wywołania, kliknij dwukrotnie ramkę. Ta akcja powoduje również otwarcie odpowiedniego kodu źródłowego, jeśli jest to możliwe. Jeśli kod źródłowy jest niedostępny, ramka jest nadal aktywna i można sprawdzić zmienne lokalne.

Krok między językiem Python i kodem natywnym

Program Visual Studio udostępnia polecenia Step Into (F11) lub Step Out (Shift+F11), aby umożliwić debugerowi trybu mieszanego poprawne obsługę zmian między typami kodu.

  • Gdy język Python wywołuje metodę typu zaimplementowanego w języku C, krok po wywołaniu tej metody zatrzymuje się na początku funkcji natywnej, która implementuje metodę.

  • To samo zachowanie występuje, gdy kod natywny wywołuje funkcję interfejsu API języka Python, która powoduje wywoływanie kodu w języku Python. Przejście do wywołania PyObject_CallObject metody na wartość funkcji pierwotnie zdefiniowanej w języku Python zatrzymuje się na początku funkcji języka Python.

  • Przechodzenie z języka Python do natywnego jest również obsługiwane w przypadku funkcji natywnych wywoływanych z języka Python za pośrednictwem typów ctypes.

Używanie widoku wartości PyObject w kodzie natywnym

Gdy ramka natywna (C lub C++) jest aktywna, jej zmienne lokalne są wyświetlane w oknie Ustawień lokalnych debugera. W natywnych modułach rozszerzeń języka Python wiele z tych zmiennych jest typu PyObject (czyli typedef dla _object), lub kilka innych podstawowych typów języka Python. W debugowaniu w trybie mieszanym te wartości przedstawiają inny węzeł podrzędny oznaczony etykietą [Widok języka Python].

  • Aby wyświetlić reprezentację języka Python zmiennej, rozwiń węzeł. Widok zmiennych jest identyczny z tym, co widać, jeśli zmienna lokalna odwołująca się do tego samego obiektu znajduje się w ramce języka Python. Elementy podrzędne tego węzła można edytować.

    Zrzut ekranu przedstawiający widok języka Python w oknie Ustawienia lokalne w programie Visual Studio.

  • Aby wyłączyć tę funkcję, kliknij prawym przyciskiem myszy w dowolnym miejscu w oknie Ustawienia lokalne i przełącz opcję menu Pokaż węzły widoku języka Python>:

    Zrzut ekranu przedstawiający sposób włączania opcji Pokaż węzły widoku języka Python dla okna Ustawienia lokalne.

Typy C pokazujące węzły widoku języka Python

Następujące typy C pokazują węzły [Widok języka Python], jeśli są włączone:

  • PyObject
  • PyVarObject
  • PyTypeObject
  • PyByteArrayObject
  • PyBytesObject
  • PyTupleObject
  • PyListObject
  • PyDictObject
  • PySetObject
  • PyIntObject
  • PyLongObject
  • PyFloatObject
  • PyStringObject
  • PyUnicodeObject

[Widok języka Python] Nie jest automatycznie wyświetlane dla typów utworzonych samodzielnie. Podczas tworzenia rozszerzeń dla języka Python 3.x ten brak zwykle nie jest problemem. Każdy obiekt ma ob_base ostatecznie pole jednego z wymienionych typów języka C, co powoduje wyświetlenie widoku [Python].

Wyświetlanie wartości natywnych w kodzie języka Python

Możesz włączyć widok [C++ dla wartości natywnych w oknie Ustawienia lokalne , gdy ramka języka Python jest aktywna. Ta funkcja nie jest domyślnie włączona.

  • Aby włączyć tę funkcję, kliknij prawym przyciskiem myszy w oknie Ustawienia lokalne i ustaw opcję menu Python>Show C++ View Nodes (Pokaż węzły widoku języka C++).

    Zrzut ekranu przedstawiający sposób włączania opcji Pokaż węzły widoku języka C++ dla okna Ustawienia lokalne.

  • Węzeł [widok języka C++] zawiera reprezentację podstawowej struktury języka C/C++ dla wartości, identycznej z tym, co widać w ramce natywnej. Przedstawia wystąpienie (dla którego PyLongObject jest typedef) dla długiej _longobject liczby całkowitej języka Python i próbuje wywnioskować typy dla klas natywnych utworzonych samodzielnie. Elementy podrzędne tego węzła można edytować.

    Zrzut ekranu przedstawiający widok języka C++ w oknie Ustawienia lokalne w programie Visual Studio.

Jeśli pole podrzędne obiektu ma typ PyObjectlub inny obsługiwany typ, ma węzeł reprezentacji [Widok języka Python] (jeśli te reprezentacje są włączone). To zachowanie umożliwia nawigowanie po grafach obiektów, w których linki nie są bezpośrednio widoczne dla języka Python.

W przeciwieństwie do węzłów [Widok języka Python], które używają metadanych obiektu języka Python do określenia typu obiektu, nie ma podobnie niezawodnego mechanizmu dla widoku [C++]. Ogólnie rzecz biorąc, biorąc pod uwagę wartość języka Python (czyli odwołanie) nie można niezawodnie określić, PyObject która struktura języka C/C++ ją wspiera. Debuger trybu mieszanego próbuje odgadnąć typ, sprawdzając różne pola typu obiektu (na przykład PyTypeObject przywoływalne przez jego ob_type pole), które mają typy wskaźników funkcji. Jeśli jeden z tych wskaźników funkcji odwołuje się do funkcji, którą można rozpoznać, i ta funkcja ma self parametr o typie bardziej specyficznym niż PyObject*, przyjmuje się, że ten typ jest typem kopii zapasowej.

Rozważmy następujący przykład, w którym ob_type->tp_init wartość dla danego obiektu wskazuje następującą funkcję:

static int FobObject_init(FobObject* self, PyObject* args, PyObject* kwds) {
    return 0;
}

W takim przypadku debuger może poprawnie określić, że typ C obiektu to FobObject. Jeśli debuger nie może określić bardziej precyzyjnego typu z tp_init, przechodzi do innych pól. Jeśli nie jest w stanie wyłudić typu z dowolnego z tych pól, węzeł [C++ view] przedstawia obiekt jako PyObject wystąpienie.

Aby zawsze uzyskać przydatną reprezentację niestandardowych typów utworzonych, najlepiej zarejestrować co najmniej jedną specjalną funkcję podczas rejestrowania typu i użyć silnie typizowanego self parametru. Większość typów spełnia to wymaganie naturalnie. W przypadku innych typów inspekcja tp_init jest zwykle najwygodniejszym wejściem do użytku w tym celu. Fikcyjna implementacja tp_init typu, który jest obecny wyłącznie w celu włączenia wnioskowania typu debugera, może natychmiast zwrócić zero, jak w poprzednim przykładzie.

Przegląd różnic w porównaniu ze standardowym debugowaniem języka Python

Debuger trybu mieszanego różni się od standardowego debugera języka Python. Wprowadzono pewne dodatkowe funkcje, ale nie ma niektórych funkcji związanych z językiem Python w następujący sposób:

  • Nieobsługiwane funkcje obejmują warunkowe punkty przerwania, okno Debugowanie interakcyjne i zdalne debugowanie międzyplatformowe.
  • Okno Natychmiastowe jest dostępne, ale z ograniczonym podzestawem jego funkcjonalności, w tym wszystkie ograniczenia wymienione w tej sekcji.
  • Obsługiwane wersje języka Python obejmują tylko wersje CPython 2.7 i 3.3 lub nowsze.
  • Aby użyć języka Python z programem Visual Studio Shell (na przykład w przypadku zainstalowania go ze zintegrowanym instalatorem), program Visual Studio nie może otworzyć projektów języka C++. W związku z tym środowisko edycji dla plików C++ jest tylko podstawowym edytorem tekstów. Jednak debugowanie języka C/C++ i debugowanie w trybie mieszanym jest w pełni obsługiwane w powłoce z kodem źródłowym, przechodzenie do kodu natywnego i ocena wyrażeń języka C++ w oknach debugera.
  • Podczas wyświetlania obiektów języka Python w oknach narzędzi debugera Locals and Watch debugger debuger w trybie mieszanym pokazuje tylko strukturę obiektów. Nie ocenia automatycznie właściwości ani nie wyświetla obliczonych atrybutów. W przypadku kolekcji są wyświetlane tylko elementy dla wbudowanych typów kolekcji (tuple, list, dict, set). Niestandardowe typy kolekcji nie są wizualizowane jako kolekcje, chyba że są dziedziczone z określonego typu kolekcji wbudowanej.
  • Ocena wyrażeń jest obsługiwana zgodnie z opisem w poniższej sekcji.

Używanie oceny wyrażeń

Standardowy debuger języka Python umożliwia ocenę dowolnych wyrażeń języka Python w oknach kontrolnych i natychmiastowych , gdy debugowany proces jest wstrzymany w dowolnym momencie w kodzie, o ile nie jest blokowany w operacji we/wy lub w innym podobnym wywołaniu systemowym. W debugowaniu w trybie mieszanym dowolne wyrażenia mogą być oceniane tylko wtedy, gdy są zatrzymywane w kodzie języka Python, po punkcie przerwania lub podczas przechodzenia do kodu. Wyrażenia można oceniać tylko w wątku, w którym wystąpił punkt przerwania lub operacja kroku.

Gdy debuger zatrzymuje się w kodzie natywnym lub w kodzie języka Python, w którym nie są stosowane opisane warunki, takie jak po operacji wyjścia lub w innym wątku. Ocena wyrażeń jest ograniczona do uzyskiwania dostępu do zmiennych lokalnych i globalnych w zakresie aktualnie wybranej ramki, uzyskiwania dostępu do pól i indeksowania wbudowanych typów kolekcji z literałami. Na przykład następujące wyrażenie można ocenić w dowolnym kontekście (pod warunkiem, że wszystkie identyfikatory odnoszą się do istniejących zmiennych i pól odpowiednich typów):

foo.bar[0].baz['key']

Debuger trybu mieszanego rozwiązuje również takie wyrażenia inaczej. Wszystkie operacje dostępu do składowych wyszukują tylko pola, które są bezpośrednio częścią obiektu (np. wpis w obiekcie __dict__ lub __slots__, lub pole natywnej struktury uwidocznionej w języku Python za pośrednictwem tp_members), i ignorują dowolną __getattr__logikę deskryptora , __getattribute__lub deskryptora. Podobnie wszystkie operacje indeksowania ignorują __getitem__metodę i uzyskują bezpośredni dostęp do wewnętrznych struktur danych kolekcji.

Ze względu na spójność ten schemat rozpoznawania nazw jest używany dla wszystkich wyrażeń, które są zgodne z ograniczeniami w przypadku oceny ograniczonego wyrażenia. Ten schemat jest stosowany niezależnie od tego, czy dowolne wyrażenia są dozwolone w bieżącym punkcie zatrzymania. Aby wymusić właściwą semantyka języka Python, gdy jest dostępny w pełni funkcjonalny ewaluator, należy ująć wyrażenie w nawiasy:

(foo.bar[0].baz['key'])