From 6585060d30035dad15abe90a3a615f564e57ea5f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 4 Nov 2012 13:49:32 -0800 Subject: [PATCH] Added information on running valgrind on Android --- README.android | 46 +++++++++++++++++++++++++++++++- src/core/android/SDL_android.cpp | 37 ++++++++++++++++--------- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/README.android b/README.android index f4513de3c..252fe9eff 100644 --- a/README.android +++ b/README.android @@ -48,7 +48,7 @@ If you want to use the Eclipse IDE, skip to the Eclipse section below. 5. Edit /local.properties to point to the Android SDK directory 6. Run 'ant debug' in android/project. This compiles the .java and eventually creates a .apk with the native code embedded -7. 'ant install' will push the apk to the device or emulator (if connected) +7. 'ant debug install' will push the apk to the device or emulator (if connected) Here's an explanation of the files in the Android project, so you can customize them: @@ -275,6 +275,50 @@ If you need to build without optimization turned on, you can create a file calle APP_OPTIM := debug +================================================================================ + Memory debugging +================================================================================ + +The best (and slowest) way to debug memory issues on Android is valgrind. +Valgrind has support for Android out of the box, just grab code using: + svn co svn://svn.valgrind.org/valgrind/trunk valgrind +... and follow the instructions in the file README.android to build it. + +One thing I needed to do on Mac OS X was change the path to the toolchain, +and add ranlib to the environment variables: +export RANLIB=$NDKROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-ranlib + +Once valgrind is built, you can create a wrapper script to launch your +application with it, changing org.libsdl.app to your package identifier: +--- start_valgrind_app ------------------- +#!/system/bin/sh +export TMPDIR=/data/data/org.libsdl.app +exec /data/local/Inst/bin/valgrind --log-file=/sdcard/valgrind.log --error-limit=no $* +------------------------------------------ + +Then push it to the device: + adb push start_valgrind_app /data/local + +and make it executable: + adb shell chmod 755 /data/local/start_valgrind_app + +and tell Android to use the script to launch your application: + adb shell setprop wrap.org.libsdl.app "logwrapper /data/local/start_valgrind_app" + +If the setprop command says "could not set property", it's likely that +your package name is too long and you should make it shorter by changing +AndroidManifest.xml and the path to your class file in android-project/src + +You can then launch your application normally and waaaaaaaiiittt for it. +You can monitor the startup process with the logcat command above, and +when it's done (or even while it's running) you can grab the valgrind +output file: + adb pull /sdcard/valgrind.log + +When you're done instrumenting with valgrind, you can disable the wrapper: + adb shell setprop wrap.org.libsdl.app "" + + ================================================================================ Known issues ================================================================================ diff --git a/src/core/android/SDL_android.cpp b/src/core/android/SDL_android.cpp index b7fe7c5c0..c05152e98 100644 --- a/src/core/android/SDL_android.cpp +++ b/src/core/android/SDL_android.cpp @@ -21,6 +21,7 @@ #include "SDL_config.h" #include "SDL_stdinc.h" #include "SDL_assert.h" +#include "SDL_log.h" #ifdef __ANDROID__ @@ -41,6 +42,8 @@ extern "C" { #define LOGI(...) do {} while (false) #define LOGE(...) do {} while (false) +/* Uncomment this to log messages entering and exiting methods in this file */ +//#define DEBUG_JNI /* Implemented in audio/android/SDL_androidaudio.c */ extern void Android_RunAudioThread(); @@ -259,8 +262,15 @@ class LocalReferenceHolder } public: - LocalReferenceHolder() : m_env(NULL) { } + LocalReferenceHolder(const char *func) : m_env(NULL), m_func(func) { +#ifdef DEBUG_JNI + SDL_Log("Entering function %s", m_func); +#endif + } ~LocalReferenceHolder() { +#ifdef DEBUG_JNI + SDL_Log("Leaving function %s", m_func); +#endif if (m_env) { m_env->PopLocalFrame(NULL); --s_active; @@ -279,6 +289,7 @@ class LocalReferenceHolder protected: JNIEnv *m_env; + const char *m_func; }; int LocalReferenceHolder::s_active; @@ -497,7 +508,7 @@ static bool Android_JNI_ExceptionOccurred() static int Android_JNI_FileOpen(SDL_RWops* ctx) { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); int result = 0; jmethodID mid; @@ -592,7 +603,7 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx) extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx, const char* fileName, const char*) { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); JNIEnv *mEnv = Android_JNI_GetEnv(); if (!refs.init(mEnv)) { @@ -615,7 +626,7 @@ extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx, extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, size_t size, size_t maxnum) { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); jlong bytesRemaining = (jlong) (size * maxnum); jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position); int bytesRead = 0; @@ -661,7 +672,7 @@ extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer, static int Android_JNI_FileClose(SDL_RWops* ctx, bool release) { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); int result = 0; JNIEnv *mEnv = Android_JNI_GetEnv(); @@ -731,7 +742,7 @@ extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence Sint64 movement = newPosition - ctx->hidden.androidio.position; if (movement > 0) { - unsigned char buffer[1024]; + unsigned char buffer[4096]; // The easy case where we're seeking forwards while (movement > 0) { @@ -767,7 +778,7 @@ extern "C" int Android_JNI_FileClose(SDL_RWops* ctx) // returns a new global reference which needs to be released later static jobject Android_JNI_GetSystemServiceObject(const char* name) { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); JNIEnv* env = Android_JNI_GetEnv(); if (!refs.init(env)) { return NULL; @@ -789,7 +800,7 @@ static jobject Android_JNI_GetSystemServiceObject(const char* name) } #define SETUP_CLIPBOARD(error) \ - LocalReferenceHolder refs; \ + LocalReferenceHolder refs(__FUNCTION__); \ JNIEnv* env = Android_JNI_GetEnv(); \ if (!refs.init(env)) { \ return error; \ @@ -847,7 +858,7 @@ extern "C" SDL_bool Android_JNI_HasClipboardText() // returns the value in seconds and percent or -1 if not available extern "C" int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent) { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); JNIEnv* env = Android_JNI_GetEnv(); if (!refs.init(env)) { return -1; @@ -993,7 +1004,7 @@ extern "C" void *SDL_AndroidGetJNIEnv() extern "C" void *SDL_AndroidGetActivity() { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); jmethodID mid; JNIEnv *env = Android_JNI_GetEnv(); @@ -1012,7 +1023,7 @@ extern "C" const char * SDL_AndroidGetInternalStoragePath() static char *s_AndroidInternalFilesPath = NULL; if (!s_AndroidInternalFilesPath) { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); jmethodID mid; jobject context; jobject fileObject; @@ -1052,7 +1063,7 @@ extern "C" const char * SDL_AndroidGetInternalStoragePath() extern "C" int SDL_AndroidGetExternalStorageState() { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); jmethodID mid; jclass cls; jstring stateString; @@ -1092,7 +1103,7 @@ extern "C" const char * SDL_AndroidGetExternalStoragePath() static char *s_AndroidExternalFilesPath = NULL; if (!s_AndroidExternalFilesPath) { - LocalReferenceHolder refs; + LocalReferenceHolder refs(__FUNCTION__); jmethodID mid; jobject context; jobject fileObject;