<commit_before>/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2015 Varnish Software AS
 * All rights reserved.
 *
 * Author: <NAME> <<EMAIL>>
 * Author: <NAME> <<EMAIL>>
 * Author: <NAME> <<EMAIL>>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Log tailer for Varnish
 */

#include "config.h"

#include <sys/types.h>

#include <errno.h>
#include <limits.h>
#include <math.h>
#include <pthread.h>
#include <regex.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "vdef.h"
#include "vcurses.h"
#include "vapi/vsl.h"
#include "vapi/vsm.h"
#include "vapi/voptget.h"
#include "vas.h"
#include "vcs.h"
#include "vut.h"
#include "vtim.h"

#define HIST_N 2000		/* how far back we remember */
#define HIST_RES 100		/* bucket resolution */

static const char progname[] = "varnishhist";

static int hist_low;
static int hist_high;
static int hist_range;
static int hist_buckets;

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

static int end_of_file = 0;
static double delay = 1;
static unsigned rr_hist[HIST_N];
static unsigned nhist;
static unsigned next_hist;
static unsigned *bucket_miss;
static unsigned *bucket_hit;
static char *format;
static int match_tag;
double timebend = 0, t0;
double vsl_t0 = 0, vsl_to, vsl_ts = 0;
pthread_cond_t timebend_cv;
static double log_ten;

static int scales[] = {
	1,
	2,
	3,
	4,
	5,
	10,
	15,
	20,
	25,
	50,
	100,
	250,
	500,
	1000,
	2500,
	5000,
	10000,
	25000,
	50000,
	100000,
	INT_MAX
};

struct profile {
	const char *name;
	char VSL_arg;
	enum VSL_tag_e tag;
	const char *prefix;
	int field;
	int hist_low;
	int hist_high;
}
#define HIS_PROF(name,vsl_arg,tag,prefix,field,hist_low,high_high,doc)	\
	{name,vsl_arg,tag,prefix,field,hist_low,high_high},
#define HIS_NO_PREFIX	NULL
#define HIS_CLIENT	'c'
#define HIS_BACKEND	'b'
profiles[] = {
#include "varnishhist_profiles.h"
	{ NULL }
};
#undef HIS_NO_PREFIX
#undef HIS_BACKEND
#undef HIS_CLIENT
#undef HIS_PROF

static struct profile *active_profile;

static void
update(void)
{
	char t[VTIM_FORMAT_SIZE];
	unsigned w = COLS / hist_range;
	unsigned n = w * hist_range;
	unsigned bm[n], bh[n];
	unsigned max;
	unsigned i, j, scale;
	int k, l;

	erase();

	/* Draw horizontal axis */
	for (i = 0; i < n; ++i)
		(void)mvaddch(LINES - 2, i, '-');
	for (k = 0, l = hist_low; k < hist_range; ++k, ++l) {
		(void)mvaddch(LINES - 2, w * k, '+');
		mvprintw(LINES - 1, w * k, "|1e%d", l);
	}

	if (end_of_file)
		mvprintw(0, 0, "%*s", COLS - 1, "EOF");
	else
		mvprintw(0, 0, "%*s", COLS - 1, VUT.name);

	/* count our flock */
	for (i = 0; i < n; ++i)
		bm[i] = bh[i] = 0;
	for (k = 0, max = 1; k < hist_buckets; ++k) {
		l = k * n / hist_buckets;
		bm[l] += bucket_miss[k];
		bh[l] += bucket_hit[k];
		if (bm[l] + bh[l] > max)
			max = bm[l] + bh[l];
	}

	/* scale,time */
	assert(LINES - 3 >= 0);
	for (i = 0; max / scales[i] > (unsigned)(LINES - 3); ++i)
		/* nothing */ ;
	scale = scales[i];

	if (vsl_t0 > 0) {
		VTIM_format(vsl_ts, t);

		mvprintw(0, 0, "1:%d, n = %d, d = %g @ %s x %g",
		    scale, nhist, delay, t, timebend);
	} else
		mvprintw(0, 0, "1:%d, n = %d, d = %g",
		    scale, nhist, delay);

	for (j = 2; j < LINES - 3; j += 5)
		mvprintw(j, 0, "%d_", (LINES - 3 - j) * scale);

	/* show them */
	for (i = 0; i < n; ++i) {
		for (j = 0; j < bm[i] / scale; ++j)
			(void)mvaddch(LINES - 3 - j, i, '#');
		for (; j < (bm[i] + bh[i]) / scale; ++j)
			(void)mvaddch(LINES - 3 - j, i, '|');
	}

	refresh();
}

