[Spice-devel,v3,15/51] Generate scheleton for messages and channels

Submitted by Frediano Ziglio on July 21, 2015, 4:45 p.m.

Details

Message ID 1437497181-26929-16-git-send-email-fziglio@redhat.com
State New
Headers show

Not browsing as part of any series.

Commit Message

Frediano Ziglio July 21, 2015, 4:45 p.m.
Messages are not generated as stub.
Code partially from demarshaller.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
---
 codegen/Makefile.am         |   3 +-
 codegen/check_dissector     |  13 +++++
 codegen/dissector_test.c    |  54 ++++++++++++++++-
 python_modules/dissector.py | 138 +++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 203 insertions(+), 5 deletions(-)
 create mode 100755 codegen/check_dissector

Patch hide | download patch | download mbox

diff --git a/codegen/Makefile.am b/codegen/Makefile.am
index 129543c..b147b85 100644
--- a/codegen/Makefile.am
+++ b/codegen/Makefile.am
@@ -29,12 +29,13 @@  test.c: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS)
 test.h: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS)
 	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-dissector --client $< --header $@ >/dev/null
 
-TESTS = dissector_test
+TESTS = check_dissector
 check_PROGRAMS = dissector_test
 
 dissector_test_SOURCES = dissector_test.c test.c test.h
 
 EXTRA_DIST =				\
+	check_dissector			\
 	$(NULL)
 
 -include $(top_srcdir)/git.mk
diff --git a/codegen/check_dissector b/codegen/check_dissector
new file mode 100755
index 0000000..f1444a2
--- /dev/null
+++ b/codegen/check_dissector
@@ -0,0 +1,13 @@ 
+#!/bin/bash
+set -e
+
+error() {
+	echo "$*" >&2
+	exit 1
+}
+
+./dissector_test 1 100
+if ./dissector_test 1 99; then
+	error "This test should fail"
+fi
+exit 0
diff --git a/codegen/dissector_test.c b/codegen/dissector_test.c
index 3212655..25a33b5 100644
--- a/codegen/dissector_test.c
+++ b/codegen/dissector_test.c
@@ -4,6 +4,7 @@ 
 #include <stdlib.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <stdbool.h>
 #include <assert.h>
 
 #include <epan/expert.h>
@@ -18,6 +19,7 @@  enum {
 static int last_hf_registered = first_hf_registered - 1;
 static int last_ei_registered = first_ei_registered - 1;
 static int last_tree_registered = first_tree_registered - 1;
+static bool got_error = false;
 
 WS_DLL_PUBLIC void
 proto_register_field_array(const int parent, hf_register_info *hf, const int num_records)
@@ -54,24 +56,46 @@  expert_register_field_array(expert_module_t* module, ei_register_info *ei, const
 	}
 }
 
+WS_DLL_PUBLIC void
+expert_add_info_format(packet_info *pinfo, proto_item *pi, expert_field *eiindex,
+	const char *format, ...)
+{
+	assert(format);
+	got_error = true;
+	if (!pi)
+		return;
+}
+
 static const struct option long_options[] = {
 	{ "help", 0, NULL, 'h' },
+	{ "server", 0, NULL, 's' },
+	{ "client", 0, NULL, 'c' },
 };
-static const char options[] = "h";
+static const char options[] = "hsc";
 
 static void syntax(FILE *f, int exit_value)
 {
 	fprintf(f,
-		"Usage: dissector_test [OPTIONS]\n"
+		"Usage: dissector_test [OPTIONS] CHANNEL MESSAGE_TYPE\n"
 		"\n"
 		"Options:\n"
 		"  -h, --help               Show this help\n"
+		"  -s, --server             Process server messages (default)\n"
+		"  -c, --client             Process client messages\n"
 	);
 	exit(exit_value);
 }
 
 int main(int argc, char **argv)
 {
+	int channel, message_type;
+	GlobalInfo glb;
+	proto_tree tree;
+	spice_dissect_func_t (*msg_func)(guint8 channel);
+	spice_dissect_func_t channel_func = NULL;
+
+	msg_func = spice_server_channel_get_dissect;
+
 	while (1) {
 		int option_index;
 		int opt = getopt_long(argc, argv, options, long_options, &option_index);
@@ -82,13 +106,37 @@  int main(int argc, char **argv)
 		case 'h':
 			syntax(stdout, EXIT_SUCCESS);
 			break;
+		case 's':
+			msg_func = spice_server_channel_get_dissect;
+			break;
+		case 'c':
+			msg_func = spice_client_channel_get_dissect;
+			break;
 		default:
 			syntax(stderr, EXIT_FAILURE);
 			break;
 		}
 	}
