diff --git a/risu.h b/risu.h
index 054cef7..3bfe765 100644
--- a/risu.h
+++ b/risu.h
@@ -23,6 +23,7 @@
 extern const struct option * const arch_long_opts;
 extern const char * const arch_extra_help;
 void process_arch_opt(int opt, const char *arg);
+void arch_init(void);
 #define FIRST_ARCH_OPT   0x100
 
 /* GCC computed include to pull in the correct risu_reginfo_*.h for
diff --git a/risu.c b/risu.c
index 95b4674..d7c7556 100644
--- a/risu.c
+++ b/risu.c
@@ -249,6 +249,93 @@ static int apprentice(void)
     }
 }
 
+static int dump_trace(void)
+{
+    trace_header_t header;
+    union {
+        struct reginfo ri;
+        unsigned char memblock[MEMBLOCKLEN];
+    } u;
+    const char *op_name;
+
+    while (1) {
+        if (read_buffer(&header, sizeof(header))) {
+            fprintf(stderr, "Trace header read failed\n");
+            return EXIT_FAILURE;
+        }
+
+        if (header.magic != RISU_MAGIC) {
+            fprintf(stderr, "Unexpected header magic (%#x)\n", header.magic);
+            return EXIT_FAILURE;
+        }
+
+        switch (header.risu_op) {
+        case OP_COMPARE:
+           op_name = "COMPARE";
+           break;
+        case OP_TESTEND:
+           op_name = "TESTEND";
+           break;
+        case OP_SETMEMBLOCK:
+           op_name = "SETMEMBLOCK";
+           break;
+        case OP_GETMEMBLOCK:
+           op_name = "GETMEMBLOCK";
+           break;
+        case OP_COMPAREMEM:
+           op_name = "COMPAREMEM";
+           break;
+        case OP_SIGILL:
+           op_name = "SIGILL";
+           break;
+        default:
+           op_name = "<unknown>";
+           break;
+        }
+
+        switch (header.risu_op) {
+        case OP_COMPARE:
+        case OP_TESTEND:
+        case OP_SIGILL:
+            if (header.size > sizeof(u.ri)) {
+                fprintf(stderr, "Unexpected trace size (%u)\n", header.size);
+                return EXIT_FAILURE;
+            }
+            if (read_buffer(&u.ri, header.size)) {
+                fprintf(stderr, "Reginfo read failed\n");
+                return EXIT_FAILURE;
+            }
+            if (header.size != reginfo_size(&u.ri)) {
+                fprintf(stderr, "Unexpected trace size (%u)\n", header.size);
+                return EXIT_FAILURE;
+            }
+            printf("%s: (pc %#lx)\n", op_name, (unsigned long)header.pc);
+            reginfo_dump(&u.ri, stdout);
+            putchar('\n');
+            if (header.risu_op == OP_TESTEND) {
+                return EXIT_SUCCESS;
+            }
+            break;
+
+        case OP_COMPAREMEM:
+            if (header.size != MEMBLOCKLEN) {
+                fprintf(stderr, "Unexpected trace size (%u)\n", header.size);
+                return EXIT_FAILURE;
+            }
+            if (read_buffer(&u.memblock, MEMBLOCKLEN)) {
+                fprintf(stderr, "Memblock read failed\n");
+                return EXIT_FAILURE;
+            }
+            /* TODO: Dump 8k of data? */
+            /* fall through */
+
+        default:
+            printf("%s\n", op_name);
+            break;
+        }
+    }
+}
+
 static int ismaster;
 
 static void usage(void)
@@ -261,6 +348,7 @@ static void usage(void)
     fprintf(stderr, "between master and apprentice risu processes.\n\n");
     fprintf(stderr, "Options:\n");
     fprintf(stderr, "  --master          Be the master (server)\n");
