[Spice-devel,v2,25/43] test: Allows to write items to tree and dump saved tree

Submitted by Frediano Ziglio on July 8, 2015, 1:53 p.m.

Details

Message ID 1436363656-4266-26-git-send-email-fziglio@redhat.com
State New
Headers show

Not browsing as part of any series.

Commit Message

Frediano Ziglio July 8, 2015, 1:53 p.m.
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
---
 codegen/dissector_test.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 423 insertions(+), 1 deletion(-)

Patch hide | download patch | download mbox

diff --git a/codegen/dissector_test.c b/codegen/dissector_test.c
index 25a33b5..5a49f40 100644
--- a/codegen/dissector_test.c
+++ b/codegen/dissector_test.c
@@ -21,6 +21,176 @@  static int last_ei_registered = first_ei_registered - 1;
 static int last_tree_registered = first_tree_registered - 1;
 static bool got_error = false;
 
+static GPtrArray *hfs;
+
+struct tvbuff {
+	guint8 *data;
+	size_t len;
+};
+
+static int check(const char *chk_str, int line, int chk, const char *fmt, ...)
+{
+	if (!chk) {
+		va_list ap;
+		va_start(ap, fmt);
+		fprintf(stderr, "Check failed at line %d\n", line);
+		fprintf(stderr, "Check: %s\n", chk_str);
+		vfprintf(stderr, fmt, ap);
+		fprintf(stderr, "\n");
+		va_end(ap);
+		abort();
+	}
+	return 1;
+}
+#define check(chk, ...) check(#chk, __LINE__, chk, __VA_ARGS__)
+
+static int check_tree(proto_node *node)
+{
+	assert(node->tree_data == (void*) node);
+	assert(node->next == NULL);
+	assert(node->finfo == NULL);
+	if (!node->first_child) {
+		assert(node->last_child == NULL);
+	} else {
+		assert(node->last_child->next == NULL);
+	}
+	return 1;
+}
+
+static int check_item(proto_node *node)
+{
+	assert(node->tree_data == NULL);
+	assert(node->finfo != NULL);
+	assert(node->finfo->rep != NULL);
+	assert(node->first_child == node->last_child);
+	assert(node->first_child == NULL || check_tree(node->first_child));
+	assert(node->parent);
+	assert(node->finfo->start >= 0);
+	assert(node->finfo->length >= 0);
+	return 1;
+}
+
+guint8 *tvb_bytes(tvbuff_t *tvb, const gint offset, unsigned len)
+{
+	if (!tvb)
+		return NULL;
+
+	assert(offset >= 0);
+	assert(offset + len <= tvb->len);
+	return tvb->data + offset;
+}
+
+static guint64 read_ule(const guint8 *p, unsigned len)
+{
+	guint64 low, high;
+
+	switch (len) {
+	case 1:
+		return p[0];
+	case 2:
+		return p[0] + 0x100u * p[1];
+	case 4:
+		return p[0] + 0x100u * p[1] + 0x10000u * p[2] + 0x1000000u * p[3];
+	case 8:
+		low  = p[0] + 0x100u * p[1] + 0x10000u * p[2] + 0x1000000u * p[3];
+		p += 4;
+		high = p[0] + 0x100u * p[1] + 0x10000u * p[2] + 0x1000000u * p[3];
+		return high << 32 | low;
+	}
+	assert(0);
+	return 0;
+}
+
+static gint64 read_sle(const guint8 *p, unsigned len)
+{
+	guint64 sign_bit = (((guint64) 0x80) << ((len-1) * 8));
+	guint64 val = read_ule(p, len);
+
+	if ((val & sign_bit) != 0)
+		return (gint64) ((val ^ sign_bit) - sign_bit);
+	return (gint64) val;
+}
+
+static guint64 tvb_get_ule(tvbuff_t *tvb, const gint offset, unsigned len)
+{
+	guint8 *p = tvb_bytes(tvb, offset, len);
+	if (!p)
+		return 0;
+	return read_ule(p, len);
+}
+
+static gint64 tvb_get_sle(tvbuff_t *tvb, const gint offset, unsigned len)
+{
+	guint8 *p = tvb_bytes(tvb, offset, len);
+	if (!p)
+		return 0;
+	return read_sle(p, len);
+}
+
+static const char *describe_fttype(enum ftenum type)
+{
+	switch (type) {
+#define FT(name) case FT_ ## name: return "FT_" #name;
+	FT(NONE)	/* used for text labels with no value */
+	FT(PROTOCOL)
+	FT(BOOLEAN)	/* TRUE and FALSE come from <glib.h> */
+	FT(UINT8)
+	FT(UINT16)
+	FT(UINT24)	/* really a UINT32, but displayed as 3 hex-digits if FD_HEX*/
+	FT(UINT32)
+	FT(UINT64)
+	FT(INT8)
+	FT(INT16)
+	FT(INT24)	/* same as for UINT24 */
+	FT(INT32)
+	FT(INT64)
+	FT(FLOAT)
+	FT(DOUBLE)
+	FT(ABSOLUTE_TIME)
+	FT(RELATIVE_TIME)
+	FT(STRING)
+	FT(STRINGZ)	/* for use with proto_tree_add_item() */
+	FT(UINT_STRING)	/* for use with proto_tree_add_item() */
+	FT(ETHER)
+	FT(BYTES)
+	FT(UINT_BYTES)
+	FT(IPv4)
+	FT(IPv6)
+	FT(IPXNET)
+	FT(FRAMENUM)	/* a UINT32, but if selected lets you go to frame with that number */
+	FT(PCRE)	/* a compiled Perl-Compatible Regular Expression object */
+	FT(GUID)	/* GUID, UUID */
+	FT(OID)		/* OBJECT IDENTIFIER */
+	FT(EUI64)
+	FT(AX25)
+	FT(VINES)
+	FT(REL_OID)	/* RELATIVE-OID */
+	FT(SYSTEM_ID)
+	FT(STRINGZPAD)	/* for use with proto_tree_add_item() */
+	default:
+		check(false, "Unknown fttype %d", type);
+		break;
+	}
+	return NULL;
+}
+
+static const char *describe_base(int base)
+{
+	switch (base) {
+#define BASE(base) case base: return #base;
+	BASE(BASE_NONE)
+	BASE(BASE_DEC)
+	BASE(BASE_HEX)
+	BASE(BASE_OCT)
+	BASE(BASE_DEC_HEX)
+	BASE(BASE_HEX_DEC)
+	BASE(BASE_CUSTOM)
+	BASE(STR_UNICODE)
+	}
+	check(false, "Unknown base %d", base);
+	return NULL;
+}
+
 WS_DLL_PUBLIC void
 proto_register_field_array(const int parent, hf_register_info *hf, const int num_records)
 {
@@ -31,6 +201,7 @@  proto_register_field_array(const int parent, hf_register_info *hf, const int num
 		assert(hf[i].p_id);
 		assert(*hf[i].p_id == -1);
 		*hf[i].p_id = ++last_hf_registered;
+		g_ptr_array_add(hfs, &hf[i].hfinfo);
 	}
 }
 
@@ -66,12 +237,245 @@  expert_add_info_format(packet_info *pinfo, proto_item *pi, expert_field *eiindex
 		return;
 }
 
+struct all_ti
+{
+	proto_item ti;
+	field_info info;
+	item_label_t label;
+};
+
+WS_DLL_PUBLIC proto_item *
+proto_tree_add_text(proto_tree *tree, tvbuff_t *tvb, gint start, gint length, const char *format,
+	...)
+{
+	struct all_ti *all;
+	proto_item *ti;
+	va_list ap;
+
+	assert(tvb);
+	assert(start >= 0);
+	assert(start <= tvb->len);
+	assert(length >= 0);
+	check(start + length <= tvb->len, "start %d len %d tvb_len %d", start, length, tvb->len);
+	if (!tree)
+		return NULL;
+
+	check_tree(tree);
+	all = calloc(1, sizeof(*all));
+	assert(all);
+	ti = &all->ti;
+	ti->finfo = &all->info;
+	ti->finfo->rep = &all->label;
+	ti->parent = tree;
+	if (tree->first_child) {
+		assert(tree->last_child->next == NULL);
+		tree->last_child->next = ti;
+		tree->last_child = ti;
+	} else {
+		tree->first_child = tree->last_child = ti;
+	}
+	va_start(ap, format);
+	vsnprintf(ti->finfo->rep->representation, sizeof(ti->finfo->rep->representation),
+		format, ap);
+	va_end(ap);
+	check_item(ti);
+	check_tree(tree);
+	return ti;
+}
+
+WS_DLL_PUBLIC proto_item *
+proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb,
+		    const gint start, gint length, const guint encoding)
+{
+	proto_item *ti;
+	header_field_info *hfinfo = NULL;
+	enum ftenum type = FT_NONE;
+	char *label;
+	guint64 uval;
+	gint64  sval;
+	unsigned size;
+	const struct true_false_string *tfs;
+
+	assert(hfindex >= first_hf_registered);
+	assert(hfindex <= last_hf_registered);
+
+	hfindex -= first_hf_registered;
+	assert(hfs && hfindex < hfs->len);
+	hfinfo = g_ptr_array_index(hfs, hfindex);
+
+	ti = proto_tree_add_text(tree, tvb, start, length, "");
+	if (!ti)
+		return NULL;
+
+	/* encoding is just used to read data and put in ti->finfo->rep */
+	if (hfinfo)
+		type = hfinfo->type;
+	label = ti->finfo->rep->representation;
+	switch (type) {
+	case FT_NONE:
+		break;
+	case FT_UINT8:
+		size = 1;
+		goto str_uint;
+	case FT_UINT16:
+		size = 2;
+		goto str_uint;
+	case FT_UINT32:
+		size = 4;
+		goto str_uint;
+	case FT_UINT64:
+		size = 8;
+	str_uint:
+		uval = tvb_get_ule(tvb, start, size);
+		sprintf(label, "%" G_GINT64_MODIFIER "u", uval);
+		break;
+	case FT_INT8:
+		size = 1;
+		goto str_int;
+	case FT_INT16:
+		size = 2;
+		goto str_int;
+	case FT_INT32:
+		size = 4;
+		goto str_int;
+	case FT_INT64:
+		size = 8;
+	str_int:
+		sval = tvb_get_sle(tvb, start, size);
+		sprintf(label, "%" G_GINT64_MODIFIER "d", sval);
+		break;
+	case FT_BOOLEAN:
+		uval = tvb_get_ule(tvb, start, length);
+		uval &= hfinfo->bitmask;
+		assert(hfinfo->strings);
+		tfs = (const struct true_false_string *) hfinfo->strings;
+		strcpy(label, uval ? tfs->true_string : tfs->false_string);
+		break;
+	case FT_STRING:
+		/* TODO read value */
+		assert(0);
+		break;
+	case FT_STRINGZ:
+		/* TODO read value */
+		assert(0);
+		break;
+	case FT_GUID:
+		/* TODO read value */
+		assert(0);
+		break;
+	case FT_BYTES:
+		/* TODO read value */
+		assert(0);
+		break;
+	default:
+		assert(0);
+	}
+	ti->finfo->hfinfo = hfinfo;
+	check_item(ti);
+	check_tree(tree);
+	return ti;
+}
+
+WS_DLL_PUBLIC guint8 tvb_get_guint8(tvbuff_t *tvb, const gint offset)
+{
+	return (guint8) tvb_get_ule(tvb, offset, 1);
+}
+
+WS_DLL_PUBLIC guint16 tvb_get_letohs(tvbuff_t *tvb, const gint offset)
+{
+	return (guint16) tvb_get_ule(tvb, offset, 2);
+}
+
+WS_DLL_PUBLIC guint32 tvb_get_letohl(tvbuff_t *tvb, const gint offset)
+{
+	return (guint32) tvb_get_ule(tvb, offset, 4);
+}
+
+WS_DLL_PUBLIC guint64 tvb_get_letoh64(tvbuff_t *tvb, const gint offset)
+{
+	return tvb_get_ule(tvb, offset, 8);
+}
+
+static int indentation = -1;
+static enum { PLAIN, XML } format = PLAIN;
+static FILE *output_file;
+#define oprintf(...) fprintf(output_file, __VA_ARGS__)
+#define INDENTED(s) "%*s" s, indentation*4, ""
+
+static void format_start(const char *name)
+{
+	++indentation;
+	if (format == XML)
+		oprintf("<%s>\n", name);
+	else
+		oprintf(INDENTED("--- %s\n"), name);
+}
+
+static void format_end(const char *name)
+{
+	if (format == XML)
+		oprintf("</%s>\n", name);
+	--indentation;
+}
+
+static void format_item(const char *name, const char *value)
+{
+	if (format == XML)
+		/* TODO quote value for XML */
+		oprintf("<%s>%s</%s>\n", name, value, name);
+	else
+		oprintf(INDENTED("%s: %s\n"), name, value);
+}
+
+static void dump_tree(proto_tree *tree);
+
+static void dump_item(proto_item *ti)
+{
+	header_field_info *info = NULL;
+	check_item(ti);
+
+	format_start("item");
+	format_item("Text", ti->finfo->rep->representation);
+	info = ti->finfo->hfinfo;
+	if (info) {
+		format_item("Name", info->name);
+		format_item("Abbrev", info->abbrev);
+		if (info->type != FT_NONE)
+			format_item("Type", describe_fttype(info->type));
+		if (info->type == FT_BOOLEAN) {
+			char buf[32];
+			sprintf(buf, "%d", info->display);
+			format_item("Base", buf);
+		} else {
+			if (info->display != BASE_NONE)
+				format_item("Base", describe_base(info->display));
+		}
+	}
+	dump_tree(ti->first_child);
+	format_end("item");
+}
+
+static void dump_tree(proto_tree *tree)
+{
+	proto_item *ti;
+	if (!tree)
+		return;
+
+	check_tree(tree);
+	format_start("tree");
+	for (ti = tree->first_child; ti; ti = ti->next)
+		dump_item(ti);
+	format_end("tree");
+}
+
 static const struct option long_options[] = {
 	{ "help", 0, NULL, 'h' },
 	{ "server", 0, NULL, 's' },
 	{ "client", 0, NULL, 'c' },
+	{ "output-file", required_argument, NULL, 'o' },
+	{ "xml", 0, NULL, 'x' },
 };
