From patchwork Fri Dec 2 15:59:29 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Alex_Benn=C3=A9e?= X-Patchwork-Id: 86333 Delivered-To: patch@linaro.org Received: by 10.140.20.101 with SMTP id 92csp326882qgi; Fri, 2 Dec 2016 08:13:02 -0800 (PST) X-Received: by 10.55.161.83 with SMTP id k80mr37221656qke.136.1480695182902; Fri, 02 Dec 2016 08:13:02 -0800 (PST) Return-Path: Received: from lists.gnu.org (lists.gnu.org. [2001:4830:134:3::11]) by mx.google.com with ESMTPS id 32si3297136qtf.321.2016.12.02.08.13.02 for (version=TLS1 cipher=AES128-SHA bits=128/128); Fri, 02 Dec 2016 08:13:02 -0800 (PST) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) client-ip=2001:4830:134:3::11; Authentication-Results: mx.google.com; dkim=fail header.i=@linaro.org; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) smtp.mailfrom=qemu-devel-bounces+patch=linaro.org@nongnu.org; dmarc=fail (p=NONE dis=NONE) header.from=linaro.org Received: from localhost ([::1]:35359 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cCqSL-0004wJ-PD for patch@linaro.org; Fri, 02 Dec 2016 11:13:01 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:60502) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cCqFZ-0002zH-CO for qemu-devel@nongnu.org; Fri, 02 Dec 2016 10:59:52 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cCqFX-0004ye-JV for qemu-devel@nongnu.org; Fri, 02 Dec 2016 10:59:49 -0500 Received: from mail-wm0-x232.google.com ([2a00:1450:400c:c09::232]:37984) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1cCqFX-0004xb-71 for qemu-devel@nongnu.org; Fri, 02 Dec 2016 10:59:47 -0500 Received: by mail-wm0-x232.google.com with SMTP id f82so19692944wmf.1 for ; Fri, 02 Dec 2016 07:59:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=KgTDMiZ9Tq5WQQPZvWiXNB0ojt7ZJX0tPSInlvl6nyk=; b=StzVdD2YcOKogSgI4aY6ID+SrD5Gf4u1zO62wV4ZnzL45Tp4yMzvVJ5ed9ERlTJItx JwJ1kMtpzDKHiN1fxRi/y6FeA+GYqWb/NIWNcoV1N1oqjyOajNAi3yVaGk95ewi5W2Fk HvO2JKAERkGM/zkY3JWIKP8i6Wgzv3+VEKKaA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=KgTDMiZ9Tq5WQQPZvWiXNB0ojt7ZJX0tPSInlvl6nyk=; b=CmLlDdzr+IUnYulYHI4lhUPkLgY1+SApccJRq7SvRCeWQgVg1XuOTkqBz1BaZxmAnE NnU6KqbKsYOpGFfeXEp8oBlc1hIbbWT8G1X9fIjPm/z6hLLYU8AMEi9fUBi+UvIXJDmZ SC0Jkh89RVbC6k0N8cEVu1G8HYnW0KALCVH26lxZgVCgWKcAraj1AtBb+z//cHuIg+E8 3UCQHsV8RiTniTvjMB3bRxptpj5pvoh5ycqszo6ehz1ojRwUxenQtsmQBFD6I53bol3D 8dnmeAcN5W41GR7LFmKS73fpo6uOHpSAu65Q886xtYk6hNS2NXkxTscFIkDKlXBflu39 FVaw== X-Gm-Message-State: AKaTC03ibB05SQKhI4EsLTFnu2njTlT5ZkMmqqBO3bM0nPOoQZetJsboxBXRh7D6VliWpyMO X-Received: by 10.28.194.135 with SMTP id s129mr3843614wmf.55.1480694385893; Fri, 02 Dec 2016 07:59:45 -0800 (PST) Received: from zen.linaro.local ([81.128.185.34]) by smtp.gmail.com with ESMTPSA id z6sm6225002wjt.24.2016.12.02.07.59.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 02 Dec 2016 07:59:43 -0800 (PST) Received: from zen.linaroharston (localhost [127.0.0.1]) by zen.linaro.local (Postfix) with ESMTP id 694283E0354; Fri, 2 Dec 2016 15:59:40 +0000 (GMT) From: =?UTF-8?q?Alex=20Benn=C3=A9e?= To: peter.maydell@linaro.org Date: Fri, 2 Dec 2016 15:59:29 +0000 Message-Id: <20161202155935.3130-4-alex.bennee@linaro.org> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20161202155935.3130-1-alex.bennee@linaro.org> References: <20161202155935.3130-1-alex.bennee@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::232 Subject: [Qemu-devel] [RISU PATCH 3/9] risu: add simple trace and replay support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Alex=20Benn=C3=A9e?= , joserz@linux.vnet.ibm.com, qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: "Qemu-devel" 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 --- 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 --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 #include #include +#include #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);