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, jak poprawiliśmy inspektora debugowania w języku C/C++, a także opowiemy o wyzwaniach technicznych, które wystąpiły podczas tego procesu.

Oto kilka przydatnych postów na blogu dla osób, które dopiero zaczynają korzystać z debugowania C/C++ i korzystania z Inspektora pamięci:

Wstęp

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

Problemem było rozpoznanie bajtów obiektu w otaczającej go pamięci WebAssembly. Musisz znać rozmiar obiektu i liczyć bajty od samego początku. Na zrzucie ekranu poniżej wybrany jest pierwszy bajt 10-elementowej tablicy int32, ale nie można od razu stwierdzić, które pozostałe bajty należą do tablicy. Czy nie byłoby fajnie, gdyby udało Ci się natychmiast rozpoznać wszystkie bajty należące do obiektu?

Zrzut ekranu pokazujący oryginalny inspektor pamięci z podświetlonym bajtem

Podświetlanie obiektów w inspektorze pamięci

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

Zrzut ekranu zaktualizowanego inspektora pamięci z kolorowo podświetloną tablicą

Obejrzyj poniższy film, aby zobaczyć, jak działa Inspektor pamięci. Gdy w inspektorze pamięci wyświetlisz tablicę x, w przeglądarce pamięci pojawi się wyróżniona pamięć oraz nowy element prowadzący bezpośrednio nad nią. Ten element przypomina nazwę i typ wyróżnionego wspomnienia. Kliknij element, aby przejść do pamięci obiektu. Gdy najedziesz kursorem na element, pojawi się ikona krzyżyka – kliknij ją, aby usunąć zaznaczenie.

Gdy zaznaczysz bajt poza badanym obiektem, wyróżnienie zostanie rozmyte, aby nie rozpraszać Twojej uwagi. Aby ponownie go aktywować, ponownie kliknij dowolny bajt obiektu lub element.

Obsługa podświetlania obiektów nie jest ograniczona do tablic. Możesz też sprawdzać struktury, obiekty i wskaźniki. Dzięki tym zmianom przeglądanie pamięci aplikacji C/C++ jest łatwiejsze niż kiedykolwiek.

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

  • mieć Chrome 107 lub nowszą wersję,
  • Zainstaluj rozszerzenie C/C++ DWARF.
  • Włącz debugowanie DWARF, wybierając DevTools > Ustawienia. DevTools > DevTools > DevTools.
  • Otwórz tę stronę demonstracyjną.
  • Postępuj zgodnie z instrukcjami na stronie.

Przykład debugowania

W tej sekcji przyjrzyjmy się błędomowi zabawkowemu, by zilustrować, jak można użyć inspektora pamięci do debugowania C/C++. W przykładowym kodzie poniżej programista tworzy tablicę liczb całkowitych i postanawia użyć arytmetyki wskaźników, aby wybrać ostatni element. Niestety programista popełnił błąd w obliczaniu wskaźnika i teraz zamiast drukować ostatni element, program wyświetla 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 zwraca się do Inspektora pamięci, aby debugować problem. Możesz go śledzić w tej prezentacji. Najpierw sprawdzają tablicę w inspektorze pamięci i zauważają, że tablica numbers zawiera tylko liczby całkowite 1, 2, 3 i 4.

Zrzut ekranu z otwartym inspektorem pamięci ze sprawdzoną tablicą int32. Wszystkie elementy tablicy są podświetlone.

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

Zrzut ekranu z otwartym inspektorem pamięci, na którym widać podświetloną pamięć, na którą wskazuje wskaźnik o nazwie „lastNumber”. Wyróżniona pamięć znajduje się tuż za ostatnim bajtem wcześniej podświetlonej tablicy.

To tylko przykład z zabawkami, ale obrazuje, jak wyróżnianie obiektów skutecznie przekazuje rozmiar i położenie obiektów pamięci, co pomaga lepiej zrozumieć, co dzieje się w pamięci aplikacji w C/C++.

W jaki sposób Narzędzia deweloperskie określają, co wyróżniać

