[Spice-devel] [PATCH v6 16/42] dissector: Allows to write items to tree and dump saved tree
Frediano Ziglio
fziglio at redhat.com
Thu Aug 13 06:11:55 PDT 2015
Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
---
tests/dissector_test.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 423 insertions(+), 1 deletion(-)
diff --git a/tests/dissector_test.c b/tests/dissector_test.c
index 25a33b5..5a49f40 100644
--- a/tests/dissector_test.c
+++ b/tests/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;
}
--
2.4.3
More information about the Spice-devel
mailing list