-static const char options[] = "hsc";
+static const char options[] = "hscxo:";
 
 static void syntax(FILE *f, int exit_value)
 {
@@ -82,6 +486,8 @@  static void syntax(FILE *f, int exit_value)
 		"  -h, --help               Show this help\n"
 		"  -s, --server             Process server messages (default)\n"
 		"  -c, --client             Process client messages\n"
+		"  -x, --xml                Output in XML format\n"
+		"  -o, --output-file=FILE   Output to specified file\n"
 	);
 	exit(exit_value);
 }
@@ -94,6 +500,7 @@  int main(int argc, char **argv)
 	spice_dissect_func_t (*msg_func)(guint8 channel);
 	spice_dissect_func_t channel_func = NULL;
 
+	output_file = stdout;
 	msg_func = spice_server_channel_get_dissect;
 
 	while (1) {
@@ -112,6 +519,13 @@  int main(int argc, char **argv)
 		case 'c':
 			msg_func = spice_client_channel_get_dissect;
 			break;
+		case 'x':
+			format = XML;
+			break;
+		case 'o':
+			output_file = fopen(optarg, "w");
+			check(output_file != NULL, "Error opening output file");
+			break;
 		default:
 			syntax(stderr, EXIT_FAILURE);
 			break;
@@ -123,6 +537,7 @@  int main(int argc, char **argv)
 	channel      = strtol(argv[optind++], NULL, 0);
 	message_type = strtol(argv[optind++], NULL, 0);
 
+	hfs = g_ptr_array_new_with_free_func(free);
 	spice_register_fields(1, NULL);
 
 	memset(&glb, 0, sizeof(glb));
@@ -134,9 +549,16 @@  int main(int argc, char **argv)
 	assert(channel_func);
 
 	memset(&tree, 0, sizeof(tree));
+	tree.tree_data = (void *) &tree;
+	check_tree(&tree);
 
 	/* TODO check offset ?? */
 	channel_func(message_type, &glb, &tree, 0);
 
+	dump_tree(&tree);
+
+	if (output_file != stdout)
+		fclose(output_file);
+
 	return got_error ? EXIT_FAILURE : EXIT_SUCCESS;
 }