2012-07-05 10 views
17

Recientemente he estado experimentando con Rcpp (en línea) para generar archivos DLL que realizan varias tareas en las entradas R suministradas. Me gustaría poder depurar el código en estas DLL línea por línea, dado un conjunto específico de entradas R. (estoy trabajando en Windows.)Depuración (línea por línea) de DLL generada por Rcpp en Windows

Para ilustrar, vamos a considerar un ejemplo específico que cualquier persona debería ser capaz de ejecutar ...

El código siguiente es un cxxfunction muy simple, que simplemente se duplica el vector de entrada . Sin embargo, tenga en cuenta que hay una variable adicional myvar que cambia el valor varias veces pero que no afecta la salida; esto se ha agregado para que podamos ver cuándo el proceso de depuración se está ejecutando correctamente.

library(inline) 
library(Rcpp) 

f0 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body=' 
    Rcpp::NumericVector xa(a); 
    int myvar = 19; 
    int na = xa.size(); 
    myvar = 27; 
    Rcpp::NumericVector out1(na); 
    for(int i=0; i < na; i++) { 
     out1[i] = 2*xa[i]; 
     myvar++; 
    } 
    myvar = 101; 
    return(Rcpp::List::create(_["out1"] = out1)); 
') 

Después corremos el anterior, escribiendo el comando

getLoadedDLLs() 

resultado será una lista de archivos DLL en la sesión R. El último de la lista debe ser el DLL creado por el proceso anterior - tiene un nombre temporal al azar, que en mi caso es

file7e61645c 

la columna "Nombre" muestra que cxxfunction ha puesto este DLL en la ubicación tempdir(), que para mí es actualmente

C:/Users/TimP/AppData/Local/Temp/RtmpXuxtpa/file7e61645c.dll 

Ahora, la manera obvia de llamar a la DLL es a través de f0, de la siguiente manera

> f0(c(-7,0.7,77)) 

$out1 
[1] -14.0 1.4 154.0 

Pero podemos, por supuesto, También llamar a la DLL directamente por su nombre usando el comando .Call:

> .Call("file7e61645c",c(-7,0.7,77)) 

$out1 
[1] -14.0 1.4 154.0 

Así que he llegado al punto en el que estoy llamando a una DLL independiente directamente con la entrada R (en este caso, el vector c(-7,0.7,77)), y hacer que el retorno la respuesta correcta a R.

lo que realmente necesito, sin embargo, es una instalación para la línea por línea depuración (usando gDB, creo) que me permitirá observar el valor de myvar se establece en 19, 27, 28, 29, 30 y finalmente 101 a medida que el código progresa. El ejemplo anterior se ha configurado deliberadamente para que llamar al DLL no nos diga nada sobre myvar.

Para aclarar, la "condición de victoria" aquí es poder observar el cambio de myvar (ver el valor myvar = 19 sería el primer paso) sin agregar nada más al cuerpo del código. Obviamente, esto puede requerir cambios en la forma en que se compila el código (¿hay alguna configuración de modo de depuración para encender?), O la forma en que se llama R, pero no sé por dónde empezar. Como se señaló anteriormente, todo esto está basado en Windows.

Nota final: en realidad, realicé algunas modificaciones menores en una copia de cxxfunction para que la DLL de salida (y el código que contiene) reciba un nombre definido por el usuario y se encuentre en un directorio definido por el usuario, que un nombre temporal y una ubicación. Pero esto no afecta la esencia de la pregunta.Menciono esto solo para enfatizar que debería ser bastante fácil alterar la configuración de compilación si alguien me da un codazo :)

Para completar, el ajuste verbose = TRUE en la llamada cxxfunction original anterior muestra el argumento de compilación para ser del siguiente forma:

C:/R/R-2.13.2/bin/i386/R CMD SHLIB file7e61645c.cpp 2> file7e61645c.cpp.err.txt 
g++ -I"C:/R/R-213~1.2/include" -I"C:/R/R-2.13.2/library/Rcpp/include"  -O2 -Wall -c file7e61645c.cpp -o file7e61645c.o 
g++ -shared -s -static-libgcc -o file7e61645c.dll tmp.def file7e61645c.o C:/R/R-2.13.2/library/Rcpp/lib/i386/libRcpp.a -LC:/R/R-213~1.2/bin/i386 -lR 

Mi versión adaptada tiene un argumento compilación idéntica a la anterior, excepto que la cadena "file7e61645c" se sustituye por todas partes por la elección del usuario del nombre (por ejemplo, "testdll") los archivos y la pertinentes copian a una ubicación más permanente.

Gracias de antemano por su ayuda chicos :)

+0

No puedo ayudarlo directamente, pero sé que Dirk, etc. siempre me ayuda. Sin embargo, generalmente hacen negocios en la [lista de correo electrónico de Rcpp] (http://lists.r-forge.r-project.org/mailman/listinfo/rcpp-devel) –

+0

Han hecho un progreso leve en esto, por lo que una breve actualización . Jugando con inline ::: compileCode, que se llama dentro de la función cxx, encontré que agregar '--debug' al final de' R CMD SHLIB' me permitió examinar lo que está pasando dentro de la DLL mediante la combinación de gdb y R. SIN EMBARGO , esta no es una solución completa ya que algunas variables eran inaccesibles (como 'i', durante el ciclo sobre' i'); surgió el mensaje de que habían sido "optimizados". Creo que, por lo tanto, necesito reemplazar el "-O2" con "-O0" en el argumento de compilación ... pero no tengo idea de cómo hacer que esto suceda ... –

+0

He reunido una buena cantidad de evidencia de que todo lo que tengo que hacer es cambiar los indicadores del compilador R CMD SHLIB de -O2 a algo así como -g -O0 ... por ejemplo ver la publicación en https://stat.ethz.ch/pipermail/r-devel/2008-November/051390.html - pero me falta una declaración precisa de lo que debe especificarse y cómo. Algunas fuentes en línea mencionan la creación de un archivo en '/.R/Makevars.win' pero no describen el archivo, y esta no es una ubicación válida en Windows debido al punto antes de la letra R ... –

Respuesta

18

estoy un poco aturdido por la obsesión algunos usuarios tienen Rcpp con el paquete inline y su cxxfunction(). Sí, de hecho es muy útil y seguramente ha impulsado la adopción de Rcpp, ya que hace que la experimentación rápida sea mucho más fácil. Sí, nos permitió usar más de 700 pruebas unitarias en las fuentes. Sí, lo uso todo el tiempo para demostrar ejemplos aquí, en el rcpp-devel list o incluso vivo en presentations.

¿Pero eso significa que debemos usarlo para cada tarea? ¿Significa que no tiene "costos" como nombres de archivos aleatorios en un directorio temporal, etc.? Romain y yo discutimos lo contrario en nuestra documentación.

Por último, la depuración de módulos R dinámicamente cargados es difícil en su estado actual. Hay una sección completa en el (obligatorio) Writing R Extensions al respecto, y Doug Bates publicó una o dos veces un tutorial sobre cómo hacerlo a través del ESS y Emacs (aunque siempre olvido dónde lo publicó; una vez fue IIRC en el rcpp-devel list).

Edición 2012-Jul-07:

Aquí es su paso a paso:

  • (Preámbulo: He usado gcc y g ++ durante muchos años, e incluso cuando añado -g No siempre cambio -O2 a -O0. Realmente no estoy seguro de que lo necesite, pero cuando lo pida ...)

  • Establezca su variable de entorno CXXFLAGS en "-g -O0 - Pared". Existen numerosas formas de hacerlo, algunas dependen de la plataforma (por ejemplo, el panel de control de Windows) y, por lo tanto, son menos universales e interesantes. Uso ~/.R/Makevars en Windows y Unix. Podrías usar eso, o podrías anular $ RHOME/etc/Makeconf en todo el sistema de R o podrías usar Makeconf.site o ... Ver los documentos completos --- pero como dije, ~/.R/Makevars es mi forma preferida ya que lo hace no interfiere con la compilación fuera de R.

  • Ahora cada compilación R lo hace a través de R CMD shlib, R CMD COMPILE, R CMD INSTALAR, ... va a usar. Por lo tanto, ya no importa si usa inline o un paquete local. Continuando con la línea ...

  • Por lo demás, que en su mayoría seguimos 'Sección 4.4.1 Encontrar puntos de entrada en el código de carga dinámica' de "Extensiones de escritura R":

  • iniciar otra sesión de R con R - d gdb.

  • Recopila tu código.Para

fun <- cxxfunction(signature(), plugin="Rcpp", verbose=TRUE, body=' 
    int theAnswer = 42; 
    return wrap(theAnswer); 
') 

consigo

[...] 
Compilation argument: 
/usr/lib/R/bin/R CMD SHLIB file11673f928501.cpp 2> file11673f928501.cpp.err.txt 
ccache g++-4.6 -I/usr/share/R/include -DNDEBUG -I"/usr/local/lib/R/site- library/Rcpp/include" -fpic -g -O0 -Wall -c file11673f928501.cpp -o file11673f928501.o 
g++-4.6 -shared -o file11673f928501.so file11673f928501.o -L/usr/local/lib/R/site-library/Rcpp/lib -lRcpp -Wl,-rpath,/usr/local/lib/R/site-library/Rcpp/lib -L/usr/lib/R/lib -lR 
  • invocación por ejemplo tempdir() para ver el directorio temporal, cd para este directorio temporal usado arriba y dyn.load() el archivo construido sobre: ​​
dyn.load("file11673f928501.so") 
  • Ahora suspender R mediante el envío de una señal de interrupción (en Emacs, una simple elección de un menú desplegable).

  • En gdb, establezca un punto de interrupción. La única cesión anterior se convirtió en la línea 32 para mí, así

break file11673f928501.cpp 32 
cont 
  • vuelta en R, llamar a la función:

    diversión()

  • Presto, en la depuración ger en el punto de ruptura que quería:

R> fun() 

Breakpoint 1, file11673f928501() at file11673f928501.cpp:32 
32  int theAnswer = 42; 
(gdb) 
  • ahora es "sólo" hasta que le permite trabajar BGF a su magia

Ahora, como dije en mi primer intento, todo esto sería más fácil (en mi opinión) a través de un paquete simple que Rcpp.package.skeleton() puede escribir para usted, ya que no tiene que lidiar con directorios y nombres de archivos aleatorizados. Pero cada uno a su propio ...

+0

si uno estaba usando un paquete con Rcpp adentro, entonces solo se trata de cambiar CXXFLAGS, comenzar R con R -d gdb, romper R, establecer un punto de interrupción en el archivo cpp con gdb y reanudar? ¿No es posible adjuntar al proceso R y establecer el descanso, sin tener que romper R? ¿Alguna guía sobre paquetes de depuración con Rcpp? ¡Muchas gracias por Rcpp BTW! – Juancentro

+1

Con RInside, su programa C++ inicia R por usted. Podría intentar adjuntar al proceso R desde gdb, o simplemente usar gdb en su programa C++ externo. –

+0

Lo siento si no estaba claro, no estoy hablando de RInside. Estoy hablando de un paquete R que depende de Rcpp porque tiene código C++. – Juancentro

Cuestiones relacionadas