Muy buenas, me llamo Luis y aquí les traigo este tutorial.
Esto puede ayudar o no a alguien. Es una guía rápida y sucia escrita por un usuario de memcheck de valgrind por primera vez. Deseaba algo similar, así que aquí está.
Suponga, como yo, que envía su nuevo paquete R, versión 1.0.0, a CRAN y se acepta. Te regocijas felizmente y pasas a cosas más importantes, como crear una actualización fantástica. Después de unos meses, tiene 8000 descargas, su nueva y genial actualización está lista y envía la versión 1.0.1. Siendo el increíble profesional que es, espera que su trabajo se deslice fácilmente a CRAN.
Recibe un correo electrónico de CRAN que dice que todo está bien, excepto por un pequeño problema: se detectaron algunos errores en su versión previa (1.0.0) durante algún tipo de procedimiento de verificación secundario, informado por esta cosa llamada valgrind, que aparentemente detecta «errores de gestión de memoria y subprocesos», pero no tienes idea de qué es. Tu ánimo todavía está alto. Estás seguro de que esto no es gran cosa …
Espero que esta guía rápida, de mi experiencia similar, pueda aliviar parte del dolor que puede venir. De esa forma, puede obtener su paquete R en CRAN y pasar el fin de semana haciendo las cosas que le gustan, como leer diarios de estadísticas.
Índice
Paso 0: Obtenga Linux (omita si tiene Linux)
Es hora de obtener Linux, si aún no lo tiene. Realmente hace la vida más fácil. Además, no tengo idea de cómo hacer esto sin Linux. Para un novato, Linux Mint es la mejor distribución al configurar (en mi humilde opinión).
A tener en cuenta: Es posible que se encuentre con una serie de errores relacionados con las dependencias de paquetes si está utilizando una instalación nueva de Linux. La solución más fácil es esperar hasta que obtenga un error y luego copiar una parte del error en un motor de búsqueda y encontrar un foro donde alguien haya explicado qué dependencias necesita instalar.
A tener en cuenta: Sugiero usar git para copiar los archivos de su paquete desde su repositorio remoto (por ejemplo, un repositorio de GitHub) a su máquina Linux.
Paso 1: Instale valgrind
Algo como esto debería funcionar
sudo apt-get install valgrind
Paso 2: Ejecute R CMD Check
Necesitas especificar el directorio “./check”
con check_dir
para que puedas recuperar mypkg-Ex.R
de los resultados.
rcmdcheck::rcmdcheck( check_dir = ”./check” )
Paso 3: Ejecute valgrind en los ejemplos de comprobación de CMD de R
Abra su terminal en el directorio del paquete. En Ubuntu puede hacer esto de la siguiente manera: abra el directorio del paquete en el explorador de archivos, presione CTRL-L
para resaltar la barra de direcciones, seguido de CTRL+C
para copiar la dirección, luego abra el terminal y escriba cd seguido de un espacio, luego presione CTRL+SHIFT+V
para pegar la dirección. Ahora, ejecuta (reemplazando mypkg
con el nombre de su paquete):
R -d valgrind --vanilla < “./check/mypkg.Rcheck/mypkg-Ex.R”
La secuencia de comandos mypkg-Ex.R
se ejecutará con valgrind ejecutándose en segundo plano. Valgrind busca problemas de memoria. Repasará todos los ejemplos en mypkg-Ex.R
y cada vez que valgrind detecta un problema, imprimirá información (¿útil?) , como:
==27138== Conditional jump or move depends on uninitialised value(s) ==27138== at 0x55F4CEC: __wcsnlen_avx2 (strlen-avx2.S:103) ==27138== by 0x5522EC1: wcsrtombs (wcsrtombs.c:104) ==27138== by 0x54A8B20: wcstombs (wcstombs.c:34) ==27138== by 0x4EDACC2: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F3EE4A: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4E1D7: Rf_eval (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F5066D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4569D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4E1D7: Rf_eval (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F5066D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4E3A1: Rf_eval (in /usr/lib/R/lib/libR.so) ==27138== by 0x4EE5176: ??? (in /usr/lib/R/lib/libR.so) ==27138==
O tal vez…
==27138== Invalid read of size 32 ==27138== at 0x55F4C91: __wcsnlen_avx2 (strlen-avx2.S:62) ==27138== by 0x5522EC1: wcsrtombs (wcsrtombs.c:104) ==27138== by 0x54A8B20: wcstombs (wcstombs.c:34) ==27138== by 0x4EDACC2: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F3EE4A: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4E1D7: Rf_eval (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F5066D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4569D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4E1D7: Rf_eval (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F5066D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4E3A1: Rf_eval (in /usr/lib/R/lib/libR.so) ==27138== by 0x4EE5176: ??? (in /usr/lib/R/lib/libR.so) ==27138== Address 0x1fb9d5d0 is 0 bytes inside a block of size 12 alloc'd ==27138== at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==27138== by 0x4F7B1B0: R_chk_calloc (in /usr/lib/R/lib/libR.so) ==27138== by 0x4EDAC3D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F3EE4A: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4E1D7: Rf_eval (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F5066D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4569D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4E1D7: Rf_eval (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F5066D: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F4E3A1: Rf_eval (in /usr/lib/R/lib/libR.so) ==27138== by 0x4EE5176: ??? (in /usr/lib/R/lib/libR.so) ==27138== by 0x4F3EE4A: ??? (in /usr/lib/R/lib/libR.so) ==27138==
O incluso…
==27138== Invalid read of size 1 ==27138== at 0x4C33DA3: strcmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==27138== by 0x243B1683: _XimUnRegisterIMInstantiateCallback (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0) ==27138== by 0x24398D32: XUnregisterIMInstantiateCallback (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0) ==27138== by 0x243B1566: _XimRegisterIMInstantiateCallback (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0) ==27138== by 0x24398CBA: XRegisterIMInstantiateCallback (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0) ==27138== by 0x23C8BDFB: TkpOpenDisplay (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23BFA769: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23BFB0AB: TkCreateMainWindow (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23C06CC6: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23C04C7A: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23BFD6C4: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x235E4D5B: ??? (in /usr/lib/R/library/tcltk/libs/tcltk.so) ==27138== Address 0x1f4f12d0 is 0 bytes inside a block of size 9 free'd ==27138== at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==27138== by 0x243A7D8C: XSetLocaleModifiers (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0) ==27138== by 0x23C8C536: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23C8C4BB: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x243B1566: _XimRegisterIMInstantiateCallback (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0) ==27138== by 0x24398CBA: XRegisterIMInstantiateCallback (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0) ==27138== by 0x23C8BDFB: TkpOpenDisplay (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23BFA769: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23BFB0AB: TkCreateMainWindow (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23C06CC6: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23C04C7A: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23BFD6C4: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== Block was alloc'd at ==27138== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==27138== by 0x243A7984: _XlcDefaultMapModifiers (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0) ==27138== by 0x243A7D75: XSetLocaleModifiers (in /usr/lib/x86_64-linux-gnu/libX11.so.6.3.0) ==27138== by 0x23C8C536: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23C8BDE3: TkpOpenDisplay (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23BFA769: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23BFB0AB: TkCreateMainWindow (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23C06CC6: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23C04C7A: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x23BFD6C4: ??? (in /usr/lib/x86_64-linux-gnu/libtk8.6.so) ==27138== by 0x235E4D5B: ??? (in /usr/lib/R/library/tcltk/libs/tcltk.so) ==27138== by 0x4F12633: ??? (in /usr/lib/R/lib/libR.so)
Son divertidos de ver :). El último es un poco complicado, a continuación he reemplazado algunas partes con «…» para que la estructura sea más evidente:
==27138== Invalid read of size 1 ==27138== at 0x4C33DA3: strcmp (in...) ==27138== by 0x243B1683: _XimUnRegisterIMInst... ==27138== by 0x24398D32: XUnregisterIMInstant... ==27138== by 0x243B1566: _XimRegisterIMInstant... ==27138== by 0x24398CBA: XRegisterIMInstantiat... ==27138== by 0x23C8BDFB: TkpOpenDisplay (in...) ==27138== by 0x23BFA769: ??? (in...) ==27138== by 0x23BFB0AB: TkCreateMainWindow (in...) ==27138== by 0x23C06CC6: ??? (in...) ==27138== by 0x23C04C7A: ??? (in...) ==27138== by 0x23BFD6C4: ??? (in...) ==27138== by 0x235E4D5B: ??? (in...) ==27138== Address .... is 0 bytes inside a block of size 9 free'd ==27138== at 0x4C30D3B: free (in...) ==27138== by 0x243A7D8C: XSetLocaleModifiers (in...) ==27138== by 0x23C8C536: ??? (in...) ==27138== by 0x23C8C4BB: ??? (in...) ==27138== by 0x243B1566: _XimRegisterIMInstant...) ==27138== by 0x24398CBA: XRegisterIMInstant...) ==27138== by 0x23C8BDFB: TkpOpenDisplay (in...) ==27138== by 0x23BFA769: ??? (in...) ==27138== by 0x23BFB0AB: TkCreateMainWindow (in...) ==27138== by 0x23C06CC6: ??? (in...) ==27138== by 0x23C04C7A: ??? (in...) ==27138== by 0x23BFD6C4: ??? (in...) ==27138== Block was alloc'd at ==27138== at 0x4C2FB0F: malloc (in...) ==27138== by 0x243A7984: _XlcDefaultMapModifiers (in...) ==27138== by 0x243A7D75: XSetLocaleModifiers (in...) ==27138== by 0x23C8C536: ??? (in...) ==27138== by 0x23C8BDE3: TkpOpenDisplay (in...) ==27138== by 0x23BFA769: ??? (in...) ==27138== by 0x23BFB0AB: TkCreateMainWindow (in...) ==27138== by 0x23C06CC6: ??? (in...) ==27138== by 0x23C04C7A: ??? (in...) ==27138== by 0x23BFD6C4: ??? (in...) ==27138== by 0x235E4D5B: ??? (in...) ==27138== by 0x4F12633: ??? (in...)
Valgrind Memcheck informa de dos tipos de errores: pérdidas de memoria y errores de memoria. Lea más aquí si lo desea.
Ahora saca las armas grandes:
R -d "valgrind --tool=memcheck --leak-check=full --track-origins=yes --show-leak-kinds=definite" --vanilla < “./check/mypkg.Rcheck/mypkg-Ex.R”
Paso 4: Comienza la búsqueda
Cree un nuevo script R vacío ./check/mypkg.Rcheck/debug-Ex.R
. Ahora, abre el guión ./check/mypkg.Rcheck/mypkg-Ex.R
y busque la primera aparición de cleanEx()
. Copie todo lo que está arriba de la primera aparición de cleanEx()
en su nuevo script. Entonces ve al final de mypkg-Ex.R
y copie las últimas líneas debajo de la palabra «PIE de página» y péguelas al final de debug-Ex.R
. Ahora tu debug-Ex.R
debería verse algo como esto (donde agregué # símbolos para separar visualmente el encabezado del pie de página):
pkgname <- "mypkg" source(file.path(R.home("share"), "R", "examples-header.R")) options(warn = 1) library('mypkg')base::assign(".oldSearch", base::search(), pos = 'CheckExEnv') ##################################################################################################################################### ### * <FOOTER> ### options(digits = 7L) base::cat("Time elapsed: ", proc.time() - base::get("ptime", pos = 'CheckExEnv'),"n") grDevices::dev.off() ### ### Local variables: *** ### mode: outline-minor *** ### outline-regexp: "\(> \)?### [*]+" *** ### End: *** quit('no')
Resultados
La función que provocó el error fue minDist. Se veía así:
double minDist_internal1(Rcpp::DataFrame cmbdf, NumericVector point) int n = cmbdf.nrow(); NumericVector x = cmbdf["x"]; NumericVector y = cmbdf["y"]; NumericVector z = cmbdf["z"]; double px = point[1]; double py = point[2]; double pz = point[3];// Find the minimum distance double mindot = -1; for ( int i = 0; i < n; i++ ) // Find d(xi, xj) double dot = x[i]*px + y[i]*py + z[i]*pz;//if ( dot > mindot ) mindot = dot; if ( true ) mindot = dot; return acos(std::min<double>(std::max<double>( mindot, -1),1)); }
Resulta que estaba indexando comenzando en 1 en lugar de 0.
double px = point[0]; double py = point[1]; double pz = point[2];
Además, si solo usara corchetes, esto nunca habría sucedido.
double px = point(0); double py = point(1); double pz = point(2);
Esto se debe a que Rcpp lanzará una excepción de índice fuera de límites cuando se usen corchetes, pero los corchetes seguirán felizmente con el acceso fuera de límites.
Referencias
Sección 4.3 de Escritura de extensiones R (https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Using-valgrind).
Añadir comentario