#include "lbuf.h"
#include "mdup.h"
#include "quis.h"
+
#include "tvec.h"
+#include "tvec-adhoc.h"
+#include "tvec-bench.h"
+#include "tvec-output.h"
+#include "tvec-remote.h"
+#include "tvec-types.h"
/*----- Preliminaries -----------------------------------------------------*/
/* --- @release_comms@ --- *
*
- * Arguments: @struct tvec_remotecomms *rc@ = communication state
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @struct tvec_remotecomms *rc@ = communication state
*
* Returns: ---
*
* @close_comms@.
*/
-static void release_comms(struct tvec_remotecomms *rc)
- { close_comms(rc); xfree(rc->bin); DBDESTROY(&rc->bout); }
+static void release_comms(struct tvec_state *tv, struct tvec_remotecomms *rc)
+ { close_comms(rc); x_free(tv->a, rc->bin); DBDESTROY(&rc->bout); }
/* --- @setup_comms@ --- *
*
if (rc->binlen - rc->binoff >= want) return (RECV_OK);
/* If the buffer is too small then we must grow it. */
- GROWBUF_EXTEND(&arena_stdlib, rc->bin, rc->binsz, want, RECVBUFSZ, 1);
+ GROWBUF_EXTEND(size_t, tv->a, rc->bin, rc->binsz, want, RECVBUFSZ, 1);
/* Shunt the unused existing material to the start of the buffer. */
memmove(rc->bin, rc->bin + rc->binoff, rc->binlen - rc->binoff);
#define TVPK_FAIL 0x0108u /* <-- flag: u8, detail: str16 */
#define TVPK_DUMPREG 0x010au /* <-- ri: u16; disp: u16;
* flag: u8, rv: value */
-#define TVPK_BBENCH 0x010cu /* <-- ident: str32; unit: u16 */
-#define TVPK_EBENCH 0x010eu /* <-- ident: str32; unit: u16;
- * flags: u16; n, t, cy: f64 */
+#define TVPK_BBENCH 0x010cu /* <-- unit: u16, ident: regs */
+#define TVPK_EBENCH 0x010eu /* <-- unit: u16; flags: u16;
+ * n, t, cy: f64 */
/*----- Server ------------------------------------------------------------*/
unsigned i;
buf b;
dstr d = DSTR_INIT;
- const struct tvec_test *t;
+ const struct tvec_test *t, *const *tt;
void *p; size_t sz;
const struct tvec_env *env = 0;
const struct tvec_vardef *vd = 0; void *varctx;
if (BLEFT(&b)) goto bad;
/* Find the group given its name. */
- for (t = srvtv.cfg.tests; t->name; t++)
+ for (tt = srvtv.cfg.tests; *tt; tt++) {
+ t = *tt;
if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
goto found_group;
+ }
rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
(int)sz, (char *)p);
goto end;
if (env && env->setup == tvec_remotesetup)
env = ((struct tvec_remoteenv *)env)->r.env;
if (!env || !env->ctxsz) ctx = 0;
- else ctx = xmalloc(env->ctxsz);
+ else ctx = x_alloc(srvtv.a, env->ctxsz);
if (env && env->setup) env->setup(&srvtv, env, 0, ctx);
/* Initialize the registers. */
if (vd->regsz <= sizeof(rbuf))
r = &rbuf;
else {
- GROWBUF_REPLACE(&arena_stdlib, r_alloc, rsz, vd->regsz,
+ GROWBUF_REPLACE(size_t, srvtv.a, r_alloc, rsz, vd->regsz,
8*sizeof(void *), 1);
r = r_alloc;
}
/* Parse the packet payload. */
if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
- srvtv.cfg.nreg, srvtv.cfg.regsz))
+ 0, 0, srvtv.cfg.nreg, srvtv.cfg.regsz))
goto bad;
if (BLEFT(&b)) goto bad;
/* Tear down the environment and release other resources. */
if (env && env->teardown) env->teardown(&srvtv, ctx);
tvec_releaseregs(&srvtv);
- xfree(ctx); srvtv.test = 0; env = 0; ctx = 0;
+ x_free(srvtv.a, ctx); srvtv.test = 0; env = 0; ctx = 0;
/* Report completion. */
QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_EGROUP | TVPF_ACK);
/* Clean up and return. */
if (env && env->teardown) env->teardown(&srvtv, ctx);
if (vd) vd->def.ty->release(&r->v, &vd->def);
- xfree(ctx); xfree(r_alloc);
+ x_free(srvtv.a, ctx); x_free(srvtv.a, r_alloc);
if (srvtv.test) tvec_releaseregs(&srvtv);
- release_comms(&srvrc); tvec_end(&srvtv);
+ release_comms(&srvtv, &srvrc); tvec_end(&srvtv);
return (rc ? 2 : 0);
bad:
static void remote_etest(struct tvec_output *o, unsigned outcome)
{ ; }
+/* --- @remote_report@ --- *
+ *
+ * Arguments: @struct tvec_output *o@ = output sink (ignored)
+ * @unsigned level@ = message level (@TVLEV_...@)
+ * @const char *msg@, @va_list *ap@ = format string and
+ * arguments
+ *
+ * Returns: ---
+ *
+ * Use: Report a message to the user.
+ *
+ * The remote driver sends a @TVPK_REPORT@ packet to its
+ * client. If its attempt to transmit the packet fails, then
+ * the message is written to the standard error stream instead,
+ * in the hope that this will help it be noticed.
+ */
+
+static void remote_report(struct tvec_output *o, unsigned level,
+ const char *msg, va_list *ap)
+{
+ QUEUEPK(&srvtv, &srvrc, 0, TVPK_REPORT) {
+ dbuf_putu16l(&srvrc.bout, level);
+ dbuf_vputstrf16l(&srvrc.bout, msg, ap);
+ } else {
+ fprintf(stderr, "%s %s: ", QUIS, tvec_strlevel(level));
+ vfprintf(stderr, msg, *ap);
+ fputc('\n', stderr);
+ }
+}
+
/* --- @remote_bbench@ --- *
*
* Arguments: @struct tvec_output *o@ = output sink (ignored)
- * @const char *ident@ = identifying register values
- * @unsigned unit@ = measurement unit (@TVBU_...@)
+ * @const char *desc@ = adhoc test description, must be null
+ * @unsigned unit@ = measurement unit (@BTU_...@)
*
* Returns: ---
*
*/
static void remote_bbench(struct tvec_output *o,
- const char *ident, unsigned unit)
+ const char *desc, unsigned unit)
{
+ assert(!desc);
QUEUEPK(&srvtv, &srvrc, 0, TVPK_BBENCH) {
- dbuf_putstr32l(&srvrc.bout, ident);
dbuf_putu16l(&srvrc.bout, unit);
+ tvec_serialize(srvtv.in, DBUF_BUF(&srvrc.bout), srvtv.test->regs,
+ TVRF_ID, TVRF_ID, srvtv.cfg.nreg, srvtv.cfg.regsz);
}
}
/* --- @remote_ebench@ --- *
*
* Arguments: @struct tvec_output *o@ = output sink (ignored)
- * @const char *ident@ = identifying register values
- * @unsigned unit@ = measurement unit (@TVBU_...@)
- * @const struct bench_timing *tm@ = measurement
+ * @const char *desc@ = adhoc test description, must be null
+ * @unsigned unit@ = measurement unit (@BTU_...@)
+ * @const struct bench_timing *t@ = measurement
*
* Returns: ---
*
- * Use: Report a benchmark's results
+ * Use: Report a benchmark's results.
*
* The remote driver sends a @TVPK_EBENCH@ packet to its client.
*/
static void remote_ebench(struct tvec_output *o,
- const char *ident, unsigned unit,
+ const char *desc, unsigned unit,
const struct bench_timing *t)
{
+ assert(!desc);
QUEUEPK(&srvtv, &srvrc, 0, TVPK_EBENCH) {
- dbuf_putstr32l(&srvrc.bout, ident);
dbuf_putu16l(&srvrc.bout, unit);
if (!t || !(t->f&BTF_ANY))
dbuf_putu16l(&srvrc.bout, 0);
}
}
-/* --- @remote_report@ --- *
+static const struct tvec_benchoutops remote_benchops =
+ { remote_bbench, remote_ebench };
+
+/* --- @remote_extend@ --- *
*
* Arguments: @struct tvec_output *o@ = output sink (ignored)
- * @unsigned level@ = message level (@TVLEV_...@)
- * @const char *msg@, @va_list *ap@ = format string and
- * arguments
- *
- * Returns: ---
- *
- * Use: Report a message to the user.
+ * @const char *name@ = extension name
*
- * The remote driver sends a @TVPK_REPORT@ packet to its
- * client. If its attempt to transmit the packet fails, then
- * the message is written to the standard error stream instead,
- * in the hope that this will help it be noticed.
+ * Returns: A pointer to the extension implementation, or null.
*/
-static void remote_report(struct tvec_output *o, unsigned level,
- const char *msg, va_list *ap)
+static const void *remote_extend(struct tvec_output *o, const char *name)
{
- QUEUEPK(&srvtv, &srvrc, 0, TVPK_REPORT) {
- dbuf_putu16l(&srvrc.bout, level);
- dbuf_vputstrf16l(&srvrc.bout, msg, ap);
- } else {
- fprintf(stderr, "%s %s: ", QUIS, tvec_strlevel(level));
- vfprintf(stderr, msg, *ap);
- fputc('\n', stderr);
- }
+ if (STRCMP(name, ==, TVEC_BENCHOUTEXT)) return (&remote_benchops);
+ else return (0);
}
/* --- @remote_destroy@ --- *
remote_bsession, remote_esession,
remote_bgroup, remote_skipgroup, remote_egroup,
remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
- remote_bbench, remote_ebench,
- remote_report,
- remote_destroy
+ remote_report, remote_extend, remote_destroy
};
/*----- Pseudoregister definitions ----------------------------------------*/
static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
unsigned f, uint16 end, buf *b_out)
{
- struct tvec_output *o = tv->output;
+ struct tvec_output *o;
uint16 pk, u, v;
const char *p; size_t n;
dstr d = DSTR_INIT;
buf *b = b_out;
const struct tvec_regdef *rd;
struct bench_timing bt;
+ const struct tvec_benchoutops *bo;
+ struct tvec_fallbackoutput fo;
struct tvec_reg *reg = 0; size_t rsz = 0;
unsigned i;
int rc;
if (!rc)
tvec_dumpreg(tv, v, 0, rd);
else {
- GROWBUF_REPLACE(&arena_stdlib, reg, rsz, tv->cfg.regsz,
+ GROWBUF_REPLACE(size_t, tv->a, reg, rsz, tv->cfg.regsz,
8*sizeof(void *), 1);
rd->ty->init(®->v, rd);
rc = rd->ty->frombuf(b, ®->v, rd);
case TVPK_BBENCH:
/* A report that we're starting a benchmark. Pass this along. */
- p = buf_getmem32l(b, &n); if (!p) goto bad;
if (buf_getu16l(b, &u)) goto bad;
+ if (tvec_deserialize(tv->in, b, tv->test->regs,
+ TVRF_ID, TVRF_ID, tv->cfg.nreg, tv->cfg.regsz))
+ goto bad;
if (BLEFT(b)) goto bad;
- if (u >= TVBU_LIMIT) {
+ if (u >= BTU_LIMIT) {
rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
goto end;
}
- DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
- o->ops->bbench(o, d.buf, u);
+ bo = tvec_outputext(tv, &o, &fo,
+ TVEC_BENCHOUTEXT, &tvec_benchoutputfallback);
+ bo->bbench(o, 0, u);
break;
case TVPK_EBENCH:
/* A report that a benchmark completed. Pass this along. */
- p = buf_getmem32l(b, &n); if (!p) goto bad;
if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
- if (u >= TVBU_LIMIT) {
+ if (u >= BTU_LIMIT) {
rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
goto end;
}
if ((v&BTF_TIMEOK) && buf_getf64l(b, &bt.t)) goto bad;
if ((v&BTF_CYOK) && buf_getf64l(b, &bt.cy)) goto bad;
if (BLEFT(b)) goto bad;
+ bt.f = v;
- DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
- o->ops->ebench(o, d.buf, u, v&BTF_ANY ? &bt : 0);
+ bo = tvec_outputext(tv, &o, &fo,
+ TVEC_BENCHOUTEXT, &tvec_benchoutputfallback);
+ bo->ebench(o, 0, u, v&BTF_ANY ? &bt : 0);
break;
default:
end:
DDESTROY(&d);
- xfree(reg);
+ x_free(tv->a, reg);
return (rc);
bad:
rc = malformed(tv, &r->rc); goto end;
*/
#define DCF_KILL 0x0100u
+#define DCF_QUITOK 0200u
static void disconnect_remote(struct tvec_state *tv,
struct tvec_remotectx *r, unsigned f)
{
+ union tvec_regval rv;
+ dstr d = DSTR_INIT;
+
if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
close_comms(&r->rc);
- drain_errfd(tv, r, f | ERF_CLOSE); reap_kid(tv, r);
+ drain_errfd(tv, r, f | ERF_CLOSE);
+ if (r->kid >= 0) {
+ reap_kid(tv, r);
+ if (!(f&ERF_SILENT) &&
+ (!(f&DCF_QUITOK) || (r->exit != TVXST_EXIT &&
+ r->exit != TVXST_DISCONN))) {
+ rv.u = r->exit;
+ tvty_flags.dump(&rv, &exit_var.def, TVSF_COMPACT, &dstr_printops, &d);
+ tvec_error(tv, "remote connection closed with status %s", d.buf);
+ }
+ }
+ dstr_destroy(&d);
}
/* --- @connect_remote@ --- *
if (connect_remote(tv, r))
tvec_skipgroup(tv, "failed to connect to test backend");
reset_vars(r);
- if (subenv && subenv->ctxsz) r->subctx = xmalloc(subenv->ctxsz);
+ if (subenv && subenv->ctxsz) r->subctx = x_alloc(tv->a, subenv->ctxsz);
else r->subctx = 0;
if (subenv && subenv->setup) subenv->setup(tv, subenv, r, r->subctx);
}
/* Send the command to the server and handle output. */
QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_TEST)
- tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout),
- tv->test->regs, tv->cfg.nreg, tv->cfg.regsz);
+ tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout), tv->test->regs,
+ 0, 0, tv->cfg.nreg, tv->cfg.regsz);
else { goto fail; }
rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
buf b;
if (subenv && subenv->teardown) subenv->teardown(tv, r->subctx);
- xfree(r->subctx);
+ x_free(tv->a, r->subctx);
if (r->rc.outfd >= 0) {
QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_EGROUP);
if (!handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_EGROUP | TVPF_ACK, &b))
if (BLEFT(&b)) malformed(tv, &r->rc);
}
- disconnect_remote(tv, r, 0); release_comms(&r->rc);
+ disconnect_remote(tv, r, DCF_QUITOK); release_comms(tv, &r->rc);
DDESTROY(&r->prgwant); DDESTROY(&r->progress);
}