inline static void
upd_vsl_ts(const char *p)
{
	double t;

	if (timebend == 0)
		return;

	p = strchr(p, ' ');

	if (p == NULL)
		return;

	t = strtod(p + 1, NULL);

	if (t > vsl_ts)
		vsl_ts = t;
}

static int /*__match_proto__ (VSLQ_dispatch_f)*/
accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[],
    void *priv)
{
	int i, tag, skip, match, hit;
	unsigned u;
	double value;
	struct VSL_transaction *tr;
	double t;
	const char *tsp;

	(void)vsl;
	(void)priv;

	for (tr = pt[0]; tr != NULL; tr = *++pt) {
		if (tr->reason == VSL_r_esi)
			/* Skip ESI requests */
			continue;

		hit = 0;
		skip = 0;
		match = 0;
		tsp = NULL;
		while (skip == 0) {
			i = VSL_Next(tr->c);
			if (i == -3) {
				/* overrun - need to skip forward */
				pthread_mutex_lock(&mtx);
				vsl_to = vsl_t0 = vsl_ts = 0;
				t0 = VTIM_mono();
				pthread_mutex_unlock(&mtx);
				break;
			}
			if (i != 1)
				break;

			/* get the value we want and register if it's a hit */
			tag = VSL_TAG(tr->c->rec.ptr);

			switch (tag) {
			case SLT_Hit:
				hit = 1;
				break;
			case SLT_VCL_return:
				if (!strcasecmp(VSL_CDATA(tr->c->rec.ptr),
				    "restart") ||
				    !strcasecmp(VSL_CDATA(tr->c->rec.ptr),
				    "retry"))
					skip = 1;
				break;
			case SLT_Timestamp:
				tsp = VSL_CDATA(tr->c->rec.ptr);
			default:
				if (tag != match_tag)
					break;<commit_msg>Add missing break to switch.<commit_after>/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2015 Varnish Software AS
 * All rights reserved.
 *
 * Author: <NAME> <<EMAIL>>
 * Author: <NAME> <<EMAIL>>
 * Author: <NAME> <<EMAIL>>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Log tailer for Varnish
 */

#include "config.h"

#include <sys/types.h>

#include <errno.h>
#include <limits.h>
#include <math.h>
#include <pthread.h>
#include <regex.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "vdef.h"
#include "vcurses.h"
#include "vapi/vsl.h"
#include "vapi/vsm.h"
#include "vapi/voptget.h"
#include "vas.h"
#include "vcs.h"
#include "vut.h"
#include "vtim.h"

#define HIST_N 2000		/* how far back we remember */
#define HIST_RES 100		/* bucket resolution */

static const char progname[] = "varnishhist";

static int hist_low;
static int hist_high;
static int hist_range;
static int hist_buckets;

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

static int end_of_file = 0;
static double delay = 1;
static unsigned rr_hist[HIST_N];
static unsigned nhist;
static unsigned next_hist;
static unsigned *bucket_miss;
static unsigned *bucket_hit;
static char *format;
static int match_tag;
double timebend = 0, t0;
double vsl_t0 = 0, vsl_to, vsl_ts = 0;
pthread_cond_t timebend_cv;
static double log_ten;

static int scales[] = {
	1,
	2,
	3,
	4,
	5,
	10,
	15,
	20,
	25,
	50,
	100,
	250,
	500,
	1000,
	2500,
	5000,
	10000,
	25000,
	50000,
	100000,
	INT_MAX
};

struct profile {
	const char *name;
	char VSL_arg;
	enum VSL_tag_e tag;
	const char *prefix;
	int field;
	int hist_low;
	int hist_high;
}
#define HIS_PROF(name,vsl_arg,tag,prefix,field,hist_low,high_high,doc)	\
	{name,vsl_arg,tag,prefix,field,hist_low,high_high},
#define HIS_NO_PREFIX	NULL
#define HIS_CLIENT	'c'
#define HIS_BACKEND	'b'
profiles[] = {
#include "varnishhist_profiles.h"
	{ NULL }
};
#undef HIS_NO_PREFIX
#undef HIS_BACKEND
#undef HIS_CLIENT
#undef HIS_PROF

static struct profile *active_profile;

static void
update(void)
{
	char t[VTIM_FORMAT_SIZE];
	unsigned w = COLS / hist_range;
	unsigned n = w * hist_range;
	unsigned bm[n], bh[n];
	unsigned max;
	unsigned i, j, scale;
	int k, l;

	erase();

	/* Draw horizontal axis */
	for (i = 0; i < n; ++i)
		(void)mvaddch(LINES - 2, i, '-');
	for (k = 0, l = hist_low; k < hist_range; ++k, ++l) {
		(void)mvaddch(LINES - 2, w * k, '+');
		mvprintw(LINES - 1, w * k, "|1e%d", l);
	}

	if (end_of_file)
		mvprintw(0, 0, "%*s", COLS - 1, "EOF");
	else
		mvprintw(0, 0, "%*s", COLS - 1, VUT.name);

	/* count our flock */
	for (i = 0; i < n; ++i)
		bm[i] = bh[i] = 0;
	for (k = 0, max = 1; k < hist_buckets; ++k) {
		l = k * n / hist_buckets;
		bm[l] += bucket_miss[k];
		bh[l] += bucket_hit[k];
		if (bm[l] + bh[l] > max)
			max = bm[l] + bh[l];
	}

	/* scale,time */
	assert(LINES - 3 >= 0);
	for (i = 0; max / scales[i] > (unsigned)(LINES - 3); ++i)
		/* nothing */ ;
	scale = scales[i];

	if (vsl_t0 > 0) {
		VTIM_format(vsl_ts, t);

		mvprintw(0, 0, "1:%d, n = %d, d = %g @ %s x %g",
		    scale, nhist, delay, t, timebend);
	} else
		mvprintw(0, 0, "1:%d, n = %d, d = %g",
		    scale, nhist, delay);

	for (j = 2; j < LINES - 3; j += 5)
		mvprintw(j, 0, "%d_", (LINES - 3 - j) * scale);

	/* show them */
	for (i = 0; i < n; ++i) {
		for (j = 0; j < bm[i] / scale; ++j)
			(void)mvaddch(LINES - 3 - j, i, '#');
		for (; j < (bm[i] + bh[i]) / scale; ++j)
			(void)mvaddch(LINES - 3 - j, i, '|');
	}

	refresh();
}

inline static void
upd_vsl_ts(const char *p)
{
	double t;

	if (timebend == 0)
		return;

	p = strchr(p, ' ');

	if (p == NULL)
		return;

	t = strtod(p + 1, NULL);

	if (t > vsl_ts)
		vsl_ts = t;
}

static int /*__match_proto__ (VSLQ_dispatch_f)*/
accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[],
    void *priv)
{
	int i, tag, skip, match, hit;
	unsigned u;
	double value;
	struct VSL_transaction *tr;
	double t;
	const char *tsp;

	(void)vsl;
	(void)priv;

	for (tr = pt[0]; tr != NULL; tr = *++pt) {
		if (tr->reason == VSL_r_esi)
			/* Skip ESI requests */
			continue;

		hit = 0;
		skip = 0;
		match = 0;
		tsp = NULL;
		while (skip == 0) {
			i = VSL_Next(tr->c);
			if (i == -3) {
				/* overrun - need to skip forward */
				pthread_mutex_lock(&mtx);
				vsl_to = vsl_t0 = vsl_ts = 0;
				t0 = VTIM_mono();
				pthread_mutex_unlock(&mtx);
				break;
			}
			if (i != 1)
				break;

			/* get the value we want and register if it's a hit */
			tag = VSL_TAG(tr->c->rec.ptr);

			switch (tag) {
			case SLT_Hit:
				hit = 1;
				break;
			case SLT_VCL_return:
				if (!strcasecmp(VSL_CDATA(tr->c->rec.ptr),
				    "restart") ||
				    !strcasecmp(VSL_CDATA(tr->c->rec.ptr),
				    "retry"))
					skip = 1;
				break;
			case SLT_Timestamp:
				tsp = VSL_CDATA(tr->c->rec.ptr);
				break;
			default:
				if (tag != match_tag)
					break;
{'fraction_non_alphanumeric': 0.14670805423950334, 'fraction_numerical': 0.0222185917333769, 'mean_word_length': 3.3928955866523145, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5259937', 'n_tokens_mistral': 5432, 'n_tokens_neox': 4809, 'n_words': 1498}