Cómo localizar errores encontrados durante el desarrollo del NDK de Android
Como parte del programa, el archivo .so generado por la compilación NDK también hará que el programa se bloquee cuando se produzca una excepción durante la operación. A diferencia de los bloqueos del programa causados por excepciones del código Java, cuando ocurre una excepción NDK, el programa se cerrará inmediatamente en el dispositivo Android, lo que comúnmente se conoce como bloqueo, y no aparecerá una ventana emergente como "El programa xxx no responde, si ¿Cerrarla inmediatamente?" cuadro de aviso.
NDK se desarrolla utilizando C/C. Los programadores familiarizados con C/C saben que los punteros y la administración de memoria son los más importantes y los más propensos a tener problemas. acceso no válido a la memoria, objetos no válidos, pérdidas de memoria, desbordamientos de pila, etc., todos terminan con el mismo resultado: falla del programa. Por ejemplo, el error de puntero nulo al que a menudo nos referimos es cuando un puntero de memoria se establece en NULL y luego se accede nuevamente. Otro error que ocurre con frecuencia es que se libera un determinado espacio de memoria en una determinada ubicación del programa y luego se intenta; acceder a esta dirección de memoria en otra parte del programa genera un error de dirección no válida. Los tipos de error comunes son los siguientes:
Error de inicialización
Error de acceso
Acceso al índice de matriz fuera de límites
Acceso al objeto puntero fuera de límites
Acceder a un objeto puntero nulo
Acceder a un objeto puntero no válido
Acceso al iterador fuera de límites
Pérdida de memoria
Error de parámetro
Desbordamiento de pila
Error de conversión de tipo
Error de división de números por 0
Al desarrollar aplicaciones locales utilizando Android NDK Casi todos los programadores se han encontrado con el problema de la falla del programa, pero su falla imprimirá una gran cantidad de información de pila en logcat que parece una biblia, lo que hace que la gente se sienta impotente. Sin duda, es frustrante simplemente agregar líneas de información de impresión para ubicar el número de línea donde se encuentra el código de error. Si busca "fallo del NDK de Android" en Internet, puede encontrar muchos artículos que presentan cómo encontrar y localizar errores del NDK a través de las herramientas proporcionadas por Android, pero la mayoría de ellos son oscuros y difíciles de entender. A continuación se utiliza un ejemplo práctico para ilustrar: primero se genera un error y luego se demuestra cómo localizar el nombre de función y la línea de código incorrectos mediante dos métodos diferentes.
Primero, veamos lo que hicimos en el código del programa hello-jni (aquí se omite cómo crear o importar el proyecto), mire la siguiente imagen: En la función JNI_OnLoad(), es decir, cargando Cuando se llama a la función willCrash (), y en la función willCrash (), este método de asignación de ?std::string generará un error de puntero nulo. De esta forma, el programa hello-jni fallará cuando se cargue. Recordemos estas dos líneas: la función willCrash() fue llamada en la línea 61; se produjo un bloqueo en la línea 69.
Echemos un vistazo al registro logcat que imprime el sistema cuando se produce un fallo:
[plain] ver copia simple
***?** *?***?***?***?***?***?***?***?***?***?***?***?***? ***?*** Build?fingerprint:?'vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/JOP40D/1372668680:user/test-keys'?pid:?32607,?tid:?32607,?name:? .hellojni?gt;gt;gt;?com.example.hellojni?lt;lt;lt;?señal?11?(SIGSEGV),?código?1?(SEGV_MAPERR),?fallo?dirección?00000000?r0? ?r1?beb123a8?r2?80808080?r3?00000000?r4?5d635f68?r5?5cdc3198?r6?41efcb18?r7?5d62df44?r8?4121b0c0?r9?00000001?sl?0000 0000?fp? ¿beb1238c?ip?5d635f7c? sp?beb12380?lr?5d62ddec?pc?400e7438?cpsr?60000010? seguimiento:? #00?pc?00023438?/system/lib/libc.so #01?pc?00004de8?/data/app-lib/com. ejemplo.hellojni-2/libhello-jni.so? #02?pc?000056c8?/data/app-lib/com.example.hellojni-2/libhello-jni.so? #03?pc?00004fb4?/data/ app-lib/com.example.hellojni-2/libhello-jni.so? #04?pc?00004f58?/data/app-lib/com.example.hellojni-2/libhello-jni.so? ?000505b9?/system/lib/libdvm.so? #06?pc?00068005?/system/lib/libdvm.so? #07?pc?000278a0?/system/lib/libdvm.so? ?/system/lib/libdvm.so?#09?pc?00060fe1?/system/lib/libdvm.so?#10?pc?0006100b?/system/lib/libdvm.so?/ system/lib/libdvm.so?#12?pc?00067a1f?/system/lib/libdvm.so?#13?pc?000278a0?/system/lib/libdvm.so?#14?pc?0002b7fc?/system/ lib/libdvm.so?#15?pc?00061307?/system/lib/libdvm.so?#16?pc?000691
2d?/system/lib/libdvm.so?#17?pc?000278a0?/system/lib/libdvm.so?#18?pc?0002b7fc?/system/lib/libdvm.so? /system/lib/libdvm.so?#20?pc?00049ff9?/system/lib/libdvm.so?#21?pc?0004d419?/system/lib/libandroid_runtime.so?#22?pc?0004e1bd?/system /lib/libandroid_runtime.so?#23?pc?00001d37?/system/bin/app_process?#24?pc?0001bd98?/system/lib/libc.so? ? pila:? beb12340?012153f8 beb12344?00054290 beb12348?00000035 beb1234c?beb123c0?[stack]?
……?
Si ha visto el registro de error de NDK impreso por logcat Sabrás que he omitido mucho contenido más adelante. Muchas personas ya se sienten mareadas cuando ven tantos registros densos. Incluso muchos desarrolladores senior de Android optan en su mayoría por ignorarlos cuando se enfrentan a registros NDK.
Cómo "simbolizar" los mensajes de error del NDK
De hecho, siempre que lo compruebes cuidadosamente y cooperes con las herramientas proporcionadas por Google, podrás localizar de forma rápida y precisa la ubicación del código de error. A esta obra la llamamos "simbolización". Cabe señalar que si desea simbolizar los errores de NDK, debe conservar los archivos so que contienen la tabla de símbolos generada durante el proceso de compilación. Estos archivos generalmente se guardan en el directorio $PROJECT_PATH/obj/local/.
El primer método: ndk-stack
Esta herramienta de línea de comandos se incluye en el directorio de instalación de la herramienta NDK, junto con ndk-build y algunos otros comandos NDK de uso común, como Como en mi computadora, su ubicación es /android-ndk-r9d/ndk-stack. Según la documentación oficial de Google, NDK proporciona el comando ndk-stack a partir de la versión r6. Si está utilizando una versión anterior, se recomienda actualizar a la última versión lo antes posible.
Hay dos formas de usar el comando ndk –stack
Usar ndk-stack para analizar registros en tiempo real
Mientras ejecuta el programa, use adb para obtener el registro de logcat y generarlo. a ndk- a través de la pila de caracteres de tubería, y debe especificar la ubicación del archivo so que contiene la tabla de símbolos; si su programa contiene varias arquitecturas de CPU, debe seleccionar diferentes directorios de arquitectura de CPU según el tipo de CPU del teléfono cuando se produce el error; ocurre, como por ejemplo:
[plain] ver plano ?copiar ?
adb?shell?logcat?|?ndk-stack?-sym?$PROJECT_PATH/obj/local/armeabi ?
Cuando falla Cuando esto ocurre, obtendrá la siguiente información:
[plain] ver simple ?copiar ?
******* ***?Crash?dump:?*** *******?
Construir?huella digital:?'vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/JOP40D/1372668680:user/test -keys'?
pid :?32607,?tid:?32607,?nombre:?xample.hellojni?gt;gt;gt;?com.example.hellojni?lt;lt;lt;?
¿señal?11?( SIGSEGV),?código?1?(SEGV_MAPERR),?fault?addr?00000000?
¿Pila?frame?#00?pc?00023438?/ system/lib/libc.so?(strlen 72) ?
Stack?frame?#01?pc?00004de8?/data/app-lib/com.example.hellojni-2/libhello-jni. entonces?(std::char_traitslt;chargt;:: length(char?const*) 20):?Rutina?std::char_traitslt;chargt;::length(char?const*)?at?/android-ndk-r9d /sources/cxx-stl/stlport/stlport/stl /char_traits.h:229?
Stack?frame?#02?pc?000056c8?/data/app-lib/com.example.hellojni- 2/libhello-jni.so?(std:: basic_stringlt;char,?std::char_traitslt;chargt;,?std::allocatorlt;chargt;?gt;:basic_string(char?const*,?std::allocatorlt; chargt;?constamp;) 44):?Rutina ?basic_string?at?/android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/_string.c:639?
¿Pila? marco?#03?pc?00004fb4?/data /app-lib/com.example.hellojni-2/
libhello-jni.so?(willCrash() 68):?Rutina?willCrash()?at?/home/testin/hello-jni/jni/hello-jni.cpp: 69?
¿Pila? frame?#04?pc?00004f58?/data/app-lib/com.example.hellojni-2/libhello-jni.so?(JNI_OnLoad 20):?Routine?JNI_OnLoad?at?/home/testin/hello-jni /jni/hello-jni.cpp:61?
Stack?frame?#05?pc?000505b9?/system/lib/libdvm.so?(dvmLoadNativeCode(char?const*,?Object*, ?char**) 516)?
Stack?frame?#06?pc?00068005?/system/lib/libdvm.so?
Stack?frame?#07?pc ?000278a0?/system/lib/libdvm.so?
Stack?frame?#08?pc?0002b7fc?/system/lib/libdvm.so?(dvmInterpret(Thread*,?Method?const* ,?JValue*) 180)?
Stack?frame?#09?pc?00060fe1?/system/lib/libdvm.so?(dvmCallMethodV(Thread*,?Method?const*,?Object* , ?bool, ?JValue*, ?std::__va_list) 272)?
……(¿se omite más adelante)?
Centrémonos en #03 y #04, estas dos líneas son mensajes de error en libhello-jni.so que generamos nosotros mismos, luego encontraremos la siguiente información clave:
[plain] view Plain ?copy ?
#03? () 68):?Rutina?willCrash()?at?/home/testin/hello-jni/jni/hello-jni.cpp: 69?
#04?(JNI_OnLoad 20): ?Rutina ?JNI_OnLoad?at?/home/testin/hello-jni/jni/hello-jni.cpp: 61?
Recuerde nuestro código, en la función JNI_OnLoad() (línea 61), llamamos a willCrash () función; en la función willCrash() (línea 69), creamos un error. ¡Esta información fue extraída con precisión! ¿No es muy sencillo?
Primero obtenga los registros y luego use ndk-stack para analizar
Este método en realidad no es muy diferente del método anterior, excepto que la forma en que se obtienen los registros de logcat es diferente. Puede guardar el registro de logcat en un archivo mientras el programa se está ejecutando. Incluso puede guardar rápidamente el registro de logcat cuando ocurre un bloqueo y luego analizarlo. Es un poco más flexible que el método anterior y el registro se puede dejar. análisis futuro.
[plain] ver simple ?copiar ?
adb?shell?logcat?gt;?1.log?
ndk-stack?-sym?$ PROJECT_PATH/obj/local/armeabi?–dump?1.log?
Segundo método: usar los comandos addr2line y objdump
Este método es adecuado para aquellos que no están satisfechos con lo anterior ndk El uso simple de -stack, y para los programadores a quienes les gusta profundizar, estos dos métodos pueden revelar cómo funciona el comando ndk-stack. Aunque es un poco más problemático de usar, puede satisfacer la curiosidad de los programadores.
Hablemos brevemente de estos dos comandos. Se pueden encontrar en la mayoría de las distribuciones de Linux. Si su sistema operativo es Linux y el teléfono que está probando usa la serie Intel x86, entonces puede usar los comandos que. viene con el sistema. Sin embargo, si este es el caso, entonces la mayoría de la gente estará desesperada, porque la mayoría de los desarrolladores usan Windows y es probable que el teléfono móvil sea de la serie armeabi.
No te preocupes, el NDK viene con cadenas de herramientas adecuadas para varios sistemas operativos y arquitecturas de CPU, que incluyen estos dos comandos, pero los nombres han cambiado ligeramente. Puedes usarlos en el directorio Buscar. en el directorio de cadenas de herramientas. Tomando mi computadora Mac como ejemplo, si estoy buscando herramientas adecuadas para la arquitectura armeabi, entonces son arm-linux-androideabi-addr2line y arm-linux-androideabi-objdump respectivamente, las ubicaciones están en el directorio a continuación, que serán: se introducirá más adelante. Esta ubicación se omitirá:
[plain] ¿ver plano? ¿copiar?
/Developer/android_sdk/android-ndk-r9d/toolchains/arm-linux-androideabi. -4.8/prebuilt/ darwin-x86_64/bin/?
Suponiendo que su computadora es Windows y la arquitectura de la CPU es mips, entonces la herramienta que desea puede estar incluida en este directorio:
[simple] ¿ver ?copiar?
D:\?android-ndk-r9d\toolchains\mipsel-linux-android-4.8\prebuilt\windows-x86_64\bin\?