Improved the use of Memento with C++.
authorJulian Smith <[email protected]>
Tue, 4 Oct 2022 12:19:26 +0000 (13:19 +0100)
committerJulian Smith <[email protected]>
Fri, 7 Oct 2022 16:06:45 +0000 (17:06 +0100)
We expose a new C API Memento_cpp_new(), Memento_delete() etc,
that is always built. This API needs to be called by operator
new/delete/new[]/delete[] etc. There are various ways this
can happen.

If memento.c is built with a C compiler, no global new/delete
operators are provided.

If memento.c is built with a C++ compiler, and MEMENTO_NO_CPLUSPLUS
is not defined global new/delete operators are provided.

If memento.h is included by a C++ file, and MEMENTO_NO_CPLUSPLUS
is not defined the library user must provide their own.

This can be done by copying a bunch of lines from memento.c into
a C++ file in the project, or by #including memento.c, with
MEMENTO_CPP_EXTRAS_ONLY defined.

Work around problems with C++ on Linux; if MEMENTO is defined we #include
<string.h> early on in memento.h to avoid an obscure conflict with strdup()
having a `throw()` attribute, unlike Memento_strdup().

Git may get the attribution wrong here; the original work was from
Julian Smith, reworked by Robin Watts. Credit to Julian, blame to Robin.

include/mupdf/memento.h
source/fitz/memento.c

index 9b513e8940e8c79315baa26bf6e5543276f59e0d..5806fc0b69b707c30d0eeccdbd246ded6710205d 100644 (file)
  *    Memento has some experimental code in it to trap new/delete (and
  *    new[]/delete[] if required) calls.
  *
- *    In order for this to work, either:
+ *    In all cases, Memento will provide a C API that new/delete
+ *    operators can be built upon:
+ *         void *Memento_cpp_new(size_t size);
+ *         void Memento_cpp_delete(void *pointer);
+ *         void *Memento_cpp_new_array(size_t size);
+ *         void Memento_cpp_delete_array(void *pointer);
  *
- *    1) Build memento.c with the c++ compiler.
+ *    There are various ways that actual operator definitions can be
+ *    provided:
+ *
+ *    1) If memento.c is built with the c++ compiler, then global new
+ *    and delete operators will be built in to memento by default.
+ *
+ *    2) If memento.c is built as normal with the C compiler, then
+ *    no such veneers will be built in. The caller must provide them
+ *    themselves. This can be done either by:
+ *
+ *       a) Copying the lines between:
+ *               // C++ Operator Veneers - START
+ *       and
+ *               // C++ Operator Veneers - END
+ *       from memento.c into a C++ file within their own project.
  *
  *    or
  *
- *    2) Build memento.c as normal with the C compiler, then from any
- *       one of your .cpp files, do:
+ *       b) Add the following lines to a C++ file in the project:
+ *          #define MEMENTO_CPP_EXTRAS_ONLY
+ *          #include "memento.c"
  *
- *       #define MEMENTO_CPP_EXTRAS_ONLY
- *       #include "memento.c"
+ *    3) For those people that would like to be able to compile memento.c
+ *    with a C compiler, and provide new/delete veneers globally
+ *    within their own C++ code (so avoiding the need for memento.h to
+ *    be included from every file), define MEMENTO_NO_CPLUSPLUS as you
+ *    build, and Memento will not provide any veneers itself, instead
+ *    relying on the library user to provide them.
  *
- *       In the case where MEMENTO is not defined, this will not do anything.
+ *    For convenience the lines to implement such veneers can be found
+ *    further down this file between:
+ *
+ *    Memento's interception of new/delete can be disabled at runtime
+ *    by using Memento_setIgnoreNewDelete(1). Alternatively the
+ *    MEMENTO_IGNORENEWDELETE environment variable can be set to 1 to
+ *    achieve the same result.
  *
  *    Both Windows and GCC provide separate new[] and delete[] operators
  *    for arrays. Apparently some systems do not. If this is the case for
 #include <stdlib.h>
 #include <stdarg.h>
 
+#ifdef __cplusplus
+
+// Avoids problems with strdup()'s throw() attribute on Linux.
+#include <string.h>
+
+extern "C" {
+#endif
+
 #define MEMENTO_H
 
 #ifndef MEMENTO_UNDERLYING_MALLOC
@@ -233,6 +271,8 @@ size_t Memento_setMax(size_t);
 void Memento_stats(void);
 void *Memento_label(void *, const char *);
 void Memento_tick(void);
+int Memento_setVerbose(int verbose);
+int Memento_setIgnoreNewDelete(int ignore);
 
 void *Memento_malloc(size_t s);
 void *Memento_realloc(void *, size_t s);
@@ -274,6 +314,12 @@ void Memento_fin(void);
 
 void Memento_bt(void);
 
+void *Memento_cpp_new(size_t size);
+void Memento_cpp_delete(void *pointer);
+void *Memento_cpp_new_array(size_t size);
+void Memento_cpp_delete_array(void *pointer);
+
+
 #ifdef MEMENTO
 
 #ifndef COMPILING_MEMENTO_C
@@ -331,6 +377,8 @@ void Memento_bt(void);
 #define Memento_checkBytePointerOrNull(A)  0
 #define Memento_checkShortPointerOrNull(A) 0
 #define Memento_checkIntPointerOrNull(A)   0
+#define Memento_setVerbose(v)              0
+#define Memento_setIgnoreNewDelete(v)      0
 
 #define Memento_tick()                     do {} while (0)
 #define Memento_startLeaking()             do {} while (0)
@@ -342,4 +390,8 @@ void Memento_bt(void);
 
 #endif /* MEMENTO */
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* MEMENTO_H */
index 9e5ed303c2f174936af20c518fd35f59496aa346..2fea386f556fcad8204d7fd809fcb17e35d54cd6 100644 (file)
@@ -11,6 +11,8 @@
    Novato, CA 94945, U.S.A., +1(415)492-9861, for further information.
 */
 
+#ifndef MEMENTO_CPP_EXTRAS_ONLY
+
 /* Inspired by Fortify by Simon P Bullen. */
 
 /* Set the following if you're only looking for leaks, not memory overwrites
@@ -108,8 +110,6 @@ int atexit(void (*)(void));
 
 #ifdef MEMENTO
 
-#ifndef MEMENTO_CPP_EXTRAS_ONLY
-
 #ifdef MEMENTO_ANDROID
 #include <android/log.h>
 
@@ -468,6 +468,7 @@ static struct {
     Memento_range *squeezes;
     int            squeezes_num;
     int            squeezes_pos;
+    int            ignoreNewDelete;
 } memento;
 
 #define MEMENTO_EXTRASIZE (sizeof(Memento_BlkHeader) + Memento_PostSize)
@@ -1947,6 +1948,9 @@ static void Memento_init(void)
     env = getenv("MEMENTO_VERBOSE");
     memento.verbose = (env ? atoi(env) : 1);
 
+    env = getenv("MEMENTO_IGNORENEWDELETE");
+    memento.ignoreNewDelete = (env ? atoi(env) : 0);
+
     atexit(Memento_fin);
 
     Memento_initMutex(&memento.mutex);
@@ -3190,6 +3194,13 @@ int Memento_setVerbose(int verbose)
     return ret;
 }
 
+int Memento_setIgnoreNewDelete(int ignore)
+{
+    int ret = memento.ignoreNewDelete;
+    memento.ignoreNewDelete = ignore;
+    return ret;
+}
+
 int Memento_paranoidAt(int i)
 {
     memento.paranoidAt = i;
@@ -3360,18 +3371,16 @@ int Memento_squeezing(void)
     return memento.squeezing;
 }
 
-#endif /* MEMENTO_CPP_EXTRAS_ONLY */
-
-#ifdef __cplusplus
-/* Dumb overrides for the new and delete operators */
-
-void *operator new(size_t size)
+void *Memento_cpp_new(size_t size)
 {
     void *ret;
 
     if (!memento.inited)
         Memento_init();
 
+    if (memento.ignoreNewDelete)
+        return MEMENTO_UNDERLYING_MALLOC(size);
+
     if (size == 0)
         size = 1;
     MEMENTO_LOCK();
@@ -3380,11 +3389,19 @@ void *operator new(size_t size)
     return ret;
 }
 
-void  operator delete(void *pointer)
+void Memento_cpp_delete(void *pointer)
 {
     if (!pointer)
         return;
 
+    if (!memento.inited)
+        Memento_init();
+    if (memento.ignoreNewDelete)
+    {
+        MEMENTO_UNDERLYING_FREE(pointer);
+        return;
+    }
+
     MEMENTO_LOCK();
     do_free(pointer, Memento_EventType_delete);
     MEMENTO_UNLOCK();
@@ -3392,8 +3409,7 @@ void  operator delete(void *pointer)
 
 /* Some C++ systems (apparently) don't provide new[] or delete[]
  * operators. Provide a way to cope with this */
-#ifndef MEMENTO_CPP_NO_ARRAY_CONSTRUCTORS
-void *operator new[](size_t size)
+void *Memento_cpp_new_array(size_t size)
 {
     void *ret;
     if (!memento.inited)
@@ -3401,22 +3417,30 @@ void *operator new[](size_t size)
 
     if (size == 0)
         size = 1;
+
+    if (memento.ignoreNewDelete)
+        return MEMENTO_UNDERLYING_MALLOC(size);
+
     MEMENTO_LOCK();
     ret = do_malloc(size, Memento_EventType_newArray);
     MEMENTO_UNLOCK();
     return ret;
 }
 
-void  operator delete[](void *pointer)
+void  Memento_cpp_delete_array(void *pointer)
 {
+    if (memento.ignoreNewDelete)
+    {
+        MEMENTO_UNDERLYING_FREE(pointer);
+        return;
+    }
+
     MEMENTO_LOCK();
     do_free(pointer, Memento_EventType_deleteArray);
     MEMENTO_UNLOCK();
 }
-#endif /* MEMENTO_CPP_NO_ARRAY_CONSTRUCTORS */
-#endif /* __cplusplus */
 
-#else
+#else /* MEMENTO */
 
 /* Just in case anyone has left some debugging code in... */
 void (Memento_breakpoint)(void)
@@ -3592,6 +3616,16 @@ void (Memento_listNewBlocks)(void)
 {
 }
 
+int (Memento_setIgnoreNewDelete)(int ignore)
+{
+    return 0;
+}
+
+int (Memento_setVerbose)(int verbose)
+{
+    return 0;
+}
+
 size_t (Memento_setMax)(size_t max)
 {
     return 0;
@@ -3631,4 +3665,48 @@ int (Memento_squeezing)(void)
     return 0;
 }
 
-#endif
+#endif /* MEMENTO */
+
+#endif /* MEMENTO_CPP_EXTRAS_ONLY */
+
+/* Everything here is only for C++, and then only if we haven't
+ * disabled it. */
+
+#ifndef MEMENTO_NO_CPLUSPLUS
+#ifdef __cplusplus
+
+// C++ Operator Veneers - START
+void *operator new(size_t size)
+{
+    return Memento_cpp_new(size);
+}
+void  operator delete(void *pointer)
+{
+    Memento_cpp_delete(pointer);
+}
+void* operator new[](size_t size)
+{
+    return Memento_cpp_new_array(size);
+}
+void  operator delete[](void *pointer)
+{
+   Memento_cpp_delete_array(pointer);
+}
+
+/* Some C++ systems (apparently) don't provide new[] or delete[]
+ * operators. Provide a way to cope with this */
+#ifndef MEMENTO_CPP_NO_ARRAY_CONSTRUCTORS
+void *operator new[](size_t size)
+{
+    return Memento_cpp_new_array(size);
+}
+
+void  operator delete[](void *pointer)
+{
+    Memento_cpp_delete_array(pointer);
+}
+#endif /* MEMENTO_CPP_NO_ARRAY_CONSTRUCTORS */
+// C++ Operator Veneers - END
+
+#endif /* __cplusplus */
+#endif /* MEMENTO_NO_CPLUSPLUS */