From 3ea2a61f2a1f8d621c423236ca46cc388c079a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Valverde?= Date: Sun, 26 Sep 2021 16:28:39 +0100 Subject: [PATCH] dfilter: Display syntax tree for debugging Use wslog to output debug information. Being able to control it at runtime is a big advantage. We extend the syntax tree nodes with a method to return a canonical string representation. Add a routine to walk the tree and return an textual representation for debugging purposes. --- epan/dfilter/dfilter-int.h | 2 + epan/dfilter/dfilter.c | 46 ++++++++++++- epan/dfilter/semcheck.c | 74 ++++++++++---------- epan/dfilter/sttype-function.c | 18 ++++- epan/dfilter/sttype-integer.c | 1 + epan/dfilter/sttype-pointer.c | 38 ++++++++++- epan/dfilter/sttype-range.c | 3 +- epan/dfilter/sttype-set.c | 1 + epan/dfilter/sttype-string.c | 15 ++++- epan/dfilter/sttype-test.c | 63 ++++++++++++++++- epan/dfilter/syntax-tree.c | 119 +++++++++++++++++++++++++++++++++ epan/dfilter/syntax-tree.h | 26 ++++++- ws_log_defs.h | 2 + wsutil/str_util.h | 2 + 14 files changed, 358 insertions(+), 52 deletions(-) diff --git a/epan/dfilter/dfilter-int.h b/epan/dfilter/dfilter-int.h index 648892aed6..7124b2159f 100644 --- a/epan/dfilter/dfilter-int.h +++ b/epan/dfilter/dfilter-int.h @@ -73,4 +73,6 @@ dfilter_fail(dfwork_t *dfw, const char *format, ...) G_GNUC_PRINTF(2, 3); void DfilterTrace(FILE *TraceFILE, char *zTracePrompt); +const char *tokenstr(int token); + #endif diff --git a/epan/dfilter/dfilter.c b/epan/dfilter/dfilter.c index 641d0ff7ed..450ab77dac 100644 --- a/epan/dfilter/dfilter.c +++ b/epan/dfilter/dfilter.c @@ -7,7 +7,7 @@ */ #include "config.h" -#define WS_LOG_DOMAIN "Dfilter" +#define WS_LOG_DOMAIN LOG_DOMAIN_DFILTER #include #include @@ -23,6 +23,7 @@ #include "scanner_lex.h" #include #include +#include "grammar.h" #define DFILTER_TOKEN_ID_OFFSET 1 @@ -197,6 +198,44 @@ dfwork_free(dfwork_t *dfw) g_free(dfw); } +const char *tokenstr(int token) +{ + switch (token) { + case TOKEN_TEST_AND: return "TEST_AND"; + case TOKEN_TEST_OR: return "TEST_OR"; + case TOKEN_TEST_EQ: return "TEST_EQ"; + case TOKEN_TEST_NE: return "TEST_NE"; + case TOKEN_TEST_LT: return "TEST_LT"; + case TOKEN_TEST_LE: return "TEST_LE"; + case TOKEN_TEST_GT: return "TEST_GT"; + case TOKEN_TEST_GE: return "TEST_GE"; + case TOKEN_TEST_CONTAINS: return "TEST_CONTAINS"; + case TOKEN_TEST_MATCHES: return "TEST_MATCHES"; + case TOKEN_TEST_BITWISE_AND: return "TEST_BITWISE_AND"; + case TOKEN_TEST_NOT: return "TEST_NOT"; + case TOKEN_FIELD: return "FIELD"; + case TOKEN_STRING: return "STRING"; + case TOKEN_CHARCONST: return "CHARCONST"; + case TOKEN_UNPARSED: return "UNPARSED"; + case TOKEN_LBRACKET: return "LBRACKET"; + case TOKEN_RBRACKET: return "RBRACKET"; + case TOKEN_COMMA: return "COMMA"; + case TOKEN_INTEGER: return "INTEGER"; + case TOKEN_COLON: return "COLON"; + case TOKEN_HYPHEN: return "HYPHEN"; + case TOKEN_TEST_IN: return "TEST_IN"; + case TOKEN_LBRACE: return "LBRACE"; + case TOKEN_RBRACE: return "RBRACE"; + case TOKEN_WHITESPACE: return "WHITESPACE"; + case TOKEN_DOTDOT: return "DOTDOT"; + case TOKEN_FUNCTION: return "FUNCTION"; + case TOKEN_LPAREN: return "LPAREN"; + case TOKEN_RPAREN: return "RPAREN"; + default: return ""; + } + ws_assert_not_reached(); +} + gboolean dfilter_compile(const gchar *text, dfilter_t **dfp, gchar **err_msg) { @@ -280,6 +319,8 @@ dfilter_compile(const gchar *text, dfilter_t **dfp, gchar **err_msg) g_ptr_array_add(deprecated, g_strdup(depr_test)); } + ws_debug("Token: %d %s", token, tokenstr(token)); + /* Give the token to the parser */ Dfilter(ParserObj, token, df_lval, dfw); /* We've used the stnode_t, so we don't want to free it */ @@ -331,12 +372,15 @@ dfilter_compile(const gchar *text, dfilter_t **dfp, gchar **err_msg) g_ptr_array_free(deprecated, TRUE); } else { + log_syntax_tree(LOG_LEVEL_NOISY, dfw->st_root, "Syntax tree before semantic check"); /* Check semantics and do necessary type conversion*/ if (!dfw_semcheck(dfw, deprecated)) { goto FAILURE; } + log_syntax_tree(LOG_LEVEL_NOISY, dfw->st_root, "Syntax tree after successful semantic check"); + /* Create bytecode */ dfw_gencode(dfw); diff --git a/epan/dfilter/semcheck.c b/epan/dfilter/semcheck.c index fc3ebcfcc0..36595c1f8f 100644 --- a/epan/dfilter/semcheck.c +++ b/epan/dfilter/semcheck.c @@ -8,6 +8,8 @@ #include "config.h" +#define WS_LOG_DOMAIN LOG_DOMAIN_DFILTER + #include #include "dfilter-int.h" @@ -22,21 +24,10 @@ #include #include +#include #include -/* Enable debug logging by defining AM_CFLAGS - * so that it contains "-DDEBUG_dfilter". - * Usage: DebugLog(("Error: string=%s\n", str)); */ - -#ifdef DEBUG_dfilter -#define DebugLog(x) \ - printf("%s:%u: ", __FILE__, (unsigned int)__LINE__); \ - printf x; \ - fflush(stdout) -#else -#define DebugLog(x) ; -#endif static void semcheck(dfwork_t *dfw, stnode_t *st_node, GPtrArray *deprecated); @@ -411,7 +402,7 @@ dfilter_g_regex_from_string(dfwork_t *dfw, const char *s) */ cflags = (GRegexCompileFlags)(cflags | G_REGEX_RAW); - DebugLog(("Compile regex pattern: '%s'\n", s)); + ws_debug("Compile regex pattern: %s", s); pcre = g_regex_new( s, /* pattern */ @@ -436,11 +427,13 @@ dfilter_g_regex_from_string(dfwork_t *dfw, const char *s) static void check_exists(dfwork_t *dfw, stnode_t *st_arg1) { -#ifdef DEBUG_dfilter +#ifndef WS_DISABLE_DEBUG static guint i = 0; #endif - DebugLog((" 4 check_exists() [%u]\n", i++)); + ws_debug("4 check_exists() [%u]", i++); + stnode_log(st_arg1); + switch (stnode_type_id(st_arg1)) { case STTYPE_FIELD: /* This is OK */ @@ -683,9 +676,9 @@ check_relation_LHS_FIELD(dfwork_t *dfw, const char *relation_string, ftype1 = hfinfo1->type; if (stnode_type_id(st_node) == STTYPE_TEST) { - DebugLog((" 5 check_relation_LHS_FIELD(%s)\n", relation_string)); + ws_debug("5 check_relation_LHS_FIELD(%s)", relation_string); } else { - DebugLog((" 6 check_relation_LHS_FIELD(%s)\n", relation_string)); + ws_debug("6 check_relation_LHS_FIELD(%s)", relation_string); } if (!can_func(ftype1)) { @@ -871,7 +864,7 @@ check_relation_LHS_STRING(dfwork_t *dfw, const char* relation_string, type2 = stnode_type_id(st_arg2); - DebugLog((" 5 check_relation_LHS_STRING()\n")); + ws_debug("5 check_relation_LHS_STRING()"); if (type2 == STTYPE_FIELD) { hfinfo2 = (header_field_info*)stnode_data(st_arg2); @@ -965,7 +958,7 @@ check_relation_LHS_UNPARSED(dfwork_t *dfw, const char* relation_string, type2 = stnode_type_id(st_arg2); - DebugLog((" 5 check_relation_LHS_UNPARSED()\n")); + ws_debug("5 check_relation_LHS_UNPARSED()"); if (type2 == STTYPE_FIELD) { hfinfo2 = (header_field_info*)stnode_data(st_arg2); @@ -1060,7 +1053,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string, char *s; int len_range; - DebugLog((" 5 check_relation_LHS_RANGE(%s)\n", relation_string)); + ws_debug("5 check_relation_LHS_RANGE(%s)", relation_string); type2 = stnode_type_id(st_arg2); entity1 = sttype_range_entity(st_arg1); @@ -1098,7 +1091,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string, check_drange_sanity(dfw, st_arg1); if (type2 == STTYPE_FIELD) { - DebugLog((" 5 check_relation_LHS_RANGE(type2 = STTYPE_FIELD)\n")); + ws_debug("5 check_relation_LHS_RANGE(type2 = STTYPE_FIELD)"); hfinfo2 = (header_field_info*)stnode_data(st_arg2); ftype2 = hfinfo2->type; @@ -1117,7 +1110,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string, } } else if (type2 == STTYPE_STRING) { - DebugLog((" 5 check_relation_LHS_RANGE(type2 = STTYPE_STRING)\n")); + ws_debug("5 check_relation_LHS_RANGE(type2 = STTYPE_STRING)"); s = (char*)stnode_data(st_arg2); if (strcmp(relation_string, "matches") == 0) { /* Convert to a GRegex * */ @@ -1129,7 +1122,6 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string, } else { fvalue = dfilter_fvalue_from_string(dfw, FT_BYTES, s); if (!fvalue) { - DebugLog((" 5 check_relation_LHS_RANGE(type2 = STTYPE_STRING): Could not convert from string!\n")); THROW(TypeError); } new_st = stnode_new(STTYPE_FVALUE, fvalue); @@ -1138,7 +1130,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string, stnode_free(st_arg2); } else if (type2 == STTYPE_UNPARSED) { - DebugLog((" 5 check_relation_LHS_RANGE(type2 = STTYPE_UNPARSED)\n")); + ws_debug("5 check_relation_LHS_RANGE(type2 = STTYPE_UNPARSED)"); s = (char*)stnode_data(st_arg2); len_range = drange_get_total_length(sttype_range_drange(st_arg1)); if (strcmp(relation_string, "matches") == 0) { @@ -1182,7 +1174,6 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string, fvalue = dfilter_fvalue_from_unparsed(dfw, FT_BYTES, s, allow_partial_value); } if (!fvalue) { - DebugLog((" 5 check_relation_LHS_RANGE(type2 = STTYPE_UNPARSED): Could not convert from string!\n")); THROW(TypeError); } new_st = stnode_new(STTYPE_FVALUE, fvalue); @@ -1191,7 +1182,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string, stnode_free(st_arg2); } else if (type2 == STTYPE_CHARCONST) { - DebugLog((" 5 check_relation_LHS_RANGE(type2 = STTYPE_CHARCONST)\n")); + ws_debug("5 check_relation_LHS_RANGE(type2 = STTYPE_CHARCONST)"); s = (char*)stnode_data(st_arg2); if (strcmp(relation_string, "matches") == 0) { /* Convert to a GRegex */ @@ -1205,7 +1196,6 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string, * one-byte byte string. */ fvalue = dfilter_fvalue_from_charconst_string(dfw, FT_BYTES, s, allow_partial_value); if (!fvalue) { - DebugLog((" 5 check_relation_LHS_RANGE(type2 = STTYPE_UNPARSED): Could not convert from string!\n")); THROW(TypeError); } new_st = stnode_new(STTYPE_FVALUE, fvalue); @@ -1214,7 +1204,7 @@ check_relation_LHS_RANGE(dfwork_t *dfw, const char *relation_string, stnode_free(st_arg2); } else if (type2 == STTYPE_RANGE) { - DebugLog((" 5 check_relation_LHS_RANGE(type2 = STTYPE_RANGE)\n")); + ws_debug("5 check_relation_LHS_RANGE(type2 = STTYPE_RANGE)"); check_drange_sanity(dfw, st_arg2); } else if (type2 == STTYPE_FUNCTION) { @@ -1298,7 +1288,7 @@ check_relation_LHS_FUNCTION(dfwork_t *dfw, const char *relation_string, /* params = */sttype_function_params(st_arg1); /* XXX: is this done for the side-effect ? */ - DebugLog((" 5 check_relation_LHS_FUNCTION(%s)\n", relation_string)); + ws_debug("5 check_relation_LHS_FUNCTION(%s)", relation_string); if (!can_func(ftype1)) { dfilter_fail(dfw, "Function %s (type=%s) cannot participate in '%s' comparison.", @@ -1415,14 +1405,16 @@ check_relation(dfwork_t *dfw, const char *relation_string, FtypeCanFunc can_func, stnode_t *st_node, stnode_t *st_arg1, stnode_t *st_arg2) { -#ifdef DEBUG_dfilter +#ifndef WS_DISABLE_DEBUG static guint i = 0; #endif header_field_info *hfinfo; stnode_t *new_st; char *s; - DebugLog((" 4 check_relation(\"%s\") [%u]\n", relation_string, i++)); + ws_debug("4 check_relation(\"%s\") [%u]", relation_string, i++); + stnode_log(st_arg1); + stnode_log(st_arg2); /* Protocol can only be on LHS (for "contains" or "matches" operators). * Check to see if protocol is on RHS, and re-interpret it as UNPARSED @@ -1504,11 +1496,12 @@ check_test(dfwork_t *dfw, stnode_t *st_node, GPtrArray *deprecated) { test_op_t st_op, st_arg_op; stnode_t *st_arg1, *st_arg2; -#ifdef DEBUG_dfilter +#ifndef WS_DISABLE_DEBUG static guint i = 0; #endif - DebugLog((" 3 check_test(stnode_t *st_node = %p) [%u]\n", st_node, i)); + ws_debug("3 check_test(stnode_t *st_node = %p) [%u]\n", st_node, i++); + stnode_log(st_node); sttype_test_get(st_node, &st_op, &st_arg1, &st_arg2); @@ -1583,7 +1576,6 @@ check_test(dfwork_t *dfw, stnode_t *st_node, GPtrArray *deprecated) default: ws_assert_not_reached(); } - DebugLog((" 3 check_test(stnode_t *st_node = %p) [%u] - End\n", st_node, i++)); } @@ -1591,10 +1583,11 @@ check_test(dfwork_t *dfw, stnode_t *st_node, GPtrArray *deprecated) static void semcheck(dfwork_t *dfw, stnode_t *st_node, GPtrArray *deprecated) { -#ifdef DEBUG_dfilter +#ifndef WS_DISABLE_DEBUG static guint i = 0; #endif - DebugLog((" 2 semcheck(stnode_t *st_node = %p) [%u]\n", st_node, i++)); + ws_debug("2 semcheck(stnode_t *st_node = %p) [%u]", st_node, i++); + /* The parser assures that the top-most syntax-tree * node will be a TEST node, no matter what. So assert that. */ switch (stnode_type_id(st_node)) { @@ -1614,11 +1607,12 @@ gboolean dfw_semcheck(dfwork_t *dfw, GPtrArray *deprecated) { volatile gboolean ok_filter = TRUE; -#ifdef DEBUG_dfilter +#ifndef WS_DISABLE_DEBUG static guint i = 0; #endif - DebugLog(("1 dfw_semcheck(dfwork_t *dfw = %p) [%u]\n", dfw, i)); + ws_debug("1 dfw_semcheck(dfwork_t *dfw = %p) [%u]", dfw, i); + /* Instead of having to check for errors at every stage of * the semantic-checking, the semantic-checking code will * throw an exception if a problem is found. */ @@ -1630,8 +1624,8 @@ dfw_semcheck(dfwork_t *dfw, GPtrArray *deprecated) } ENDTRY; - DebugLog(("1 dfw_semcheck(dfwork_t *dfw = %p) [%u] - Returns %d\n", - dfw, i++,ok_filter)); + ws_debug("1 dfw_semcheck(dfwork_t *dfw = %p) [%u] - Returns %d", + dfw, i++, ok_filter); return ok_filter; } diff --git a/epan/dfilter/sttype-function.c b/epan/dfilter/sttype-function.c index 0d0c3ee404..4bc3825239 100644 --- a/epan/dfilter/sttype-function.c +++ b/epan/dfilter/sttype-function.c @@ -52,6 +52,21 @@ function_dup(gconstpointer data) return (gpointer) stfuncrec; } +static char * +function_tostr(const void *data) +{ + const function_t *stfuncrec = (const function_t *)data; + const df_func_def_t *def = stfuncrec->funcdef; + guint args_len = 0; + + ws_assert(def); + + if (stfuncrec->params != NULL) + args_len = g_slist_length(stfuncrec->params); + + return g_strdup_printf("%s(n = %u)", def->name, args_len); +} + static void slist_stnode_free(gpointer data) { @@ -118,7 +133,8 @@ sttype_register_function(void) "FUNCTION", function_new, function_free, - function_dup + function_dup, + function_tostr }; sttype_register(&function_type); diff --git a/epan/dfilter/sttype-integer.c b/epan/dfilter/sttype-integer.c index c7fd24c656..dc58ce984a 100644 --- a/epan/dfilter/sttype-integer.c +++ b/epan/dfilter/sttype-integer.c @@ -20,6 +20,7 @@ sttype_register_integer(void) "INTEGER", NULL, NULL, + NULL, NULL }; diff --git a/epan/dfilter/sttype-pointer.c b/epan/dfilter/sttype-pointer.c index 489db05872..452630e75b 100644 --- a/epan/dfilter/sttype-pointer.c +++ b/epan/dfilter/sttype-pointer.c @@ -40,6 +40,35 @@ pcre_free(gpointer value) } } +static char * +fvalue_tostr(const void *data) +{ + fvalue_t *fvalue = (fvalue_t*)data; + + char *s, *repr; + + s = fvalue_to_string_repr(NULL, fvalue, FTREPR_DFILTER, BASE_NONE); + repr = g_strdup_printf("%s[%s]", fvalue_type_name(fvalue), s); + g_free(s); + return repr; +} + +static char * +field_tostr(const void *data) +{ + header_field_info *hfinfo = (header_field_info *)data; + + return g_strdup(hfinfo->abbrev); +} + +static char * +pcre_tostr(const void *data) +{ + const GRegex *pcre = (const GRegex *)data; + + return g_strdup(g_regex_get_pattern(pcre)); +} + void sttype_register_pointer(void) { @@ -48,21 +77,24 @@ sttype_register_pointer(void) "FIELD", NULL, NULL, - NULL + NULL, + field_tostr }; static sttype_t fvalue_type = { STTYPE_FVALUE, "FVALUE", NULL, fvalue_free, - NULL + NULL, + fvalue_tostr }; static sttype_t pcre_type = { STTYPE_PCRE, "PCRE", NULL, pcre_free, - NULL + NULL, + pcre_tostr }; sttype_register(&field_type); diff --git a/epan/dfilter/sttype-range.c b/epan/dfilter/sttype-range.c index 448eedf2aa..9b9b256370 100644 --- a/epan/dfilter/sttype-range.c +++ b/epan/dfilter/sttype-range.c @@ -116,7 +116,8 @@ sttype_register_range(void) "RANGE", range_new, range_free, - range_dup + range_dup, + NULL }; sttype_register(&range_type); diff --git a/epan/dfilter/sttype-set.c b/epan/dfilter/sttype-set.c index 15dfd2afc2..aad28c438a 100644 --- a/epan/dfilter/sttype-set.c +++ b/epan/dfilter/sttype-set.c @@ -65,6 +65,7 @@ sttype_register_set(void) "SET", NULL, sttype_set_free, + NULL, NULL }; diff --git a/epan/dfilter/sttype-string.c b/epan/dfilter/sttype-string.c index 00ffda5181..bc65e26381 100644 --- a/epan/dfilter/sttype-string.c +++ b/epan/dfilter/sttype-string.c @@ -27,6 +27,12 @@ string_free(gpointer value) g_free(value); } +static char * +string_tostr(const void *data) +{ + return g_strdup(data); +} + void sttype_register_string(void) @@ -36,7 +42,8 @@ sttype_register_string(void) "STRING", string_new, string_free, - string_dup + string_dup, + string_tostr }; static sttype_t charconst_type = { @@ -44,7 +51,8 @@ sttype_register_string(void) "CHARCONST", string_new, string_free, - string_dup + string_dup, + string_tostr }; static sttype_t unparsed_type = { @@ -52,7 +60,8 @@ sttype_register_string(void) "UNPARSED", string_new, string_free, - string_dup + string_dup, + string_tostr }; sttype_register(&string_type); diff --git a/epan/dfilter/sttype-test.c b/epan/dfilter/sttype-test.c index 18bfbf5b1c..8fc1e9f447 100644 --- a/epan/dfilter/sttype-test.c +++ b/epan/dfilter/sttype-test.c @@ -64,6 +64,66 @@ test_free(gpointer value) g_free(test); } +static char * +test_tostr(const void *value) +{ + const test_t *test = (const test_t *)value; + assert_magic(test, TEST_MAGIC); + + const char *s = ""; + + switch(test->op) { + case TEST_OP_EXISTS: + s = "TEST_EXIST"; + break; + case TEST_OP_NOT: + s = "TEST_NOT"; + break; + case TEST_OP_AND: + s = "TEST_AND"; + break; + case TEST_OP_OR: + s = "TEST_OR"; + break; + case TEST_OP_EQ: + s = "TEST_EQ"; + break; + case TEST_OP_NE: + s = "TEST_NE"; + break; + case TEST_OP_GT: + s = "TEST_GT"; + break; + case TEST_OP_GE: + s = "TEST_GE"; + break; + case TEST_OP_LT: + s = "TEST_LT"; + break; + case TEST_OP_LE: + s = "TEST_LE"; + break; + case TEST_OP_BITWISE_AND: + s = "TEST_BITAND"; + break; + case TEST_OP_CONTAINS: + s = "TEST_CONTAINS"; + break; + case TEST_OP_MATCHES: + s = "TEST_MATCHES"; + break; + case TEST_OP_IN: + s = "TEST_IN"; + break; + case TEST_OP_UNINITIALIZED: + s = ""; + break; + default: + break; + } + return g_strdup(s); +} + static int num_operands(test_op_t op) { @@ -158,7 +218,8 @@ sttype_register_test(void) "TEST", test_new, test_free, - test_dup + test_dup, + test_tostr }; sttype_register(&test_type); diff --git a/epan/dfilter/syntax-tree.c b/epan/dfilter/syntax-tree.c index d9ac1c1fd5..3a8f213c39 100644 --- a/epan/dfilter/syntax-tree.c +++ b/epan/dfilter/syntax-tree.c @@ -8,8 +8,15 @@ #include "config.h" +#define WS_LOG_DOMAIN LOG_DOMAIN_DFILTER + +#include #include "syntax-tree.h" #include +#include +#include +#include +#include "sttype-test.h" /* Keep track of sttype_t's via their sttype_id_t number */ static sttype_t* type_list[STTYPE_NUM_TYPES]; @@ -227,6 +234,118 @@ stnode_deprecated(stnode_t *node) return node->deprecated_token; } +char * +stnode_tostr(stnode_t *node) +{ + char *s, *repr; + + if (stnode_type_id(node) == STTYPE_TEST) + return node->type->func_tostr(node->data); + + if (stnode_type_id(node) == STTYPE_INTEGER) + return g_strdup_printf("%s<%"PRId32">", stnode_type_name(node), stnode_value(node)); + + if (node->type->func_tostr == NULL) + return g_strdup_printf("%s", stnode_type_name(node)); + + s = node->type->func_tostr(node->data); + repr = g_strdup_printf("%s<%s>", stnode_type_name(node), s); + g_free(s); + + return repr; +} + +static char * +sprint_node(stnode_t *node) +{ + wmem_strbuf_t *buf = wmem_strbuf_new(NULL, NULL); + char *s; + + wmem_strbuf_append_printf(buf, "stnode <%p> = {\n", (void *)node); + wmem_strbuf_append_printf(buf, "\tmagic = %"PRIx32"\n", node->magic); + wmem_strbuf_append_printf(buf, "\ttype = %s\n", stnode_type_name(node)); + s = node->type->func_tostr(node->data); + wmem_strbuf_append_printf(buf, "\tdata = %s\n", s); + g_free(s); + wmem_strbuf_append_printf(buf, "\tvalue = %"PRId32"\n", node->value); + wmem_strbuf_append_printf(buf, "\tinside_brackets = %s\n", true_or_false(node->inside_brackets)); + wmem_strbuf_append_printf(buf, "\tdeprecated_token = %s\n", node->deprecated_token); + wmem_strbuf_append_printf(buf, "}\n"); + return wmem_strbuf_finalize(buf); +} + +void +stnode_log_full(enum ws_log_level level, + const char *file, int line, const char *func, + stnode_t *node, const char *msg) +{ + if (!ws_log_msg_is_active(LOG_DOMAIN_DFILTER, level)) + return; + + char *str = sprint_node(node); + ws_log_write_always_full(LOG_DOMAIN_DFILTER, level, + file, line, func, "%s:\n%s", msg, str); + g_free(str); +} + +static void indent(wmem_strbuf_t *buf, int level) +{ + for (int i = 0; i < level * 2; i++) { + wmem_strbuf_append_c(buf, ' '); + } +} + +static void +visit_tree(wmem_strbuf_t *buf, stnode_t *node, int level) +{ + stnode_t *left, *right; + char *str; + + if (stnode_type_id(node) == STTYPE_TEST) { + str = stnode_tostr(node); + wmem_strbuf_append_printf(buf, "%s(", str); + g_free(str); + sttype_test_get(node, NULL, &left, &right); + if (left && right) { + wmem_strbuf_append_c(buf, '\n'); + indent(buf, level + 1); + wmem_strbuf_append(buf, "LHS = "); + visit_tree(buf, left, level + 1); + wmem_strbuf_append_c(buf, '\n'); + indent(buf, level + 1); + wmem_strbuf_append(buf, "RHS = "); + visit_tree(buf, right, level + 1); + wmem_strbuf_append(buf, "\n"); + indent(buf, level); + } + else if (left) { + visit_tree(buf, left, level); + } + else if (right) { + visit_tree(buf, right, level); + } + wmem_strbuf_append(buf, ")"); + } + else { + str = stnode_tostr(node); + wmem_strbuf_append_printf(buf, "%s", str); + g_free(str); + } +} + +void +log_syntax_tree(enum ws_log_level level, stnode_t *root, const char *msg) +{ + if (!ws_log_msg_is_active(LOG_DOMAIN_DFILTER, level)) + return; + + wmem_strbuf_t *buf = wmem_strbuf_new(NULL, NULL); + + visit_tree(buf, root, 0); + ws_log(LOG_DOMAIN_DFILTER, level, "%s:\n%s", msg, wmem_strbuf_get_str(buf)); + wmem_strbuf_destroy(buf); +} + /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/epan/dfilter/syntax-tree.h b/epan/dfilter/syntax-tree.h index da2c237521..dd99b3d4c1 100644 --- a/epan/dfilter/syntax-tree.h +++ b/epan/dfilter/syntax-tree.h @@ -9,8 +9,11 @@ #ifndef SYNTAX_TREE_H #define SYNTAX_TREE_H +#include +#include #include #include "cppmagic.h" +#include "ws_log_defs.h" /** @file */ @@ -34,6 +37,7 @@ typedef enum { typedef gpointer (*STTypeNewFunc)(gpointer); typedef gpointer (*STTypeDupFunc)(gconstpointer); typedef void (*STTypeFreeFunc)(gpointer); +typedef char* (*STTypeToStrFunc)(gconstpointer); /* Type information */ @@ -43,17 +47,18 @@ typedef struct { STTypeNewFunc func_new; STTypeFreeFunc func_free; STTypeDupFunc func_dup; + STTypeToStrFunc func_tostr; } sttype_t; /** Node (type instance) information */ typedef struct { - guint32 magic; + uint32_t magic; sttype_t *type; /* This could be made an enum, but I haven't * set aside to time to do so. */ gpointer data; - gint32 value; + int32_t value; gboolean inside_brackets; const char *deprecated_token; } stnode_t; @@ -112,6 +117,23 @@ stnode_value(stnode_t *node); const char * stnode_deprecated(stnode_t *node); +char * +stnode_tostr(stnode_t *node); + +void +stnode_log_full(enum ws_log_level level, + const char *file, int line, const char *func, + stnode_t *node, const char *msg); + +#ifdef WS_DISABLE_DEBUG +#define stnode_log(node) (void)0; +#else +#define stnode_log(node) \ + stnode_log_full(LOG_LEVEL_NOISY, __FILE__, __LINE__, __func__, node, #node) +#endif + +void log_syntax_tree(enum ws_log_level, stnode_t *root, const char *msg); + #define assert_magic(obj, mnum) \ g_assert_true((obj)); \ if ((obj)->magic != (mnum)) { \ diff --git a/ws_log_defs.h b/ws_log_defs.h index 214f0ae397..b573ffc742 100644 --- a/ws_log_defs.h +++ b/ws_log_defs.h @@ -32,6 +32,8 @@ #define LOG_DOMAIN_EPAN "Epan" +#define LOG_DOMAIN_DFILTER "DFilter" + #define LOG_DOMAIN_WSUTIL "WSUtil" #define LOG_DOMAIN_QTUI "GUI" diff --git a/wsutil/str_util.h b/wsutil/str_util.h index 46e75f6d74..a7f56d3553 100644 --- a/wsutil/str_util.h +++ b/wsutil/str_util.h @@ -117,6 +117,8 @@ gchar printable_char_or_period(gchar c); /* To pass one of two strings, singular or plural */ #define plurality(d,s,p) ((d) == 1 ? (s) : (p)) +#define true_or_false(val) ((val) ? "TRUE" : "FALSE") + #ifdef __cplusplus }