1. Cómo conservar la información del símbolo de depuración
Si queremos poder ver claramente cada línea de código, información de la pila de llamadas, nombres de variables, nombres de funciones, etc. durante la depuración, podemos agregar la opción -g al compilar programas con gcc .
Ejemplo: con gcc -g hello.c -o hello1
información gdb ./hello1
Después de la ejecución, la salida de información es la siguiente, y la última línea indica que gdb se cargó correctamente.
Ejemplo de contador: gcc hello.c -o hello2
Ejecutar gdb ./hello2
, la salida de información es la siguiente, la última línea indica que gdb no se pudo cargar.
2. Cómo eliminar la información del símbolo de depuración
Hay dos formas: una se menciona en la Sección 1 anterior, sin agregar la opción -g al compilar ; la otra es usar el comando strip . Por ejemplo, se sabe que el programa ejecutable hello1 tiene información de símbolo de depuración, luego de ejecutarlo con el comando strip, ingrese a gdb depuración, y la información impresa es la siguiente:
3. ¿Cuáles son las formas de iniciar la depuración de gdb?
Depure el programa de destino
Inicie el programa directamente e ingrese al modo de depuración: gdb filename
, comogdb ./hello
Depurar proceso adicional
Si desea depurar un programa en ejecución pero no desea reiniciarlo, puede usar sudo gdb attach pid
sudo para indicar el uso de la autorización de root y pid es el PID del proceso del programa de destino ( ps -ef | grep filename
que se puede obtener ).
Después de adjuntar el programa de destino con gdb, el depurador se detendrá.En este momento, puede usar
continue
el comando para permitir que el programa continúe ejecutándose, o agregar un punto de interrupción correspondiente para continuar ejecutando el programa.
Cuando desee finalizar la depuración después de depurar el programa sin afectar el trabajo normal del proceso actual, puede ingresardetach
comandos en la interfaz de línea de comandos de gdb para separar el programa del depurador de gdb.
Depuración del archivo central
El programa del servidor se bloquea repentinamente después de ejecutarse durante un período de tiempo. Puede localizar la causa depurando el archivo central generado por el programa bloqueado:gdb filename corefile
Ejemplo: testcore.c
Ejecute el programa y vea el archivo central generado después del bloqueo:
Use gdb testcore core
el comando para depurar la causa del bloqueo:
4. Cómo generar un archivo central cuando el programa falla
1) Verifique si el sistema ha habilitado el mecanismo de generación de archivos centrales para fallas del programa. Comando: El valor del parámetro de tamaño de archivo centralulimit -a
en la figura anterior es 0, lo que indica que el sistema no ha habilitado la generación de archivos centrales.
2) Encienda el mecanismo de bloqueo del programa para generar archivos principales, comando:, ulimit -c unlimited
ilimitado significa tamaño ilimitado, pasos de operación:
Se
ulimit -c unlimited
agregará a /etc/profile;
Ejecutarsource /etc/profile
;
Ejecutarulimit -a
para ver si tiene efecto.
5. Especifique el directorio de generación del archivo central
En circunstancias normales, de acuerdo con la configuración predeterminada del sistema, el núcleo generalmente se genera en el directorio de ejecución del programa después de que el programa falla; si es necesario para el proyecto, especifique el directorio de generación del archivo central y nosotros puede modificar /etc/sysctl.conf
el archivo paso:
1) Primero, agregue el directorio de generación del archivo principal en /etc/sysctl.conf: kernel.core_pattern=/home/myubuntu/myfile/core_dump/core-%e-%p-%t
descripción del parámetro:
%%: Equivalente a %
%p: Equivalente a
%u: Equivalente
a %g: Equivalente a
%s: Equivalente al número de la señal que provocó el volcado
%t: Equivalente a la hora del volcado
%e: Equivalente a el nombre del ejecutable
% h: Equivalente a hostname
2) Luego, ejecuta sudo sysctl -p /etc/sysctl.conf
el comando para que surta efecto.
3) Comprobar si ha surtido efecto:cat /proc/sys/kernel/core_pattern
4) Genere el archivo central y confirme si se ha generado en el directorio especificado
6. Descripción de los comandos comunes
7. Funcionamiento práctico de los comandos comunes
Tome hello.c como ejemplo
#include <stdio.h>
int main()
{
int i = 1;
int j = 2;
int m = i+j;
for(int k = 0; k < 100; k++)
{
printf("number:%d\n");
}
printf("hello world!\n");
return 0;
}
7.1 ejecutar comando
De forma predeterminada, después de ejecutar gdb filename , el programa en realidad no comienza a ejecutarse, debe usar el comando ejecutar para iniciarlo.
7.2 Pausar y continuar comandos
El comando ctrl+c puede pausar el programa:
El programa puede continuar ejecutándose con el comando continuar :
7.3 Comando romper añadir punto de interrupción
Hay varias formas de agregar un punto de interrupción:
1) break functionname : significa agregar un punto de interrupción en la entrada del nombre de la función;
2) break LineNo : agrega un punto de interrupción en el número de línea del archivo actual en LineNo;
3) break filename: LineNo : agregue un punto de interrupción en el número de línea del archivo de nombre de archivo LineNo.
7.4 Interrupción de información, habilitar, deshabilitar, eliminar el comando de operación de punto de interrupción
1) información de punto de interrupción de consulta de ruptura de información
2) deshabilitar número deshabilitar punto de interrupción
3) habilitar número habilitar punto de interrupción
4) eliminar número eliminar punto de interrupción
7.4 rastreo inverso, operación de apilamiento de cuadros
1) retroceder para ver la pila de llamadas actual
2) el marco cambia a la pila especificada
7.5 imprimir Imprimir valor variable
1) Imprimir el valor de la variable en la posición actual
2) Cambiar a otras pilas e imprimir los valores de las variables de esa pila
3) El comando de impresión no solo puede mostrar valores de variables, sino también mostrar los valores de resultados de cálculo de expresiones que realizan ciertas operaciones, e incluso mostrar los valores de resultados de ejecución de algunas funciones.
Por ejemplo, entrada: p &i
el valor de la dirección de la variable i se puede generar
También puede p i+j+m
imprimir
7.6 código de visualización de lista
1) La lista muestra el código cercano que está ejecutando el código
2) lista: muestra el código que se ejecuta antes de su código
3) la ubicación de la lista muestra los códigos cercanos del código de ubicación especificado
- list lineno , como list 11, muestra el código fuente cerca del número de línea 11
- list filename:lineno , como list kafkaconsumer.c:101 muestra el código cerca de la línea 101 en el archivo kafkaconsumer.c
- list functionname , como list main enumera los códigos cerca de la función principal en el archivo de código actual
- list filename:functionname , como list kafkaconsumer.c:dealdata muestra el código cerca de la función dealdata en el archivo kafkaconsumer.c
- Enumere primero, último , especifique el rango específico de códigos mostrados.
4) establezca el conteo de tamaño de lista para establecer el número de líneas de código mostradas
list muestra 10 líneas de código de forma predeterminada, que se pueden configurar con el comando set listsize count .
5) mostrar tamaño de lista para ver el número de líneas de código mostradas
7.7 funciones de información muestra los nombres de todas las funciones
El comando de funciones de información mostrará los nombres de todas las funciones en el programa, formatos de parámetros, tipos de valores devueltos y en qué
archivo de código se encuentran las funciones.
7.8 ptype genera el tipo de una variable
1) Salida del tipo de una variable
2) Si la variable es un tipo de estructura, ptype no solo puede enumerar el tipo de esta variable, sino también enumerar los nombres de campo de cada variable miembro en detalle
El comando de subproceso 7.9 se puede usar para depurar subprocesos múltiples
1) subproceso de información para ver el estado de ejecución de todos los subprocesos del proceso actual
2) Número de subproceso cambia a otros subprocesos.
Por ejemplo, al cambiar al subproceso 2, se agrega un asterisco delante del subproceso 2 (a qué subproceso se cambia, qué subproceso tendrá un asterisco)
7.10 next Ejecuta la siguiente instrucción
El siguiente comando se llama "pasar por encima" en términos de depuración, es decir, salta directamente cuando encuentra una llamada de función y no ingresa al cuerpo de la función. Si presiona directamente la tecla Intro en la interfaz de línea de comandos de GDB, el valor predeterminado es volver a ejecutar el último comando. Por lo tanto, cuando use el siguiente comando para depurar paso a paso, no necesita ingresar el comando n repetidamente, simplemente presione Entrar directamente .
7.11 paso ejecuta la siguiente instrucción
El comando de paso (abreviado como s) es "entrar en" (entrar), como sugiere el nombre, es encontrar una llamada de función e ingresar al interior de la función.
7.12 volver y terminar ejecutar la función actual
1) La función del comando de retorno es finalizar la ejecución de la función actual
2) El comando de finalización ejecutará la función hasta que salga de la función normalmente. Si quedan códigos en la función actual que no se han ejecutado
, no se ejecutarán.
7.13 hasta correr a una línea especificada
La siguiente figura usa la instrucción hasta para hacer que el programa se ejecute directamente desde el punto de interrupción de 18 líneas hasta las 23 líneas especificadas.
7.14 salto Saltar a la posición especificada
Sintaxis básica: saltar <ubicación>
jump lineno , salta al número de línea ejecutado - posición absoluta, como jump 29
7.15 desmontar Ver código de montaje
desensamblar Al realizar una depuración avanzada, es posible que deba consultar las instrucciones de ensamblaje de una determinada pieza de código para solucionar problemas, o
al depurar algunos programas de versión de lanzamiento sin información de depuración, puede usar el código desensamblado para localizar el problema.
7.16 establecer argumentos y mostrar comandos de argumentos
1) establecer argumentos establecer parámetros de entrada del programa
2) mostrar argumentos ver parámetros de entrada del programa
7.17 vigilancia del reloj
El comando watch es un comando poderoso que se puede usar para monitorear una variable o una sección de la memoria.Cuando el valor de esta variable o la memoria cambia, GDB se descompondrá. Una variable o una dirección de memoria que se esté monitoreando generará un punto de vigilancia (punto de observación).
Uso: ver nombre de variable o dirección de memoria
1) Variable entera: int i; watch i
2) Tipo de puntero: char*p; watch p o watch *p
Nota: hay una diferencia entre watch p y watch *p, el primero es para ver *(&p), que es la propia variable p; el segundo es la contenido de la memoria apuntada por p . Necesitamos verificar la dirección, porque el propósito es ver cómo cambian los datos en una determinada dirección de memoria.
3) Una matriz o memoria: char buf[128], watch buf
aquí es para monitorear los datos de buf 128. En este momento, en lugar de usar puntos de interrupción de hardware, se realiza mediante interrupciones suaves. El uso de interrupciones suaves para verificar las variables de memoria consume más recursos de la CPU, y especificar la dirección con precisión es una interrupción de hardware.
Nota: Cuando el punto de observación establecido es una variable local, después de que la variable local deje de ser válida, el punto de observación también se invalidará.
7.18 Monitoreo de pantalla
Las variables o direcciones de memoria monitoreadas por el comando de visualización generarán automáticamente los valores de estas variables o memoria cada vez que se interrumpa el programa.
8. Consejos de depuración
8.1 Cómo mostrar el resultado de la impresión por completo
Cuando el comando de impresión imprime una cadena o matriz de caracteres, si la cadena es demasiado larga, el comando de impresión no la mostrará completamente de forma predeterminada; se
puede configurar con el comando set print element 0 para mostrar completamente todas las cadenas de la variable.
8.2 Deshabilitar el cambio de subprocesos en subprocesos múltiples
Múltiples subprocesos llaman a la misma función. Cuando ingresamos a la función y realizamos la depuración línea por línea, originalmente esperábamos ejecutar las siguientes 11 líneas después de ejecutar la línea 10. En este momento, el sistema puede cambiar a otro subproceso y saltar a Al código fuente de la línea 5 de la función, causando confusión.
Para evitar la interferencia de otros subprocesos, puede usar el comando set scheduler-locking on para bloquear el flujo de ejecución en el subproceso actual; también puede usar el comando set scheduler-locking off para desbloquearlo.
8.3 Puntos de corte condicionales
En la depuración real, generalmente usamos tres tipos de puntos de interrupción: puntos de interrupción ordinarios, puntos de interrupción condicionales y puntos de interrupción de hardware.
1) Los puntos de interrupción de hardware también se denominan puntos de interrupción de datos. Dichos puntos de interrupción son en realidad algunos puntos de interrupción agregados con el comando watch presentado en el curso anterior (algunos puntos de interrupción agregados por el reloj se realizan a través de interrupciones suaves, no puntos de interrupción de hardware). Se activa un punto de interrupción de hardware cuando cambia la dirección de memoria supervisada o el valor de la variable.
2) Los puntos de interrupción ordinarios son puntos de interrupción distintos de los puntos de interrupción condicionales y los puntos de interrupción de hardware.
3) Un punto de interrupción condicional es un punto de interrupción que se activa solo cuando se cumple una determinada condición
8.4 Usar gdb para depurar programas multiproceso
Un programa multiproceso se refiere a un programa con una estructura similar a Nginx.El proceso principal genera procesos secundarios a través de la función fork().
1) Método 1: utilice gdb para depurar el proceso principal y, después de que aparezca el proceso secundario fork(), utilice gdb para adjuntarlo al proceso secundario. Al mismo tiempo, debe volver a abrir una ventana de sesión para la depuración.
2) Método 2: utilice el modo show follow-fork para ver el valor actual, o configure el modo follow-fork para establecer si GDB continuará depurando el proceso principal o el proceso secundario cuando un proceso bifurca un nuevo proceso secundario (el valor es hijo), el valor predeterminado es el proceso padre (el valor es padre).