From ed3cda02ae8a94371e63d0c1def0318ce4a4d8af Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 15 Nov 2017 15:04:05 +0100 Subject: [PATCH] MINOR: tools: add a function to dump a scope-aware tree to a file It emits a dump in DOT format for graphing purposes during debugging sessions. It's convenient to dump the run queue. --- include/common/standard.h | 3 +++ src/standard.c | 57 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/include/common/standard.h b/include/common/standard.h index 9819c9aa3..376233821 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -823,6 +823,9 @@ const void *my_memmem(const void *, size_t, const void *, size_t); */ unsigned int get_next_id(struct eb_root *root, unsigned int key); +/* dump the full tree to in DOT format for debugging purposes */ +void eb32sc_to_file(FILE *file, struct eb_root *root); + /* This function compares a sample word possibly followed by blanks to another * clean word. The compare is case-insensitive. 1 is returned if both are equal, * otherwise zero. This intends to be used when checking HTTP headers for some diff --git a/src/standard.c b/src/standard.c index 9be783200..ff6cb945a 100644 --- a/src/standard.c +++ b/src/standard.c @@ -31,6 +31,7 @@ #include #include #include +#include /* This macro returns false if the test __x is false. Many * of the following parsing function must be abort the processing @@ -2227,6 +2228,62 @@ unsigned int get_next_id(struct eb_root *root, unsigned int key) return key; } +/* dump the full tree to in DOT format for debugging purposes */ +void eb32sc_to_file(FILE *file, struct eb_root *root) +{ + struct eb32sc_node *node; + unsigned long scope = -1; + + fprintf(file, + "digraph ebtree {\n" + " node [fontname=\"courier-bold\" shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n" + " edge [fontname=\"arial\" style=\"solid\" color=\"magenta\" dir=\"forward\"];\n" + " \"%lx_n\" [label=\"root\\n%lx\"]\n", (long)eb_root_to_node(root), (long)root + ); + + fprintf(file, " \"%lx_n\" -> \"%lx_%c\" [taillabel=\"l:%c\"];\n", + (long)eb_root_to_node(root), + (long)eb_root_to_node(eb_clrtag(root->b[0])), + eb_gettag(root->b[0]) == EB_LEAF ? 'l' : 'n', + eb_gettag(root->b[0]) == EB_LEAF ? 'l' : 'n'); + + node = eb32sc_first(root, scope); + while (node) { + if (node->node.node_p) { + /* node part is used */ + fprintf(file, " \"%lx_n\" [label=\"%lx\\nbit=%d\\nscope=%lx\" fillcolor=\"lightskyblue1\"];\n", + (long)node, (long)node, node->node.bit, node->node_s); + + fprintf(file, " \"%lx_n\" -> \"%lx_n\" [taillabel=\"np:%c\"];\n", + (long)node, + (long)eb_root_to_node(eb_clrtag(node->node.node_p)), + eb_gettag(node->node.node_p) ? 'r' : 'l'); + + fprintf(file, " \"%lx_n\" -> \"%lx_%c\" [taillabel=\"l:%c\"];\n", + (long)node, + (long)eb_root_to_node(eb_clrtag(node->node.branches.b[0])), + eb_gettag(node->node.branches.b[0]) == EB_LEAF ? 'l' : 'n', + eb_gettag(node->node.branches.b[0]) == EB_LEAF ? 'l' : 'n'); + + fprintf(file, " \"%lx_n\" -> \"%lx_%c\" [taillabel=\"r:%c\"];\n", + (long)node, + (long)eb_root_to_node(eb_clrtag(node->node.branches.b[1])), + eb_gettag(node->node.branches.b[1]) == EB_LEAF ? 'l' : 'n', + eb_gettag(node->node.branches.b[1]) == EB_LEAF ? 'l' : 'n'); + } + + fprintf(file, " \"%lx_l\" [label=\"%lx\\nkey=%u\\nscope=%lx\\npfx=%u\" fillcolor=\"yellow\"];\n", + (long)node, (long)node, node->key, node->leaf_s, node->node.pfx); + + fprintf(file, " \"%lx_l\" -> \"%lx_n\" [taillabel=\"lp:%c\"];\n", + (long)node, + (long)eb_root_to_node(eb_clrtag(node->node.leaf_p)), + eb_gettag(node->node.leaf_p) ? 'r' : 'l'); + node = eb32sc_next(node, scope); + } + fprintf(file, "}\n"); +} + /* This function compares a sample word possibly followed by blanks to another * clean word. The compare is case-insensitive. 1 is returned if both are equal, * otherwise zero. This intends to be used when checking HTTP headers for some