Rozszerzanie narzędzia Memory Investor na potrzeby debugowania C/C++

W Chrome 92 wprowadziliśmy Inspektor pamięci – narzędzie do sprawdzania liniowych buforów pamięci. W tym artykule omówimy ulepszenia w Inspektorze do debugowania w języku C/C++ oraz napotkane problemy techniczne.

Oto kilka przydatnych postów na blogu, jeśli dopiero zaczynasz korzystać z debugowania w C/C++ i narzędzia Memory Inspector:

Wprowadzenie

Inspektor pamięci udostępnia zaawansowane opcje debugowania liniowych buforów pamięci. W języku C/C++ możesz sprawdzać obiekty pamięci w języku C/C++ w pamięci WebAssembly.

Rozpoznawanie bajtów obiektu w otaczającej pamięci WebAssembly było kłopotliwe. Od początku musisz znać rozmiar obiektu i liczbę bajtów. Na zrzucie ekranu poniżej zaznaczono pierwszy bajt 10-elementowej tablicy int32, ale nie wiadomo od razu, które pozostałe bajty należą do tablicy. Czy nie byłoby miło, gdyby można było od razu rozpoznać wszystkie bajty należące do obiektu?

Zrzut ekranu oryginalnego inspektora pamięci z 1 zaznaczonym bajtem

Wyróżnianie obiektów w inspektorze pamięci

Od Chrome 107 Inspektor pamięci wyróżnia wszystkie bajty obiektu pamięci w języku C/C++. Pomoże Ci to odróżnić je od otaczającej pamięci.

Zrzut ekranu przedstawiający zaktualizowany inspektor pamięci z jasno podświetloną tablicą

Aby zobaczyć, jak działa Memory Inspector, obejrzyj film poniżej. Gdy ujawnisz tablicę x w narzędzie do inspekcji pamięci, w przeglądarce pamięci pojawi się wyróżniona pamięć wraz z nowym elementem graficznym tuż nad nią. Ten element przypomina nazwę i typ wyróżnionej pamięci. Kliknij chip, aby przejść do pamięci obiektu. Gdy najedziesz kursorem na element, pojawi się ikona krzyżyka. Kliknij ją, aby usunąć wyróżnienie.

Gdy wybierzesz bajt poza obiektem, który sprawdzasz, podświetlenie zniknie, aby nie rozpraszać. Aby ponownie ustawić ostrość, kliknij dowolny bajt obiektu lub ponownie kliknij chip.

Podświetlenie obiektów nie jest ograniczone do tablic. Możesz też przeglądać obiekty struct, obiekty i wskaźniki. Dzięki tym zmianom eksplorowanie pamięci aplikacji C/C++ jest teraz łatwiejsze niż kiedykolwiek.

Chcesz spróbować? Wykonaj te czynności:

  • mieć Chrome w wersji 107 lub nowszej.
  • Zainstaluj rozszerzenie DWARF dla C/C++.
  • Włącz debugowanie DWARF w Narzędzia deweloperskie > Ustawienia. Ustawienia > Eksperymenty > Debugowanie WebAssembly: włącz obsługę DWARF.
  • Otwórz tę stronę demonstracyjną.
  • Postępuj zgodnie z instrukcjami na stronie.

Przykład debugowania

W tej sekcji przyjrzymy się bliżej zabawce, by pokazać, jak można użyć narzędzia Memory Inspector do debugowania w języku C/C++. W poniższym przykładowym kodzie programista tworzy tablicę całkowitą i postanawia użyć arytmetyki wskaźnika, aby wybrać ostatni element. Niestety programista popełnił błąd w obliczaniu wskaźnika i teraz zamiast drukowania ostatniego elementu, program wydrukuje bezsensowne wartości.

#include <iostream>

int main()
{
    int numbers[] = {1, 2, 3, 4};
    int *ptr = numbers;
    int arraySize = sizeof(numbers)/sizeof(int);
    int* lastNumber = ptr + arraySize;  // Can you notice the bug here?
    std::cout <<../ *lastNumber <<../ '\n';
    return 0;
}

Programista używa inspektora pamięci, aby debugować problem. Możesz śledzić to demonstracja. Najpierw sprawdzają tablicę w inspektorze pamięci i widzi, że tablica numbers zawiera tylko liczby całkowite 1, 2, 34, zgodnie z oczekiwaniami.

Zrzut ekranu z otwartym inspektorem pamięci ze sprawdzoną tablicą int32. Wszystkie elementy tablicy są wyróżnione.

Następnie ujawniają zmienną lastNumber w panelu Zakres i zauważają, że wskaźnik wskazuje na liczbę całkowitą spoza tablicy. Dzięki tej wiedzy programista zdaje sobie sprawę, że w wierszu 8 błędnie policzył przesunięcie wskaźnika. Powinna być ptr + arraySize - 1.

Zrzut ekranu przedstawiający otwarty inspektor pamięci z zaznaczoną pamięcią wskazaną przez wskaźnik o nazwie „lastNumber”. Podświetlona pamięć znajduje się zaraz po ostatnim bajcie wcześniej podświetlonej tablicy.

