Suggestion for patch

Frediano Ziglio frediano.ziglio at citrix.com
Thu Feb 7 15:15:17 UTC 2013


Hi,
  I was trying to add support to Xen ioctl to strace. To distinguish
from other ioctl I used the entire code (not only type and number but
also size and direction) so I changed sys_ioctl in io.c to call a
function that check for the entire code and return true if it handled
the syscall. Is it fine to do it?

The ioctl of Xen are quite complicated to decode cause there are single
ioctls that accept a structure that internally have a command field that
specify the operation and an union with all data for operations,
something like

struct my_operation {
  int operation;
  union {
    struct operation1_data operation1;
    struct operation2_data operation2;
    struct operation3_data operation3;
    ...
  } u;
};

Just to complicate more some field in operationX_data can be for output,
input or input/output. I found no way to store some informations in tcb
structure. I'd like to print something when syscall enters (input
parameters) and something when syscall leaves, somethings like

ioctl(FD, IOCTL, {op=my_op,u={field_inout=5}}->{u={field_inout=3}}) = 0

(->) to separate input and output. Is there a  specific syntax to
separate input and output?

Frediano


------------------------------------
Add a function to decode ioctl based on full iop

Standard sys_ioctl use only type and number but to distinguish Xen ioctl
the full iop (with size and direction) is better.

This first version add support for event channel ioctl.

From: Frediano Ziglio <frediano.ziglio at citrix.com>

diff --git a/Makefile.am b/Makefile.am
index 3e8c810..963466a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,7 +18,7 @@ strace_SOURCES = strace.c syscall.c count.c util.c
desc.c file.c ipc.c \
 		 io.c ioctl.c mem.c net.c process.c bjm.c quota.c \
 		 resource.c signal.c sock.c system.c term.c time.c \
 		 scsi.c stream.c block.c pathtrace.c mtd.c vsprintf.c \
-		 loop.c
+		 loop.c ioctlexact.c
 noinst_HEADERS = defs.h
 
 EXTRA_DIST = $(man_MANS) errnoent.sh signalent.sh syscallent.sh
ioctlsort.c \
diff --git a/io.c b/io.c
index 6b3f4b7..8569e6f 100644
--- a/io.c
+++ b/io.c
@@ -376,6 +376,8 @@ sys_vmsplice(struct tcb *tcp)
 	return 0;
 }
 
