Commit 318f717e authored by Rusty Russell's avatar Rusty Russell

ccan/io: implement debug.

Now a simple flag, with an external toggle (no compile time DEBUG
define required).  But it's completely synchronous.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 94dd4c2b
......@@ -133,6 +133,7 @@ int main(int argc, char *argv[])
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/container_of\n");
printf("ccan/list\n");
printf("ccan/tal\n");
printf("ccan/time\n");
......
......@@ -25,6 +25,9 @@ struct io_listener {
/* One connection per client. */
struct io_conn {
struct fd fd;
bool debug;
/* For duplex to save. */
bool debug_saved;
/* always or closing list. */
struct io_conn *list;
......@@ -44,7 +47,7 @@ void del_listener(struct io_listener *l);
void backend_new_closing(struct io_conn *conn);
void backend_new_always(struct io_conn *conn);
void backend_new_plan(struct io_conn *conn);
void remove_from_always(struct io_conn *conn);
void backend_plan_done(struct io_conn *conn);
void backend_wake(const void *wait);
......
......@@ -10,6 +10,7 @@
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <ccan/container_of/container_of.h>
void *io_loop_return;
......@@ -80,6 +81,7 @@ struct io_conn *io_new_conn_(const tal_t *ctx, int fd,
conn->finish = NULL;
conn->finish_arg = NULL;
conn->list = NULL;
conn->debug = false;
if (!add_conn(conn))
return tal_free(conn);
......@@ -116,12 +118,9 @@ static struct io_plan *set_always(struct io_conn *conn,
void *),
void *arg)
{
plan->next = next;
plan->next_arg = arg;
plan->status = IO_ALWAYS;
backend_new_always(conn);
return plan;
return io_set_plan(conn, plan, NULL, next, arg);
}
struct io_plan *io_always_(struct io_conn *conn,
......@@ -138,9 +137,7 @@ struct io_plan *io_always_(struct io_conn *conn,
plan = io_get_plan(conn, IO_OUT);
assert(next);
set_always(conn, plan, next, arg);
return plan;
return set_always(conn, plan, next, arg);
}
static int do_write(int fd, struct io_plan *plan)
......@@ -161,18 +158,13 @@ struct io_plan *io_write_(struct io_conn *conn, const void *data, size_t len,
{
struct io_plan *plan = io_get_plan(conn, IO_OUT);
assert(next);
if (len == 0)
return set_always(conn, plan, next, arg);
plan->u1.const_vp = data;
plan->u2.s = len;
plan->io = do_write;
plan->next = next;
plan->next_arg = arg;
return plan;
return io_set_plan(conn, plan, do_write, next, arg);
}
static int do_read(int fd, struct io_plan *plan)
......@@ -201,11 +193,8 @@ struct io_plan *io_read_(struct io_conn *conn,
plan->u1.cp = data;
plan->u2.s = len;
plan->io = do_read;
plan->next = next;
plan->next_arg = arg;
return plan;
return io_set_plan(conn, plan, do_read, next, arg);
}
static int do_read_partial(int fd, struct io_plan *plan)
......@@ -227,8 +216,6 @@ struct io_plan *io_read_partial_(struct io_conn *conn,
{
struct io_plan *plan = io_get_plan(conn, IO_IN);
assert(next);
if (maxlen == 0)
return set_always(conn, plan, next, arg);
......@@ -236,11 +223,8 @@ struct io_plan *io_read_partial_(struct io_conn *conn,
/* We store the max len in here temporarily. */
*len = maxlen;
plan->u2.vp = len;
plan->io = do_read_partial;
plan->next = next;
plan->next_arg = arg;
return plan;
return io_set_plan(conn, plan, do_read_partial, next, arg);
}
static int do_write_partial(int fd, struct io_plan *plan)
......@@ -262,8 +246,6 @@ struct io_plan *io_write_partial_(struct io_conn *conn,
{
struct io_plan *plan = io_get_plan(conn, IO_OUT);
assert(next);
if (maxlen == 0)
return set_always(conn, plan, next, arg);
......@@ -271,11 +253,8 @@ struct io_plan *io_write_partial_(struct io_conn *conn,
/* We store the max len in here temporarily. */
*len = maxlen;
plan->u2.vp = len;
plan->io = do_write_partial;
plan->next = next;
plan->next_arg = arg;
return plan;
return io_set_plan(conn, plan, do_write_partial, next, arg);
}
static int do_connect(int fd, struct io_plan *plan)
......@@ -319,11 +298,7 @@ struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr,
if (errno != EINPROGRESS)
return io_close(conn);
plan->next = next;
plan->next_arg = arg;
plan->io = do_connect;
return plan;
return io_set_plan(conn, plan, do_connect, next, arg);
}
struct io_plan *io_wait_(struct io_conn *conn,
......@@ -340,14 +315,10 @@ struct io_plan *io_wait_(struct io_conn *conn,
else
plan = io_get_plan(conn, IO_OUT);
assert(next);
plan->next = next;
plan->next_arg = arg;
plan->u1.const_vp = wait;
plan->status = IO_WAITING;
plan->u1.const_vp = wait;
return plan;
return io_set_plan(conn, plan, NULL, next, arg);
}
void io_wake(const void *wait)
......@@ -355,11 +326,11 @@ void io_wake(const void *wait)
backend_wake(wait);
}
static void do_plan(struct io_conn *conn, struct io_plan *plan)
static int do_plan(struct io_conn *conn, struct io_plan *plan)
{
/* Someone else might have called io_close() on us. */
if (plan->status == IO_CLOSING)
return;
return -1;
/* We shouldn't have polled for this event if this wasn't true! */
assert(plan->status == IO_POLLING);
......@@ -367,12 +338,12 @@ static void do_plan(struct io_conn *conn, struct io_plan *plan)
switch (plan->io(conn->fd.fd, plan)) {
case -1:
io_close(conn);
break;
return -1;
case 0:
break;
return 0;
case 1:
next_plan(conn, plan);
break;
return 1;
default:
/* IO should only return -1, 0 or 1 */
abort();
......@@ -414,7 +385,7 @@ struct io_plan *io_close(struct io_conn *conn)
conn->plan[IO_IN].u1.s = errno;
backend_new_closing(conn);
return &conn->plan[IO_IN];
return io_set_plan(conn, &conn->plan[IO_IN], NULL, NULL, NULL);
}
struct io_plan *io_close_cb(struct io_conn *conn, void *arg)
......@@ -439,10 +410,85 @@ int io_conn_fd(const struct io_conn *conn)
return conn->fd.fd;
}
struct io_plan *io_duplex(struct io_plan *in_plan, struct io_plan *out_plan)
void io_duplex_prepare(struct io_conn *conn)
{
assert(conn->plan[IO_IN].status == IO_UNSET);
assert(conn->plan[IO_OUT].status == IO_UNSET);
conn->debug_saved = conn->debug;
conn->debug = false;
}
struct io_plan *io_duplex_(struct io_plan *in_plan, struct io_plan *out_plan)
{
struct io_conn *conn;
/* in_plan must be conn->plan[IO_IN], out_plan must be [IO_OUT] */
assert(out_plan == in_plan + 1);
/* Restore debug. */
conn = container_of(in_plan, struct io_conn, plan[IO_IN]);
conn->debug = conn->debug_saved;
/* Now set the plans again, to invoke sync debug. */
io_set_plan(conn,
out_plan, out_plan->io, out_plan->next, out_plan->next_arg);
io_set_plan(conn,
in_plan, in_plan->io, in_plan->next, in_plan->next_arg);
return out_plan + 1;
}
struct io_plan *io_set_plan(struct io_conn *conn, struct io_plan *plan,
int (*io)(int fd, struct io_plan *plan),
struct io_plan *(*next)(struct io_conn *, void *),
void *next_arg)
{
struct io_plan *other;
plan->io = io;
plan->next = next;
plan->next_arg = next_arg;
assert(plan->status == IO_CLOSING || next != NULL);
if (!conn->debug)
return plan;
if (io_loop_return) {
io_debug_complete(conn);
return plan;
}
switch (plan->status) {
case IO_POLLING:
while (do_plan(conn, plan) == 0);
break;
/* Shouldn't happen, since you said you did plan! */
case IO_UNSET:
abort();
case IO_ALWAYS:
/* If other one is ALWAYS, leave in list! */
if (plan == &conn->plan[IO_IN])
other = &conn->plan[IO_OUT];
else
other = &conn->plan[IO_IN];
if (other->status != IO_ALWAYS)
remove_from_always(conn);
next_plan(conn, plan);
break;
case IO_WAITING:
case IO_CLOSING:
io_debug_complete(conn);
}
return plan;
}
void io_set_debug(struct io_conn *conn, bool debug)
{
conn->debug = debug;
}
void io_debug_complete(struct io_conn *conn)
{
}
......@@ -432,11 +432,16 @@ struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr,
*
* static struct io_plan *read_and_write(struct io_conn *conn, struct buf *b)
* {
* return io_duplex(io_read(conn, b->in, sizeof(b->in), io_close_cb, b),
* io_write(conn, b->out, sizeof(b->out), io_close_cb, b));
* return io_duplex(conn,
* io_read(conn, b->in, sizeof(b->in), io_close_cb, b),
* io_write(conn, b->out, sizeof(b->out), io_close_cb,b));
* }
*/
struct io_plan *io_duplex(struct io_plan *in_plan, struct io_plan *out_plan);
#define io_duplex(conn, in_plan, out_plan) \
(io_duplex_prepare(conn), io_duplex_(in_plan, out_plan))
struct io_plan *io_duplex_(struct io_plan *in_plan, struct io_plan *out_plan);
void io_duplex_prepare(struct io_conn *conn);
/**
* io_wait - leave a plan idle until something wakes us.
......@@ -575,4 +580,38 @@ void *io_loop(struct timers *timers, struct list_head *expired);
* Sometimes useful, eg for getsockname().
*/
int io_conn_fd(const struct io_conn *conn);
/**
* io_set_debug - set synchronous mode on a connection.
* @conn: the connection.
* @debug: whether to enable or disable debug.
*
* Once @debug is true on a connection, all I/O is done synchronously
* as soon as it is set, until it is unset or @conn is closed. This
* makes it easy to debug what's happening with a connection, but note
* that other connections are starved while this is being done.
*
* See also: io_debug_complete()
*
* Example:
* // Dumb init function to set debug and tell conn to close.
* static struct io_plan *conn_init(struct io_conn *conn, const char *msg)
* {
* io_set_debug(conn, true);
* return io_close(conn);
* }
*/
void io_set_debug(struct io_conn *conn, bool debug);
/**
* io_debug_complete - empty function called when conn is closing/waiting.
* @conn: the connection.
*
* This is for putting a breakpoint onto, when debugging. It is called
* when a conn with io_set_debug() true can no longer be synchronous:
* 1) It is io_close()'d
* 2) It enters io_wait() (sychronous debug will resume after io_wake())
* 3) io_break() is called (sychronous debug will resume after io_loop())
*/
void io_debug_complete(struct io_conn *conn);
#endif /* CCAN_IO_H */
......@@ -52,7 +52,51 @@ struct io_plan {
union io_plan_arg u1, u2;
};
/* Helper to get a conn's io_plan. */
/**
* io_get_plan - get a conn's io_plan for a given direction.
* @conn: the connection.
* @dir: IO_IN or IO_OUT.
*
* This is how an io helper gets a plan to store into; you must call
* io_done_plan() when you've initialized it.
*
* Example:
* // Simple helper to read a single char.
* static int do_readchar(int fd, struct io_plan *plan)
* {
* return read(fd, plan->u1.cp, 1) <= 0 ? -1 : 1;
* }
*
* struct io_plan *io_read_char_(struct io_conn *conn, char *in,
* struct io_plan *(*next)(struct io_conn*,void*),
* void *arg)
* {
* struct io_plan *plan = io_get_plan(conn, IO_IN);
*
* // Store information we need in the plan unions u1 and u2.
* plan->u1.cp = in;
*
* return io_set_plan(conn, plan, do_readchar, next, arg);
* }
*/
struct io_plan *io_get_plan(struct io_conn *conn, enum io_direction dir);
/**
* io_set_plan - set a conn's io_plan.
* @conn: the connection.
* @plan: the plan
* @io: the IO function to call when the fd is ready.
* @next: the next callback when @io returns 1.
* @next_arg: the argument to @next.
*
* If @conn has debug set, the io function will be called immediately,
* so it's important that this be the last thing in your function!
*
* See also:
* io_get_plan()
*/
struct io_plan *io_set_plan(struct io_conn *conn, struct io_plan *plan,
int (*io)(int fd, struct io_plan *plan),
struct io_plan *(*next)(struct io_conn *, void *),
void *next_arg);
#endif /* CCAN_IO_PLAN_H */
......@@ -88,17 +88,21 @@ bool add_listener(struct io_listener *l)
return true;
}
void backend_new_closing(struct io_conn *conn)
void remove_from_always(struct io_conn *conn)
{
/* Already on always list? Remove it. */
if (conn->list) {
struct io_conn **p = &always;
struct io_conn **p = &always;
while (*p != conn)
p = &(*p)->list;
while (*p != conn)
p = &(*p)->list;
*p = conn->list;
}
*p = conn->list;
}
void backend_new_closing(struct io_conn *conn)
{
/* Already on always list? Remove it. */
if (conn->list)
remove_from_always(conn);
conn->list = closing;
closing = conn;
......
#define DEBUG_CONN
#include "run-01-start-finish.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64001"
#else
#define PORT "65001"
#endif
static int expected_fd;
......@@ -21,6 +23,9 @@ static void finish_ok(struct io_conn *conn, int *state)
static struct io_plan *init_conn(struct io_conn *conn, int *state)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(*state == 0);
(*state)++;
expected_fd = io_conn_fd(conn);
......
#define DEBUG_CONN
#include "run-02-read.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64002"
#else
#define PORT "65002"
#endif
......@@ -24,6 +26,9 @@ static void finish_ok(struct io_conn *conn, struct data *d)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
......
#define DEBUG_CONN
#include "run-03-readpartial.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64003"
#else
#define PORT "65003"
#endif
......@@ -25,6 +27,9 @@ static void finish_ok(struct io_conn *conn, struct data *d)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
......
#define DEBUG_CONN
#include "run-04-writepartial.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64004"
#else
#define PORT "65004"
#endif
......@@ -25,6 +27,9 @@ static void finish_ok(struct io_conn *conn, struct data *d)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
io_set_finish(conn, finish_ok, d);
......
#define DEBUG_CONN
#include "run-05-write.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64005"
#else
#define PORT "65005"
#endif
......@@ -25,6 +27,9 @@ static void finish_ok(struct io_conn *conn, struct data *d)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
io_set_finish(conn, finish_ok, d);
......
......@@ -9,7 +9,9 @@
#include <sys/stat.h>
#include <fcntl.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64006"
#else
#define PORT "65006"
#endif
......
#define DEBUG_CONN
#include "run-07-break.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64007"
#else
#define PORT "65007"
#endif
......@@ -30,6 +32,9 @@ static void finish_ok(struct io_conn *conn, struct data *d)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
......
#define DEBUG_CONN
#include "run-09-connect.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64009"
#else
#define PORT "65009"
#endif
......@@ -33,6 +35,9 @@ static struct io_plan *connected(struct io_conn *conn, struct data *d2)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
io_close_listener(l);
......
#define DEBUG_CONN
#include "run-12-bidir.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64012"
#else
#define PORT "65012"
#endif
......@@ -34,6 +36,9 @@ static struct io_plan *rw_done(struct io_conn *conn, struct data *d)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
......@@ -42,7 +47,8 @@ static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
memset(d->wbuf, 7, sizeof(d->wbuf));
io_set_finish(conn, finish_ok, d);
return io_duplex(io_read(conn, d->buf, sizeof(d->buf), rw_done, d),
return io_duplex(conn,
io_read(conn, d->buf, sizeof(d->buf), rw_done, d),
io_write(conn, d->wbuf, sizeof(d->wbuf), rw_done, d));
}
......
#define DEBUG_CONN
#include "run-14-duplex-both-read.c"
......@@ -8,7 +8,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64014"
#else
#define PORT "65014"
#endif
......@@ -37,12 +39,16 @@ static struct io_plan *make_duplex(struct io_conn *conn, struct data *d)
{
d->state++;
/* Have duplex read the rest of the buffer. */
return io_duplex(io_read(conn, d->buf+1, sizeof(d->buf)-1, end, d),
return io_duplex(conn,
io_read(conn, d->buf+1, sizeof(d->buf)-1, end, d),
io_write(conn, d->wbuf, sizeof(d->wbuf), end, d));
}
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
......
......@@ -8,7 +8,9 @@
#include <stdio.h>
#include <unistd.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64015"
#else
#define PORT "65015"
#endif
......@@ -36,6 +38,9 @@ static struct io_plan *no_timeout(struct io_conn *conn, struct data *d)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
......
#define DEBUG_CONN
#include "run-16-duplex-test.c"
......@@ -8,7 +8,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64016"
#else
#define PORT "65016"
#endif
......@@ -34,6 +36,9 @@ static struct io_plan *io_done(struct io_conn *conn, struct data *d)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
......@@ -43,7 +48,8 @@ static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
io_close_listener(d->l);
return io_duplex(io_read(conn, d->buf, sizeof(d->buf), io_done, d),
return io_duplex(conn,
io_read(conn, d->buf, sizeof(d->buf), io_done, d),
io_write(conn, d->wbuf, sizeof(d->wbuf), io_done, d));
}
......
#define DEBUG_CONN
#include "run-17-homemade-io.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64017"
#else
#define PORT "65017"
#endif
......@@ -77,15 +79,15 @@ static struct io_plan *io_read_packet(struct io_conn *conn,
pkt->contents = NULL;
plan->u1.vp = pkt;
plan->u2.s = 0;
plan->io = do_read_packet;
plan->next = cb;
plan->next_arg = arg;
return plan;
return io_set_plan(conn, plan, do_read_packet, cb, arg);
}
static struct io_plan *init_conn(struct io_conn *conn, struct packet *pkt)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(pkt->state == 0);
pkt->state++;
......
#define DEBUG_CONN
#include "run-18-errno.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64018"
#else
#define PORT "65018"
#endif
......@@ -27,6 +29,9 @@ static void finish_EBADF(struct io_conn *conn, int *state)
static struct io_plan *init_conn(struct io_conn *conn, int *state)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
if (*state == 0) {
(*state)++;
errno = 100;
......
#define DEBUG_CONN
#include "run-19-always.c"
......@@ -6,7 +6,9 @@
#include <sys/wait.h>
#include <stdio.h>
#ifndef PORT
#ifdef DEBUG_CONN
#define PORT "64019"
#else
#define PORT "65019"
#endif
......@@ -30,6 +32,9 @@ static struct io_plan *write_buf(struct io_conn *conn, struct data *d)
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
{
#ifdef DEBUG_CONN
io_set_debug(conn, true);
#endif
ok1(d->state == 0);
d->state++;
io_set_finish(conn, finish_ok, d);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment