2012-07-05 19 views
17

Gần đây tôi đã thử nghiệm với Rcpp (inline) để tạo các tệp DLL thực hiện các tác vụ khác nhau trên các đầu vào R được cung cấp. Tôi muốn có thể gỡ lỗi mã trong các dòng DLL này theo dòng, được đưa ra một bộ đầu vào R cụ thể. (Tôi đang làm việc trong môi trường Windows.)Gỡ lỗi (từng dòng) của DLL do Rcpp tạo dưới Windows

Để minh họa, chúng ta hãy xem xét một ví dụ cụ thể mà ai cũng sẽ có thể chạy ...

Đoạn code dưới đây là một cxxfunction thực sự đơn giản mà chỉ đơn giản tăng gấp đôi vector đầu vào . Tuy nhiên, lưu ý rằng có một biến bổ sung myvar thay đổi giá trị một vài lần nhưng không ảnh hưởng đến đầu ra - điều này đã được thêm vào để chúng tôi có thể thấy khi nào quá trình gỡ lỗi đang chạy chính xác.

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

Sau khi chúng tôi chạy trên, gõ lệnh

getLoadedDLLs() 

sẽ trả về một danh sách các file DLL trong phiên R. Người cuối cùng được liệt kê nên là DLL tạo ra bởi quá trình trên - nó có một cái tên tạm thời ngẫu nhiên, mà trong trường hợp của tôi là

file7e61645c 

Các "Tên tệp" cột cho thấy cxxfunction đã đưa DLL này ở vị trí tempdir(), mà đối với tôi hiện đang

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

Bây giờ, một cách rõ ràng để gọi DLL là thông qua f0, như sau

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

$out1 
[1] -14.0 1.4 154.0 

Nhưng chúng ta có thể đương nhiên cũng gọi DLL trực tiếp bằng tên sử dụng .Call lệnh:

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

$out1 
[1] -14.0 1.4 154.0 

Vì vậy, tôi đã đạt đến điểm mà tôi gọi một DLL độc lập trực tiếp với đầu vào R (ở đây, vector c(-7,0.7,77)), và có nó trở lại Câu trả lời đúng cho R.

Điều tôi thực sự cần là cơ sở để gỡ lỗi từng dòng (sử dụng gdb, tôi đoán) sẽ cho phép tôi quan sát giá trị myvar được đặt thành 19, 27, 28, 29, 30 và cuối cùng là 101 khi mã tiến triển. Ví dụ trên được thiết lập một cách có chủ ý để gọi DLL cho chúng ta biết gì về myvar.

Để làm rõ, "điều kiện giành chiến thắng" ở đây là có thể quan sát myvar thay đổi (xem giá trị myvar = 19 sẽ là bước đầu tiên!) Mà không thêm bất kỳ thứ gì khác vào phần thân của mã. Điều này rõ ràng có thể yêu cầu thay đổi cách thức mã được biên dịch (có cài đặt chế độ gỡ lỗi để bật không?) Hoặc cách R được gọi - nhưng tôi không biết bắt đầu từ đâu. Như đã nói ở trên, tất cả điều này là dựa trên Windows.

Lưu ý cuối cùng: Trong thử nghiệm của mình, tôi đã thực hiện một số sửa đổi nhỏ cho bản sao của hàm cxxfunction để DLL đầu ra - và mã bên trong - nhận tên do người dùng xác định và nằm trong thư mục do người dùng xác định. hơn tên và vị trí tạm thời. Nhưng điều này không ảnh hưởng đến bản chất của câu hỏi.Tôi đề cập đến điều này chỉ để nhấn mạnh rằng nó sẽ khá dễ dàng để thay đổi các thiết lập biên dịch nếu ai đó đưa cho tôi một cú huých :)

Để hoàn thành, thiết lập verbose = TRUE trong lệnh cxxfunction gốc ở trên cho thấy đối số biên dịch là của hình thức sau đây:

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 

