Extensión del Inspector de memoria para la depuración de C/C++

En Chrome 92, presentamos el Inspector de memoria, una herramienta para inspeccionar búferes de memoria lineales. En este artículo, analizaremos cómo mejoramos el Inspector para la depuración C/C++ y los desafíos técnicos que se encontraron en el camino.

Estas son algunas entradas de blog relevantes si es la primera vez que realizas la depuración de C/C++ y el Inspector de memoria:

Introducción

El Inspector de memoria te ofrece opciones de depuración más potentes para búferes de memoria lineales. En el caso de C/C++, puedes inspeccionar los objetos de memoria de C/C++ en la memoria de WebAssembly.

Reconocer los bytes de tu objeto entre la memoria de WebAssembly circundante fue un problema. Debes conocer el tamaño del objeto y contar los bytes desde su inicio. En la siguiente captura de pantalla, se seleccionó el primer byte de un array int32 de 10 elementos, pero no es claro de inmediato qué otros bytes pertenecen al array. ¿No sería genial si pudieras reconocer al instante todos los bytes que pertenecen al objeto?

Captura de pantalla del inspector de memoria original con un solo byte destacado

Resaltado de objetos en el Inspector de memoria

A partir de Chrome 107, el Inspector de memoria destaca todos los bytes de un objeto de memoria C/C++. Esto te ayuda a diferenciarlos de la memoria que los rodea.

Captura de pantalla del inspector de memoria actualizado con un array destacado con colores intensos

Mira el siguiente video para ver el Inspector de memoria en acción. A medida que revelas el array x en el Inspector de memoria, la memoria destacada aparece en Memory Viewer junto con un chip nuevo justo encima. Este chip te recuerda el nombre y el tipo de la memoria destacada. Haz clic en el chip para ir a la memoria del objeto. Si colocas el cursor sobre el chip, aparecerá un ícono de cruz. Haz clic en él para quitar el elemento destacado.

Cuando seleccionas un byte fuera del objeto que inspeccionas, el enfoque se quita para evitar distraerte. Para volver a enfocarlo, haz clic en cualquiera de los bytes del objeto o en el chip.

La compatibilidad con el resalte de objetos no se limita a los arrays. También puedes inspeccionar structs, objetos y punteros. Estos cambios hacen que explorar la memoria de tus apps de C/C++ sea más fácil que nunca.

¿Quieres probarlo? Deberás hacer lo siguiente:

  • Tener Chrome 107 o una versión posterior
  • Instala la extensión de DWARF de C/C++.
  • Habilita la depuración DWARF en DevTools > Configuración. Configuración > Experimentos > Depuración de WebAssemble: Habilita la compatibilidad con DWARF.
  • Abre esta página de demostración.
  • Sigue las instrucciones de la página.

Ejemplo de depuración

En esta sección, observemos un error de prueba para ilustrar cómo puedes usar el Inspector de memoria para la depuración C/C++. En la siguiente muestra de código, un programador crea un array de enteros y decide usar la aritmética de puntero para seleccionar el último elemento. Lamentablemente, el programador cometió un error en el cálculo de su puntero y ahora, en lugar de imprimir el último elemento, el programa imprime valores sin sentido.

#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;
}

El programador recurre al Inspector de memoria para depurar el problema. Puedes continuar con esta demostración. Primero, inspeccionan el array en el Inspector de memoria y ven que el array numbers contiene solo los números enteros 1, 2, 3 y 4, como se esperaba.

Captura de pantalla del inspector de memoria abierto con un array int32 inspeccionado. Todos los elementos del array están destacados.

A continuación, revela la variable lastNumber del panel Scope y observa que el puntero apunta a un número entero fuera del array. Con este conocimiento, el programador se da cuenta de que contó de manera incorrecta el desplazamiento del puntero en la línea 8. Debería haber sido ptr + arraySize - 1.

Captura de pantalla del inspector de memoria abierto que muestra la memoria destacada a la que apunta un puntero llamado &quot;lastNumber&quot;. La memoria destacada se encuentra justo después del último byte del array previamente destacado.

