diff mupdf-source/thirdparty/extract/src/memento.h @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/extract/src/memento.h	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,345 @@
+/* Copyright (C) 2009-2018 Artifex Software, Inc.
+   All Rights Reserved.
+
+   This software is provided AS-IS with no warranty, either express or
+   implied.
+
+   This software is distributed under license and may not be copied,
+   modified or distributed except as expressly authorized under the terms
+   of the license contained in the file COPYING in this distribution.
+
+   Refer to licensing information at http://www.artifex.com or contact
+   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
+   CA 94129, USA, for further information.
+*/
+
+/* Memento: A library to aid debugging of memory leaks/heap corruption.
+ *
+ * Usage (with C):
+ *    First, build your project with MEMENTO defined, and include this
+ *    header file wherever you use malloc, realloc or free.
+ *    This header file will use macros to point malloc, realloc and free to
+ *    point to Memento_malloc, Memento_realloc, Memento_free.
+ *
+ *    Run your program, and all mallocs/frees/reallocs should be redirected
+ *    through here. When the program exits, you will get a list of all the
+ *    leaked blocks, together with some helpful statistics. You can get the
+ *    same list of allocated blocks at any point during program execution by
+ *    calling Memento_listBlocks();
+ *
+ *    Every call to malloc/free/realloc counts as an 'allocation event'.
+ *    On each event Memento increments a counter. Every block is tagged with
+ *    the current counter on allocation. Every so often during program
+ *    execution, the heap is checked for consistency. By default this happens
+ *    after 1024 events, then after 2048 events, then after 4096 events, etc.
+ *    This can be changed at runtime by using Memento_setParanoia(int level).
+ *    0 turns off such checking, 1 sets checking to happen on every event,
+ *    any positive number n sets checking to happen once every n events,
+ *    and any negative number n sets checking to happen after -n events, then
+ *    after -2n events etc.
+ *
+ *    The default paranoia level is therefore -1024.
+ *
+ *    Memento keeps blocks around for a while after they have been freed, and
+ *    checks them as part of these heap checks to see if they have been
+ *    written to (or are freed twice etc).
+ *
+ *    A given heap block can be checked for consistency (it's 'pre' and
+ *    'post' guard blocks are checked to see if they have been written to)
+ *    by calling Memento_checkBlock(void *blockAddress);
+ *
+ *    A check of all the memory can be triggered by calling Memento_check();
+ *    (or Memento_checkAllMemory(); if you'd like it to be quieter).
+ *
+ *    A good place to breakpoint is Memento_breakpoint, as this will then
+ *    trigger your debugger if an error is detected. This is done
+ *    automatically for debug windows builds.
+ *
+ *    If a block is found to be corrupt, information will be printed to the
+ *    console, including the address of the block, the size of the block,
+ *    the type of corruption, the number of the block and the event on which
+ *    it last passed a check for correctness.
+ *
+ *    If you rerun, and call Memento_paranoidAt(int event); with this number
+ *    the code will wait until it reaches that event and then start
+ *    checking the heap after every allocation event. Assuming it is a
+ *    deterministic failure, you should then find out where in your program
+ *    the error is occurring (between event x-1 and event x).
+ *
+ *    Then you can rerun the program again, and call
+ *    Memento_breakAt(int event); and the program will call
+ *    Memento_Breakpoint() when event x is reached, enabling you to step
+ *    through.
+ *
+ *    Memento_find(address) will tell you what block (if any) the given
+ *    address is in.
+ *
+ * An example:
+ *    Suppose we have a gs invocation that crashes with memory corruption.
+ *     * Build with -DMEMENTO.
+ *     * In your debugger put a breakpoint on Memento_breakpoint.
+ *     * Run the program. It will stop in Memento_inited.
+ *     * Execute Memento_setParanoia(1);  (In VS use Ctrl-Alt-Q). (Note #1)
+ *     * Continue execution.
+ *     * It will detect the memory corruption on the next allocation event
+ *       after it happens, and stop in Memento_breakpoint. The console should
+ *       show something like:
+ *
+ *       Freed blocks:
+ *         0x172e610(size=288,num=1415) index 256 (0x172e710) onwards corrupted
+ *           Block last checked OK at allocation 1457. Now 1458.
+ *
+ *     * This means that the block became corrupted between allocation 1457
+ *       and 1458 - so if we rerun and stop the program at 1457, we can then
+ *       step through, possibly with a data breakpoint at 0x172e710 and see
+ *       when it occurs.
+ *     * So restart the program from the beginning. When we stop after
+ *       initialisation execute Memento_breakAt(1457); (and maybe
+ *       Memento_setParanoia(1), or Memento_setParanoidAt(1457))
+ *     * Continue execution until we hit Memento_breakpoint.
+ *     * Now you can step through and watch the memory corruption happen.
+ *
+ *    Note #1: Using Memento_setParanoia(1) can cause your program to run
+ *    very slowly. You may instead choose to use Memento_setParanoia(100)
+ *    (or some other figure). This will only exhaustively check memory on
+ *    every 100th allocation event. This trades speed for the size of the
+ *    average allocation event range in which detection of memory corruption
+ *    occurs. You may (for example) choose to run once checking every 100
+ *    allocations and discover that the corruption happens between events
+ *    X and X+100. You can then rerun using Memento_paranoidAt(X), and
+ *    it'll only start exhaustively checking when it reaches X.
+ *
+ * More than one memory allocator?
+ *
+ *    If you have more than one memory allocator in the system (like for
+ *    instance the ghostscript chunk allocator, that builds on top of the
+ *    standard malloc and returns chunks itself), then there are some things
+ *    to note:
+ *
+ *    * If the secondary allocator gets its underlying blocks from calling
+ *      malloc, then those will be checked by Memento, but 'subblocks' that
+ *      are returned to the secondary allocator will not. There is currently
+ *      no way to fix this other than trying to bypass the secondary
+ *      allocator. One way I have found to do this with the chunk allocator
+ *      is to tweak its idea of a 'large block' so that it puts every
+ *      allocation in its own chunk. Clearly this negates the point of having
+ *      a secondary allocator, and is therefore not recommended for general
+ *      use.
+ *
+ *    * Again, if the secondary allocator gets its underlying blocks from
+ *      calling malloc (and hence Memento) leak detection should still work
+ *      (but whole blocks will be detected rather than subblocks).
+ *
+ *    * If on every allocation attempt the secondary allocator calls into
+ *      Memento_failThisEvent(), and fails the allocation if it returns true
+ *      then more useful features can be used; firstly memory squeezing will
+ *      work, and secondly, Memento will have a "finer grained" paranoia
+ *      available to it.
+ *
+ * Usage with C++:
+ *
+ *    Memento has some experimental code in it to trap new/delete (and
+ *    new[]/delete[] if required) calls.
+ *
+ *    In order for this to work, either:
+ *
+ *    1) Build memento.c with the c++ compiler.
+ *
+ *    or
+ *
+ *    2) Build memento.c as normal with the C compiler, then from any
+ *       one of your .cpp files, do:
+ *
+ *       #define MEMENTO_CPP_EXTRAS_ONLY
+ *       #include "memento.c"
+ *
+ *       In the case where MEMENTO is not defined, this will not do anything.
+ *
+ *    Both Windows and GCC provide separate new[] and delete[] operators
+ *    for arrays. Apparently some systems do not. If this is the case for
+ *    your system, define MEMENTO_CPP_NO_ARRAY_CONSTRUCTORS.
+ *
+ * "libbacktrace.so failed to load"
+ *
+ *    In order to give nice backtraces on unix, Memento will try to use
+ *    a libbacktrace dynamic library. If it can't find it, you'll see
+ *    that warning, and your backtraces won't include file/line information.
+ *
+ *    To fix this you'll need to build your own libbacktrace. Don't worry
+ *    it's really easy:
+ *       git clone git://github.com/ianlancetaylor/libbacktrace
+ *       cd libbacktrace
+ *       ./configure
+ *       make
+ *
+ *    This leaves the build .so as .libs/libbacktrace.so
+ *
+ *    Memento will look for this on LD_LIBRARY_PATH, or in /opt/lib/,
+ *    or in /lib/, or  in /usr/lib/, or in /usr/local/lib/. I recommend
+ *    using /opt/lib/ as this won't conflict with anything that you
+ *    get via a package manager like apt.
+ *
+ *       sudo mkdir /opt
+ *       sudo mkdir /opt/lib
+ *       sudo cp .libs/libbacktrace.so /opt/lib/
+ */
+
+#ifndef MEMENTO_H
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define MEMENTO_H
+
+#ifndef MEMENTO_UNDERLYING_MALLOC
+#define MEMENTO_UNDERLYING_MALLOC malloc
+#endif
+#ifndef MEMENTO_UNDERLYING_FREE
+#define MEMENTO_UNDERLYING_FREE free
+#endif
+#ifndef MEMENTO_UNDERLYING_REALLOC
+#define MEMENTO_UNDERLYING_REALLOC realloc
+#endif
+#ifndef MEMENTO_UNDERLYING_CALLOC
+#define MEMENTO_UNDERLYING_CALLOC calloc
+#endif
+
+#ifndef MEMENTO_MAXALIGN
+#define MEMENTO_MAXALIGN (sizeof(int))
+#endif
+
+#define MEMENTO_PREFILL   0xa6
+#define MEMENTO_POSTFILL  0xa7
+#define MEMENTO_ALLOCFILL 0xa8
+#define MEMENTO_FREEFILL  0xa9
+
+#define MEMENTO_FREELIST_MAX 0x2000000
+
+int Memento_checkBlock(void *);
+int Memento_checkAllMemory(void);
+int Memento_check(void);
+
+int Memento_setParanoia(int);
+int Memento_paranoidAt(int);
+int Memento_breakAt(int);
+void Memento_breakOnFree(void *a);
+void Memento_breakOnRealloc(void *a);
+int Memento_getBlockNum(void *);
+int Memento_find(void *a);
+void Memento_breakpoint(void);
+int Memento_failAt(int);
+int Memento_failThisEvent(void);
+void Memento_listBlocks(void);
+void Memento_listNewBlocks(void);
+size_t Memento_setMax(size_t);
+void Memento_stats(void);
+void *Memento_label(void *, const char *);
+void Memento_tick(void);
+
+void *Memento_malloc(size_t s);
+void *Memento_realloc(void *, size_t s);
+void  Memento_free(void *);
+void *Memento_calloc(size_t, size_t);
+char *Memento_strdup(const char*);
+int Memento_asprintf(char **ret, const char *format, ...);
+int Memento_vasprintf(char **ret, const char *format, va_list ap);
+
+void Memento_info(void *addr);
+void Memento_listBlockInfo(void);
+void *Memento_takeByteRef(void *blk);
+void *Memento_dropByteRef(void *blk);
+void *Memento_takeShortRef(void *blk);
+void *Memento_dropShortRef(void *blk);
+void *Memento_takeIntRef(void *blk);
+void *Memento_dropIntRef(void *blk);
+void *Memento_takeRef(void *blk);
+void *Memento_dropRef(void *blk);
+void *Memento_adjustRef(void *blk, int adjust);
+void *Memento_reference(void *blk);
+
+int Memento_checkPointerOrNull(void *blk);
+int Memento_checkBytePointerOrNull(void *blk);
+int Memento_checkShortPointerOrNull(void *blk);
+int Memento_checkIntPointerOrNull(void *blk);
+
+void Memento_startLeaking(void);
+void Memento_stopLeaking(void);
+
+/* Returns number of allocation events so far. */
+int Memento_sequence(void);
+
+/* Returns non-zero if our process was forked by Memento squeeze. */
+int Memento_squeezing(void);
+
+void Memento_fin(void);
+
+void Memento_bt(void);
+
+#ifdef MEMENTO
+
+#ifndef COMPILING_MEMENTO_C
+#define malloc    Memento_malloc
+#define free      Memento_free
+#define realloc   Memento_realloc
+#define calloc    Memento_calloc
+#define strdup    Memento_strdup
+#define asprintf  Memento_asprintf
+#define vasprintf Memento_vasprintf
+#endif
+
+#else
+
+#define Memento_malloc    MEMENTO_UNDERLYING_MALLOC
+#define Memento_free      MEMENTO_UNDERLYING_FREE
+#define Memento_realloc   MEMENTO_UNDERLYING_REALLOC
+#define Memento_calloc    MEMENTO_UNDERLYING_CALLOC
+#define Memento_strdup    strdup
+#define Memento_asprintf  asprintf
+#define Memento_vasprintf vasprintf
+
+#define Memento_checkBlock(A)              0
+#define Memento_checkAllMemory()           0
+#define Memento_check()                    0
+#define Memento_setParanoia(A)             0
+#define Memento_paranoidAt(A)              0
+#define Memento_breakAt(A)                 0
+#define Memento_breakOnFree(A)             0
+#define Memento_breakOnRealloc(A)          0
+#define Memento_getBlockNum(A)             0
+#define Memento_find(A)                    0
+#define Memento_breakpoint()               do {} while (0)
+#define Memento_failAt(A)                  0
+#define Memento_failThisEvent()            0
+#define Memento_listBlocks()               do {} while (0)
+#define Memento_listNewBlocks()            do {} while (0)
+#define Memento_setMax(A)                  0
+#define Memento_stats()                    do {} while (0)
+#define Memento_label(A,B)                 (A)
+#define Memento_info(A)                    do {} while (0)
+#define Memento_listBlockInfo()            do {} while (0)
+#define Memento_takeByteRef(A)             (A)
+#define Memento_dropByteRef(A)             (A)
+#define Memento_takeShortRef(A)            (A)
+#define Memento_dropShortRef(A)            (A)
+#define Memento_takeIntRef(A)              (A)
+#define Memento_dropIntRef(A)              (A)
+#define Memento_takeRef(A)                 (A)
+#define Memento_dropRef(A)                 (A)
+#define Memento_adjustRef(A,V)             (A)
+#define Memento_reference(A)               (A)
+#define Memento_checkPointerOrNull(A)      0
+#define Memento_checkBytePointerOrNull(A)  0
+#define Memento_checkShortPointerOrNull(A) 0
+#define Memento_checkIntPointerOrNull(A)   0
+
+#define Memento_tick()                     do {} while (0)
+#define Memento_startLeaking()             do {} while (0)
+#define Memento_stopLeaking()              do {} while (0)
+#define Memento_fin()                      do {} while (0)
+#define Memento_bt()                       do {} while (0)
+#define Memento_sequence()                 (0)
+#define Memento_squeezing()                (0)
+
+#endif /* MEMENTO */
+
+#endif /* MEMENTO_H */