diff mbox

[RISU,3/9] risu: add simple trace and replay support

Message ID 20161202155935.3130-4-alex.bennee@linaro.org
State New
Headers show

Commit Message

Alex Bennée Dec. 2, 2016, 3:59 p.m. UTC
This adds a very dumb and easily breakable trace and replay support to
the aarch64 build of risu. In --master mode the various risu ops trigger
a write of register/memory state into a binary file which can be played
back to an apprentice. Currently there is no validation of the image
source so feeding the wrong image will fail straight away.

The trace files will get very big for any appreciable sized test file
and this will be addressed in later patches.

There is an inevitable amount of copy&paste between the trace file and
socket based code. Ideally it would be nice to have as much of this as
common as possible and reduce the re-implementation between the various
backends.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>


---

v2
  - moved read/write functions into main risu.c
  - cleaned up formatting
  - report more in apprentice --trace mode
v3
  - fix options parsing
---
 risu.c                 | 104 +++++++++++++++++++++++++++++++++++++------------
 risu.h                 |  12 ++++++
 risu_aarch64.c         |  90 ++++++++++++++++++++++++++++++++++++++++--
 risu_reginfo_aarch64.h |   7 ++++
 4 files changed, 185 insertions(+), 28 deletions(-)

-- 
2.10.2
diff mbox

Patch

diff --git a/risu.c b/risu.c
index 756ca46..f36b4c6 100644
--- a/risu.c
+++ b/risu.c
@@ -31,6 +31,7 @@ 
 void *memblock = 0;
 
 int apprentice_socket, master_socket;
+int trace_file = 0;
 
 sigjmp_buf jmpbuf;
 
@@ -40,31 +41,57 @@  int test_fp_exc = 0;
 long executed_tests = 0;
 void report_test_status(void *pc)
 {
-   executed_tests += 1;
-   if (executed_tests % 100 == 0) {
-      fprintf(stderr,"Executed %ld test instructions (pc=%p)\r",
-              executed_tests, pc);
+   if (ismaster || trace_file) {
+      executed_tests += 1;
+      if (executed_tests % 100 == 0) {
+         fprintf(stderr,"Executed %ld test instructions (pc=%p)\r",
+                 executed_tests, pc);
+      }
    }
 }
 
+int write_trace(void *ptr, size_t bytes)
+{
+   size_t res = write(trace_file, ptr, bytes);
+   return res == bytes;
+}
+
+int read_trace(void *ptr, size_t bytes)
+{
+   size_t res = read(trace_file, ptr, bytes);
+   return res == bytes;
+}
+
 void master_sigill(int sig, siginfo_t *si, void *uc)
 {
-   switch (recv_and_compare_register_info(master_socket, uc))
-   {
-      case 0:
-         /* match OK */
-         advance_pc(uc);
-         return;
-      default:
-         /* mismatch, or end of test */
-         siglongjmp(jmpbuf, 1);
+   if (trace_file) {
+     if (write_to_tracefile(write_trace, uc)!=0) {
+       /* end of test */
+       siglongjmp(jmpbuf, 1);
+       /* never */
+       return;
+     }
+   } else {
+     if (recv_and_compare_register_info(master_socket, uc)!=0) {
+       /* mismatch, or end of test */
+       siglongjmp(jmpbuf, 1);
+       /* never */
+       return;
+     }
    }
+   advance_pc(uc);
 }
 
 void apprentice_sigill(int sig, siginfo_t *si, void *uc)
 {
-   switch (send_register_info(apprentice_socket, uc))
-   {
+  int resp;
+  if (trace_file) {
+    resp = read_tracefile_and_check(read_trace, uc);
+  } else {
+    resp = send_register_info(apprentice_socket, uc);
+  }
+  switch (resp)
+  {
       case 0:
          /* match OK */
          advance_pc(uc);
@@ -75,6 +102,9 @@  void apprentice_sigill(int sig, siginfo_t *si, void *uc)
          exit(0);
       default:
          /* mismatch */
+         if (trace_file) {
+            report_match_status();
+         }
          exit(1);
    }
 }
@@ -136,7 +166,13 @@  int master(int sock)
 {
    if (sigsetjmp(jmpbuf, 1))
    {
-      return report_match_status();
+      if (trace_file) {
+         close(trace_file);
+         fprintf(stderr,"Done...\n");
+         return 0;
+      } else {
+         return report_match_status();
+      }
    }
    master_socket = sock;
    set_sigill_handler(&master_sigill);
@@ -165,6 +201,7 @@  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, "  -t, --trace=FILE  Record/playback trace file\n");
    fprintf(stderr, "  -h, --host=HOST   Specify master host machine (apprentice only)\n");
    fprintf(stderr, "  -p, --port=PORT   Specify the port to connect to/listen on (default 9191)\n");
 }
@@ -175,6 +212,7 @@  int main(int argc, char **argv)
    uint16_t port = 9191;
    char *hostname = "localhost";
    char *imgfile;
+   char *trace_fn = NULL;
    int sock;
 
    // TODO clean this up later
@@ -185,13 +223,14 @@  int main(int argc, char **argv)
          {
             { "help", no_argument, 0, '?'},
             { "master", no_argument, &ismaster, 1 },
+            { "trace", required_argument, 0, 't' },
             { "host", required_argument, 0, 'h' },
             { "port", required_argument, 0, 'p' },
             { "test-fp-exc", no_argument, &test_fp_exc, 1 },
             { 0,0,0,0 }
          };
       int optidx = 0;
-      int c = getopt_long(argc, argv, "h:p:", longopts, &optidx);
+      int c = getopt_long(argc, argv, "h:p:t:", longopts, &optidx);
       if (c == -1)
       {
          break;
@@ -204,6 +243,11 @@  int main(int argc, char **argv)
             /* flag set by getopt_long, do nothing */
             break;
          }
+         case 't':
+         {
+           trace_fn = optarg;
+           break;
+         }
          case 'h':
          {
             hostname = optarg;
@@ -234,18 +278,28 @@  int main(int argc, char **argv)
    }
 
    load_image(imgfile);
-   
+
    if (ismaster)
    {
-      fprintf(stderr, "master port %d\n", port);
-      sock = master_connect(port);
-      return master(sock);
+     if (trace_fn)
+       {
+         trace_file = open(trace_fn, O_WRONLY|O_CREAT, S_IRWXU);
+       } else {
+         fprintf(stderr, "master port %d\n", port);
+         sock = master_connect(port);
+       }
+     return master(sock);
    }
    else
-   {
-      fprintf(stderr, "apprentice host %s port %d\n", hostname, port);
-      sock = apprentice_connect(hostname, port);
-      return apprentice(sock);
+     {
+     if (trace_fn)
+       {
+         trace_file = open(trace_fn, O_RDONLY);
+       } else {
+         fprintf(stderr, "apprentice host %s port %d\n", hostname, port);
+         sock = apprentice_connect(hostname, port);
+       }
+     return apprentice(sock);
    }
 }
 
diff --git a/risu.h b/risu.h
index e4bb323..57161f2 100644
--- a/risu.h
+++ b/risu.h
@@ -52,6 +52,18 @@  int send_register_info(int sock, void *uc);
  */
 int recv_and_compare_register_info(int sock, void *uc);
 
+/* To keep the read/write logic from multiplying across all arches
+ * we wrap up the function here to keep all the changes in one place
+ */
+typedef int (*trace_write_fn) (void *ptr, size_t bytes);
+typedef int (*trace_read_fn) (void *ptr, size_t bytes);
+
+/* Write out to trace file */
+int write_to_tracefile(trace_write_fn write_fn, void *uc);
+
+/* Read from trace file and check */
+int read_tracefile_and_check(trace_read_fn read_fn, void * uc);
+
 /* Print a useful report on the status of the last comparison
  * done in recv_and_compare_register_info(). This is called on
  * exit, so need not restrict itself to signal-safe functions.
diff --git a/risu_aarch64.c b/risu_aarch64.c
index 1595604..9d538cb 100644
--- a/risu_aarch64.c
+++ b/risu_aarch64.c
@@ -13,6 +13,7 @@ 
 #include <stdio.h>
 #include <ucontext.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "risu.h"
 #include "risu_reginfo_aarch64.h"
@@ -28,9 +29,7 @@  void advance_pc(void *vuc)
 {
     ucontext_t *uc = vuc;
     uc->uc_mcontext.pc += 4;
-    if (ismaster) {
-      report_test_status((void *) uc->uc_mcontext.pc);
-    }
+    report_test_status((void *) uc->uc_mcontext.pc);
 }
 
 static void set_x0(void *vuc, uint64_t x0)
@@ -135,6 +134,91 @@  int recv_and_compare_register_info(int sock, void *uc)
     return resp;
 }
 
+int write_to_tracefile(trace_write_fn write_fn, void *uc)
+{
+   int resp = 0, op;
+   trace_header_t header;
+
+   reginfo_init(&master_ri, uc);
+   op = get_risuop(master_ri.faulting_insn);
+   header.pc = master_ri.pc;
+   header.risu_op = op;
+   write_fn(&header, sizeof(header));
+
+   switch (op) {
+      case OP_TESTEND:
+         resp = 1;
+      case OP_COMPARE:
+      default:
+         write_fn(&master_ri, sizeof(master_ri));
+         break;
+      case OP_SETMEMBLOCK:
+         memblock = (void *)master_ri.regs[0];
+         break;
+      case OP_GETMEMBLOCK:
+         set_x0(uc, master_ri.regs[0] + (uintptr_t)memblock);
+         break;
+      case OP_COMPAREMEM:
+         write_fn(memblock, MEMBLOCKLEN);
+         break;
+   }
+
+   return resp;
+}
+
+
+int read_tracefile_and_check(trace_read_fn read_fn, void * uc)
+{
+   int resp = 0, op;
+   trace_header_t header;
+
+   reginfo_init(&master_ri, uc);
+   op = get_risuop(master_ri.faulting_insn);
+
+   read_fn(&header, sizeof(header));
+   if ( header.pc == master_ri.pc &&
+        header.risu_op == op )
+   {
+      switch (op) {
+         case OP_COMPARE:
+         case OP_TESTEND:
+         default:
+            if (!read_fn(&apprentice_ri, sizeof(apprentice_ri))) {
+               packet_mismatch = 1;
+               resp = 2;
+            } else if (!reginfo_is_eq(&master_ri, &apprentice_ri)) {
+               /* register mismatch */
+               resp = 2;
+            } else if (op == OP_TESTEND) {
+               resp = 1;
+            }
+            break;
+         case OP_SETMEMBLOCK:
+            memblock = (void *)master_ri.regs[0];
+            break;
+         case OP_GETMEMBLOCK:
+            set_x0(uc, master_ri.regs[0] + (uintptr_t)memblock);
+            break;
+         case OP_COMPAREMEM:
+            mem_used = 1;
+            if (!read_fn(apprentice_memblock, MEMBLOCKLEN)) {
+               packet_mismatch = 1;
+               resp = 2;
+            } else if (memcmp(memblock, apprentice_memblock, MEMBLOCKLEN) != 0) {
+               /* memory mismatch */
+               resp = 2;
+            }
+            break;
+      }
+   } else {
+      fprintf(stderr, "out of sync %lx/%lx %d/%d\n",
+              master_ri.pc, header.pc,
+              op, header.risu_op);
+      resp = 2;
+   }
+   return resp;
+}
+
 /* Print a useful report on the status of the last comparison
  * done in recv_and_compare_register_info(). This is called on
  * exit, so need not restrict itself to signal-safe functions.
diff --git a/risu_reginfo_aarch64.h b/risu_reginfo_aarch64.h
index 166b76c..db51cb2 100644
--- a/risu_reginfo_aarch64.h
+++ b/risu_reginfo_aarch64.h
@@ -28,6 +28,13 @@  struct reginfo
     __uint128_t vregs[32];
 };
 
+typedef struct
+{
+    uint64_t pc;
+    uint32_t risu_op;
+} trace_header_t;
+
+
 /* initialize structure from a ucontext */
 void reginfo_init(struct reginfo *ri, ucontext_t *uc);