Commit 70a7fcc9 authored by Geoff Simmons's avatar Geoff Simmons

Add the .create_stats() method.

parent cacd60f1
......@@ -32,6 +32,7 @@ Makefile.in
/src/vcc_if.c
/src/vcc_if.h
/src/vmod_*rst
/src/VSC_selector.*
/src/tests/*.log
/src/tests/*.trs
......
......@@ -26,6 +26,7 @@ CONTENTS
* :ref:`obj_set`
* :ref:`func_set.add`
* :ref:`func_set.backend`
* :ref:`func_set.create_stats`
* :ref:`func_set.debug`
* :ref:`func_set.element`
* :ref:`func_set.hasprefix`
......@@ -310,6 +311,25 @@ Example::
}
.. _func_set.create_stats:
VOID xset.create_stats()
------------------------
Creates statistics counters for this object that are displayed by
tools such as varnishstat(1).
Example::
sub vcl_init {
new myset = selector.set();
set.add("foo");
set.add("bar");
set.add("baz");
set.create_stats();
}
.. _func_set.debug:
STRING xset.debug()
......@@ -332,6 +352,11 @@ Example::
std.log("Using VMOD selector version: " + selector.version());
STATISTICS
==========
XXX ...
REQUIREMENTS
============
......
......@@ -39,6 +39,7 @@ m4_ifndef([VARNISH_PREREQ], AC_MSG_ERROR([Need varnish.m4 -- see README.rst]))
VARNISH_PREREQ([6.0.0])
VARNISH_VMODS([selector])
VARNISH_COUNTERS([selector])
VMOD_TESTS="$(cd $srcdir/src && echo tests/*.vtc)"
AC_SUBST(VMOD_TESTS)
......
......@@ -12,10 +12,14 @@ libvmod_selector_la_SOURCES = \
nodist_libvmod_selector_la_SOURCES = \
vcc_if.c \
vcc_if.h
vcc_if.h \
VSC_selector.c \
VSC_selector.h
dist_man_MANS = vmod_selector.3
@BUILD_VSC_SELECTOR@
vmod_selector.c patricia.c: patricia.h
vmod_selector.lo: $(nodist_libvmod_selector_la_SOURCES)
......@@ -43,6 +47,7 @@ TESTS = @VMOD_TESTS@
EXTRA_DIST = \
vmod_selector.vcc \
selector.vsc \
$(VMOD_TESTS)
CLEANFILES = \
......@@ -50,4 +55,6 @@ CLEANFILES = \
$(builddir)/vcc_if.h \
$(builddir)/vmod_selector.rst \
$(builddir)/vmod_selector.man.rst \
$(builddir)/vmod_selector.3
$(builddir)/vmod_selector.3 \
$(builddir)/VSC_selector.c \
$(builddir)/VSC_selector.h
......@@ -364,3 +364,50 @@ PT_Dump(struct pt_y *root, char **strings)
VSB_finish(sb);
return (sb);
}
void
pt_stats(const struct pt_y * const restrict y,
char * const restrict * const restrict strings,
struct pt_stats * const restrict stats, unsigned depth)
{
if (y == NULL)
return;
CHECK_OBJ(y, PT_Y_MAGIC);
depth++;
stats->nodes++;
if (strings[y->idx][y->off + y->len] == '\0') {
if (depth < stats->dmin)
stats->dmin = depth;
if (depth > stats->dmax)
stats->dmax = depth;
stats->davg += (depth - stats->davg) / (stats->terms + 1.);
stats->terms++;
}
if (y->leaf[0] == NULL && y->leaf[1] == NULL) {
stats->leaves++;
return;
}
pt_stats(y->leaf[0], strings, stats, depth);
pt_stats(y->leaf[1], strings, stats, depth);
}
void
PT_Stats(const struct pt_y * const restrict root,
char * const restrict * const restrict strings,
struct pt_stats * const restrict stats)
{
CHECK_OBJ_NOTNULL(stats, PT_STATS_MAGIC);
stats->nodes = 0;
stats->leaves = 0;
stats->terms = 0;
stats->dmin = UINT64_MAX;
stats->dmax = 0;
stats->davg = 0.;
pt_stats(root, strings, stats, 0);
}
......@@ -45,6 +45,17 @@ struct match_data {
unsigned max;
};
struct pt_stats {
unsigned magic;
#define PT_STATS_MAGIC 0xf1c1114e
uint64_t nodes;
uint64_t leaves;
uint64_t terms;
uint64_t dmin;
uint64_t dmax;
double davg;
};
void PT_Init(void);
int PT_Inited(void);
int PT_Insert(struct pt_y * * restrict root, unsigned idx,
......@@ -56,5 +67,8 @@ int PT_Prefixes(const struct pt_y * const restrict root,
char * const restrict * const restrict strings,
const char * const restrict subject,
struct match_data * const restrict match);
void PT_Stats(const struct pt_y * const restrict root,
char * const restrict * const restrict strings,
struct pt_stats * const restrict stats);
void PT_Free(struct pt_y *y);
struct vsb * PT_Dump(struct pt_y *root, char **strings);
..
This is *NOT* a RST file but the syntax has been chosen so
that it may become an RST file at some later date.
.. varnish_vsc_begin:: selector
:oneliner: VMOD selector set object stats
:order: 90
.. varnish_vsc:: elements
:type: gauge
:oneliner: Elements
Number of elements (strings) in the set.
.. varnish_vsc:: nodes
:type: gauge
:oneliner: Nodes
Total number of nodes in the internal data structure.
.. varnish_vsc:: leaves
:type: gauge
:oneliner: Leaf nodes
Number of leaf nodes in the internal data structure.
.. varnish_vsc:: dmin
:type: gauge
:oneliner: Minimum terminating node depth
Minimum depth of a node in the internal data structure at
which an element of the set may be found.
.. varnish_vsc:: dmax
:type: gauge
:oneliner: Maximum terminating node depth
Maximum depth of a node in the internal data structure at
which an element of the set may be found.
.. varnish_vsc:: davg
:type: gauge
:oneliner: Average terminating node depth
Average depth of nodes in the internal data structure at which
an element of the set may be found, rounded to the nearest integer.
.. varnish_vsc_end:: selector
# looks like -*- vcl -*-
varnishtest ".create_stats() method"
varnish v1 -vcl {
import ${vmod_selector};
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new s = selector.set();
s.add("foo");
s.add("bar");
s.add("baz");
s.add("quux");
s.create_stats();
}
} -start
varnish v1 -vsc SELECTOR.*
varnish v1 -expect SELECTOR.vcl1.s.elements == 4
varnish v1 -expect SELECTOR.vcl1.s.nodes > 0
varnish v1 -expect SELECTOR.vcl1.s.leaves <= 4
varnish v1 -expect SELECTOR.vcl1.s.dmin > 0
varnish v1 -expect SELECTOR.vcl1.s.dmax > 0
varnish v1 -expect SELECTOR.vcl1.s.davg > 0
varnish v1 -vcl {
import ${vmod_selector};
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new p = selector.set();
p.add("foo");
p.add("foobar");
p.add("foobarbaz");
p.add("foobarbazquux");
p.create_stats();
# No .create_stats() call.
new n = selector.set();
n.add("foo");
n.add("bar");
n.add("baz");
n.add("quux");
}
}
# Stats for vc1.s and vcl2.p appear, but not for vcl2.n.
varnish v1 -vsc SELECTOR.*
varnish v1 -expect SELECTOR.vcl2.p.elements == 4
varnish v1 -expect SELECTOR.vcl2.p.nodes > 0
varnish v1 -expect SELECTOR.vcl2.p.leaves <= 4
varnish v1 -expect SELECTOR.vcl2.p.dmin > 0
varnish v1 -expect SELECTOR.vcl2.p.dmax > 0
varnish v1 -expect SELECTOR.vcl2.p.davg > 0
# When vcl1 is set to cold, stats for vcl2.p but not vcl1.s appear.
varnish v1 -cliok "vcl.state vcl1 cold"
varnish v1 -vsc SELECTOR.*
# When vcl1 is set back to warm, stats for vcl2.p and vcl1.s appear.
varnish v1 -cliok "vcl.state vcl1 warm"
varnish v1 -vsc SELECTOR.*
varnish v1 -expect SELECTOR.vcl1.s.elements == 4
varnish v1 -expect SELECTOR.vcl1.s.nodes > 0
varnish v1 -expect SELECTOR.vcl1.s.leaves <= 4
varnish v1 -expect SELECTOR.vcl1.s.dmin > 0
varnish v1 -expect SELECTOR.vcl1.s.dmax > 0
varnish v1 -expect SELECTOR.vcl1.s.davg > 0
# The same 100 words from /usr/share/dict/words as in match.vtc
varnish v1 -vcl {
import ${vmod_selector};
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new words = selector.set();
words.add("trustee's");
words.add("Marc");
words.add("remover's");
words.add("brutishly");
words.add("Blythe");
words.add("tastier");
words.add("backed");
words.add("rain");
words.add("banality");
words.add("unstrung");
words.add("barnyards");
words.add("paperweight");
words.add("Kazan's");
words.add("fanfares");
words.add("Donny's");
words.add("faze");
words.add("redefinition");
words.add("Schulz");
words.add("Lanai's");
words.add("bastions");
words.add("kicker's");
words.add("Denny");
words.add("disgraced");
words.add("downswings");
words.add("pullback's");
words.add("Gregorio's");
words.add("spillways");
words.add("puller");
words.add("basilica's");
words.add("serviced");
words.add("insistently");
words.add("Frisian's");
words.add("question");
words.add("mien");
words.add("rockier");
words.add("indivisible");
words.add("megahertzes");
words.add("Oldfield's");
words.add("accusatory");
words.add("Mabel");
words.add("magnetize");
words.add("Philly");
words.add("Katheryn's");
words.add("policewoman's");
words.add("ashcan");
words.add("deviousness's");
words.add("suspends");
words.add("furnishings");
words.add("compiler's");
words.add("Claudio");
words.add("zestfully");
words.add("laughter's");
words.add("Manuel");
words.add("palatal's");
words.add("eminent");
words.add("strongboxes");
words.add("pinafores");
words.add("Glendale");
words.add("dethronement");
words.add("chlorinate");
words.add("Souths");
words.add("tilting");
words.add("trenched");
words.add("run");
words.add("initialized");
words.add("breakfast");
words.add("winning's");
words.add("mediates");
words.add("triads");
words.add("verdict");
words.add("Irish");
words.add("Jeremy");
words.add("handouts");
words.add("Billie's");
words.add("romanticist's");
words.add("descanting");
words.add("bidders");
words.add("play");
words.add("navigability's");
words.add("leapfrogging");
words.add("Libby");
words.add("smelter's");
words.add("hermit's");
words.add("Tabatha's");
words.add("churlish");
words.add("spuriousness's");
words.add("Salish's");
words.add("Curry");
words.add("hula");
words.add("ruse's");
words.add("bureaucratic");
words.add("Moseley");
words.add("confluence");
words.add("inseams");
words.add("producers");
words.add("cozier");
words.add("augur's");
words.add("electrode's");
words.add("disposition");
words.add("Rena's");
words.create_stats();
}
}
varnish v1 -expect SELECTOR.vcl3.words.elements == 100
varnish v1 -expect SELECTOR.vcl3.words.nodes > 0
varnish v1 -expect SELECTOR.vcl3.words.leaves <= 100
varnish v1 -expect SELECTOR.vcl3.words.dmin > 0
varnish v1 -expect SELECTOR.vcl3.words.dmax > 0
varnish v1 -expect SELECTOR.vcl3.words.davg > 0
varnish v1 -cliok "vcl.state vcl1 cold"
varnish v1 -cli "vcl.discard vcl1"
# No stats for vcl1 appear after discard.
varnish v1 -vsc SELECTOR.vc1.*
# The same for vcl2 after discard.
varnish v1 -cli "vcl.discard vcl2"
varnish v1 -vsc SELECTOR.vc2.*
......@@ -43,6 +43,7 @@
#include "vcc_if.h"
#include "patricia.h"
#include "VSC_selector.h"
#define VFAIL(ctx, fmt, ...) \
VRT_fail((ctx), "vmod selector failure: " fmt, __VA_ARGS__)
......@@ -89,17 +90,67 @@ struct vmod_selector_set {
VCL_BOOL case_sensitive;
};
struct vsc_entry {
unsigned magic;
#define VMOD_SELECTOR_VSC_MAGIC 0x4b99b64a
VSLIST_ENTRY(vsc_entry) list;
struct vsc_seg *vsc_seg;
};
VSLIST_HEAD(vsc_head, vsc_entry);
/* Event function */
int
event(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
{
(void) ctx;
(void) priv;
struct vsc_head *vsc_head;
struct vsc_entry *vsc_entry;
if (e == VCL_EVENT_LOAD && !PT_Inited())
PT_Init();
return 0;
ASSERT_CLI();
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(priv);
if (priv->priv == NULL) {
vsc_head = malloc(sizeof(*vsc_head));
AN(vsc_head);
priv->priv = vsc_head;
VSLIST_INIT(vsc_head);
}
else
vsc_head = priv->priv;
switch(e) {
case VCL_EVENT_LOAD:
if (!PT_Inited())
PT_Init();
break;
case VCL_EVENT_DISCARD:
while (!VSLIST_EMPTY(vsc_head)) {
vsc_entry = VSLIST_FIRST(vsc_head);
CHECK_OBJ_NOTNULL(vsc_entry, VMOD_SELECTOR_VSC_MAGIC);
VSC_selector_Destroy(&vsc_entry->vsc_seg);
VSLIST_REMOVE_HEAD(vsc_head, list);
FREE_OBJ(vsc_entry);
}
free(vsc_head);
break;
case VCL_EVENT_WARM:
VSLIST_FOREACH(vsc_entry, vsc_head, list) {
CHECK_OBJ_NOTNULL(vsc_entry, VMOD_SELECTOR_VSC_MAGIC);
VRT_VSC_Reveal(vsc_entry->vsc_seg);
}
break;
case VCL_EVENT_COLD:
VSLIST_FOREACH(vsc_entry, vsc_head, list) {
CHECK_OBJ_NOTNULL(vsc_entry, VMOD_SELECTOR_VSC_MAGIC);
VRT_VSC_Hide(vsc_entry->vsc_seg);
}
break;
default:
WRONG("illegal event enum");
}
return 0;
}
/* Object regex */
......@@ -695,6 +746,59 @@ vmod_set_debug(VRT_CTX, struct vmod_selector_set *set)
return output;
}
VCL_VOID
vmod_set_create_stats(VRT_CTX, struct vmod_selector_set *set,
struct vmod_priv *priv)
{
struct pt_stats stats = { .magic = PT_STATS_MAGIC };
struct VSC_selector *vsc;
struct vsc_seg *vsc_seg;
struct vsc_head *vsc_head;
struct vsc_entry *vsc_entry;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
if ((ctx->method & VCL_MET_INIT) == 0) {
VFAIL(ctx, "%s.add() may only be called in vcl_init",
set->vcl_name);
return;
}
AN(priv);
AN(priv->priv);
vsc_head = priv->priv;
if (set->nmembers == 0)
memset(&stats, 0, sizeof(stats));
else {
char **members = set->members;
if (!set->case_sensitive)
members = set->lomembers;
AN(members);
PT_Stats(set->origo, members, &stats);
assert(stats.terms == set->nmembers);
assert(stats.leaves <= stats.terms);
assert(stats.terms <= stats.nodes);
assert(stats.dmin <= stats.dmax);
assert(stats.dmin <= stats.davg);
assert(stats.davg <= stats.dmax);
}
vsc = VSC_selector_New(NULL, &vsc_seg, "%s.%s", VCL_Name(ctx->vcl),
set->vcl_name);
vsc->elements = set->nmembers;
vsc->nodes = stats.nodes;
vsc->leaves = stats.leaves;
vsc->dmin = stats.dmin;
vsc->dmax = stats.dmax;
vsc->davg = (uint64_t)(stats.davg + 0.5);
ALLOC_OBJ(vsc_entry, VMOD_SELECTOR_VSC_MAGIC);
AN(vsc_entry);
vsc_entry->vsc_seg = vsc_seg;
VSLIST_INSERT_HEAD(vsc_head, vsc_entry, list);
}
VCL_STRING
vmod_version(VRT_CTX)
{
......
......@@ -177,6 +177,21 @@ Example::
# ...
}
$Method VOID .create_stats(PRIV_VCL)
Creates statistics counters for this object that are displayed by
tools such as varnishstat(1).
Example::
sub vcl_init {
new myset = selector.set();
set.add("foo");
set.add("bar");
set.add("baz");
set.create_stats();
}
$Method STRING .debug()
Intentionally not documented.
......@@ -189,6 +204,11 @@ Example::
std.log("Using VMOD selector version: " + selector.version());
STATISTICS
==========
XXX ...
REQUIREMENTS
============
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment