| | varnish-cache/bin/varnishhist/varnishhist.c |
| 0 |
|
/*- |
| 1 |
|
* Copyright (c) 2006 Verdens Gang AS |
| 2 |
|
* Copyright (c) 2006-2015 Varnish Software AS |
| 3 |
|
* All rights reserved. |
| 4 |
|
* |
| 5 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
| 6 |
|
* Author: Dag-Erling Smørgrav <des@des.no> |
| 7 |
|
* Author: Guillaume Quintard <guillaume.quintard@gmail.com> |
| 8 |
|
* |
| 9 |
|
* SPDX-License-Identifier: BSD-2-Clause |
| 10 |
|
* |
| 11 |
|
* Redistribution and use in source and binary forms, with or without |
| 12 |
|
* modification, are permitted provided that the following conditions |
| 13 |
|
* are met: |
| 14 |
|
* 1. Redistributions of source code must retain the above copyright |
| 15 |
|
* notice, this list of conditions and the following disclaimer. |
| 16 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
| 17 |
|
* notice, this list of conditions and the following disclaimer in the |
| 18 |
|
* documentation and/or other materials provided with the distribution. |
| 19 |
|
* |
| 20 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 21 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 22 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 23 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
| 24 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 25 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 26 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 27 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 28 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 29 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 30 |
|
* SUCH DAMAGE. |
| 31 |
|
* |
| 32 |
|
* Log tailer for Varnish |
| 33 |
|
*/ |
| 34 |
|
|
| 35 |
|
#include "config.h" |
| 36 |
|
|
| 37 |
|
#include <limits.h> |
| 38 |
|
#include <math.h> |
| 39 |
|
#include <pthread.h> |
| 40 |
|
#include <stdarg.h> |
| 41 |
|
#include <stdlib.h> |
| 42 |
|
#include <string.h> |
| 43 |
|
#include <unistd.h> |
| 44 |
|
|
| 45 |
|
#define VOPT_DEFINITION |
| 46 |
|
#define VOPT_INC "varnishhist_options.h" |
| 47 |
|
|
| 48 |
|
#include "vdef.h" |
| 49 |
|
#include "vcurses.h" |
| 50 |
|
#include "vapi/vsl.h" |
| 51 |
|
#include "vapi/vsm.h" |
| 52 |
|
#include "vapi/voptget.h" |
| 53 |
|
#include "vapi/vsig.h" |
| 54 |
|
#include "vas.h" |
| 55 |
|
#include "vut.h" |
| 56 |
|
#include "vtim.h" |
| 57 |
|
|
| 58 |
|
#define HIST_N 2000 /* how far back we remember */ |
| 59 |
|
#define HIST_RES 100 /* bucket resolution */ |
| 60 |
|
|
| 61 |
|
static struct VUT *vut; |
| 62 |
|
|
| 63 |
|
static int hist_low; |
| 64 |
|
static int hist_high; |
| 65 |
|
static int hist_range; |
| 66 |
|
static unsigned hist_buckets; |
| 67 |
|
|
| 68 |
|
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; |
| 69 |
|
|
| 70 |
|
static int end_of_file = 0; |
| 71 |
|
static unsigned ms_delay = 1000; |
| 72 |
|
static unsigned rr_hist[HIST_N]; |
| 73 |
|
static unsigned nhist; |
| 74 |
|
static unsigned next_hist; |
| 75 |
|
static unsigned *bucket_miss; |
| 76 |
|
static unsigned *bucket_hit; |
| 77 |
|
static char *format; |
| 78 |
|
static int match_tag; |
| 79 |
|
static double timebend = 0, t0; |
| 80 |
|
static double vsl_t0 = 0, vsl_to, vsl_ts = 0; |
| 81 |
|
static pthread_cond_t timebend_cv; |
| 82 |
|
static double log_ten; |
| 83 |
|
static char *ident; |
| 84 |
|
|
| 85 |
|
static const unsigned scales[] = { |
| 86 |
|
1, |
| 87 |
|
2, |
| 88 |
|
3, |
| 89 |
|
4, |
| 90 |
|
5, |
| 91 |
|
10, |
| 92 |
|
15, |
| 93 |
|
20, |
| 94 |
|
25, |
| 95 |
|
50, |
| 96 |
|
100, |
| 97 |
|
250, |
| 98 |
|
500, |
| 99 |
|
1000, |
| 100 |
|
2500, |
| 101 |
|
5000, |
| 102 |
|
10000, |
| 103 |
|
25000, |
| 104 |
|
50000, |
| 105 |
|
100000, |
| 106 |
|
UINT_MAX |
| 107 |
|
}; |
| 108 |
|
|
| 109 |
|
struct profile { |
| 110 |
|
const char *name; |
| 111 |
|
char VSL_arg; |
| 112 |
|
enum VSL_tag_e tag; |
| 113 |
|
const char *prefix; |
| 114 |
|
int field; |
| 115 |
|
int hist_low; |
| 116 |
|
int hist_high; |
| 117 |
|
}; |
| 118 |
|
|
| 119 |
|
#define HIS_PROF(name,vsl_arg,tag,prefix,field,hist_low,high_high,doc) \ |
| 120 |
|
{name,vsl_arg,tag,prefix,field,hist_low,high_high}, |
| 121 |
|
#define HIS_NO_PREFIX NULL |
| 122 |
|
#define HIS_CLIENT 'c' |
| 123 |
|
#define HIS_BACKEND 'b' |
| 124 |
|
static const struct profile profiles[] = { |
| 125 |
|
#include "varnishhist_profiles.h" |
| 126 |
|
{ NULL } |
| 127 |
|
}; |
| 128 |
|
#undef HIS_NO_PREFIX |
| 129 |
|
#undef HIS_BACKEND |
| 130 |
|
#undef HIS_CLIENT |
| 131 |
|
#undef HIS_PROF |
| 132 |
|
|
| 133 |
|
static const struct profile *active_profile; |
| 134 |
|
|
| 135 |
|
static void |
| 136 |
46179 |
update(void) |
| 137 |
|
{ |
| 138 |
|
char t[VTIM_FORMAT_SIZE]; |
| 139 |
46179 |
const unsigned w = COLS / hist_range; |
| 140 |
46179 |
const unsigned n = w * hist_range; |
| 141 |
46179 |
unsigned bm[n], bh[n]; |
| 142 |
|
unsigned max; |
| 143 |
|
unsigned scale; |
| 144 |
|
int i, j; |
| 145 |
|
unsigned k, l; |
| 146 |
|
|
| 147 |
|
/* Draw horizontal axis */ |
| 148 |
3373603 |
for (k = 0; k < n; ++k) |
| 149 |
3327424 |
(void)mvaddch(LINES - 2, k, '-'); |
| 150 |
459571 |
for (i = 0, j = hist_low; i < hist_range; ++i, ++j) { |
| 151 |
413392 |
(void)mvaddch(LINES - 2, w * i, '+'); |
| 152 |
413392 |
IC(mvprintw(LINES - 1, w * i, "|1e%d", j)); |
| 153 |
413392 |
} |
| 154 |
|
|
| 155 |
46179 |
if (end_of_file) |
| 156 |
0 |
IC(mvprintw(0, 0, "%*s", COLS - 1, "EOF")); |
| 157 |
|
else |
| 158 |
46179 |
IC(mvprintw(0, 0, "%*s", COLS - 1, ident)); |
| 159 |
|
|
| 160 |
|
/* count our flock */ |
| 161 |
46179 |
memset(bm, 0, sizeof bm); |
| 162 |
46179 |
memset(bh, 0, sizeof bh); |
| 163 |
41385379 |
for (k = 0, max = 1; k < hist_buckets; ++k) { |
| 164 |
41339200 |
l = k * n / hist_buckets; |
| 165 |
41339200 |
assert(l < n); |
| 166 |
41339200 |
bm[l] += bucket_miss[k]; |
| 167 |
41339200 |
bh[l] += bucket_hit[k]; |
| 168 |
41339200 |
max = vmax(max, bm[l] + bh[l]); |
| 169 |
41339200 |
} |
| 170 |
|
|
| 171 |
|
/* scale,time */ |
| 172 |
46179 |
assert(LINES - 3 >= 0); |
| 173 |
46179 |
for (i = 0; max / scales[i] > (unsigned)(LINES - 3); ++i) |
| 174 |
|
/* nothing */ ; |
| 175 |
46179 |
scale = scales[i]; |
| 176 |
|
|
| 177 |
46179 |
if (vsl_t0 > 0) { |
| 178 |
45229 |
VTIM_format(vsl_ts, t); |
| 179 |
|
|
| 180 |
45229 |
IC(mvprintw(0, 0, "1:%u, n = %u, d = %g @ %s x %g", |
| 181 |
|
scale, nhist, 1e-3 * ms_delay, t, timebend)); |
| 182 |
45229 |
} else { |
| 183 |
950 |
IC(mvprintw(0, 0, "1:%u, n = %u, d = %g", |
| 184 |
|
scale, nhist, 1e-3 * ms_delay)); |
| 185 |
|
} |
| 186 |
|
|
| 187 |
230895 |
for (j = 5; j < LINES - 2; j += 5) |
| 188 |
184716 |
IC(mvprintw((LINES - 2) - j, 0, "%u_", j * scale)); |
| 189 |
|
|
| 190 |
|
/* show them */ |
| 191 |
3373603 |
for (k = 0; k < n; ++k) { |
| 192 |
3373380 |
for (l = 0; l < bm[k] / scale; ++l) |
| 193 |
45956 |
(void)mvaddch((LINES - 3) - l, k, '#'); |
| 194 |
3327865 |
for (; l < (bm[k] + bh[k]) / scale; ++l) |
| 195 |
441 |
(void)mvaddch((LINES - 3) - l, k, '|'); |
| 196 |
3327424 |
} |
| 197 |
46179 |
} |
| 198 |
|
|
| 199 |
|
inline static void |
| 200 |
160 |
upd_vsl_ts(const char *p) |
| 201 |
|
{ |
| 202 |
|
|
| 203 |
160 |
if (timebend == 0) |
| 204 |
120 |
return; |
| 205 |
|
|
| 206 |
40 |
p = strchr(p, ' '); |
| 207 |
|
|
| 208 |
40 |
if (p == NULL) |
| 209 |
0 |
return; |
| 210 |
|
|
| 211 |
40 |
vsl_ts = vmax_t(double, vsl_ts, strtod(p + 1, NULL)); |
| 212 |
160 |
} |
| 213 |
|
|
| 214 |
|
static void |
| 215 |
160 |
delorean(void) |
| 216 |
|
{ |
| 217 |
|
int i; |
| 218 |
160 |
double t = VTIM_mono(); |
| 219 |
|
|
| 220 |
160 |
if (vsl_t0 == 0) |
| 221 |
160 |
vsl_to = vsl_t0 = vsl_ts; |
| 222 |
|
|
| 223 |
160 |
assert(t > t0); |
| 224 |
160 |
vsl_to = vsl_t0 + (t - t0) * timebend; |
| 225 |
|
|
| 226 |
160 |
if (vsl_ts > vsl_to) { |
| 227 |
0 |
double when = VTIM_real() + vsl_ts - vsl_to; |
| 228 |
0 |
struct timespec ts = VTIM_timespec(when); |
| 229 |
0 |
i = pthread_cond_timedwait(&timebend_cv, &mtx, &ts); |
| 230 |
0 |
assert(i == 0 || i == ETIMEDOUT); |
| 231 |
0 |
} |
| 232 |
160 |
} |
| 233 |
|
|
| 234 |
|
static int v_matchproto_ (VSLQ_dispatch_f) |
| 235 |
240 |
accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], |
| 236 |
|
void *priv) |
| 237 |
|
{ |
| 238 |
|
int i, tag, skip, match, hit; |
| 239 |
|
unsigned u; |
| 240 |
240 |
double value = 0; |
| 241 |
|
struct VSL_transaction *tr; |
| 242 |
|
const char *tsp; |
| 243 |
|
enum vsl_status stat; |
| 244 |
|
|
| 245 |
240 |
(void)vsl; |
| 246 |
240 |
(void)priv; |
| 247 |
|
|
| 248 |
480 |
for (tr = pt[0]; tr != NULL; tr = *++pt) { |
| 249 |
240 |
if (VSIG_int || VSIG_term || VSIG_hup) |
| 250 |
0 |
return (-1); |
| 251 |
|
|
| 252 |
240 |
if (tr->reason == VSL_r_esi) { |
| 253 |
|
/* Skip ESI requests */ |
| 254 |
0 |
continue; |
| 255 |
|
} |
| 256 |
|
|
| 257 |
240 |
hit = 0; |
| 258 |
240 |
skip = 0; |
| 259 |
240 |
match = 0; |
| 260 |
240 |
tsp = NULL; |
| 261 |
7400 |
while (skip == 0) { |
| 262 |
7400 |
stat = VSL_Next(tr->c); |
| 263 |
7400 |
if (stat == vsl_e_overrun) { |
| 264 |
|
/* need to skip forward */ |
| 265 |
0 |
PTOK(pthread_mutex_lock(&mtx)); |
| 266 |
0 |
vsl_to = vsl_t0 = vsl_ts = 0; |
| 267 |
0 |
t0 = VTIM_mono(); |
| 268 |
0 |
PTOK(pthread_mutex_unlock(&mtx)); |
| 269 |
0 |
break; |
| 270 |
|
} |
| 271 |
7400 |
if (stat != vsl_more) |
| 272 |
240 |
break; |
| 273 |
|
|
| 274 |
|
/* get the value we want and register if it's a hit */ |
| 275 |
7160 |
tag = VSL_TAG(tr->c->rec.ptr); |
| 276 |
|
|
| 277 |
7160 |
if (VSL_tagflags[tag]) |
| 278 |
800 |
continue; |
| 279 |
|
|
| 280 |
6360 |
switch (tag) { |
| 281 |
|
case SLT_Hit: |
| 282 |
40 |
hit = 1; |
| 283 |
40 |
break; |
| 284 |
|
case SLT_VCL_return: |
| 285 |
480 |
if (!strcasecmp(VSL_CDATA(tr->c->rec.ptr), |
| 286 |
480 |
"restart") || |
| 287 |
480 |
!strcasecmp(VSL_CDATA(tr->c->rec.ptr), |
| 288 |
|
"retry")) |
| 289 |
0 |
skip = 1; |
| 290 |
480 |
break; |
| 291 |
|
case SLT_Timestamp: |
| 292 |
920 |
tsp = VSL_CDATA(tr->c->rec.ptr); |
| 293 |
|
/* FALLTHROUGH */ |
| 294 |
|
default: |
| 295 |
5840 |
if (tag != match_tag) |
| 296 |
5160 |
break; |
| 297 |
|
|
| 298 |
680 |
if (active_profile->prefix && |
| 299 |
1280 |
strncmp(VSL_CDATA(tr->c->rec.ptr), |
| 300 |
640 |
active_profile->prefix, |
| 301 |
1280 |
strlen(active_profile->prefix)) != 0) |
| 302 |
520 |
break; |
| 303 |
|
|
| 304 |
320 |
i = sscanf(VSL_CDATA(tr->c->rec.ptr), |
| 305 |
160 |
format, &value); |
| 306 |
160 |
if (i != 1) |
| 307 |
0 |
break; |
| 308 |
160 |
match = 1; |
| 309 |
160 |
break; |
| 310 |
|
} |
| 311 |
|
} |
| 312 |
|
|
| 313 |
240 |
if (skip || !match || value <= 0) |
| 314 |
80 |
continue; |
| 315 |
|
|
| 316 |
|
/* select bucket */ |
| 317 |
160 |
i = vlimit_t(int, lround(HIST_RES * log(value) / log_ten), |
| 318 |
160 |
hist_low * HIST_RES, hist_high * HIST_RES - 1) - |
| 319 |
160 |
hist_low * HIST_RES; |
| 320 |
160 |
assert(i >= 0); |
| 321 |
160 |
assert((unsigned)i < hist_buckets); |
| 322 |
|
|
| 323 |
160 |
PTOK(pthread_mutex_lock(&mtx)); |
| 324 |
|
|
| 325 |
|
/* |
| 326 |
|
* only parse the last tsp seen in this transaction - |
| 327 |
|
* it should be the latest. |
| 328 |
|
*/ |
| 329 |
160 |
if (tsp) |
| 330 |
160 |
upd_vsl_ts(tsp); |
| 331 |
|
|
| 332 |
|
/* phase out old data */ |
| 333 |
160 |
if (nhist == HIST_N) { |
| 334 |
0 |
u = rr_hist[next_hist]; |
| 335 |
0 |
if (u >= hist_buckets) { |
| 336 |
0 |
u -= hist_buckets; |
| 337 |
0 |
assert(u < hist_buckets); |
| 338 |
0 |
assert(bucket_hit[u] > 0); |
| 339 |
0 |
bucket_hit[u]--; |
| 340 |
0 |
} else { |
| 341 |
0 |
assert(bucket_miss[u] > 0); |
| 342 |
0 |
bucket_miss[u]--; |
| 343 |
|
} |
| 344 |
0 |
} else { |
| 345 |
160 |
++nhist; |
| 346 |
|
} |
| 347 |
|
|
| 348 |
|
/* phase in new data */ |
| 349 |
160 |
if (hit) { |
| 350 |
40 |
bucket_hit[i]++; |
| 351 |
40 |
rr_hist[next_hist] = i + hist_buckets; |
| 352 |
40 |
} else { |
| 353 |
120 |
bucket_miss[i]++; |
| 354 |
120 |
rr_hist[next_hist] = i; |
| 355 |
|
} |
| 356 |
160 |
if (++next_hist == HIST_N) { |
| 357 |
0 |
next_hist = 0; |
| 358 |
0 |
} |
| 359 |
160 |
if (vsl_ts >= vsl_to) |
| 360 |
160 |
delorean(); |
| 361 |
160 |
PTOK(pthread_mutex_unlock(&mtx)); |
| 362 |
160 |
} |
| 363 |
240 |
return (0); |
| 364 |
240 |
} |
| 365 |
|
|
| 366 |
|
static void * v_matchproto_(pthread_t) |
| 367 |
120 |
do_curses(void *arg) |
| 368 |
|
{ |
| 369 |
|
int ch; |
| 370 |
120 |
(void)arg; |
| 371 |
|
|
| 372 |
120 |
(void)initscr(); |
| 373 |
120 |
AC(raw()); |
| 374 |
120 |
AC(noecho()); |
| 375 |
120 |
AC(nonl()); |
| 376 |
120 |
AC(intrflush(stdscr, FALSE)); |
| 377 |
120 |
IC(curs_set(0)); |
| 378 |
120 |
AC(erase()); |
| 379 |
46299 |
while (!VSIG_int && !VSIG_term && !VSIG_hup) { |
| 380 |
|
|
| 381 |
46179 |
AC(erase()); |
| 382 |
46179 |
PTOK(pthread_mutex_lock(&mtx)); |
| 383 |
46179 |
update(); |
| 384 |
46179 |
PTOK(pthread_mutex_unlock(&mtx)); |
| 385 |
46179 |
AC(refresh()); |
| 386 |
|
|
| 387 |
46179 |
assert(ms_delay > 0); |
| 388 |
46179 |
timeout(ms_delay); |
| 389 |
46179 |
switch ((ch = getch())) { |
| 390 |
|
case ERR: |
| 391 |
45219 |
break; |
| 392 |
|
#ifdef KEY_RESIZE |
| 393 |
|
case KEY_RESIZE: |
| 394 |
40 |
AC(erase()); |
| 395 |
40 |
break; |
| 396 |
|
#endif |
| 397 |
|
case '\014': /* Ctrl-L */ |
| 398 |
|
case '\024': /* Ctrl-T */ |
| 399 |
40 |
AC(redrawwin(stdscr)); |
| 400 |
40 |
AC(refresh()); |
| 401 |
40 |
break; |
| 402 |
|
case '\032': /* Ctrl-Z */ |
| 403 |
0 |
AC(endwin()); |
| 404 |
0 |
AZ(raise(SIGTSTP)); |
| 405 |
0 |
break; |
| 406 |
|
case '\003': /* Ctrl-C */ |
| 407 |
|
case '\021': /* Ctrl-Q */ |
| 408 |
|
case 'Q': |
| 409 |
|
case 'q': |
| 410 |
40 |
AZ(raise(SIGINT)); |
| 411 |
40 |
break; |
| 412 |
|
case '0': |
| 413 |
|
case '1': |
| 414 |
|
case '2': |
| 415 |
|
case '3': |
| 416 |
|
case '4': |
| 417 |
|
case '5': |
| 418 |
|
case '6': |
| 419 |
|
case '7': |
| 420 |
|
case '8': |
| 421 |
|
case '9': |
| 422 |
80 |
ms_delay = 1000U << (ch - '0'); |
| 423 |
80 |
break; |
| 424 |
|
case '+': |
| 425 |
480 |
ms_delay = vmax(ms_delay >> 1, 1U); |
| 426 |
480 |
break; |
| 427 |
|
case '-': |
| 428 |
80 |
ms_delay *= 2; |
| 429 |
80 |
break; |
| 430 |
|
case '>': |
| 431 |
|
case '<': |
| 432 |
|
/* see below */ |
| 433 |
160 |
break; |
| 434 |
|
default: |
| 435 |
40 |
AC(beep()); |
| 436 |
40 |
break; |
| 437 |
|
} |
| 438 |
|
|
| 439 |
46179 |
if (ch == '<' || ch == '>') { |
| 440 |
160 |
PTOK(pthread_mutex_lock(&mtx)); |
| 441 |
160 |
vsl_to = vsl_t0 = vsl_ts; |
| 442 |
160 |
t0 = VTIM_mono(); |
| 443 |
160 |
if (timebend == 0) |
| 444 |
40 |
timebend = 1; |
| 445 |
120 |
else if (ch == '<') |
| 446 |
80 |
timebend /= 2; |
| 447 |
|
else |
| 448 |
40 |
timebend *= 2; |
| 449 |
160 |
PTOK(pthread_cond_broadcast(&timebend_cv)); |
| 450 |
160 |
PTOK(pthread_mutex_unlock(&mtx)); |
| 451 |
160 |
} |
| 452 |
|
} |
| 453 |
120 |
AC(endwin()); |
| 454 |
120 |
return (NULL); |
| 455 |
|
} |
| 456 |
|
|
| 457 |
|
/*--------------------------------------------------------------------*/ |
| 458 |
|
|
| 459 |
|
static void v_noreturn_ |
| 460 |
200 |
profile_error(const char *s) |
| 461 |
|
{ |
| 462 |
400 |
fprintf(stderr, "-P: '%s' is not a valid" |
| 463 |
200 |
" profile name or definition\n", s); |
| 464 |
200 |
exit(1); |
| 465 |
|
} |
| 466 |
|
|
| 467 |
|
int |
| 468 |
760 |
main(int argc, char **argv) |
| 469 |
|
{ |
| 470 |
|
int i; |
| 471 |
|
char *colon; |
| 472 |
760 |
const char *ptag, *profile = "responsetime"; |
| 473 |
|
pthread_t thr; |
| 474 |
|
int fnum; |
| 475 |
760 |
struct profile cli_p = {0}; |
| 476 |
|
|
| 477 |
760 |
vut = VUT_InitProg(argc, argv, &vopt_spec); |
| 478 |
760 |
AN(vut); |
| 479 |
760 |
PTOK(pthread_cond_init(&timebend_cv, NULL)); |
| 480 |
|
|
| 481 |
1120 |
while ((i = getopt(argc, argv, vopt_spec.vopt_optstring)) != -1) { |
| 482 |
880 |
switch (i) { |
| 483 |
|
case 'h': |
| 484 |
|
/* Usage help */ |
| 485 |
40 |
VUT_Usage(vut, &vopt_spec, 0); |
| 486 |
|
case 'p': |
| 487 |
120 |
ms_delay = lround(1e3 * strtod(optarg, NULL)); |
| 488 |
120 |
if (ms_delay == 0) |
| 489 |
80 |
VUT_Error(vut, 1, "-p: invalid '%s'", optarg); |
| 490 |
40 |
break; |
| 491 |
|
case 'P': |
| 492 |
440 |
colon = strchr(optarg, ':'); |
| 493 |
|
/* no colon, take the profile as a name */ |
| 494 |
440 |
if (colon == NULL) { |
| 495 |
80 |
profile = optarg; |
| 496 |
80 |
break; |
| 497 |
|
} |
| 498 |
|
/* else check if valid definition */ |
| 499 |
360 |
if (colon == optarg + 1 && (*optarg == 'b' || |
| 500 |
0 |
*optarg == 'c' || *optarg == 'E')) { |
| 501 |
280 |
cli_p.VSL_arg = *optarg; |
| 502 |
280 |
ptag = colon + 1; |
| 503 |
280 |
colon = strchr(colon + 1, ':'); |
| 504 |
280 |
if (colon == NULL) |
| 505 |
40 |
profile_error(optarg); |
| 506 |
240 |
} else { |
| 507 |
80 |
ptag = optarg; |
| 508 |
80 |
cli_p.VSL_arg = 'c'; |
| 509 |
|
} |
| 510 |
|
|
| 511 |
320 |
assert(colon); |
| 512 |
|
|
| 513 |
320 |
match_tag = VSL_Name2Tag(ptag, colon - ptag); |
| 514 |
320 |
if (match_tag < 0) |
| 515 |
80 |
VUT_Error(vut, 1, |
| 516 |
|
"-P: '%s' is not a valid tag name", |
| 517 |
40 |
optarg); |
| 518 |
|
|
| 519 |
280 |
if (VSL_tagflags[match_tag]) |
| 520 |
80 |
VUT_Error(vut, 1, |
| 521 |
|
"-P: '%s' is an unsafe or binary record", |
| 522 |
40 |
optarg); |
| 523 |
|
|
| 524 |
240 |
cli_p.prefix = colon + 1; |
| 525 |
|
|
| 526 |
240 |
colon = strchr(colon + 1, ':'); |
| 527 |
240 |
if (colon == NULL) |
| 528 |
40 |
profile_error(optarg); |
| 529 |
|
|
| 530 |
200 |
*colon = '\0'; |
| 531 |
200 |
if (*cli_p.prefix == '\0') |
| 532 |
160 |
cli_p.prefix = NULL; |
| 533 |
|
|
| 534 |
200 |
if (sscanf(colon + 1, "%d", &cli_p.field) != 1) |
| 535 |
80 |
profile_error(optarg); |
| 536 |
|
|
| 537 |
120 |
cli_p.name = "custom"; |
| 538 |
120 |
cli_p.tag = (enum VSL_tag_e)match_tag; |
| 539 |
120 |
cli_p.hist_low = -6; |
| 540 |
120 |
cli_p.hist_high = 3; |
| 541 |
120 |
profile = NULL; |
| 542 |
120 |
active_profile = &cli_p; |
| 543 |
|
|
| 544 |
120 |
colon = strchr(colon + 1, ':'); |
| 545 |
120 |
if (colon == NULL) |
| 546 |
40 |
break; |
| 547 |
|
|
| 548 |
240 |
if (sscanf(colon + 1, "%d:%d", &cli_p.hist_low, |
| 549 |
160 |
&cli_p.hist_high) != 2) |
| 550 |
40 |
profile_error(optarg); |
| 551 |
|
|
| 552 |
40 |
break; |
| 553 |
|
case 'B': |
| 554 |
120 |
timebend = strtod(optarg, NULL); |
| 555 |
120 |
if (timebend == 0) |
| 556 |
80 |
VUT_Error(vut, 1, |
| 557 |
|
"-B: being able to bend time does not" |
| 558 |
|
" mean we can stop it" |
| 559 |
40 |
" (invalid factor '%s')", optarg); |
| 560 |
80 |
if (timebend < 0) |
| 561 |
80 |
VUT_Error(vut, 1, |
| 562 |
|
"-B: being able to bend time does not" |
| 563 |
|
" mean we can make it go backwards" |
| 564 |
40 |
" (invalid factor '%s')", optarg); |
| 565 |
40 |
break; |
| 566 |
|
default: |
| 567 |
160 |
if (!VUT_Arg(vut, i, optarg)) |
| 568 |
40 |
VUT_Usage(vut, &vopt_spec, 1); |
| 569 |
120 |
} |
| 570 |
|
} |
| 571 |
|
|
| 572 |
240 |
if (optind != argc) |
| 573 |
40 |
VUT_Usage(vut, &vopt_spec, 1); |
| 574 |
|
|
| 575 |
|
/* Check for valid grouping mode */ |
| 576 |
200 |
assert(vut->g_arg < VSL_g__MAX); |
| 577 |
200 |
if (vut->g_arg != VSL_g_vxid && vut->g_arg != VSL_g_request) |
| 578 |
80 |
VUT_Error(vut, 1, "Invalid grouping mode: %s" |
| 579 |
|
" (only vxid and request are supported)", |
| 580 |
40 |
VSLQ_grouping[vut->g_arg]); |
| 581 |
|
|
| 582 |
160 |
if (profile) { |
| 583 |
600 |
for (active_profile = profiles; active_profile->name; |
| 584 |
480 |
active_profile++) { |
| 585 |
560 |
if (strcmp(active_profile->name, profile) == 0) |
| 586 |
80 |
break; |
| 587 |
480 |
} |
| 588 |
120 |
} |
| 589 |
160 |
AN(active_profile); |
| 590 |
160 |
if (!active_profile->name) |
| 591 |
40 |
VUT_Error(vut, 1, "-P: No such profile '%s'", profile); |
| 592 |
|
|
| 593 |
120 |
assert(active_profile->VSL_arg == 'b' || |
| 594 |
|
active_profile->VSL_arg == 'c' || |
| 595 |
|
active_profile->VSL_arg == 'E'); |
| 596 |
120 |
assert(VUT_Arg(vut, active_profile->VSL_arg, NULL)); |
| 597 |
120 |
match_tag = active_profile->tag; |
| 598 |
120 |
fnum = active_profile->field; |
| 599 |
120 |
hist_low = active_profile->hist_low; |
| 600 |
120 |
hist_high = active_profile->hist_high; |
| 601 |
|
|
| 602 |
120 |
hist_range = hist_high - hist_low; |
| 603 |
120 |
hist_buckets = hist_range * HIST_RES; |
| 604 |
120 |
bucket_hit = calloc(hist_buckets, sizeof *bucket_hit); |
| 605 |
120 |
bucket_miss = calloc(hist_buckets, sizeof *bucket_miss); |
| 606 |
|
|
| 607 |
120 |
if (timebend > 0) |
| 608 |
40 |
t0 = VTIM_mono(); |
| 609 |
|
|
| 610 |
120 |
format = malloc(4L * fnum); |
| 611 |
120 |
AN(format); |
| 612 |
440 |
for (i = 0; i < fnum - 1; i++) |
| 613 |
320 |
strcpy(format + 4 * i, "%*s "); |
| 614 |
120 |
strcpy(format + 4 * (fnum - 1), "%lf"); |
| 615 |
|
|
| 616 |
120 |
log_ten = log(10.0); |
| 617 |
|
|
| 618 |
120 |
VUT_Setup(vut); |
| 619 |
120 |
if (vut->vsm) |
| 620 |
120 |
ident = VSM_Dup(vut->vsm, "Arg", "-i"); |
| 621 |
|
else |
| 622 |
0 |
ident = strdup(""); |
| 623 |
120 |
PTOK(pthread_create(&thr, NULL, do_curses, NULL)); |
| 624 |
120 |
vut->dispatch_f = accumulate; |
| 625 |
120 |
vut->dispatch_priv = NULL; |
| 626 |
120 |
(void)VUT_Main(vut); |
| 627 |
120 |
end_of_file = 1; |
| 628 |
120 |
PTOK(pthread_join(thr, NULL)); |
| 629 |
120 |
VUT_Fini(&vut); |
| 630 |
120 |
exit(0); |
| 631 |
|
} |