summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2011-12-19 20:48:30 +0000
committerRobin Watts <robin.watts@artifex.com>2011-12-19 20:48:30 +0000
commitbd0ebd25aad6abd19d98221caefc4d6566ec1e64 (patch)
tree07be8bf342b1fd7623e7c1116c6847b821c48f25
parent3028fbf00d2d571b6f67001b5c09c15e3e2cfa26 (diff)
downloadmupdf-bd0ebd25aad6abd19d98221caefc4d6566ec1e64.tar.xz
Add Memento 'pattern' code.
Disabled for now by default. Better SEGV handling under windows (to facilitate scripted squeezing)
-rw-r--r--fitz/memento.c226
1 files changed, 161 insertions, 65 deletions
diff --git a/fitz/memento.c b/fitz/memento.c
index 2fc911b1..76a27c48 100644
--- a/fitz/memento.c
+++ b/fitz/memento.c
@@ -55,6 +55,10 @@ char *getenv(const char *);
/* mupdf needs at least 34000ish (sizeof(fz_shade))/ */
#define MEMENTO_PTRSEARCH 65536
+#ifndef MEMENTO_MAXPATTERN
+#define MEMENTO_MAXPATTERN 0
+#endif
+
#ifdef MEMENTO
#ifdef HAVE_VALGRIND
@@ -117,9 +121,13 @@ static struct {
int breakAt;
int failAt;
int failing;
+ int nextFailAt;
int squeezeAt;
int squeezing;
- int squeezed;
+ int segv;
+ int pattern;
+ int nextPattern;
+ int patternBit;
size_t maxMemory;
size_t alloc;
size_t peakAlloc;
@@ -425,7 +433,7 @@ static void blockDisplay(Memento_BlkHeader *b, int n)
n++;
while(i < n)
{
- fprintf(stderr, &" "[32-((n-i)&31)]);
+ fprintf(stderr, "%s", &" "[32-((n-i)&31)]);
i += ((n-i)&31);
}
showBlock(b, '\t');
@@ -572,17 +580,17 @@ void Memento_listNewBlocks(void) {
static void Memento_endStats(void)
{
- fprintf(stderr, "Total memory malloced = %d bytes\n", globals.totalAlloc);
- fprintf(stderr, "Peak memory malloced = %d bytes\n", globals.peakAlloc);
- fprintf(stderr, "%d mallocs, %d frees, %d reallocs\n", globals.numMallocs,
- globals.numFrees, globals.numReallocs);
- fprintf(stderr, "Average allocation size %d bytes\n",
+ fprintf(stderr, "Total memory malloced = %u bytes\n", (unsigned int)globals.totalAlloc);
+ fprintf(stderr, "Peak memory malloced = %u bytes\n", (unsigned int)globals.peakAlloc);
+ fprintf(stderr, "%u mallocs, %u frees, %u reallocs\n", (unsigned int)globals.numMallocs,
+ (unsigned int)globals.numFrees, (unsigned int)globals.numReallocs);
+ fprintf(stderr, "Average allocation size %u bytes\n", (unsigned int)
(globals.numMallocs != 0 ? globals.totalAlloc/globals.numMallocs: 0));
}
void Memento_stats(void)
{
- fprintf(stderr, "Current memory malloced = %d bytes\n", globals.alloc);
+ fprintf(stderr, "Current memory malloced = %u bytes\n", (unsigned int)globals.alloc);
Memento_endStats();
}
@@ -594,8 +602,23 @@ static void Memento_fin(void)
Memento_listBlocks();
Memento_breakpoint();
}
- if (globals.squeezed) {
- fprintf(stderr, "Memory squeezing @ %d complete\n", globals.squeezed);
+ if (globals.segv) {
+ fprintf(stderr, "Memory dumped on SEGV while squeezing @ %d\n", globals.failAt);
+ } else if (globals.squeezing) {
+ if (globals.pattern == 0)
+ fprintf(stderr, "Memory squeezing @ %d complete\n", globals.squeezeAt);
+ else
+ fprintf(stderr, "Memory squeezing @ %d (%d) complete\n", globals.squeezeAt, globals.pattern);
+ }
+ if (globals.failing)
+ {
+ fprintf(stderr, "MEMENTO_FAILAT=%d\n", globals.failAt);
+ fprintf(stderr, "MEMENTO_PATTERN=%d\n", globals.pattern);
+ }
+ if (globals.nextFailAt != 0)
+ {
+ fprintf(stderr, "MEMENTO_NEXTFAILAT=%d\n", globals.nextFailAt);
+ fprintf(stderr, "MEMENTO_NEXTPATTERN=%d\n", globals.nextPattern);
}
}
@@ -630,6 +653,9 @@ static void Memento_init(void)
env = getenv("MEMENTO_SQUEEZEAT");
globals.squeezeAt = (env ? atoi(env) : 0);
+ env = getenv("MEMENTO_PATTERN");
+ globals.pattern = (env ? atoi(env) : 0);
+
env = getenv("MEMENTO_MAXMEMORY");
globals.maxMemory = (env ? atoi(env) : 0);
@@ -638,52 +664,9 @@ static void Memento_init(void)
Memento_inited();
}
-static void Memento_event(void)
-{
- globals.sequence++;
- if ((globals.sequence >= globals.paranoidAt) && (globals.paranoidAt != 0)) {
- globals.paranoia = 1;
- globals.countdown = 1;
- }
- if ((globals.sequence >= globals.failAt) && (globals.failAt != 0)) {
- globals.failing = 1;
- }
- if ((globals.sequence >= globals.squeezeAt) && (globals.squeezeAt != 0)) {
- globals.squeezing = 1;
- }
-
- if (--globals.countdown == 0) {
- Memento_checkAllMemory();
- globals.countdown = globals.paranoia;
- }
-
- if (globals.sequence == globals.breakAt) {
- fprintf(stderr, "Breaking at event %d\n", globals.breakAt);
- Memento_breakpoint();
- }
-}
-
-int Memento_breakAt(int event)
-{
- globals.breakAt = event;
- return event;
-}
-
-void *Memento_label(void *ptr, const char *label)
-{
- Memento_BlkHeader *block;
-
- if (ptr == NULL)
- return NULL;
- block = MEMBLK_FROMBLK(ptr);
- block->label = label;
- return ptr;
-}
-
#ifdef MEMENTO_HAS_FORK
#include <unistd.h>
#include <sys/wait.h>
-#include <signal.h>
#ifdef MEMENTO_STACKTRACE_METHOD
#if MEMENTO_STACKTRACE_METHOD == 1
#include <signal.h>
@@ -697,9 +680,12 @@ void *Memento_label(void *ptr, const char *label)
/* stashed_map[j] = i means that filedescriptor i-1 was duplicated to j */
int stashed_map[OPEN_MAX];
+extern size_t backtrace(void **, int);
+extern void backtrace_symbols_fd(void **, size_t, int);
+
static void Memento_signal(void)
{
- fprintf(stderr, "SEGV after Memory squeezing @ %d\n", globals.squeezed);
+ fprintf(stderr, "SEGV after Memory squeezing @ %d\n", globals.squeezeAt);
#ifdef MEMENTO_STACKTRACE_METHOD
#if MEMENTO_STACKTRACE_METHOD == 1
@@ -719,12 +705,23 @@ static void Memento_signal(void)
exit(1);
}
-static void squeeze(void)
+static int squeeze(void)
{
pid_t pid;
int i, status;
- fprintf(stderr, "Memory squeezing @ %d\n", globals.sequence);
+ if (globals.patternBit < 0)
+ return 1;
+ if (globals.squeezing && globals.patternBit >= MEMENTO_MAXPATTERN)
+ return 1;
+
+ if (globals.patternBit == 0)
+ globals.squeezeAt = globals.sequence;
+
+ if (!globals.squeezing) {
+ fprintf(stderr, "Memory squeezing @ %d\n", globals.squeezeAt);
+ } else
+ fprintf(stderr, "Memory squeezing @ %d (%x,%x)\n", globals.squeezeAt, globals.pattern, globals.patternBit);
/* When we fork below, the child is going to snaffle all our file pointers
* and potentially corrupt them. Let's make copies of all of them before
@@ -740,12 +737,20 @@ static void squeeze(void)
if (pid == 0) {
/* Child */
signal(SIGSEGV, Memento_signal);
- /* We must fail all new allocations from here */
- globals.failing = 1;
- globals.squeezed = globals.sequence;
- return;
+ /* In the child, we always fail the next allocation. */
+ if (globals.patternBit == 0) {
+ globals.patternBit = 1;
+ } else
+ globals.patternBit <<= 1;
+ globals.squeezing = 1;
+ return 1;
}
+ /* In the parent if we hit another allocation, pass it (and record the
+ * fact we passed it in the pattern. */
+ globals.pattern |= globals.patternBit;
+ globals.patternBit <<= 1;
+
/* Wait for pid to finish */
waitpid(pid, &status, 0);
@@ -757,25 +762,116 @@ static void squeeze(void)
stashed_map[i] = 0;
}
}
+
+ return 0;
}
#else
-void squeeze(void)
+#include <signal.h>
+
+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
+ * just exit to avoid the JIT (and get the usual atexit handling). */
+ if (getenv("MEMENTO_NOJIT"))
+ exit(1);
+ else
+ Memento_fin();
+}
+
+int squeeze(void)
{
fprintf(stderr, "Memento memory squeezing disabled as no fork!\n");
+ return 0;
}
#endif
+static void Memento_startFailing(void)
+{
+ if (!globals.failing) {
+ fprintf(stderr, "Starting to fail...\n");
+ fflush(stderr);
+ globals.failing = 1;
+ globals.failAt = globals.sequence;
+ globals.nextFailAt = globals.sequence+1;
+ globals.pattern = 0;
+ globals.patternBit = 0;
+ signal(SIGSEGV, Memento_signal);
+ signal(SIGABRT, Memento_signal);
+ Memento_breakpoint();
+ }
+}
+
+static void Memento_event(void)
+{
+ globals.sequence++;
+ if ((globals.sequence >= globals.paranoidAt) && (globals.paranoidAt != 0)) {
+ globals.paranoia = 1;
+ globals.countdown = 1;
+ }
+ if (--globals.countdown == 0) {
+ Memento_checkAllMemory();
+ globals.countdown = globals.paranoia;
+ }
+
+ if (globals.sequence == globals.breakAt) {
+ fprintf(stderr, "Breaking at event %d\n", globals.breakAt);
+ Memento_breakpoint();
+ }
+}
+
+int Memento_breakAt(int event)
+{
+ globals.breakAt = event;
+ return event;
+}
+
+void *Memento_label(void *ptr, const char *label)
+{
+ Memento_BlkHeader *block;
+
+ if (ptr == NULL)
+ return NULL;
+ block = MEMBLK_FROMBLK(ptr);
+ block->label = label;
+ return ptr;
+}
+
int Memento_failThisEvent(void)
{
+ int failThisOne;
+
if (!globals.inited)
Memento_init();
Memento_event();
- if ((globals.squeezing) && (!globals.squeezed))
- squeeze();
+ if ((globals.sequence >= globals.failAt) && (globals.failAt != 0))
+ Memento_startFailing();
+ if ((globals.sequence >= globals.squeezeAt) && (globals.squeezeAt != 0)) {
+ return squeeze();
+ }
- return globals.failing;
+ if (!globals.failing)
+ return 0;
+ failThisOne = ((globals.patternBit & globals.pattern) == 0);
+ /* If we are failing, and we've reached the end of the pattern and we've
+ * still got bits available in the pattern word, and we haven't already
+ * set a nextPattern, then extend the pattern. */
+ if (globals.failing &&
+ ((~(globals.patternBit-1) & globals.pattern) == 0) &&
+ (globals.patternBit != 0) &&
+ globals.nextPattern == 0)
+ {
+ /* We'll fail this one, and set the 'next' one to pass it. */
+ globals.nextFailAt = globals.failAt;
+ globals.nextPattern = globals.pattern | globals.patternBit;
+ }
+ globals.patternBit = (globals.patternBit ? globals.patternBit << 1 : 1);
+
+ return failThisOne;
}
void *Memento_malloc(size_t s)
@@ -1143,7 +1239,7 @@ int Memento_failAt(int i)
globals.failAt = i;
if ((globals.sequence > globals.failAt) &&
(globals.failing != 0))
- globals.failing = 1;
+ Memento_startFailing();
return i;
}