Chociaż jest to przykładowy przypadek, pokazuje on, jak podświetlenie obiektu skutecznie przekazuje rozmiar i położenie obiektów pamięci, co może pomóc w lepszym zrozumieniu tego, co dzieje się w pamięci aplikacji C/C++.

Jak Narzędzia deweloperskie określają, co wyróżnić

W tej sekcji przyjrzymy się ekosystemowi narzędzi do debugowania kodu C/C++. W szczególności dowiesz się, jak Debugowanie w Chrome umożliwia debugowanie w języku C/C++ za pomocą Narzędzi dla programistów, V8, rozszerzenia C/C++ DWARF i Emscripten.

Aby w pełni wykorzystać możliwości debugowania w języku C/C++ w Narzędziach deweloperskich, potrzebujesz 2 elementów:

  • Rozszerzenie C/C++ DWARF zainstalowane w Chrome
  • Pliki źródłowe w języku C/C++ skompilowane do formatu WebAssembly za pomocą najnowszego kompilatora Emscripten zgodnie z instrukcjami podanymi w tym poście na blogu.

Ale dlaczego? V8, czyli silnik JavaScriptu i WebAssembly w Chrome, nie wie, jak wykonać kod w językach C ani C++. Dzięki Emscriptenowi, czyli kompilatorowi C/C++ na WebAssembly, możesz kompilować aplikacje napisane w C lub C++ jako WebAssembly i uruchamiać je w przeglądarce.

Podczas kompilacji emscripten umieszcza dane debugowania DWARF w pliku binarnym. Ogólnie rzecz biorąc, te dane pomagają rozszerzeniu ustalić, które zmienne WebAssembly odpowiadają Twoim zmiennym C/C++ i nie tylko. Dzięki temu DevTools może wyświetlać zmienne C++, mimo że V8 faktycznie wykonuje WebAssembly. Przykładowe dane debugowania w formacie DWARF znajdziesz w tym poście na blogu.

Co więc się stanie, gdy odkryjesz lastNumber? Gdy tylko klikniesz ikonę pamięci, Narzędzia dla programistów sprawdzą, którą zmienną chcesz sprawdzić. Następnie wysyła zapytanie do rozszerzenia dotyczące typu danych i lokalizacji lastNumber. Gdy tylko rozszerzenie poda te informacje, inspektor pamięci może wyświetlić odpowiedni fragment pamięci i znając jego typ, może też wyświetlić rozmiar obiektu.

Jeśli spojrzysz na funkcję lastNumber z wcześniejszego przykładu, możesz zauważyć, że sprawdziliśmy element lastNumber: int *, ale element inspektora pamięci ma komunikat *lastNumber: int. Co daje? Inspektor używa odwołania do wskaźnika w stylu C++, aby wskazać typ wyświetlanego obiektu. Jeśli sprawdzisz wskaźnik, inspektor pokaże, na co on wskazuje.

Zachowywanie wyróżnionych informacji dotyczących kroków debugera

Gdy odkryjesz obiekt w Inspektorze pamięci i wykonasz czynność z debugerem, zachowa on wyróżnienie, jeśli uzna, że nadal ma znaczenie. Początkowo nie było to uwzględnione w planach, ale szybko zdaliśmy sobie sprawę, że utrudnia to debugowanie. Wyobraź sobie, że po każdym kroku trzeba ponownie sprawdzać macierz, tak jak na poniższym filmie.

Gdy debuger dotrze do nowego punktu przerwania, Memory Inspector ponownie wysyła zapytanie do V8 i do rozszerzenia o zmienną powiązaną z poprzednim wyróżnieniem. Następnie porównuje lokalizacje i typy obiektów. Jeśli będą zgodne, wyróżnienie się nie zmieni. W filmie powyżej widać pętlę zapisu w tablicy x. Te operacje nie zmieniają typu ani pozycji tablicy, więc pozostaje ona podświetlona.

Zastanawiasz się pewnie, jak to wpływa na wskaźniki. Jeśli masz podświetlony wskaźnik i przypiszesz go do innego obiektu, stare i nowe położenie podświetlonych obiektów będą się różnić, a podświetlenie zniknie. Ponieważ nowo wskazany obiekt może znajdować się w dowolnym miejscu w pamięci WebAssembly i prawdopodobnie będzie miał niewielki związek z poprzednim miejscem w pamięci, usunięcie podświetlenia jest prostsze niż przejście do nowego miejsca w pamięci. Możesz ponownie wyróżnić wskaźnik, klikając jego ikonę pamięci w panelu Zakres.

Podsumowanie

W tym artykule opisano ulepszenia wprowadzone w narzędziu Memory Inspector na potrzeby debugowania w języku C/C++. Mamy nadzieję, że nowe funkcje uprością debugowanie pamięci aplikacji w języku C/C++. Jeśli masz jakieś sugestie, jak ulepszyć tę funkcję, daj nam znać, zgłaszając błąd.

Co dalej?

Więcej informacji: