2012-07-05 6 views
17

Recentemente ho sperimentato con Rcpp (inline) per generare DLL che eseguono varie attività sugli ingressi R forniti. Mi piacerebbe essere in grado di eseguire il debug del codice in queste DLL riga per riga, dato un set specifico di input R. (sto lavorando in ambiente Windows.)Debug (riga per riga) della DLL generata da Rcpp in Windows

Per illustrare, prendiamo in considerazione un esempio specifico che chiunque dovrebbe essere in grado di eseguire ...

Il codice qui sotto è davvero semplice cxxfunction che raddoppia semplicemente il vettore di input . Si noti tuttavia che esiste una variabile aggiuntiva myvar che modifica il valore alcune volte ma non influisce sull'output: è stato aggiunto in modo che sia possibile vedere quando il processo di debug è in esecuzione correttamente.

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)); 
') 

Dopo corriamo quanto sopra, digitando il comando

getLoadedDLLs() 

viene visualizzato un elenco di DLL nella sessione R. L'ultimo elencato dovrebbe essere la DLL creata dal processo di cui sopra - ha un nome temporaneo casuale, che nel mio caso è

file7e61645c 

La colonna "Nome file" mostra che cxxfunction ha messo questa DLL nella posizione tempdir(), che per me è attualmente

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

Ora, il modo più ovvio per chiamare la DLL è via f0, come segue

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

$out1 
[1] -14.0 1.4 154.0 

Ma possiamo naturalmente anche chiamare la DLL direttamente in base al nome utilizzando il comando .Call:

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

$out1 
[1] -14.0 1.4 154.0 

Così ho raggiunto il punto in cui sto chiamando una DLL standalone direttamente con ingresso R (qui, il vettore c(-7,0.7,77)), e di averlo ritorno la risposta corretta a R.

quello che ho veramente bisogno, però, è una struttura per la linea per linea il debugging (usando gdb, presumo) che mi permetterà di osservare il valore di myvar essere impostato su 19, 27, 28, 29, 30 e infine 101 mentre il codice progredisce. L'esempio sopra è impostato deliberatamente in modo che la chiamata alla DLL non ci dica nulla su myvar.

Per chiarire, la "condizione di vincita" qui è in grado di osservare il cambiamento di myvar (vedere il valore myvar = 19 sarebbe il primo passo!) Senza aggiungere altro al corpo del codice. Questo ovviamente potrebbe richiedere delle modifiche al modo in cui il codice è compilato (ci sono le impostazioni della modalità di debug per attivare?), O il modo in cui viene chiamato R - ma non so da dove cominciare. Come notato sopra, tutto questo è basato su Windows.

Nota finale: Nei miei esperimenti, in realtà ho fatto alcune piccole modifiche a una copia di cxxfunction modo che l'uscita DLL - e il codice al suo interno - riceve un nome definito dall'utente e si siede in una directory definita dall'utente, piuttosto di un nome e una posizione temporanei. Ma questo non influisce sull'essenza della domanda.Lo dico solo per sottolineare che dovrebbe essere abbastanza semplice modificare le impostazioni della compilation se qualcuno mi dà una spinta :)

Per completezza, l'impostazione verbose = TRUE nella chiamata cxxfunction originale sopra mostra l'argomento della compilazione per essere del seguente modulo:

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 

la mia versione adattata ha un argomento compilation identico al precedente, tranne che la stringa "file7e61645c" è sostituito ovunque dalla scelta dell'utente di nome (ad esempio "testdll") i file e le relative copiati in una posizione più permanente.

Grazie in anticipo per il vostro aiuto ragazzi :)

+0