+	if (optind + 2 > argc)
+		syntax(stderr, EXIT_FAILURE);
+
+	channel      = strtol(argv[optind++], NULL, 0);
+	message_type = strtol(argv[optind++], NULL, 0);
 
 	spice_register_fields(1, NULL);
 
-	return EXIT_SUCCESS;
+	memset(&glb, 0, sizeof(glb));
+	glb.tvb = NULL;
+	glb.message_offset = 0;
+	glb.message_end = 0;
+
+	channel_func = msg_func(channel);
+	assert(channel_func);
+
+	memset(&tree, 0, sizeof(tree));
+
+	/* TODO check offset ?? */
+	channel_func(message_type, &glb, &tree, 0);
+
+	return got_error ? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/python_modules/dissector.py b/python_modules/dissector.py
index f9ad08a..af0494c 100644
--- a/python_modules/dissector.py
+++ b/python_modules/dissector.py
@@ -1,4 +1,5 @@ 
 
+from . import ptypes
 from . import codegen
 import re
 
@@ -65,6 +66,124 @@  def write_parser_helpers(writer):
     writer.writeln('#endif')
     writer.newline()
 
+def write_msg_parser(writer, message, server):
+    msg_name = message.c_name()
+    function_name = "dissect_spice_%s_%s" % ('server' if server else 'client', msg_name)
+    if writer.is_generated("msg_parser", function_name):
+        return function_name
+    writer.set_is_generated("msg_parser", function_name)
+
+    msg_type = message.c_type()
+    msg_sizeof = message.sizeof()
+
+    writer.newline()
+    writer.ifdef(message)
+    parent_scope = writer.function(function_name,
+                                   "guint32",
+                                   "GlobalInfo *glb _U_, proto_tree *tree0 _U_, guint32 offset", True)
+
+    writer.statement("return offset")
+    writer.end_block()
+
+    writer.endif(message)
+
+    return function_name
+
+def write_channel_parser(writer, channel, server):
+    writer.newline()
+    ids = {}
+    if server:
+        messages = channel.server_messages
+    else:
+        messages = channel.client_messages
+    for m in messages:
+        ids[m.value] = m
+
+    ranges = []
+    ids2 = ids.copy()
+    while len(ids2) > 0:
+        end = start = min(ids2.keys())
+        while end in ids2:
+            del ids2[end]
+            end = end + 1
+
+        ranges.append( (start, end) )
+
+    if server:
+        function_name = "dissect_spice_data_server_%s" % channel.name
+    else:
+        function_name = "dissect_spice_data_client_%s" % channel.name
+    writer.newline()
+    writer.ifdef(channel)
+    scope = writer.function(function_name,
+                            "guint32",
+                            "guint16 message_type, GlobalInfo *glb _U_, proto_tree *tree _U_, guint32 offset", True)
+
+    helpers = writer.function_helper()
+
+    d = 0
+    for r in ranges:
+        d = d + 1
+        writer.write("static const parse_msg_func_t funcs%d[%d] = " % (d, r[1] - r[0]))
+        writer.begin_block()
+        for i in range(r[0], r[1]):
+            func = write_msg_parser(helpers, ids[i].message_type, server)
+            writer.write(func)
+            if i != r[1] -1:
+                writer.write(",")
+            writer.newline()
+
+        writer.end_block(semicolon = True)
+
+    d = 0
+    for r in ranges:
+        d = d + 1
+        with writer.if_block("message_type >= %d && message_type < %d" % (r[0], r[1]), d > 1, False):
+            writer.statement("return funcs%d[message_type-%d](glb, tree, offset)" % (d, r[0]))
+    writer.newline()
+
+    writer.statement('expert_add_info_format(glb->pinfo, glb->msgtype_item, &ei_spice_unknown_message, "Unknown display server message - cannot dissect")')
+    writer.statement("return offset")
+    writer.end_block()
+    writer.endif(channel)
+
+    return function_name
+
+def write_get_channel_parser(writer, channel_parsers, max_channel, is_server):
+    writer.newline()
+    function_name = "spice_%s%s_channel_get_dissect" % ('server' if is_server else 'client', writer.public_prefix)
+
+    writer.function(function_name, "spice_dissect_func_t", "guint8 channel")
+
+    writer.write("static const spice_dissect_func_t channels[%d] = " % (max_channel+1))
+    writer.begin_block()
+    channel = None
+    for i in range(0, max_channel + 1):
+        if i in channel_parsers:
+            channel = channel_parsers[i][0]
+            writer.ifdef(channel)
+            writer.write(channel_parsers[i][1])
+        else:
+            writer.write("NULL")
+
+        if i != max_channel:
+            writer.write(",")
+        writer.newline()
+        if channel and channel.has_attr("ifdef"):
+            writer.ifdef_else(channel)
+            writer.write("NULL")
+            if i != max_channel:
+                writer.write(",")
+            writer.newline()
+            writer.endif(channel)
+    writer.end_block(semicolon = True)
+
+    with writer.if_block("channel < %d" % (max_channel + 1)):
+        writer.statement("return channels[channel]")
+
+    writer.statement("return NULL")
+    writer.end_block()
+
 
 def write_protocol_definitions(writer):
     global hf_defs
