summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2013-04-29 11:52:09 +0100
committerRobin Watts <robin.watts@artifex.com>2013-04-29 13:00:14 +0100
commit7696a4678e1eee59488649ef20f7fbb4d7d3fc0c (patch)
tree095dc8589574d9a16d8d5e20be33045a6cc4310c
parentae40ceccb46df6d45e45c40b27f426286a46a7e6 (diff)
downloadmupdf-7696a4678e1eee59488649ef20f7fbb4d7d3fc0c.tar.xz
Add Memento build option to Android build. Fix memory leaks.
Following up on a report from a potential customer, fix various places in mupdf.c where we were leaking memory (devices not freed, context not properly freed etc). In order to track this down, I added a Memento build - just do: ndk-build MEMENTO=1 when building. This only checks for leaks, not for memory overwrites by default as it uses MEMENTO_LEAKONLY to avoid any possibility of the android kernel killing stuff for being too slow or using too much memory.
-rw-r--r--android/jni/Application.mk3
-rw-r--r--android/jni/Core.mk6
-rw-r--r--android/jni/Core2.mk3
-rw-r--r--android/jni/ThirdParty.mk3
-rw-r--r--android/jni/mupdf.c86
-rw-r--r--fitz/memento.c45
-rw-r--r--fitz/memento.h6
7 files changed, 122 insertions, 30 deletions
diff --git a/android/jni/Application.mk b/android/jni/Application.mk
index 8fec4403..2b2f67c0 100644
--- a/android/jni/Application.mk
+++ b/android/jni/Application.mk
@@ -23,6 +23,9 @@ endif
ifdef V8_BUILD
APP_STL := stlport_static
endif
+ifdef MEMENTO
+APP_CFLAGS += -DMEMENTO -DMEMENTO_LEAKONLY
+endif
# If the ndk is r8b then workaround bug by uncommenting the following line
#NDK_TOOLCHAIN_VERSION=4.4.3
diff --git a/android/jni/Core.mk b/android/jni/Core.mk
index b3619261..f1312bf2 100644
--- a/android/jni/Core.mk
+++ b/android/jni/Core.mk
@@ -17,6 +17,9 @@ LOCAL_CFLAGS += -pg -DNDK_PROFILER -O2
endif
endif
LOCAL_CFLAGS += -DAA_BITS=8
+ifdef MEMENTO
+LOCAL_CFLAGS += -DMEMENTO -DMEMENTO_LEAKONLY
+endif
LOCAL_C_INCLUDES := \
../thirdparty/jbig2dec \
@@ -141,6 +144,9 @@ LOCAL_SRC_FILES := \
$(MY_ROOT)/xps/xps_util.c \
$(MY_ROOT)/xps/xps_zip.c \
$(MY_ROOT)/cbz/mucbz.c
+ifdef MEMENTO
+ LOCAL_SRC_FILES += $(MY_ROOT)/fitz/memento.c
+endif
ifdef V8_BUILD
ifeq ($(V8_OK),1)
LOCAL_SRC_FILES += \
diff --git a/android/jni/Core2.mk b/android/jni/Core2.mk
index b8239e18..3c8c1841 100644
--- a/android/jni/Core2.mk
+++ b/android/jni/Core2.mk
@@ -18,6 +18,9 @@ NDK_APP_CFLAGS :=
endif
endif
LOCAL_CFLAGS += -DAA_BITS=8
+ifdef MEMENTO
+LOCAL_CFLAGS += -DMEMENTO -DMEMENTO_LEAKONLY
+endif
LOCAL_C_INCLUDES := \
../thirdparty/jbig2dec \
diff --git a/android/jni/ThirdParty.mk b/android/jni/ThirdParty.mk
index 660c8e9e..e92ca2c4 100644
--- a/android/jni/ThirdParty.mk
+++ b/android/jni/ThirdParty.mk
@@ -24,6 +24,9 @@ LOCAL_CFLAGS := \
ifdef NDK_PROFILER
LOCAL_CFLAGS += -pg -DNDK_PROFILER -O2
endif
+ifdef MEMENTO
+LOCAL_CFLAGS += -DMEMENTO -DMEMENTO_LEAKONLY
+endif
LOCAL_MODULE := mupdfthirdparty
LOCAL_SRC_FILES := \
diff --git a/android/jni/mupdf.c b/android/jni/mupdf.c
index 95d2e10a..2282d7dc 100644
--- a/android/jni/mupdf.c
+++ b/android/jni/mupdf.c
@@ -249,8 +249,11 @@ static void alerts_fin(globals *glo)
static globals *get_globals(JNIEnv *env, jobject thiz)
{
globals *glo = (globals *)(void *)((*env)->GetLongField(env, thiz, global_fid));
- glo->env = env;
- glo->thiz = thiz;
+ if (glo != NULL)
+ {
+ glo->env = env;
+ glo->thiz = thiz;
+ }
return glo;
}
@@ -430,6 +433,10 @@ JNI_FN(MuPDFCore_openBuffer)(JNIEnv * env, jobject thiz)
}
LOGE("Done!");
}
+ fz_always(ctx)
+ {
+ fz_close(stream);
+ }
fz_catch(ctx)
{
LOGE("Failed: %s", ctx->error->message);
@@ -655,19 +662,18 @@ JNI_FN(MuPDFCore_drawPage)(JNIEnv *env, jobject thiz, jobject bitmap,
pc->page_list = fz_new_display_list(ctx);
dev = fz_new_list_device(ctx, pc->page_list);
fz_run_page_contents(doc, pc->page, dev, &fz_identity, NULL);
+ fz_free_device(dev);
+ dev = NULL;
}
if (pc->annot_list == NULL)
{
fz_annot *annot;
- if (dev)
- {
- fz_free_device(dev);
- dev = NULL;
- }
pc->annot_list = fz_new_display_list(ctx);
dev = fz_new_list_device(ctx, pc->annot_list);
for (annot = fz_first_annot(doc, pc->page); annot; annot = fz_next_annot(doc, annot))
fz_run_annot(doc, pc->page, annot, dev, &fz_identity, NULL);
+ fz_free_device(dev);
+ dev = NULL;
}
bbox.x0 = patchX;
bbox.y0 = patchY;
@@ -717,9 +723,13 @@ JNI_FN(MuPDFCore_drawPage)(JNIEnv *env, jobject thiz, jobject bitmap,
fz_drop_pixmap(ctx, pix);
LOGE("Rendered");
}
- fz_catch(ctx)
+ fz_always(ctx)
{
fz_free_device(dev);
+ dev = NULL;
+ }
+ fz_catch(ctx)
+ {
LOGE("Render failed");
}
@@ -825,17 +835,17 @@ JNI_FN(MuPDFCore_updatePageInternal)(JNIEnv *env, jobject thiz, jobject bitmap,
pc->page_list = fz_new_display_list(ctx);
dev = fz_new_list_device(ctx, pc->page_list);
fz_run_page_contents(doc, pc->page, dev, &fz_identity, NULL);
+ fz_free_device(dev);
+ dev = NULL;
}
if (pc->annot_list == NULL) {
- if (dev) {
- fz_free_device(dev);
- dev = NULL;
- }
pc->annot_list = fz_new_display_list(ctx);
dev = fz_new_list_device(ctx, pc->annot_list);
for (annot = fz_first_annot(doc, pc->page); annot; annot = fz_next_annot(doc, annot))
fz_run_annot(doc, pc->page, annot, dev, &fz_identity, NULL);
+ fz_free_device(dev);
+ dev = NULL;
}
bbox.x0 = patchX;
@@ -885,9 +895,13 @@ JNI_FN(MuPDFCore_updatePageInternal)(JNIEnv *env, jobject thiz, jobject bitmap,
LOGE("Rendered");
}
- fz_catch(ctx)
+ fz_always(ctx)
{
fz_free_device(dev);
+ dev = NULL;
+ }
+ fz_catch(ctx)
+ {
LOGE("Render failed");
}
@@ -1030,6 +1044,7 @@ JNI_FN(MuPDFCore_hasOutlineInternal)(JNIEnv * env, jobject thiz)
globals *glo = get_globals(env, thiz);
fz_outline *outline = fz_load_outline(glo->doc);
+ fz_free_outline(glo->ctx, outline);
return (outline == NULL) ? JNI_FALSE : JNI_TRUE;
}
@@ -1043,6 +1058,7 @@ JNI_FN(MuPDFCore_getOutlineInternal)(JNIEnv * env, jobject thiz)
fz_outline *outline;
int nItems;
globals *glo = get_globals(env, thiz);
+ int ret;
olClass = (*env)->FindClass(env, PACKAGENAME "/OutlineItem");
if (olClass == NULL) return NULL;
@@ -1058,9 +1074,11 @@ JNI_FN(MuPDFCore_getOutlineInternal)(JNIEnv * env, jobject thiz)
NULL);
if (arr == NULL) return NULL;
- return fillInOutlineItems(env, olClass, ctor, arr, 0, outline, 0) > 0
+ ret = fillInOutlineItems(env, olClass, ctor, arr, 0, outline, 0) > 0
? arr
:NULL;
+ fz_free_outline(glo->ctx, outline);
+ return ret;
}
JNIEXPORT jobjectArray JNICALL
@@ -1606,6 +1624,8 @@ static void close_doc(globals *glo)
fz_close_document(glo->doc);
glo->doc = NULL;
+ fz_free_context(glo->ctx);
+ glo->ctx = NULL;
}
JNIEXPORT void JNICALL
@@ -1613,12 +1633,19 @@ JNI_FN(MuPDFCore_destroying)(JNIEnv * env, jobject thiz)
{
globals *glo = get_globals(env, thiz);
+ if (glo == NULL)
+ return;
LOGI("Destroying");
- close_doc(glo);
fz_free(glo->ctx, glo->current_path);
glo->current_path = NULL;
+ close_doc(glo);
free(glo);
-
+#ifdef MEMENTO
+ LOGI("Destroying dump start");
+ Memento_listBlocks();
+ Memento_stats();
+ LOGI("Destroying dump end");
+#endif
#ifdef NDK_PROFILER
// Apparently we should really be writing to whatever path we get
// from calling getFilesDir() in the java part, which supposedly
@@ -1686,7 +1713,11 @@ JNI_FN(MuPDFCore_getPageLinksInternal)(JNIEnv * env, jobject thiz, int pageNumbe
}
arr = (*env)->NewObjectArray(env, count, linkInfoClass, NULL);
- if (arr == NULL) return NULL;
+ if (arr == NULL)
+ {
+ fz_drop_link(glo->ctx, list);
+ return NULL;
+ }
count = 0;
for (link = list; link; link = link->next)
@@ -1726,11 +1757,16 @@ JNI_FN(MuPDFCore_getPageLinksInternal)(JNIEnv * env, jobject thiz, int pageNumbe
continue;
}
- if (linkInfo == NULL) return NULL;
+ if (linkInfo == NULL)
+ {
+ fz_drop_link(glo->ctx, list);
+ return NULL;
+ }
(*env)->SetObjectArrayElement(env, arr, count, linkInfo);
(*env)->DeleteLocalRef(env, linkInfo);
count++;
}
+ fz_drop_link(glo->ctx, list);
return arr;
}
@@ -2361,3 +2397,17 @@ JNI_FN(MuPDFCore_saveInternal)(JNIEnv * env, jobject thiz)
}
}
}
+
+JNIEXPORT void JNICALL
+JNI_FN(MuPDFCore_dumpMemoryInternal)(JNIEnv * env, jobject thiz)
+{
+ globals *glo = get_globals(env, thiz);
+ fz_context *ctx = glo->ctx;
+
+#ifdef MEMENTO
+ LOGE("dumpMemoryInternal start");
+ Memento_listNewBlocks();
+ Memento_stats();
+ LOGE("dumpMemoryInternal end");
+#endif
+}
diff --git a/fitz/memento.c b/fitz/memento.c
index bbd0455f..9471b25f 100644
--- a/fitz/memento.c
+++ b/fitz/memento.c
@@ -19,12 +19,6 @@
* to speed the operation */
/* #define MEMENTO_LEAKONLY */
-#ifndef MEMENTO_STACKTRACE_METHOD
-#ifdef __GNUC__
-#define MEMENTO_STACKTRACE_METHOD 1
-#endif
-#endif
-
/* Don't keep blocks around if they'd mean losing more than a quarter of
* the freelist. */
#define MEMENTO_FREELIST_MAX_SINGLE_BLOCK (MEMENTO_FREELIST_MAX/4)
@@ -52,6 +46,29 @@ int atexit(void (*)(void));
#include <stdlib.h>
#endif
+#ifdef MEMENTO_ANDROID
+#include <android/log.h>
+
+static int
+android_fprintf(FILE *file, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ __android_log_vprint(ANDROID_LOG_ERROR,"memento", fmt, args);
+ va_end(args);
+}
+
+#define fprintf android_fprintf
+#define MEMENTO_STACKTRACE_METHOD 0
+#endif
+
+#ifndef MEMENTO_STACKTRACE_METHOD
+#ifdef __GNUC__
+#define MEMENTO_STACKTRACE_METHOD 1
+#endif
+#endif
+
#if defined(__linux__)
#define MEMENTO_HAS_FORK
#elif defined(__APPLE__) && defined(__MACH__)
@@ -476,8 +493,8 @@ static void blockDisplay(Memento_BlkHeader *b, int n)
n++;
while (n > 40)
{
- fprintf(stderr, "*");
- n -= 40;
+ fprintf(stderr, "*");
+ n -= 40;
}
while(n > 0)
{
@@ -507,14 +524,14 @@ static void doNestedDisplay(Memento_BlkHeader *b,
/* Try and avoid recursion if we can help it */
do {
blockDisplay(b, depth);
- if (b->sibling) {
+ if (b->sibling) {
if (b->child)
doNestedDisplay(b->child, depth+1);
b = b->sibling;
- } else {
+ } else {
b = b->child;
depth++;
- }
+ }
} while (b);
}
@@ -754,8 +771,12 @@ static void Memento_init(void)
/* stashed_map[j] = i means that filedescriptor i-1 was duplicated to j */
int stashed_map[OPEN_MAX];
+#ifdef MEMENTO_STACKTRACE_METHOD
+#if MEMENTO_STACKTRACE_METHOD == 1
extern size_t backtrace(void **, int);
extern void backtrace_symbols_fd(void **, size_t, int);
+#endif
+#endif
static void Memento_signal(void)
{
@@ -851,7 +872,7 @@ static void Memento_signal(void)
globals.segv = 1;
/* If we just return from this function the SEGV will be unhandled, and
* we'll launch into whatever JIT debugging system the OS provides. At
- * least output something useful first. If MEMENTO_NOJIT is set, then
+ * least fprintf(stderr, something useful first. If MEMENTO_NOJIT is set, then
* just exit to avoid the JIT (and get the usual atexit handling). */
if (getenv("MEMENTO_NOJIT"))
exit(1);
diff --git a/fitz/memento.h b/fitz/memento.h
index 4831349b..75354384 100644
--- a/fitz/memento.h
+++ b/fitz/memento.h
@@ -137,6 +137,12 @@
#include <memory.h>
+#ifdef __ANDROID__
+#define MEMENTO_ANDROID
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
#define MEMENTO_H
#ifndef MEMENTO_UNDERLYING_MALLOC