Aunque este es un ejemplo de muestra, ilustra cómo el resalte de objetos transmite de manera efectiva el tamaño y la posición de los objetos de memoria, lo que puede ayudarte a comprender mejor lo que sucede en la memoria de tu app C/C++.

Cómo las Herramientas para desarrolladores descubren qué destacar

En esta sección, veremos el ecosistema de herramientas que permiten la depuración con C/C++. Específicamente, aprenderás cómo Herramientas para desarrolladores, V8, la extensión C/C++ DWARF y Emscripten hacen posible la depuración C/C++ en Chrome.

Para desbloquear toda la potencia de la depuración C/C++ en Herramientas para desarrolladores, necesitas dos cosas:

  • La extensión de DWARF de C/C++ instalada en Chrome
  • Archivos de origen C/C++ compilados en WebAssembly con el último compilador Emscripten, como se indica en esta entrada de blog.

Pero ¿por qué? V8 , el motor JavaScript y WebAssembly de Chrome, no sabe cómo ejecutar C o C++. Gracias a Emscripten, un compilador de C/C++ a WebAssembly, puedes compilar apps integradas en C o C++ como WebAssembly y ejecutarlas en el navegador.

Durante la compilación, emscripten incorporará datos de depuración DWARF en tu objeto binario. En términos generales, estos datos ayudan a la extensión a descubrir qué variables de WebAssembly corresponden a tus variables C/C++ y mucho más. De esta manera, Herramientas para desarrolladores puede mostrarte tus variables de C++ a pesar de que V8 realmente ejecuta WebAssembly. Si te interesa, consulta esta entrada de blog para ver un ejemplo de datos de depuración de DWARF.

¿Qué sucede en realidad cuando revelas el lastNumber? En cuanto haces clic en el ícono de memoria, Herramientas para desarrolladores verificará qué variable quieres inspeccionar. Luego, consulta la extensión sobre el tipo de datos y la ubicación de lastNumber. En cuanto la extensión responde con esa información, el Inspector de memoria puede mostrar la porción relevante de memoria y, dado su tipo, también puede mostrarte el tamaño del objeto.

Si observas lastNumber en el ejemplo anterior, es posible que notes que inspeccionamos lastNumber: int *, pero el chip del Inspector de memoria dice *lastNumber: int, ¿qué proporciona? El inspector usa la desreferencia de punteros de estilo C++ para indicar el tipo del objeto que se te muestra. Si inspeccionas un puntero, el inspector te mostrará a qué apunta.

Aspectos destacados persistentes en los pasos del depurador

Cuando revelas un objeto en el Inspector de memoria y realizas un paso con el depurador, el Inspector conserva el resaltado si cree que todavía es aplicable. En un principio, no teníamos esta función en nuestra hoja de ruta, pero rápidamente nos dimos cuenta de que esto pone en riesgo tu experiencia de depuración. Imagina que tienes que volver a inspeccionar el array después de cada paso, como en el siguiente video.

Cuando el depurador alcanza un nuevo punto de interrupción, el Inspector de memoria vuelve a consultar V8 y la extensión para obtener la variable asociada con el momento destacado anterior. Luego, compara las ubicaciones y los tipos de los objetos. Si coinciden, el momento destacado persiste. En el video de arriba, hay un bucle for que escribe en el array x. Estas operaciones no cambian el tipo ni la posición del array, por lo que permanece destacada.

Es posible que te preguntes cómo afecta esto a los punteros. Si tienes un puntero destacado y lo vuelves a asignar a un objeto diferente, las posiciones anteriores y nuevas de los objetos destacados difieren y desaparece el resaltado. Dado que el objeto recientemente apuntado puede residir en cualquier lugar de la memoria de WebAssembly y es probable que tenga poca relación con la ubicación de la memoria anterior, quitar el resaltado es más claro que saltar a una nueva ubicación de memoria. Para volver a destacar el puntero, haz clic en el ícono de memoria en el panel Scope.

Conclusión

En este artículo, se describieron las mejoras que realizamos en el Inspector de memoria para la depuración C/C++. Esperamos que las nuevas funciones simplifiquen la depuración de la memoria de tus apps de C/C++. Si tienes sugerencias para seguir mejorando, informa un error para informarnos.

¿Qué sigue?

Para obtener más información, consulta: