ref: dc2f2769a366d34988472e9a088151a43851cf2f
parent: f5d16095a001e741bc8e570c0e231e65e831ea47
author: Sebastian Rasmussen <[email protected]>
date: Fri Feb 7 22:18:09 EST 2020
jbig2dec: Sync memento from ghostscript.
--- a/memento.c
+++ b/memento.c
@@ -45,6 +45,7 @@
#ifndef _MSC_VER
#include <stdint.h>
#include <limits.h>
+#include <unistd.h>
#endif
#include <stdlib.h>
@@ -59,9 +60,11 @@
#ifdef _MSC_VER
#define FMTZ "%llu"
#define FMTZ_CAST _int64
+#define FMTP "0x%p"
#else
#define FMTZ "%zu"
#define FMTZ_CAST size_t
+#define FMTP "%p"
#endif
#define UB(x) ((intptr_t)((x) & 0xFF))
@@ -251,7 +254,8 @@
Memento_Flag_BreakOnFree = 4,
Memento_Flag_BreakOnRealloc = 8,
Memento_Flag_Freed = 16,
- Memento_Flag_KnownLeak = 32
+ Memento_Flag_KnownLeak = 32,
+ Memento_Flag_Reported = 64
};
enum {
@@ -535,8 +539,31 @@
static void print_stack_libbt_failed(void *addr)
{
- char **strings = backtrace_symbols(&addr, 1);
+ char **strings;
+#if 0
+ /* Let's use a hack from Julian Smith to call gdb to extract the information */
+ /* Disabled for now, as I can't make this work. */
+ static char command[1024];
+ int e;
+ static int gdb_invocation_failed = 0;
+ if (gdb_invocation_failed == 0)
+ {
+ snprintf(command, sizeof(command),
+ //"gdb -q --batch -p=%i -ex 'info line *%p' -ex quit 2>/dev/null",
+ "gdb -q --batch -p=%i -ex 'info line *%p' -ex quit 2>/dev/null| egrep -v '(Thread debugging using)|(Using host libthread_db library)|(A debugging session is active)|(will be detached)|(Quit anyway)|(No such file or directory)|(^0x)|(^$)'",
+ getpid(), addr);
+ printf("%s\n", command);
+ e = system(command);
+ if (e == 0)
+ return; /* That'll do! */
+ gdb_invocation_failed = 1; /* If it's failed once, it'll probably keep failing. */
+ }
+#endif
+
+ /* We couldn't even get gdb! Make do. */
+ strings = backtrace_symbols(&addr, 1);
+
if (strings == NULL || strings[0] == NULL)
{
if (sizeof(void *) == 4)
@@ -553,6 +580,12 @@
static int init_libbt(void)
{
+ static int libbt_inited = 0;
+
+ if (libbt_inited)
+ return 0;
+ libbt_inited = 1;
+
libbt = dlopen("libbacktrace.so", RTLD_LAZY);
if (libbt == NULL)
libbt = dlopen("/opt/lib/libbacktrace.so", RTLD_LAZY);
@@ -588,6 +621,9 @@
return 1;
fail:
+ fprintf(stderr,
+ "MEMENTO: libbacktrace.so failed to load; backtraces will be sparse.\n"
+ "MEMENTO: See memento.h for how to rectify this.\n");
libbt = NULL;
backtrace_create_state = NULL;
backtrace_syminfo = NULL;
@@ -602,7 +638,7 @@
if (strings == NULL || strings[0] == NULL)
{
- fprintf(stderr, " [0x%p]\n", addr);
+ fprintf(stderr, " ["FMTP"]\n", addr);
}
#ifdef HAVE_LIBDL
else if (strchr(strings[0], ':') == NULL)
@@ -614,7 +650,7 @@
{
memcpy(backtrace_exe, strings[0], s - strings[0]);
backtrace_exe[s-strings[0]] = 0;
- if (init_libbt())
+ init_libbt();
print_stack_value(addr);
}
}
@@ -846,12 +882,12 @@
const char *sym = info.dli_sname ? info.dli_sname : "<unknown>";
char *demangled = __cxa_demangle(sym, NULL, 0, &status);
int offset = stack[i] - info.dli_saddr;
- fprintf(stderr, " [%p]%s(+0x%x)\n", stack[i], demangled && status == 0 ? demangled : sym, offset);
+ fprintf(stderr, " ["FMTP"]%s(+0x%x)\n", stack[i], demangled && status == 0 ? demangled : sym, offset);
free(demangled);
}
else
{
- fprintf(stderr, " [%p]\n", stack[i]);
+ fprintf(stderr, " ["FMTP"]\n", stack[i]);
}
}
}
@@ -1255,14 +1291,19 @@
}
#endif /* MEMENTO_LEAKONLY */
-static void showBlock(Memento_BlkHeader *b, int space)
+static int showBlock(Memento_BlkHeader *b, int space)
{
- fprintf(stderr, "0x%p:(size=" FMTZ ",num=%d)",
+ int seq;
+ VALGRIND_MAKE_MEM_DEFINED(b, sizeof(Memento_BlkHeader));
+ fprintf(stderr, FMTP":(size=" FMTZ ",num=%d)",
MEMBLK_TOBLK(b), (FMTZ_CAST)b->rawsize, b->sequence);
if (b->label)
fprintf(stderr, "%c(%s)", space, b->label);
if (b->flags & Memento_Flag_KnownLeak)
fprintf(stderr, "(Known Leak)");
+ seq = b->sequence;
+ VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader));
+ return seq;
}
static void blockDisplay(Memento_BlkHeader *b, int n)
@@ -1291,7 +1332,9 @@
size_t *counts = (size_t *)arg;
blockDisplay(b, 0);
counts[0]++;
+ VALGRIND_MAKE_MEM_DEFINED(b, sizeof(Memento_BlkHeader));
counts[1]+= b->rawsize;
+ VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader));
return 0;
}
@@ -1300,15 +1343,19 @@
{
/* Try and avoid recursion if we can help it */
do {
+ Memento_BlkHeader *c = NULL;
blockDisplay(b, depth);
+ VALGRIND_MAKE_MEM_DEFINED(b, sizeof(Memento_BlkHeader));
if (b->sibling) {
- if (b->child)
- doNestedDisplay(b->child, depth+1);
+ c = b->child;
b = b->sibling;
} else {
b = b->child;
depth++;
}
+ VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader));
+ if (c)
+ doNestedDisplay(c, depth+1);
} while (b);
}
@@ -1494,7 +1541,7 @@
{
Memento_BlkDetails *details;
- fprintf(stderr, "0x%p:(size="FMTZ",num=%d)",
+ fprintf(stderr, FMTP":(size="FMTZ",num=%d)",
MEMBLK_TOBLK(b), (FMTZ_CAST)b->rawsize, b->sequence);
if (b->label)
fprintf(stderr, " (%s)", b->label);
@@ -1526,9 +1573,15 @@
Memento_BlkHeader *blk = memento.used.head;
while (blk)
{
- if ((blk->flags & Memento_Flag_KnownLeak) == 0)
+ Memento_BlkHeader *next;
+ int leaked;
+ VALGRIND_MAKE_MEM_DEFINED(blk, sizeof(*blk));
+ leaked = ((blk->flags & Memento_Flag_KnownLeak) == 0);
+ next = blk->next;
+ VALGRIND_MAKE_MEM_DEFINED(blk, sizeof(*blk));
+ if (leaked)
return 1;
- blk = blk->next;
+ blk = next;
}
return 0;
}
@@ -1583,6 +1636,9 @@
env = getenv("MEMENTO_FAILAT");
memento.failAt = (env ? atoi(env) : 0);
+ env = getenv("MEMENTO_BREAKAT");
+ memento.breakAt = (env ? atoi(env) : 0);
+
env = getenv("MEMENTO_PARANOIA");
memento.paranoia = (env ? atoi(env) : 0);
if (memento.paranoia == 0)
@@ -2347,7 +2403,11 @@
}
fprintf(stderr, "Block last checked OK at allocation %d. Now %d.\n",
memblk->lastCheckedOK, memento.sequence);
+ if ((memblk->flags & Memento_Flag_Reported) == 0)
+ {
+ memblk->flags |= Memento_Flag_Reported;
Memento_breakpointLocked();
+ }
return 1;
}
#endif
@@ -2394,7 +2454,11 @@
}
fprintf(stderr, "Block last checked OK at allocation %d. Now %d.\n",
memblk->lastCheckedOK, memento.sequence);
+ if ((memblk->flags & Memento_Flag_Reported) == 0)
+ {
+ memblk->flags |= Memento_Flag_Reported;
Memento_breakpointLocked();
+ }
return 1;
}
#endif
@@ -2585,6 +2649,11 @@
data->preCorrupt = 0;
data->postCorrupt = 0;
data->freeCorrupt = 0;
+ if ((memblk->flags & Memento_Flag_Reported) == 0)
+ {
+ memblk->flags |= Memento_Flag_Reported;
+ Memento_breakpointLocked();
+ }
}
else
memblk->lastCheckedOK = memento.sequence;
@@ -2604,7 +2673,7 @@
fprintf(stderr, " ");
showBlock(memblk, ' ');
if (data->freeCorrupt) {
- fprintf(stderr, " index %d (address 0x%p) onwards", (int)data->index,
+ fprintf(stderr, " index %d (address "FMTP") onwards", (int)data->index,
&((char *)MEMBLK_TOBLK(memblk))[data->index]);
if (data->preCorrupt) {
fprintf(stderr, "+ preguard");
@@ -2621,9 +2690,16 @@
(data->preCorrupt ? "+" : ""));
}
}
+ VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(Memento_BlkHeader));
fprintf(stderr, " corrupted.\n"
" Block last checked OK at allocation %d. Now %d.\n",
memblk->lastCheckedOK, memento.sequence);
+ if ((memblk->flags & Memento_Flag_Reported) == 0)
+ {
+ memblk->flags |= Memento_Flag_Reported;
+ Memento_breakpointLocked();
+ }
+ VALGRIND_MAKE_MEM_NOACCESS(memblk, sizeof(Memento_BlkHeader));
data->preCorrupt = 0;
data->postCorrupt = 0;
data->freeCorrupt = 0;
@@ -2702,6 +2778,7 @@
int Memento_find(void *a)
{
findBlkData data;
+ int s;
MEMENTO_LOCK();
data.addr = a;
@@ -2709,27 +2786,27 @@
data.flags = 0;
Memento_appBlocks(&memento.used, Memento_containsAddr, &data);
if (data.blk != NULL) {
- fprintf(stderr, "Address 0x%p is in %sallocated block ",
+ fprintf(stderr, "Address "FMTP" is in %sallocated block ",
data.addr,
(data.flags == 1 ? "" : (data.flags == 2 ?
"preguard of " : "postguard of ")));
- showBlock(data.blk, ' ');
+ s = showBlock(data.blk, ' ');
fprintf(stderr, "\n");
MEMENTO_UNLOCK();
- return data.blk->sequence;
+ return s;
}
data.blk = NULL;
data.flags = 0;
Memento_appBlocks(&memento.free, Memento_containsAddr, &data);
if (data.blk != NULL) {
- fprintf(stderr, "Address 0x%p is in %sfreed block ",
+ fprintf(stderr, "Address "FMTP" is in %sfreed block ",
data.addr,
(data.flags == 1 ? "" : (data.flags == 2 ?
"preguard of " : "postguard of ")));
- showBlock(data.blk, ' ');
+ s = showBlock(data.blk, ' ');
fprintf(stderr, "\n");
MEMENTO_UNLOCK();
- return data.blk->sequence;
+ return s;
}
MEMENTO_UNLOCK();
return 0;
@@ -2745,13 +2822,15 @@
data.flags = 0;
Memento_appBlocks(&memento.used, Memento_containsAddr, &data);
if (data.blk != NULL) {
- fprintf(stderr, "Will stop when address 0x%p (in %sallocated block ",
+ fprintf(stderr, "Will stop when address "FMTP" (in %sallocated block ",
data.addr,
(data.flags == 1 ? "" : (data.flags == 2 ?
"preguard of " : "postguard of ")));
showBlock(data.blk, ' ');
fprintf(stderr, ") is freed\n");
+ VALGRIND_MAKE_MEM_DEFINED(data.blk, sizeof(Memento_BlkHeader));
data.blk->flags |= Memento_Flag_BreakOnFree;
+ VALGRIND_MAKE_MEM_NOACCESS(data.blk, sizeof(Memento_BlkHeader));
MEMENTO_UNLOCK();
return;
}
@@ -2759,7 +2838,7 @@
data.flags = 0;
Memento_appBlocks(&memento.free, Memento_containsAddr, &data);
if (data.blk != NULL) {
- fprintf(stderr, "Can't stop on free; address 0x%p is in %sfreed block ",
+ fprintf(stderr, "Can't stop on free; address "FMTP" is in %sfreed block ",
data.addr,
(data.flags == 1 ? "" : (data.flags == 2 ?
"preguard of " : "postguard of ")));
@@ -2768,7 +2847,7 @@
MEMENTO_UNLOCK();
return;
}
- fprintf(stderr, "Can't stop on free; address 0x%p is not in a known block.\n", a);
+ fprintf(stderr, "Can't stop on free; address "FMTP" is not in a known block.\n", a);
MEMENTO_UNLOCK();
}
@@ -2782,13 +2861,15 @@
data.flags = 0;
Memento_appBlocks(&memento.used, Memento_containsAddr, &data);
if (data.blk != NULL) {
- fprintf(stderr, "Will stop when address 0x%p (in %sallocated block ",
+ fprintf(stderr, "Will stop when address "FMTP" (in %sallocated block ",
data.addr,
(data.flags == 1 ? "" : (data.flags == 2 ?
"preguard of " : "postguard of ")));
showBlock(data.blk, ' ');
fprintf(stderr, ") is freed (or realloced)\n");
+ VALGRIND_MAKE_MEM_DEFINED(data.blk, sizeof(Memento_BlkHeader));
data.blk->flags |= Memento_Flag_BreakOnFree | Memento_Flag_BreakOnRealloc;
+ VALGRIND_MAKE_MEM_NOACCESS(data.blk, sizeof(Memento_BlkHeader));
MEMENTO_UNLOCK();
return;
}
@@ -2796,7 +2877,7 @@
data.flags = 0;
Memento_appBlocks(&memento.free, Memento_containsAddr, &data);
if (data.blk != NULL) {
- fprintf(stderr, "Can't stop on free/realloc; address 0x%p is in %sfreed block ",
+ fprintf(stderr, "Can't stop on free/realloc; address "FMTP" is in %sfreed block ",
data.addr,
(data.flags == 1 ? "" : (data.flags == 2 ?
"preguard of " : "postguard of ")));
@@ -2805,7 +2886,7 @@
MEMENTO_UNLOCK();
return;
}
- fprintf(stderr, "Can't stop on free/realloc; address 0x%p is not in a known block.\n", a);
+ fprintf(stderr, "Can't stop on free/realloc; address "FMTP" is not in a known block.\n", a);
MEMENTO_UNLOCK();
}
@@ -2834,6 +2915,11 @@
memento.leaking--;
}
+int Memento_squeezing(void)
+{
+ return memento.squeezing;
+}
+
#endif /* MEMENTO_CPP_EXTRAS_ONLY */
#ifdef __cplusplus
@@ -3031,6 +3117,11 @@
void (Memento_stopLeaking)(void)
{
+}
+
+int (Memento_squeezing)(void)
+{
+ return 0;
}
#endif
--- a/memento.h
+++ b/memento.h
@@ -75,8 +75,7 @@
* An example:
* Suppose we have a gs invocation that crashes with memory corruption.
* * Build with -DMEMENTO.
- * * In your debugger put breakpoints on Memento_inited and
- * Memento_Breakpoint.
+ * * 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.
@@ -92,9 +91,9 @@
* 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 hit Memento_inited
- * execute Memento_breakAt(1457); (and maybe Memento_setParanoia(1), or
- * Memento_setParanoidAt(1457))
+ * * 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.
*
@@ -157,6 +156,30 @@
* 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
@@ -238,6 +261,8 @@
int Memento_sequence(void);
+int Memento_squeezing(void);
+
void Memento_fin(void);
void Memento_bt(void);
@@ -299,6 +324,7 @@
#define Memento_fin() do {} while (0)
#define Memento_bt() do {} while (0)
#define Memento_sequence() (0)
+#define Memento_squeezing() (0)
#endif /* MEMENTO */