Udostępnij za pomocą


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, które wymagają wysokiej wydajności lub możliwości bezpośredniego wywoływania interfejsów API platformy i są często kodowane w językach Python oraz 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 Instalatorze 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 Eksploratorze 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.

    Wskazówka

    Po włączeniu debugowania kodu natywnego okno wyjściowe Pythona może zostać zamknięte natychmiast po zakończeniu działania programu bez wstrzymywania i wyświetlania monitu Naciśnij dowolny klawisz, aby kontynuować. 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 ctrl+Z+Enter, aby zamknąć okno.

  3. Wybierzpozycję Zapisz> (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 Instalatora 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 osadzanie języka 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 Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt C/C++, a następnie wybierz pozycję Właściwości.

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

  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 / Notatka

    Jeśli nie widzisz opcji Debugowanie języka Python/natywnego , musisz najpierw zainstalować natywne narzędzia programistyczne języka Python przy użyciu Instalatora programu Visual Studio. Opcja debugowania natywnego jest dostępna w ramach zestawu narzędzi rozwoju 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. Debugger nie łączy się z podprocesem. 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 Właściwości konfiguracji>Debugowanie.

  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łącz debuger 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 tej sytuacji rozwiązaniem jest oddzielne dołączenie debuggera.

  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.

Wskazówka

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 przeplatane ramki stosów natywnych i Pythona, z zaznaczonymi przejściami pomiędzy nimi.

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

  • Aby przejścia były wyświetlane jako [Kod zewnętrzny] bez określania kierunku przejścia, użyj okienka Narzędzia>Opcje. Rozwiń sekcję Wszystkie ustawienia>Debugowanie>ogólne , zaznacz pole wyboru Włącz tylko mój kod .
  • Aby przejścia wyglądały jak [Kod zewnętrzny] bez określania kierunku przejścia, użyj okna dialogowego Narzędzia>Opcje. Rozwiń sekcję Ogólne debugowania>, zaznacz pole wyboru Włącz tylko mój kod, a następnie wybierz przycisk OK.
  • Aby aktywować 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 kodu natywnego jest również obsługiwane w przypadku funkcji natywnych wywoływanych z języka Python za pośrednictwem 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ę zmiennej w języku Python, 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. Dzieci 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ż>:

    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świetlany dla typów utworzonych samodzielnie. Podczas tworzenia rozszerzeń dla języka Python 3.x ten brak zwykle nie jest problemem. Każdy obiekt ostatecznie ma pole ob_base jednego z wymienionych typów języka C, co powoduje wyświetlenie [Python view].

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

Możesz włączyć widok C++ dla wartości natywnych w oknie Lokalne, gdy jest aktywna ramka Pythona. 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 instancję _longobject (dla której PyLongObject jest typedef) długiej liczby całkowitej języka Python i próbuje wywnioskować typy dla natywnych klas, które tworzysz 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 PyObject), nie można niezawodnie określić, 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 wywnioskować typu z któregokolwiek z tych pól, węzeł [C++ view] przedstawia obiekt jako PyObject instancję.

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 i Watch, 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 przeprowadzana 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 ewaluować tylko w tym 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__, __slots__, lub pole natywnej struktury udostępnionej w Pythonie za pośrednictwem tp_members), i ignorują dowolną logikę __getattr__ i __getattribute__. Podobnie wszystkie operacje indeksowania ignorują __getitem__ 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 stosuje się niezależnie od tego, czy arbitralne wyrażenia są dozwolone w aktualnym 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'])