W tej sekcji przyjrzymy się ekosystemowi narzędzi, które umożliwiają debugowanie w języku C/C++. W szczególności dowiesz się, jak narzędzia deweloperskie, V8, rozszerzenie C/C++ DWARF i Emscripten umożliwiają debugowanie C/C++ w Chrome.

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

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

Ale po co? V8, czyli mechanizm JavaScript i WebAssembly w Chrome, nie wie, jak pisać kod w języku C lub C++. Dzięki Emscripten, kompilatorowi z obsługą języka C/C++ WebAssembly, możesz kompilować aplikacje utworzone w języku C lub C++ pod postacią WebAssembly i uruchamiać je w przeglądarce.

Podczas kompilacji emscripten umieści dane debugowania DWARF w pliku binarnym. Ogólnie dane te pomagają rozszerzeniu określić m.in., które zmienne WebAssembly odpowiadają zmiennym C/C++. W ten sposób możesz zobaczyć zmienne C++, mimo że V8 obsługuje WebAssembly. Jeśli Cię to ciekawi, przeczytaj tego posta na blogu, by poznać przykładowe dane debugowania DWARF.

Co się więc dzieje, gdy ujawniasz lastNumber? Gdy tylko klikniesz ikonę pamięci, Narzędzia deweloperskie sprawdzają zmienną, którą chcesz zbadać. Następnie wysyła do rozszerzenia zapytanie o typ danych i lokalizację elementu lastNumber. Gdy tylko rozszerzenie odpowie i przekaże te informacje, inspektor pamięci może wyświetlić odpowiedni wycinek pamięci i poznać jego typ oraz określić rozmiar obiektu.

Jeśli spojrzysz na funkcję lastNumber we wcześniejszym przykładzie, możesz zauważyć, że sprawdzaliśmy lastNumber: int *, ale element w Inspektorze pamięci zawiera wartość *lastNumber: int. Inspektor używa wskaźnika odwoławczego w stylu C++, aby wskazać typ wyświetlanego obiektu. Jeśli sprawdzisz wskaźnik, inspektor pokaże, do czego wskazuje.

Zachowywanie wyróżnionych informacji o krokach debugera

Gdy ujawnisz obiekt w Inspektorze pamięci i skorzystasz z debugera, zachowa on zaznaczenie, jeśli uzna, że nadal można go wykorzystać. Początkowo nie planowaliśmy tej funkcji, ale szybko zdaliśmy sobie sprawę, że może to utrudniać debugowanie. Wyobraź sobie, że musisz ponownie sprawdzić tablicę po każdym kroku, jak na filmie poniżej.

Gdy debuger trafi na nowy punkt przerwania, Inspektor pamięci ponownie wyśle zapytanie do wersji 8 i do rozszerzenia zmiennej powiązanej z poprzednim wyróżnieniem. Następnie porównuje lokalizację i typy obiektów. Jeśli pasują, wyróżnienie będzie się utrzymywać. W filmie powyżej występuje pętla for zapisująca w tablicy x. Te operacje nie zmieniają typu ani pozycji tablicy, więc pozostaje ona wyróżniona.

Być może zastanawiasz się, jak wpłynie to na wskaźniki. Jeśli wyróżnisz wyróżniony wskaźnik i przypiszesz go do innego obiektu, stare i nowe pozycje wyróżnionych obiektów będą się różnić, a wyróżnienie 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 poprzednią lokalizacją pamięci, usunięcie zaznaczenia jest łatwiejsze niż przechodzenie do nowej lokalizacji pamięci. Możesz ponownie zaznaczyć wskaźnik, klikając jego ikonę pamięci w panelu Zakres.

Podsumowanie

W tym artykule opisaliśmy nasze ulepszenia w inspektorze pamięci do debugowania w języku C/C++. Mamy nadzieję, że nowe funkcje ułatwią debugowanie pamięci aplikacji C/C++. Jeżeli masz jakieś sugestie, jak możemy ulepszyć tę usługę, daj nam znać, zgłaszając błąd.

Co dalej

Więcej informacji: