@@ -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);
}
}
@@ -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.
@@ -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.
@@ -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);
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