phiên bản chuyển thể của tôi có một cuộc tranh luận tổng hợp giống với ở trên, ngoại trừ chuỗi "file7e61645c" được thay thế ở khắp mọi nơi bởi sự lựa chọn của người sử dụng tên (ví dụ: "testdll") và có liên quan file sao chép trên đến một vị trí cố định hơn.

Cảm ơn trước cho kẻ giúp đỡ của bạn :)

+0

Tôi không thể giúp trực tiếp, nhưng tôi biết Dirk vv luôn hữu ích. Tuy nhiên, họ thường kinh doanh trên [danh sách email Rcpp] (http://lists.r-forge.r-project.org/mailman/listinfo/rcpp-devel) –

+0

Đã thực hiện một số tiến bộ nhẹ về vấn đề này, vì vậy hãy cập nhật ngắn gọn . Chơi xung quanh với inline ::: compileCode, được gọi trong cxxfunction, tôi thấy rằng việc thêm '--debug' vào cuối' R CMD SHLIB' cho phép tôi kiểm tra những gì đang xảy ra bên trong DLL thông qua việc kết hợp gdb và R. HOWEVER , đây không phải là giải pháp đầy đủ vì một số biến không truy cập được (chẳng hạn như 'i', trong vòng lặp trên' i'); thông điệp xuất hiện rằng họ đã được "tối ưu hóa". Do đó, tôi nghĩ rằng tôi cần phải thay thế "-O2" bằng "-O0" trong đối số biên dịch ... nhưng tôi không có khái niệm về cách thực hiện điều này ... –

+0

Tôi đã thu thập được một số bằng chứng rõ ràng tất cả những gì tôi cần làm là thay đổi cờ trình biên dịch R CMD SHLIB từ -O2 thành một thứ như -g -O0 ... ví dụ xem bài đăng tại https://stat.ethz.ch/pipermail/r-devel/2008-November/051390.html - nhưng tôi thiếu một tuyên bố chính xác về những gì cần xác định và làm thế nào. Một số nguồn trực tuyến đề cập đến việc tạo một tệp tại '/.R/Makevars.win' nhưng chúng không mô tả tệp và đây không phải là vị trí hợp lệ trong Windows do dấu chấm trước chữ R ... –

Trả lời

18

Tôi là một chút choáng váng bởi sự ám ảnh một số người dùng có Rcpp với inline gói và cxxfunction() của nó. Có, nó thực sự là rất hữu ích và nó đã chắc chắn đã thúc đẩy sự chấp nhận của Rcpp hơn nữa vì nó làm cho thử nghiệm nhanh chóng dễ dàng hơn rất nhiều. Có, nó cho phép chúng tôi sử dụng hơn 700 bài kiểm tra đơn vị trong các nguồn. Có, tôi luôn sử dụng nó để minh họa các ví dụ ở đây, trên rcpp-devel list hoặc thậm chí sống ở presentations.

Nhưng điều đó có nghĩa là chúng ta nên sử dụng nó cho mỗi tác vụ? Nó có nghĩa là nó không có "chi phí" như tên tập tin ngẫu nhiên trong một thư mục tạm thời, vv pp? Romain và tôi lập luận khác trong tài liệu của chúng tôi.

Cuối cùng, gỡ lỗi mô-đun R được nạp động rất khó khi đứng. Có một phần toàn bộ trong (bắt buộc) Writing R Extensions về nó, và Doug Bates một lần hoặc hai lần đăng một hướng dẫn về cách làm điều này thông qua ESS và Emacs (mặc dù tôi luôn luôn quên nơi ông đăng nó; một lần là IIRC trên rcpp-devel list).

Sửa 2012-Jul-07:

Đây là bước bạn bước:

  • (Lời nói đầu: Tôi đã sử dụng gcc và g ++ trong nhiều năm, và thậm chí khi tôi thêm -g Tôi không luôn luôn biến -O2 thành -O0. Tôi thực sự không chắc chắn bạn cần điều đó, nhưng khi bạn yêu cầu nó ...)

  • Đặt biến môi trường CXXFLAGS thành "-g -O0 - Tường". Có rất nhiều cách để làm điều đó, một số phụ thuộc vào nền tảng (ví dụ như bảng điều khiển Windows) và do đó ít phổ biến và thú vị hơn. Tôi sử dụng ~/.R/Makevars trên Windows và Unix. Bạn có thể sử dụng nó, hoặc bạn có thể ghi đè toàn bộ hệ thống ROME/etc/Makeconf của R hoặc bạn có thể sử dụng Makeconf.site hoặc ... Xem toàn bộ tài liệu --- nhưng như tôi đã nói, ~/.R/Makevars là cách ưa thích của tôi KHÔNG can thiệp vào việc biên dịch bên ngoài R.

  • Bây giờ mọi biên dịch R thực hiện thông qua R CMD SHLIB, R CMD COMPILE, R CMD INSTALL, ... sẽ sử dụng. Vì vậy, nó không còn quan trọng bạn sử dụng nội tuyến hoặc gói địa phương. Tiếp tục với inline ...

  • Đối với phần còn lại, chúng tôi chủ yếu là làm theo 'Mục 4.4.1 Tìm điểm vào trong mã lệnh được nạp tự động' của "Viết R Extensions":

  • Bắt đầu một phiên R với R - d gdb.

  • Biên dịch mã của bạn.Đối với

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

tôi nhận được

[...] 
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 ví dụ tempdir() để xem thư mục tạm thời, cd vào thư mục tạm thời này được sử dụng ở trên và dyn.load() tập tin được xây dựng trên:
dyn.load("file11673f928501.so") 
  • Bây giờ ngưng R bằng cách gửi một tín hiệu ngắt (trong Emacs, một sự lựa chọn đơn giản từ một thả xuống).

  • Trong gdb, hãy đặt điểm ngắt. Việc chuyển nhượng đơn trên đã trở thành dòng 32 đối với tôi, vì vậy

break file11673f928501.cpp 32 
cont 
  • Quay trở lại R, gọi hàm:

    fun()

  • Presto, trong gỡ lỗi ger tại điểm phá vỡ chúng tôi muốn:

R> fun() 

Breakpoint 1, file11673f928501() at file11673f928501.cpp:32 
32  int theAnswer = 42; 
(gdb) 
  • Bây giờ nó là "chỉ" tùy thuộc vào bạn để làm việc gdb để nó kỳ diệu

Bây giờ, như tôi đã nói trong nỗ lực đầu tiên của tôi, tất cả điều này sẽ dễ dàng hơn (trong mắt tôi) thông qua một gói đơn giản mà Rcpp.package.skeleton() có thể viết cho bạn vì bạn không phải đối phó với các thư mục và tên tệp ngẫu nhiên. Nhưng mỗi của riêng mình ...

+0

nếu một người đang sử dụng một gói với Rcpp bên trong, sau đó nó chỉ là vấn đề thay đổi CXXFLAGS, bắt đầu với R-gdb, phá vỡ R, thiết lập một điểm ngắt trong tệp cpp với gdb và tiếp tục? Không phải là nó có thể gắn vào quá trình R và thiết lập nghỉ, mà không cần phải phá vỡ R? Bất kỳ hướng dẫn về gỡ lỗi các gói với Rcpp? Rất nhiều cảm ơn cho Rcpp BTW! – Juancentro

+1

Với RInside, bạn bắt đầu chương trình C++ R cho bạn. Bạn có thể thử gắn vào quá trình R từ gdb - hoặc chỉ sử dụng gdb trên chương trình C++ bên ngoài của bạn. –

+0

Xin lỗi nếu tôi không rõ ràng, tôi không nói về RInside. Tôi đang nói về một gói R mà phụ thuộc vào Rcpp gây ra nó có mã C + + trong đó. – Juancentro

Các vấn đề liên quan