+    fprintf(stderr, "  -d, --dump=FILE   Dump " TRACE_TYPE " trace file\n");
     fprintf(stderr, "  -t, --trace=FILE  Record/playback " TRACE_TYPE " trace file\n");
     fprintf(stderr,
             "  -h, --host=HOST   Specify master host machine (apprentice only)"
@@ -281,11 +369,12 @@ static struct option * setup_options(char **short_opts)
         {"host", required_argument, 0, 'h'},
         {"port", required_argument, 0, 'p'},
         {"trace", required_argument, 0, 't'},
+        {"dump", required_argument, 0, 'd'},
         {0, 0, 0, 0}
     };
     struct option *lopts = &default_longopts[0];
 
-    *short_opts = "h:p:t:";
+    *short_opts = "d:h:p:t:";
 
     if (arch_long_opts) {
         const size_t osize = sizeof(struct option);
@@ -316,6 +405,7 @@ int main(int argc, char **argv)
     char *trace_fn = NULL;
     struct option *longopts;
     char *shortopts;
+    bool dump = false;
 
     longopts = setup_options(&shortopts);
 
@@ -330,6 +420,10 @@ int main(int argc, char **argv)
         case 0:
             /* flag set by getopt_long, do nothing */
             break;
+        case 'd':
+            trace_fn = optarg;
+            trace = dump = true;
+            break;
         case 't':
             trace_fn = optarg;
             trace = true;
@@ -351,6 +445,11 @@ int main(int argc, char **argv)
         }
     }
 
+    if (dump && ismaster) {
+        usage();
+        exit(1);
+    }
+
     if (trace) {
         if (strcmp(trace_fn, "-") == 0) {
             comm_fd = ismaster ? STDOUT_FILENO : STDIN_FILENO;
@@ -366,6 +465,10 @@ int main(int argc, char **argv)
         }
     }
 
+    if (dump) {
+        return dump_trace();
+    }
+
     imgfile = argv[optind];
     if (!imgfile) {
         fprintf(stderr, "Error: must specify image file name\n\n");
@@ -375,6 +478,9 @@ int main(int argc, char **argv)
 
     load_image(imgfile);
 
+    /* Select requested SVE vector length. */
+    arch_init();
+
     if (ismaster) {
         if (!trace) {
             fprintf(stderr, "master port %d\n", port);
diff --git a/risu_reginfo_aarch64.c b/risu_reginfo_aarch64.c
index a1020ac..fb8e11a 100644
--- a/risu_reginfo_aarch64.c
+++ b/risu_reginfo_aarch64.c
@@ -44,8 +44,6 @@ const char * const arch_extra_help
 void process_arch_opt(int opt, const char *arg)
 {
 #ifdef SVE_MAGIC
-    long want, got;
-
     assert(opt == FIRST_ARCH_OPT);
     test_sve = strtol(arg, 0, 10);
 
@@ -53,22 +51,37 @@ void process_arch_opt(int opt, const char *arg)
         fprintf(stderr, "Invalid value for VQ (1-%d)\n", SVE_VQ_MAX);
         exit(EXIT_FAILURE);
     }
-    want = sve_vl_from_vq(test_sve);
-    got = prctl(PR_SVE_SET_VL, want);
-    if (want != got) {
-        if (got < 0) {
-            perror("prctl PR_SVE_SET_VL");
-        } else {
-            fprintf(stderr, "Unsupported value for VQ (%d != %d)\n",
-                    test_sve, (int)sve_vq_from_vl(got));
-        }
-        exit(EXIT_FAILURE);
-    }
 #else
     abort();
 #endif
 }
 
+void arch_init(void)
+{
+#ifdef SVE_MAGIC
+    long want, got1, got2;
+
+    if (test_sve == 0) {
+        return;
+    }
+
+    want = sve_vl_from_vq(test_sve);
+    asm(".arch_extension sve\n\trdvl %0, #1" : "=r"(got1));
+    if (want != got1) {
+        got2 = prctl(PR_SVE_SET_VL, want);
+        if (want != got2) {
+            if (got2 < 0) {
+                perror("prctl PR_SVE_SET_VL");
+                got2 = got1;
+            }
+            fprintf(stderr, "Unsupported value for VQ (%d != %d)\n",
+                    test_sve, (int)sve_vq_from_vl(got1));
+            exit(EXIT_FAILURE);
+        }
+    }
+#endif
+}
+
 int reginfo_size(struct reginfo *ri)
 {
 #ifdef SVE_MAGIC
@@ -170,6 +183,7 @@ void reginfo_init(struct reginfo *ri, ucontext_t *uc)
         if (sve->head.size < SVE_SIG_CONTEXT_SIZE(vq)) {
             if (sve->head.size == sizeof(*sve)) {
                 /* SVE state is empty -- not an error.  */
+                goto do_simd;
             } else {
                 fprintf(stderr, "risu_reginfo_aarch64: "
                         "failed to get complete SVE state\n");
@@ -182,6 +196,7 @@ void reginfo_init(struct reginfo *ri, ucontext_t *uc)
                SVE_SIG_CONTEXT_SIZE(vq) - SVE_SIG_REGS_OFFSET);
         return;
     }
+ do_simd:
 #endif /* SVE_MAGIC */
 
     for (i = 0; i < 32; i++) {
@@ -260,8 +275,9 @@ int reginfo_dump(struct reginfo *ri, FILE * f)
     fprintf(f, "  fpcr   : %08x\n", ri->fpcr);
 
 #ifdef SVE_MAGIC
-    if (test_sve) {
-        int q, vq = test_sve;
+    if (ri->sve_vl) {
+        int vq = sve_vq_from_vl(ri->sve_vl);
+        int q;
 
         fprintf(f, "  vl     : %d\n", ri->sve_vl);
 
@@ -339,13 +355,12 @@ int reginfo_dump_mismatch(struct reginfo *m, struct reginfo *a, FILE * f)
     }
 
 #ifdef SVE_MAGIC
-    if (test_sve) {
+    if (m->sve_vl != a->sve_vl) {
+        fprintf(f, "  vl    : %d vs %d\n", m->sve_vl, a->sve_vl);
+    }
+    if (m->sve_vl) {
         int vq = sve_vq_from_vl(m->sve_vl);
 
-        if (m->sve_vl != a->sve_vl) {
-            fprintf(f, "  vl    : %d vs %d\n", m->sve_vl, a->sve_vl);
-        }
-
         for (i = 0; i < SVE_NUM_ZREGS; i++) {
             uint64_t *zm = reginfo_zreg(m, vq, i);
             uint64_t *za = reginfo_zreg(a, vq, i);
diff --git a/risu_reginfo_arm.c b/risu_reginfo_arm.c
index 3832e27..2982435 100644
--- a/risu_reginfo_arm.c
+++ b/risu_reginfo_arm.c
@@ -36,6 +36,10 @@ void process_arch_opt(int opt, const char *arg)
     abort();
 }
 
+void arch_init(void)
+{
+}
+
 int reginfo_size(struct reginfo *ri)
 {
     return sizeof(struct reginfo);
diff --git a/risu_reginfo_i386.c b/risu_reginfo_i386.c
index 902d33e..68f2323 100644
--- a/risu_reginfo_i386.c
+++ b/risu_reginfo_i386.c
@@ -74,6 +74,10 @@ void process_arch_opt(int opt, const char *arg)
     }
 }
 
+void arch_init(void)
+{
+}
+
 int reginfo_size(struct reginfo *ri)
 {
     return sizeof(struct reginfo);
diff --git a/risu_reginfo_m68k.c b/risu_reginfo_m68k.c
index 361f172..499fdc4 100644
--- a/risu_reginfo_m68k.c
+++ b/risu_reginfo_m68k.c
@@ -23,6 +23,10 @@ void process_arch_opt(int opt, const char *arg)
     abort();
 }
 
+void arch_init(void)
+{
+}
+
 int reginfo_size(struct reginfo *ri)
 {
     return sizeof(struct reginfo);
diff --git a/risu_reginfo_ppc64.c b/risu_reginfo_ppc64.c
index c86313c..3b04747 100644
--- a/risu_reginfo_ppc64.c
+++ b/risu_reginfo_ppc64.c
@@ -32,6 +32,10 @@ void process_arch_opt(int opt, const char *arg)
     abort();
 }
 
+void arch_init(void)
+{
+}
+
 int reginfo_size(struct reginfo *ri)
 {
     return sizeof(struct reginfo);