@@ -125,8 +244,25 @@  def write_protocol_parser(writer, proto):
         writer.newline()
     hf_writer = writer.get_subwriter()
 
+    # put everything else after
+    defs_writer = writer
+    writer = writer.get_subwriter()
+
     # put fields definition at last
-    write_protocol_definitions(writer)
+    write_protocol_definitions(defs_writer)
+
+    # scan all, looks for items and tree
+    # list add items and tree
+    # write all structure functions (get size + tree)
+    for is_server in (True, False):
+        max_channel = 0
+        parsers = {}
+        for channel in proto.channels:
+            max_channel = max(max_channel, channel.value)
+
+            parsers[channel.value] = (channel.channel_type, write_channel_parser(writer, channel.channel_type, is_server))
+
+        write_get_channel_parser(writer, parsers, max_channel, is_server)
 
 def write_includes(writer, dest_file):
     header_name = re.sub(r'\.(c|cpp|cc|cxx|C)$', '.h', dest_file)

Comments

'skeleton' in the subject.

On Tue, Jul 21, 2015 at 05:45:45PM +0100, Frediano Ziglio wrote:
> Messages are not generated as stub.
> Code partially from demarshaller.
> 
> Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
> ---
>  codegen/Makefile.am         |   3 +-
>  codegen/check_dissector     |  13 +++++
>  codegen/dissector_test.c    |  54 ++++++++++++++++-
>  python_modules/dissector.py | 138 +++++++++++++++++++++++++++++++++++++++++++-
>  4 files changed, 203 insertions(+), 5 deletions(-)
>  create mode 100755 codegen/check_dissector
> 
> diff --git a/codegen/Makefile.am b/codegen/Makefile.am
> index 129543c..b147b85 100644
> --- a/codegen/Makefile.am
> +++ b/codegen/Makefile.am
> @@ -29,12 +29,13 @@ test.c: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS)
>  test.h: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS)
>  	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-dissector --client $< --header $@ >/dev/null
>  
> -TESTS = dissector_test
> +TESTS = check_dissector
>  check_PROGRAMS = dissector_test
>  
>  dissector_test_SOURCES = dissector_test.c test.c test.h
>  
>  EXTRA_DIST =				\
> +	check_dissector			\
>  	$(NULL)
>  
>  -include $(top_srcdir)/git.mk
> diff --git a/codegen/check_dissector b/codegen/check_dissector
> new file mode 100755
> index 0000000..f1444a2
> --- /dev/null
> +++ b/codegen/check_dissector
> @@ -0,0 +1,13 @@
> +#!/bin/bash
> +set -e
> +
> +error() {
> +	echo "$*" >&2
> +	exit 1
> +}
> +
> +./dissector_test 1 100
> +if ./dissector_test 1 99; then
> +	error "This test should fail"
> +fi
> +exit 0
> diff --git a/codegen/dissector_test.c b/codegen/dissector_test.c
> index 3212655..25a33b5 100644
> --- a/codegen/dissector_test.c
> +++ b/codegen/dissector_test.c
> @@ -4,6 +4,7 @@
>  #include <stdlib.h>
>  #include <unistd.h>
>  #include <getopt.h>
> +#include <stdbool.h>
>  #include <assert.h>
>  
>  #include <epan/expert.h>
> @@ -18,6 +19,7 @@ enum {
>  static int last_hf_registered = first_hf_registered - 1;
>  static int last_ei_registered = first_ei_registered - 1;
>  static int last_tree_registered = first_tree_registered - 1;
> +static bool got_error = false;
>  
>  WS_DLL_PUBLIC void
>  proto_register_field_array(const int parent, hf_register_info *hf, const int num_records)
> @@ -54,24 +56,46 @@ expert_register_field_array(expert_module_t* module, ei_register_info *ei, const
>  	}
>  }
>  
> +WS_DLL_PUBLIC void
> +expert_add_info_format(packet_info *pinfo, proto_item *pi, expert_field *eiindex,
> +	const char *format, ...)
> +{
> +	assert(format);
> +	got_error = true;
> +	if (!pi)
> +		return;
> +}
> +
>  static const struct option long_options[] = {
>  	{ "help", 0, NULL, 'h' },
> +	{ "server", 0, NULL, 's' },
> +	{ "client", 0, NULL, 'c' },
>  };
> -static const char options[] = "h";
> +static const char options[] = "hsc";
>  
>  static void syntax(FILE *f, int exit_value)
>  {
>  	fprintf(f,
> -		"Usage: dissector_test [OPTIONS]\n"
> +		"Usage: dissector_test [OPTIONS] CHANNEL MESSAGE_TYPE\n"
>  		"\n"
>  		"Options:\n"
>  		"  -h, --help               Show this help\n"
> +		"  -s, --server             Process server messages (default)\n"
> +		"  -c, --client             Process client messages\n"
>  	);
>  	exit(exit_value);
>  }
>  
>  int main(int argc, char **argv)
>  {
> +	int channel, message_type;
> +	GlobalInfo glb;
> +	proto_tree tree;
> +	spice_dissect_func_t (*msg_func)(guint8 channel);
> +	spice_dissect_func_t channel_func = NULL;
> +
> +	msg_func = spice_server_channel_get_dissect;
> +
>  	while (1) {
>  		int option_index;
>  		int opt = getopt_long(argc, argv, options, long_options, &option_index);
> @@ -82,13 +106,37 @@ int main(int argc, char **argv)
>  		case 'h':
>  			syntax(stdout, EXIT_SUCCESS);
>  			break;
> +		case 's':
> +			msg_func = spice_server_channel_get_dissect;
> +			break;
> +		case 'c':
> +			msg_func = spice_client_channel_get_dissect;
> +			break;
>  		default:
>  			syntax(stderr, EXIT_FAILURE);
>  			break;
>  		}
>  	}
> +	if (optind + 2 > argc)
> +		syntax(stderr, EXIT_FAILURE);
> +
> +	channel      = strtol(argv[optind++], NULL, 0);
> +	message_type = strtol(argv[optind++], NULL, 0);
>  
>  	spice_register_fields(1, NULL);
>  
> -	return EXIT_SUCCESS;
> +	memset(&glb, 0, sizeof(glb));
> +	glb.tvb = NULL;
> +	glb.message_offset = 0;
> +	glb.message_end = 0;
> +
> +	channel_func = msg_func(channel);
> +	assert(channel_func);
> +
> +	memset(&tree, 0, sizeof(tree));
> +
> +	/* TODO check offset ?? */
> +	channel_func(message_type, &glb, &tree, 0);
> +
> +	return got_error ? EXIT_FAILURE : EXIT_SUCCESS;
>  }
> diff --git a/python_modules/dissector.py b/python_modules/dissector.py
> index f9ad08a..af0494c 100644
> --- a/python_modules/dissector.py
> +++ b/python_modules/dissector.py
> @@ -1,4 +1,5 @@
>  
> +from . import ptypes
>  from . import codegen
>  import re
>  
> @@ -65,6 +66,124 @@ def write_parser_helpers(writer):
>      writer.writeln('#endif')
>      writer.newline()
>  
> +def write_msg_parser(writer, message, server):
> +    msg_name = message.c_name()
> +    function_name = "dissect_spice_%s_%s" % ('server' if server else 'client', msg_name)
> +    if writer.is_generated("msg_parser", function_name):
> +        return function_name
> +    writer.set_is_generated("msg_parser", function_name)
> +
> +    msg_type = message.c_type()
> +    msg_sizeof = message.sizeof()
> +
> +    writer.newline()
> +    writer.ifdef(message)
> +    parent_scope = writer.function(function_name,
> +                                   "guint32",
> +                                   "GlobalInfo *glb _U_, proto_tree *tree0 _U_, guint32 offset", True)
> +
> +    writer.statement("return offset")
> +    writer.end_block()
> +
> +    writer.endif(message)
> +
> +    return function_name
> +
> +def write_channel_parser(writer, channel, server):
> +    writer.newline()
> +    ids = {}
> +    if server:
> +        messages = channel.server_messages
> +    else:
> +        messages = channel.client_messages
> +    for m in messages:
> +        ids[m.value] = m
> +
> +    ranges = []
> +    ids2 = ids.copy()
> +    while len(ids2) > 0:
> +        end = start = min(ids2.keys())
> +        while end in ids2:
> +            del ids2[end]
> +            end = end + 1
> +
> +        ranges.append( (start, end) )
> +
> +    if server:
> +        function_name = "dissect_spice_data_server_%s" % channel.name
> +    else:
> +        function_name = "dissect_spice_data_client_%s" % channel.name
> +    writer.newline()
> +    writer.ifdef(channel)
> +    scope = writer.function(function_name,
> +                            "guint32",
> +                            "guint16 message_type, GlobalInfo *glb _U_, proto_tree *tree _U_, guint32 offset", True)
> +
> +    helpers = writer.function_helper()
> +
> +    d = 0
> +    for r in ranges:
> +        d = d + 1
> +        writer.write("static const parse_msg_func_t funcs%d[%d] = " % (d, r[1] - r[0]))
> +        writer.begin_block()
> +        for i in range(r[0], r[1]):
> +            func = write_msg_parser(helpers, ids[i].message_type, server)
> +            writer.write(func)
> +            if i != r[1] -1:
> +                writer.write(",")
> +            writer.newline()
> +
> +        writer.end_block(semicolon = True)
> +
> +    d = 0
> +    for r in ranges:
> +        d = d + 1
> +        with writer.if_block("message_type >= %d && message_type < %d" % (r[0], r[1]), d > 1, False):
> +            writer.statement("return funcs%d[message_type-%d](glb, tree, offset)" % (d, r[0]))
> +    writer.newline()
> +
> +    writer.statement('expert_add_info_format(glb->pinfo, glb->msgtype_item, &ei_spice_unknown_message, "Unknown display server message - cannot dissect")')
> +    writer.statement("return offset")
> +    writer.end_block()
> +    writer.endif(channel)
> +
> +    return function_name
> +
> +def write_get_channel_parser(writer, channel_parsers, max_channel, is_server):
> +    writer.newline()
> +    function_name = "spice_%s%s_channel_get_dissect" % ('server' if is_server else 'client', writer.public_prefix)
> +
> +    writer.function(function_name, "spice_dissect_func_t", "guint8 channel")
> +
> +    writer.write("static const spice_dissect_func_t channels[%d] = " % (max_channel+1))
> +    writer.begin_block()
> +    channel = None
> +    for i in range(0, max_channel + 1):
> +        if i in channel_parsers:
> +            channel = channel_parsers[i][0]
> +            writer.ifdef(channel)
> +            writer.write(channel_parsers[i][1])
> +        else:
> +            writer.write("NULL")
> +
> +        if i != max_channel:
> +            writer.write(",")
> +        writer.newline()
> +        if channel and channel.has_attr("ifdef"):
> +            writer.ifdef_else(channel)
> +            writer.write("NULL")
> +            if i != max_channel:
> +                writer.write(",")
> +            writer.newline()
> +            writer.endif(channel)
> +    writer.end_block(semicolon = True)
> +
> +    with writer.if_block("channel < %d" % (max_channel + 1)):
> +        writer.statement("return channels[channel]")
> +
> +    writer.statement("return NULL")
> +    writer.end_block()
> +
>  
>  def write_protocol_definitions(writer):
>      global hf_defs
> @@ -125,8 +244,25 @@ def write_protocol_parser(writer, proto):
>          writer.newline()
>      hf_writer = writer.get_subwriter()
>  
> +    # put everything else after
> +    defs_writer = writer
> +    writer = writer.get_subwriter()
> +
>      # put fields definition at last
> -    write_protocol_definitions(writer)
> +    write_protocol_definitions(defs_writer)
> +
> +    # scan all, looks for items and tree
> +    # list add items and tree
> +    # write all structure functions (get size + tree)
> +    for is_server in (True, False):
> +        max_channel = 0
> +        parsers = {}
> +        for channel in proto.channels:
> +            max_channel = max(max_channel, channel.value)
> +
> +            parsers[channel.value] = (channel.channel_type, write_channel_parser(writer, channel.channel_type, is_server))
> +
> +        write_get_channel_parser(writer, parsers, max_channel, is_server)
>  
>  def write_includes(writer, dest_file):
>      header_name = re.sub(r'\.(c|cpp|cc|cxx|C)$', '.h', dest_file)
> -- 
> 2.1.0
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel