W Chrome 92 wprowadziliśmy inspektora pamięci, czyli narzędzie do sprawdzania liniowych buforów pamięci. W tym artykule omówimy ulepszenia w Inspektorze do debugowania w języku C/C++ i powiązane z tym problemy techniczne.
Jeśli dopiero zaczynasz debugować w C/C++ i używać przeglądarki pamięci, zapoznaj się z tymi postami na blogu:
- Interesuje Cię szczegółowe debugowanie pamięci? Zobacz Wprowadzenie do narzędzia do inspekcji pamięci.
- Chcesz zapoznać się z pełnym zestawem narzędzi do debugowania C/C++? Zapoznaj się z artykułami Debugowanie WASM przy użyciu nowoczesnych narzędzi i Debugowanie WebAssembly Faster (Szybsze debugowanie WebAssembly).
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. Musisz znać rozmiar obiektu i zliczać bajty od jego początku. Na poniższym zrzucie ekranu wybrany jest pierwszy bajt tablicy int32
o 10 elementach, ale nie jest od razu jasne, które inne bajty należą do tej tablicy. Czy nie byłoby miło, gdyby można było od razu rozpoznać wszystkie bajty należące do obiektu?
Podświetlanie obiektów w narzędziu do inspekcji pamięci
Począwszy od Chrome 107, kontroler pamięci wyróżnia wszystkie bajty obiektu pamięci C/C++. Dzięki temu można je odróżnić od pamięci sąsiadującej.
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 element, aby przejść do pamięci obiektu. Jeśli 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 korzystanie z pamięci aplikacji w języku C/C++ jest łatwiejsze niż kiedykolwiek wcześniej.
Chcesz spróbować? Wykonaj te czynności:
- Chrome w wersji 107 lub nowszej.
- Zainstaluj rozszerzenie DWARF dla C/C++.
- Włącz debugowanie DWARF w Narzędzia deweloperskie > Ustawienia > Eksperymenty > Debugowanie WebAssembly: włącz obsługę DWARF.
- Otwórz tę stronę demonstracyjną.
- Postępuj zgodnie z instrukcjami wyświetlanymi na stronie.
Przykład debugowania
W tej sekcji przyjrzymy się błądowi testowemu, aby pokazać, jak można używać Menedżera pamięci do debugowania kodu C/C++. W przykładowym kodzie poniżej programista tworzy tablicę liczb całkowitych i decyduje się użyć arytmetyki wskaźnika, aby wybrać ostatni element. Niestety programista popełnił błąd podczas obliczania wskaźnika i teraz zamiast drukowania ostatniego elementu program drukuje 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 korzysta z kontrolera pamięci, aby debugować problem. Możesz obejrzeć to demonstracja. Najpierw sprawdzają tablicę w inspektorze pamięci i widzi, że tablica numbers
zawiera tylko liczby całkowite 1
, 2
, 3
i 4
, zgodnie z oczekiwaniami.
Następnie ujawnia zmienną lastNumber
w panelu Zakres i zauważa, że wskaźnik wskazuje liczbę całkowitą poza tablicą. Dzięki tej wiedzy programista zdaje sobie sprawę, że w wierszu 8 błędnie policzył przesunięcie wskaźnika. Powinien być to ptr + arraySize - 1
.
Chociaż jest to przykładowy przypadek, pokazuje on, jak wyróżnianie obiektów 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 zwrócić
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 deweloperskich, V8, rozszerzenia C/C++ DWARF i Emscripten.
Aby w pełni wykorzystać możliwości debugowania kodu C/C++ w Narzędziach deweloperskich, musisz mieć:
- Rozszerzenie C/C++ DWARF zainstalowane w Chrome
- pliki źródłowe C/C++, skompilowane do 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 Narzędzia deweloperskie mogą wyświetlać zmienne w C++, mimo że V8 faktycznie uruchamia WebAssembly. Jeśli chcesz zobaczyć przykład danych debugowania DWARF, przeczytaj ten post na blogu.
Co się dzieje, gdy odsłonisz 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 o typ danych i lokalizację 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óżnień w krokach w debugerze
Gdy ujawnisz obiekt w Narzędzie do inspekcji pamięci i przejdziesz do debugera, Narzędzie do inspekcji zachowa podświetlenie, jeśli uzna, że nadal jest ono przydatne. 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 w przypadku zmiennej powiązanej z poprzednim wyróżnieniem. Następnie porównuje lokalizacje i typy obiektów. Jeśli pasują, wyróżnienie pozostanie. W tym filmie widać pętlę for zapisującą dane do 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 wyróżnienia 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 opisaliśmy ulepszenia narzędzia Memory Inspector na potrzeby debugowania kodu 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?
Aby dowiedzieć się więcej, zapoznaj się z tymi artykułami: