2010-10-30 30 views
10

Estoy tratando de crear una pequeña prueba de unidad con gdb, para un mcu incrustado que está controlado por OpenOCD (que me da control sobre mi objetivo a través de un servidor gdb).Cómo script de gdb (con python)? Ejemplo agregar puntos de interrupción, ejecutar, ¿qué punto de interrupción alcanzamos?

Así que me gustaría automatizar esto con algunos scripts de gdb.

me gustaría escribir una especie de guión para GDB que más o menos hace esto:

  1. Añadir un par de puntos de corte
  2. de inicio del programa
  3. Cuando nos detenemos, ¿de dónde se detiene (obtenga la información del cuadro)
  4. Salir.

¿Alguna idea?

Un ejemplo de cómo hacer esto en python gdb scripting sería bueno.

Gracias Johan


Nota:

Digamos que tenemos esta estructura básica, que más o menos entra en test_failed() o test_success() dependiendo de lo que el la función start_test() regresa.

void test_failed() {  
    while(1);  
} 

void test_success() {  
    while(1);  
} 

int main(void) {  
    int status = start_test();  

    if(status > 0) {  
     test_failed();  
    }  
    test_success(); 

    while(1);  
} 

Para hacer esto manualmente en GDB es delantero muy estrecho,

(gdb) break test_success 
Breakpoint 1 at 0x20: file src/main.c, line 9. 
(gdb) break test_failed 
Breakpoint 2 at 0x18: file src/main.c, line 5. 
(gdb) cont 
Continuing. 

Breakpoint 1, test_success() at src/main.c:9 
9  while(1); 
(gdb) frame 
#0 test_success() at src/main.c:9 
9  while(1); 
(gdb) 

Así que el siguiente paso que probé fue añadir comandos de los GDB en un script de inicio de GDB que más o menos sólo se miraba como este .

break test_success 
break test_failed 
target remote localhost:3333 
cont 
frame 

e iniciarlo con

arm-none-eabi-gdb --batch --command=commands.gdb main.elf 

Y este tipo de obras, pero no es muy agradable. ¿Cómo puedo hacer esto con los "nuevos y geniales" scripts de Python, que gdb parece ser compatible.

+1

También puedes ver el tutorial sobre la wiki: https://sourceware.org/gdb/wiki/PythonGdbTutorial –

Respuesta

8

FYI las versiones recientes de gdb son programables en Python. Puede llamar al código python desde la línea de comando gdb. Esto abre un mundo completamente nuevo, revise la documentación relevante. Desde la línea de comando de marcha:

info gdb extending python 

Si no te gusta el navegador de información basada en texto, aquí está uno (entre muchos?) Alternativo, navegador gráfico:

yelp 'info:gdb?Extending GDB' 

Aquí es una GDB muestra -python script. Adjunta gdb al primer "your_program" que se encuentra en ejecución.

#!/usr/bin/python 

import subprocess 
import string 

def backquotes(cmdwords): 
     output = subprocess.Popen(cmdwords, stdout=subprocess.PIPE).communicate()[0] 
     return output.strip() 

pid = backquotes(['pgrep', 'your_program']) 

gdb.execute("attach " + str(pid)) 
+0

No es casualidad tener algunos buenos enlaces? o un howto o algo que puede empujarme en la dirección correcta (ya que el estilo de scripts de gdb antiguo no es muy óptimo ...) – Johan

2

OK, encontré la respuesta al hacer la pregunta ... y fue algo realmente simple.

No debe usar el "--command" y el "--eval" al mismo tiempo si espera que se ejecuten en un orden específico.

Una manera más predecible es poner todo en el archivo commands.gdb e ignorar --eval.

Por lo tanto, se convierte en algo parecido a esto:

arm-none-eabi-gdb --batch --command=commands.gdb main.elf 

Dónde comandos.GDB se ve así:

break test_success 
break test_failed 
target remote localhost:3333 
cont 
frame 

Pero probablemente sería mucho más agradable que ver esto con algo como pitón en su lugar.

5

Un ejemplo reducido que actualmente estoy usando:

class DebugPrintingBreakpoint(gdb.Breakpoint): 
    debugging_IDs = frozenset({37, 153, 420}) 
    def stop(self): 
     top = gdb.newest_frame() 
     someVector = top.read_var('aVectorVar') 
     # Access the begin() & end() pointer of std::vector in GNU Standard C++ lib 
     first = someVector['_M_impl']['_M_start'] 
     last = someVector['_M_impl']['_M_finish'] 
     values = [] 
     while first != last: 
      values.append(int(first.dereference()['intID'])) 
      first = first + 1 
     if not set(values) & debugging_IDs: 
      return False # skip: none of the items we're looking for can be found by ID in the vector on the stack 
     print("Found other accompanying IDs: {}".format(values)) 
     return True # drop to gdb's prompt 
# Ensure shared libraries are loaded already 
gdb.execute("start") 
# Set our breakpoint, which happens to reside in some shared lib, hence the "start" previously 
DebugPrintingBreakpoint("source.cpp:42") 
gdb.execute("continue") 

puede ejecutar esta secuencia de comandos desde el símbolo del BGF como esto:

(gdb) source script.py 

O desde la línea de comandos:

$ gdb --command script.py ./executable.elf 

Ver el perfil mplete GDB Python API docs para más información.

+0

No tengo 'gdb.continue' en 7.7.1, ¿qué versión eres? –

+0

@CiroSantilli 视 iro iro iro 视 视 Creo que es un error tipográfico que hice. Probablemente debería ser: gdb.execute ("continue")? ¿Puedes confirmar que funciona (no tengo acceso inmediato a la configuración de prueba en la que utilicé ese script ^^)? – Giel

+1

'gdb.execute (" continuar ")' funciona :-) –

1

Solo quería señalar algo que encuentro confuso cada vez que vuelvo a este tema (Nota, actualmente estoy en Ubuntu 14.04, GNU gdb (Ubuntu 7.7.1-0ubuntu5 ~ 14.04.3) 7.7.1):

en primer lugar, hay referencias acerca de ser "posible invocar gdb como intérprete":

... sentido, se podría escribir un archivo de texto de la escritura con la línea shebang #!/usr/bin/gbd -P o #!/usr/bin/gbd --python, a continuación, escribir código Python en ella, y luego hacerlo ejecutable chmod +x pygdbscript, a continuación, ejecute ./pygdbscript; ... pero como en este post:

..., me sale gdb: unrecognized option '--python' si intento algo por el estilo. Aparentemente esta opción es/era una característica en alguna rama "arquero" de gdb?!


Por lo tanto, con el fin de ejecutar un script Python en gdb, en realidad hay dos maneras:

  • un nombre al archivo de script con la extensión .py; test.py decir aquí:
def Something(): 
    print("hello from python") 

Something() 
gdb.execute("quit"); 

Tenga en cuenta, en este caso, sólo hay que escribir código Python llanura; y no necesita import gdb para acceder al objeto gdb. Esto puede funcionar con cualquiera de:

gdb -x test.py 
gdb -x=test.py 
gdb --command test.py 
gdb --command=test.py 
gdb -command test.py 
gdb -command=test.py 

... que parecen ser equivalentes, como el resultado de cualquiera de estos es la misma impresión, antes de gdb se instruye a la salida de la secuencia de comandos:

$ gdb -x=test.py 
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1 
... 
For help, type "help". 
Type "apropos word" to search for commands related to "word". 
hello from python 

NOTA que para este caso, también nombres como test.gdb.py se interpretarán como scripts puros de Python, desde entonces terminan en .py.

  • Nombre su script cualquier otra cosa - siempre y cuando se hace no.py extremo con la extensión; decir test.pygdb aquí:
python 
def Something(): 
    print("hello from python") 

Something() 
gdb.execute("quit"); 
end 

En este caso, gdb interpreta el guión como un guión gdb, es decir, con gdb comandos - y eso significa, que todo lo que el código Python es posible que desee escribir aquí, obligada se envuelve en "python" como línea de partida y "end" al final del código de Python. Una vez más, sería llamado con cualquiera de estas llamadas equivalentes:

gdb -x test.pygdb 
gdb -x=test.pygdb 
gdb --command test.pygdb 
gdb --command=test.pygdb 
gdb -command test.pygdb 
gdb -command=test.pygdb 

... y entonces la salida es la misma que en el caso anterior (ya que es la misma secuencia de comandos de Python en ejecución):

$ gdb -x test.pygdb 
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1 
... 
hello from python 

y en respuesta a OP: si el código C en OP está en /tmp/myprog.c - con un int start_test() { return rand() % 50; } añadido en la parte superior, de lo contrario no se compilará -, y se compila con la gcc -g myprog.c -o myprog.exe en /tmp/myprog.exe; entonces se puede usar un script myprog.gdb.py así:

# need to specify the executable file which we debug (in this case, not from command line) 
# here `gdb` command `file` is used - it does not have a Python equivalent (https://sourceware.org/gdb/onlinedocs/gdb/Objfiles-In-Python.html#index-Objfile_002eframe_005ffilters) 
# so we must use gdb.execute: 

myexefile="/tmp/myprog.exe" 
print(""" 
### myprog.gdb.py is running: """ + myexefile + """ - and adding breakpoints: 
""") 

gdb.execute("file " + myexefile) 
gdb.execute("set pagination off") 

ax = gdb.Breakpoint("test_success") 
bx = gdb.Breakpoint("test_failed") 

gdb.execute("run") 

# here the program will break, so we can do: 

print(""" 
### myprog.gdb.py after the break - current stack frame: 
""") 

current_frame_at_break = gdb.selected_frame() 
print(current_frame_at_break) # instead of gdb.execute("frame") 

print(""" 
### myprog.gdb.py - backtrace: 
""") 

gdb.execute("backtrace 2") 

print(""" 
### myprog.gdb.py - go to frame that called current frame: 
""") 

parent_frame = current_frame_at_break.older() 
print(parent_frame) 
status_var = parent_frame.read_var("status") 
print("status_var is: ", status_var) 

... a continuación, ejecutar este script con:

$ gdb -x myprog.gdb.py 
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1 
.... 
For help, type "help". 
Type "apropos word" to search for commands related to "word". 

### myprog.gdb.py is running: /tmp/myprog.exe - and adding breakpoints: 

Breakpoint 1 at 0x400565: file myprog.c, line 8. 
Breakpoint 2 at 0x40055f: file myprog.c, line 4. 

Breakpoint 2, test_failed() at myprog.c:4 
4  while(1);  

### myprog.gdb.py after the break - current stack frame: 

{stack=0x7fffffffdc70,code=0x40055b,!special} 

### myprog.gdb.py - backtrace: 

#0 test_failed() at myprog.c:4 
#1 0x000000000040058c in main() at myprog.c:15 

### myprog.gdb.py - go to frame that called current frame: 

{stack=0x7fffffffdc90,code=0x400567,!special} 
status_var is: 33 
(gdb) 

Tenga en cuenta que al final de este script, sigue siendo el (gdb) modo interactivo, y puedes usarlo normalmente aquí; si no necesita el aviso interactivo, puede hacer gdb.execute("quit"); como en los scripts anteriores para forzar que gdb salga al final de la ejecución del script.

También, para un ejemplo de la clase de subclases punto de interrupción en gdb de Python, ver How to print the current line of source at breakpoint in GDB and nothing else?

Cuestiones relacionadas