+extern int ioctl_decode_exact(struct tcb *tcp);
+
 int
 sys_ioctl(struct tcb *tcp)
 {
@@ -384,6 +386,12 @@ sys_ioctl(struct tcb *tcp)
 	if (entering(tcp)) {
 		printfd(tcp, tcp->u_arg[0]);
 		tprints(", ");
+	}
+
+	if (ioctl_decode_exact(tcp))
+		return 0;
+
+	if (entering(tcp)) {
 		iop = ioctl_lookup(tcp->u_arg[1]);
 		if (iop) {
 			tprints(iop->symbol);
diff --git a/ioctlexact.c b/ioctlexact.c
new file mode 100644
index 0000000..c10516b
--- /dev/null
+++ b/ioctlexact.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2012 Citrix.
+ * Written by Frediano Ziglio <frediano.ziglio at citrix.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
the
+ *    documentation and/or other materials provided with the
distribution.
+ * 3. The name of the author may not be used to endorse or promote
products
+ *    derived from this software without specific prior written
permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+
+#include <sys/ioctl.h>
+#include <xenctrl.h>
+#include <xen/sys/evtchn.h>
+#include <xen/sys/privcmd.h>
+
+// hold information on status
+// need flush if space is not enough
+typedef struct {
+	char *dest, *dest_end;
+	char buffer[256];
+} print_status;
+
+static inline void
+status_init(print_status *status)
+{
+	status->dest     = status->buffer;
+	status->dest_end = status->buffer + sizeof(status->buffer);
+}
+
+static void
+assure_space(print_status *status, size_t needed)
+{
+	/* assert(needed <= sizeof(status->buffer)); */
+	if (needed > status->dest_end - status->dest) {
+		tprints(status->buffer);
+		status->buffer[0] = 0;
+		status->dest = status->buffer;
+	}
+}
+
+static inline void
+status_flush(print_status *status)
+{
+	assure_space(status, sizeof(status->buffer));
+}
+
+static void
+status_printf(print_status *status, const char *fmt, ...)
+{
+	int l;
+	va_list ap;
+
+	assure_space(status, 64);
+	va_start(ap, fmt);
+	l = vsprintf(status->dest, fmt, ap);
+	va_end(ap);
+	status->dest += l;
+}
+
+/* Define structure to handle print of a structure */
+typedef void (*print_func_t)(print_status *status, const void *data,
const void *info);
+#define DATA_START \
+	print_func_t print; \
+	unsigned size;
+
+/* Define base types */
+typedef struct {
+	DATA_START;
+} base_type;
+
+#define DEFINE_BASE_TYPE(name, type, fmt) \
+static void print_ ## name(print_status *status, const void *data,
const void *info) { \
+	status_printf(status, fmt, *((type*)(data))); \
+} \
+static const base_type type_ ## name = { print_ ## name,
sizeof(type) };
+
+/* Define structures */
+typedef struct {
+	const char *name;	/* name of field NULL if last */
+	const base_type* info;	/* information for printing */
+	size_t off;		/* offset intpo structure */
+	// size ??
+} struct_item;
+
+typedef struct {
+	DATA_START;
+	const struct_item *items;
+} type_struct;
+
+static void
+print_struct(print_status *status, const unsigned char *data, const
type_struct *info)
+{
+	char sep = '{';
+	const struct_item *item = info->items;
+	// foreach field
+	for (; item->name; ++item, sep = ',') {
+		// print field, value
+		status_printf(status, "%c%s=", sep, item->name);
+		item->info->print(status, (const void*) (data+item->off),
item->info);
+	}
+	status_printf(status, "}");
+}
+
+#define START_STRUCT(name) \
+static const struct_item name ## _items[] = {
+
+#define BASE_ITEM(type, name) \
+	{ #name, &type_ ## type, offsetof(STRUCT, name) },
+
+#define END_STRUCT(name) \
+	{ NULL, } \
+}; \
+	static const type_struct struct_ ## name = { \
+		(print_func_t) print_struct, \
+		sizeof(STRUCT), \
+		name ## _items \
+	};
+
+typedef struct {
+	DATA_START;
+	const void *item_type;
+} type_fixed_array;
+
+static void
+print_fixed_array(print_status *status, const unsigned char *data,
const type_fixed_array *info)
+{
+	const unsigned char *const end = data + info->size;
+	const base_type *item = (const base_type *) info->item_type;
+	const char *sep = NULL;
+
+	status_printf(status, "[");
+	for (;data < end; data += item->size, sep = ",") {
+		if (sep) status_printf(status, sep);
+		item->print(status, data, item);
+	}
+	status_printf(status, "]");
+}
+
+typedef struct {
+	unsigned value;
+	const char *name;
+	const base_type *type;
+} switch_item;
+
+typedef struct {
+	DATA_START;
+	switch_item *items;
+	ssize_t data_off;	/* offset to data from number */
+} type_switch;
+
+static void
+print_switch(print_status *status, const unsigned char *data, const
type_switch *info)
+{
+	/* check if we have */
+	unsigned val = *((unsigned *) data);
+	const switch_item *item = info->items;
+	/* TODO sort items */
+	for (; item->name; ++item) {
+		if (item->value != val)
+			continue;
+
+		/* found !*/
+		status_printf(status, "%s,", item->name);
+		item->type->print(status, data+info->data_off, item->type);
+		return;
+	}
+
+	/* not found */
+	status_printf(status, "UNKNOWN(%lu)", val);
+}
+
+DEFINE_BASE_TYPE(int, int, "%d");
+DEFINE_BASE_TYPE(uint, unsigned int, "%u");
+DEFINE_BASE_TYPE(domid, domid_t, "%u");
+DEFINE_BASE_TYPE(u64, __u64, "%" PRIu64);
+DEFINE_BASE_TYPE(u8, uint8_t, "%u");
+
+#define STRUCT struct ioctl_evtchn_bind_virq
+START_STRUCT(evtchn_bind_virq)
+	BASE_ITEM(uint, virq)
+END_STRUCT(evtchn_bind_virq)
+#undef STRUCT
+
+#define STRUCT struct ioctl_evtchn_bind_interdomain
+START_STRUCT(evtchn_bind_interdomain)
+	BASE_ITEM(uint, remote_domain)
+	BASE_ITEM(uint, remote_port)
+END_STRUCT(evtchn_bind_interdomain)
+#undef STRUCT
+
+#define STRUCT struct ioctl_evtchn_bind_unbound_port
+START_STRUCT(evtchn_bind_unbound_port)
+	BASE_ITEM(uint, remote_domain)
+END_STRUCT(evtchn_bind_unbound_port)
+#undef STRUCT
+
+#define STRUCT struct ioctl_evtchn_unbind
+START_STRUCT(evtchn_unbind)
+	BASE_ITEM(uint, port)
+END_STRUCT(evtchn_unbind)
+#undef STRUCT
+
+#define STRUCT struct ioctl_evtchn_notify
+START_STRUCT(evtchn_notify)
+	BASE_ITEM(uint, port)
+END_STRUCT(evtchn_notify)
+#undef STRUCT
+
+#define STRUCT struct ioctl_evtchn_restrict_domid
+START_STRUCT(evtchn_restrict_domid)
+	BASE_ITEM(domid, domid)
+END_STRUCT(evtchn_restrict_domid)
+#undef STRUCT
+
+#define STRUCT privcmd_hypercall_t
+type_fixed_array type_privcmd_hypercall_args = {
+	print_fixed_array,
+	sizeof(((privcmd_hypercall_t*)0)->arg),
+	&type_u64
+};
+START_STRUCT(privcmd_hypercall)
+	BASE_ITEM(u64, op)
+	BASE_ITEM(privcmd_hypercall_args, arg)
+END_STRUCT(privcmd_hypercall)
+#undef STRUCT
+
+#define STRUCT struct xen_hvm_set_pci_intx_level
+START_STRUCT(xen_hvm_set_pci_intx_level)
+	BASE_ITEM(domid, domid)
+	BASE_ITEM(u8, domain)
+	BASE_ITEM(u8, bus)
+	BASE_ITEM(u8, device)
+	BASE_ITEM(u8, intx)
+	BASE_ITEM(u8, level)
+END_STRUCT(xen_hvm_set_pci_intx_level)
+#undef STRUCT
+
+#define STRUCT privcmd_hvmop_t
+static switch_item privcmd_hvmop_cmd_items[] = {
+	{ HVMOP_set_pci_intx_level, "set_pci_intx_level",
&struct_xen_hvm_set_pci_intx_level },
+	{ 0, NULL, }
+};
+
+static const type_switch type_privcmd_hvmop_cmd = {
+	print_switch,
+	sizeof(unsigned),
+	&privcmd_hvmop_cmd_items,
+	offsetof(privcmd_hvmop_t, u) - offsetof(privcmd_hvmop_t, cmd)
+};
+START_STRUCT(privcmd_hvmop)
+	{ "cmd", &type_privcmd_hvmop_cmd, offsetof(privcmd_hvmop_t, cmd) },
+END_STRUCT(privcmd_hvmop)
+#undef STRUCT
+
+
+// for arrays ??
+
+#if 0
+#define on_enter(code, type, name) \
+	case code: if (!entering(tcp)) return 1; { type name; \
+		tprints(#code); \
+		if (umove(tcp, arg, &name) < 0) { tprintf(", %#lx", arg); return 1; }
+#define on_end() } return 1
+
+#define on_exit(code, type, name) \
+	case code: if (entering(tcp)) { \
+		tprints(#code); \
+		} else { type name; \
+		if (umove(tcp, arg, &name) < 0) { tprintf(", %#lx", arg); return 1; }
+#undef  on_end
+#define on_end() } return 1
+#endif
+
+static int
+decode_struct_input(const char *code_name, const type_struct *type,
struct tcb *tcp, long arg)
+{
+	char buf[256];
+	print_status pr;
+
+	status_init(&pr);
+
+	if (!entering(tcp))
+		return 1;
+
+	tprints(code_name);
+
+	if (umoven(tcp, arg, type->size, buf) < 0) {
+		tprintf(", %#lx", arg);
+		return 1;
+	}
+	status_printf(&pr, ", ");
+	type->print(&pr, buf, type);
+	status_flush(&pr);
+	return 1;
+}
+
+#define decode_struct_input(code, struct) \
+	case code: return decode_struct_input(#code, &struct_ ## struct, tcp,
arg)
+
+/*
+ * Try to detect and handle the exact ioctl code
+ * return 1 if handled, 0 otherwise
+ */
+int ioctl_decode_exact(struct tcb *tcp)
+{
+	long code = tcp->u_arg[1];
+	long arg =  tcp->u_arg[2];
+
+	switch (code) {
+	/* Xen event channels */
+#ifdef IOCTL_EVTCHN_BIND_VIRQ
+	decode_struct_input(IOCTL_EVTCHN_BIND_VIRQ, evtchn_bind_virq);
+#endif
+#ifdef IOCTL_EVTCHN_BIND_INTERDOMAIN
+	decode_struct_input(IOCTL_EVTCHN_BIND_INTERDOMAIN,
evtchn_bind_interdomain);
+#endif
+#ifdef IOCTL_EVTCHN_BIND_UNBOUND_PORT
+	decode_struct_input(IOCTL_EVTCHN_BIND_UNBOUND_PORT,
evtchn_bind_unbound_port);
+#endif
+#ifdef IOCTL_EVTCHN_UNBIND
+	decode_struct_input(IOCTL_EVTCHN_UNBIND, evtchn_unbind);
+#endif
+#ifdef IOCTL_EVTCHN_NOTIFY
+	decode_struct_input(IOCTL_EVTCHN_NOTIFY, evtchn_notify);
+#endif
+#ifdef IOCTL_EVTCHN_RESET
+	case IOCTL_EVTCHN_RESET:
+		if (entering(tcp))
+			tprintf("IOCTL_EVTCHN_RESET, %#lx", arg);
+		return 1;
+#endif
+#ifdef IOCTL_EVTCHN_RESTRICT_DOMID
+	decode_struct_input(IOCTL_EVTCHN_RESTRICT_DOMID,
evtchn_restrict_domid);
+#endif
+
+	/* Xen privcmd */
+#ifdef IOCTL_PRIVCMD_HYPERCALL
+	decode_struct_input(IOCTL_PRIVCMD_HYPERCALL, privcmd_hypercall);
+#endif
+#ifdef IOCTL_PRIVCMD_MMAP
+//	on_enter(IOCTL_PRIVCMD_MMAP, privcmd_mmap_t, s);
+//		int i;
+//		tprintf(", {num=%d,dom=%u,{", s.num, s.dom);
+//		for (i = 0; i < s.num; ++i)
+//			tprintf("{%" PRIu64 ",%" PRIu64 ",%" PRIu64 "}",
+//		tprintf("}");
+//	on_end();
+#endif
+#ifdef IOCTL_PRIVCMD_HVMOP
+	decode_struct_input(IOCTL_PRIVCMD_HVMOP, privcmd_hvmop);
+#endif
+	}
+	return 0;
+}
+



More information about the Strace-devel mailing list