Non posso aiutare direttamente, ma so che Dirk ecc. È sempre utile. Tuttavia generalmente fanno affari sulla [lista email di Rcpp] (http://lists.r-forge.r-project.org/mailman/listinfo/rcpp-devel) –

+0

hanno fatto alcuni lievi progressi su questo, quindi un breve aggiornamento . Giocando con inline ::: compileCode, che viene chiamato all'interno di cxxfunction, ho scoperto che l'aggiunta di '--debug' alla fine di' R CMD SHLIB' mi ha permesso di esaminare cosa succede all'interno della DLL combinando gdb e R. HOWEVER , questa non è una soluzione completa in quanto alcune variabili erano inaccessibili (come 'i', durante il ciclo su' i'); è arrivato il messaggio che erano stati "ottimizzati". Penso quindi di dover sostituire "-O2" con "-O0" nell'argomento della compilation ... ma non ho idea di come farlo accadere ... –

+0

Ho raccolto un bel po 'di prove che tutto quello che devo fare è cambiare i flag del compilatore R CMD SHLIB da -O2 a qualcosa come -g -O0 ... ad es. vedere il post su https://stat.ethz.ch/pipermail/r-devel/2008-November/051390.html - ma mi manca una dichiarazione precisa su cosa è necessario specificare e come. Alcune fonti online menzionano la creazione di un file a '/.R/Makevars.win' ma non descrivono il file, e questa non è una posizione valida in Windows a causa del punto prima della lettera R ... –

risposta

18

io sono un po 'stordito dalla ossessione alcuni Rcpp gli utenti hanno con il pacchetto e la sua inlinecxxfunction(). Sì, è davvero molto utile e ha sicuramente guidato l'adozione di Rcpp in quanto rende la sperimentazione rapida molto più facile. Sì, ci ha permesso di utilizzare oltre 700 test unitari nelle fonti. Sì, lo uso sempre per mostrare esempi qui, su rcpp-devel list o addirittura vivere in presentations.

Ma questo significa che dovremmo usarlo per ogni compito? Significa che non ha "costi" come nomi di file randomizzati in una directory temporanea, ecc. Pp? Romain e io abbiamo discusso diversamente nella nostra documentazione.

Infine, il debug dei moduli R caricati dinamicamente è difficile così com'è. C'è un'intera sezione nel (obbligatorio) Writing R Extensions su di esso, e Doug Bates ha pubblicato una o due volte un tutorial su come farlo tramite ESS e Emacs (anche se dimentico sempre dove l'ha pubblicato, una volta era IIRC su rcpp-devel list).

Edit 2012-lug-07:

Ecco il vostro passo per passo:

  • (Preambolo: ho usato gcc e g ++ per molti anni, e anche quando aggiungo -g Non cambio sempre -O2 in -O0, non sono sicuro che sia necessario, ma come lo chiedi ...)

  • Imposta la tua variabile di ambiente CXXFLAGS su "-g -O0 - Parete". Esistono numerosi modi per farlo, alcuni sono dipendenti dalla piattaforma (ad esempio il pannello di controllo di Windows) e quindi meno universali e interessanti. Io uso ~/.R/Makevars su Windows e Unix. Potresti usarlo, o puoi sovrascrivere il sistema R $ RHOME/etc/Makeconf a livello di sistema oppure potresti usare Makeconf.site o ... Vedi i documenti completi --- ma come ho detto, ~/.R/Makevars è il mio modo preferito in quanto NON interferire con la compilazione al di fuori di R.

  • Ora verrà utilizzata ogni compilazione R tramite R CMD SHLIB, R CMD COMPILE, R CMD INSTALL, .... Quindi non importa più che usi inline o un pacchetto locale . Continuando con linea ...

  • Per il resto, abbiamo principalmente seguiamo 'Sezione 4.4.1 Trovare punti di ingresso nel codice caricato in modo dinamico' di "Estensioni di scrittura R":

  • Inizia un'altra sessione R con R - d gdb.

  • Compila il tuo codice.Per

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

ottengo

[...] 
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 
  • Invoke ad esempio tempdir() per vedere la directory temporanea, cd a questa directory temporanea utilizzata sopra e dyn.load() il file costruito sopra:
dyn.load("file11673f928501.so") 
  • Ora sospendere R inviando un segnale di interruzione (in Emacs, una semplice scelta da un menu a discesa).

  • In gdb, impostare un punto di interruzione. Il singolo incarico di cui sopra è diventata la linea 32 per me, così

break file11673f928501.cpp 32 
cont 
  • Torna in R, chiamare la funzione:

    divertimento()

  • Presto, nel debug ger al punto di rottura che volevamo:

R> fun() 

Breakpoint 1, file11673f928501() at file11673f928501.cpp:32 
32  int theAnswer = 42; 
(gdb) 
  • ora è "solo" fino a voi a lavorare gdb alla sua magia

Ora, come ho detto nel mio primo tentativo, tutto questo sarebbe più facile (ai miei occhi) tramite un semplice pacchetto che Rcpp.package.skeleton() può scrivere per te dato che non devi occuparti di directory e nomi di file casuali. Ma ognuno per conto proprio ...

+0

se si stesse utilizzando un pacchetto con Rcpp all'interno, quindi si tratta semplicemente di cambiare CXXFLAGS, di avviare R con R -d gdb, di interrompere R, di impostare un punto di interruzione nel file cpp con gdb e riprendere? Non è possibile attaccare al processo R e impostare la pausa, senza dover rompere R? Qualche guida sul debug dei pacchetti con Rcpp? Mille grazie per Rcpp BTW! – Juancentro

+1

Con RInside, il programma C++ avvia R per te. Potresti provare ad allegare al processo R da gdb - o semplicemente usare gdb sul tuo programma C++ esterno. –

+0

Scusa se non sono stato chiaro, non sto parlando di RINside. Sto parlando di un pacchetto R che dipende da Rcpp perché contiene codice C++. – Juancentro