Compilando Lua lib para Android: éxito, pero segfeults extraños
android-ndk (3)
Sus cambios se ven bien y son apropiados para su sistema. La lista de correo lua sugiere que realice los cambios en luaconf.h
lugar de llex.c
( http://lua-users.org/lists/lua-l/2012-08/msg00100.html ) pero no debería importar mucho. (Entonces, en resumen, no vas a ir al infierno por estos cambios ...).
Supongo que los segfeults están sucediendo porque parte de tu puente C-lua está haciendo algo "malo". Mi suposición sería algún tipo de lua stack over / under flow, o acceder fuera de la pila lua utilizando un índice no válido. Puede rastrear esto si puede construir la parte puente en linux / os x y usar valgrind. (Existen herramientas similares para Windows también, pero no estoy seguro acerca de nativo de Android)
Perdón por una pregunta larga. Si lo desea, omita la parte sobre la compilación de Lua (que casi está bien) y pase directamente a la última pregunta.
Compilemos la biblioteca de Lua como una biblioteca estática para Android.
Descargue la última fuente y mire en doc / readme.html - Creando Lua en otra sección de sistemas para obtener una lista de archivos para compilar.
Y, por supuesto, mire en makefiles, vea qué manera casual debemos establecer como indicador de plataforma, como linux, bsd, etc. Pero, por supuesto, no hay una plataforma Android, por lo que tenemos la opción de configurar la plataforma para ANSI, Linux, Posix o Generic.
Primera pregunta: construye bien (con una excepción sobre llex.c, que describiré a continuación), incluso sin una bandera de plataforma, así que quizás esto sea innecesario.
Configuro la bandera de ANSI.
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lua
LOCAL_CFLAGS := -DLUA_ANSI
LOCAL_SRC_FILES := lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c lbitlib.c lcorolib.c ldblib.c liolib.c lmathlib.c loslib.c lstrlib.c ltablib.c loadlib.c linit.c
include $(BUILD_STATIC_LIBRARY)
Application.mk
APP_MODULES := lua
APP_PLATFORM := android-8
APP_OPTIM := release
APP_ABI := armeabi
Y obtuve errores por supuesto
Compile thumb : lua <= llex.c
jni/llex.c: In function ''trydecpoint'':
jni/llex.c:214:18: error: ''struct lconv'' has no member named ''decimal_point''
#if !defined(getlocaledecpoint)
#define getlocaledecpoint() (localeconv()->decimal_point[0]) //Missing struct member
#endif
Reparándolo de la manera más económica
#if !defined(getlocaledecpoint)
#define getlocaledecpoint() (''.'') //Code-monkey style
#endif
Existen algunas limitaciones sobre locale.h en Android NDK, por lo que este error no es sorprendente.
También obtuve errores sobre size_t, UCHAR_MAX, INT_MAX, agregando llimits.h include into llex.c y todos los errores desaparecieron.
Ahora solo existen advertencias sobre "falta de corte al final del caso" y "no retorno en la función que devuelve no válido " en int llex estático , pero ya no interferimos con el código fuente de Lua porque no es vital.
Segunda pregunta: ¿voy a ir al infierno de programadores por soluciones tan rápidas?
Tome nuestro LuaLib recién horneado en el directorio obj / armeabi y permítanos probarlo. Por supuesto, para cargar guiones del sistema de archivos de Android, necesitamos escribir algún cargador de archivos con el uso de la clase AssetManager en Java, así que hagámoslo muy simple presionando y leyendo lua global vars.
TestLua - Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lua
LOCAL_SRC_FILES := liblua.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/lua-inc //Where .h files from lua src stored
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := LuaLibTest
LOCAL_STATIC_LIBRARIES:= lua
LOCAL_SRC_FILES := LuaLibTest.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
LuaLibTest.c
#include "LuaLibTest.h"
#include "lua-inc/lua.h"
#include "lua-inc/lauxlib.h"
#include <android/log.h>
#define INFO_TAG "[INFO]"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, INFO_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_lualib_test_NativeLib_testLua(JNIEnv* env, jclass _class)
{
LOGI("HI FROM C");
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_pushstring(L, "Some string from Android C" );
lua_setglobal(L, "TEST" );
lua_getglobal(L, "TEST" );
const char* res = lua_tostring(L, lua_gettop(L));
LOGI("LUA TEST VAL: %s", res);
lua_pop(L, 1);
lua_close(L);
}
También funciona si leemos el archivo de script con AssetManager y ponemos su contenido en la cadena que se ingresa en lual_dostring () . Así que sí, creamos lua para Android (excepto las funciones lua i / o, que no funcionarán, porque stdio como printf no funciona en Android NDK).
Sin embargo, esta compilación tiene errores extraños, por ejemplo, la actualización del script fps en cada cuadro, pero puede caer en cualquier momento con el próximo error en la función, lo que actualiza los fps con el tiempo delta del marco.
FPS.lua
FPS = {}
function initFPS()
FPS.fps = 0
FPS.last_fps = 0
FPS.frames_count = 0
FPS.frames_time = 0.0
local fps_msg = "FPS: " .. FPS.fps
c_set_fps(fps_msg);//Set some label in app - c function
end
function updateFPS(frameDeltaTime)
FPS.frames_count = FPS.frames_count + 1
FPS.frames_time = FPS.frames_time + frameDeltaTime
if FPS.frames_time >= 1000.0
then
FPS.frames_time = 0.0;
FPS.fps = FPS.frames_count;
FPS.frames_count = 0;
if FPS.last_fps ~= FPS.fps
then
local fps_msg = "FPS: " .. FPS.fps
c_set_fps(fps_msg);
FPS.last_fps = FPS.fps
end
end
end
FPS.c
void update_fps(const double* frame_delta_time) //SEGFAULT at this, at random time
{
lua_State* l = get_lua();
lua_getglobal(l, "updateFPS");
lua_pushnumber(l, *frame_delta_time);
lua_call(l, 1, 0);
}
Y obtenga el siguiente mensaje de error con bloqueo de la aplicación completa en un momento aleatorio (1 min - 3 min)
Última pregunta (yay, lo hiciste / omite parte aburrida)
¿Por qué obtengo segfaults, por qué al azar? El script FPS es solo un ejemplo, como mucho, cada script de lua tiene la posibilidad de bloquear toda la aplicación (más llamadas == mejor oportunidad). Entonces, algunas secuencias de comandos de jugadores que cambian su directorio en una nueva posición de bloqueo a veces también.
Creo que es porque hay un conflicto entre el limpiador de basura Android / Java y el limpiador de basura Lua, así que intenta liberar la memoria ya liberada.
EDITAR - DESDE ALLÍ DOBLE PUNTERO VEN Y POR QUÉ:
#define MS_1_SEC 1000.0
typedef struct time_manager
{
double _time;
double delta_time;
}time_manager;
static double get_ms(s_time* time)//get time in ms
{
return MS_1_SEC * time->tv_sec + (double) time->tv_nsec / NS_1_SEC;
}
double get_time_now()
{
s_time time_now;
clock_gettime(CLOCK_REALTIME, &time_now);
return get_ms(&time_now);
}
void init_time_manager(time_manager* tm)
{
tm->_time = get_time_now();
tm->delta_time = 0.0;
}
void update_time_manager(time_manager* tm)
{
double time_now = get_time_now();
tm->delta_time = time_now - tm->_time;
tm->_time = time_now;
}
static time_manager TM;//Global static var for whole render module
En la función onInit ()
init_time_manager(&TM);
En la función onDraw ()
double* frame_time = &TM.delta_time;//get pointer to delta time
update_ui(frame_time);//Pass it every function
update_sprites(frame_time);
update_fps(frame_time);
...
draw_fps();
update_time_manager(&TM);
¿Por qué uso el puntero para duplicar en lugar de solo el doble? Bueno, ahorra 4 bytes de copia (cada puntero tiene un tamaño de 4, el doble tiene un tamaño de 8) frame_delta_time param para cada función como update_ui (), hago lo mismo para cada estructura / tipo de más de 4 bytes, const punteros en lugar de solo struct x para acceso de solo lectura. ¿Es esto malo?
Mira esto: http://comments.gmane.org/gmane.comp.security.nmap.devel/14966
static void trydecpoint (LexState *ls, SemInfo *seminfo) {
char old = ls->decpoint;
ls->decpoint = ''.''; //ls->decpoint = getlocaledecpoint(); // try to fix error: ''struct lconv'' has no member named ''decimal_point'' -------- look at here
buffreplace(ls, old, ls->decpoint); /* try new decimal separator */
if (!buff2d(ls->buff, &seminfo->r)) {
/* format error with correct decimal point: no more options */
buffreplace(ls, ls->decpoint, ''.''); /* undo change (for error message) */
lexerror(ls, "malformed number", TK_NUMBER);
}
}
Algunas veces es -DLUA_USE_APICHECK
compilar lua con -DLUA_USE_APICHECK
. Generará algunos mensajes de error en lugar de segfaults.